This commit is contained in:
xds
2025-10-16 15:06:20 +03:00
commit 040da34ff7
78 changed files with 3934 additions and 0 deletions

View File

@@ -0,0 +1,149 @@
package space.luminic.finance.services
import com.mongodb.client.model.Aggregates.sort
import kotlinx.coroutines.reactive.awaitFirst
import kotlinx.coroutines.reactive.awaitFirstOrNull
import kotlinx.coroutines.reactive.awaitSingle
import kotlinx.coroutines.reactive.awaitSingleOrNull
import org.bson.types.ObjectId
import org.springframework.data.domain.Sort
import org.springframework.data.mongodb.core.ReactiveMongoTemplate
import org.springframework.data.mongodb.core.aggregation.Aggregation.*
import org.springframework.data.mongodb.core.aggregation.AggregationOperation
import org.springframework.data.mongodb.core.aggregation.ArrayOperators
import org.springframework.data.mongodb.core.aggregation.ConvertOperators
import org.springframework.data.mongodb.core.aggregation.VariableOperators
import org.springframework.data.mongodb.core.query.Criteria
import org.springframework.stereotype.Service
import space.luminic.finance.dtos.SpaceDTO
import space.luminic.finance.models.Budget
import space.luminic.finance.models.NotFoundException
import space.luminic.finance.models.Space
import space.luminic.finance.models.User
import space.luminic.finance.repos.SpaceRepo
@Service
class SpaceServiceImpl(
private val authService: AuthService,
private val spaceRepo: SpaceRepo,
private val mongoTemplate: ReactiveMongoTemplate,
) : SpaceService {
private fun basicAggregation(user: User): List<AggregationOperation> {
val addFieldsAsOJ = addFields()
.addField("createdByOI")
.withValue(ConvertOperators.valueOf("createdById").convertToObjectId())
.addField("updatedByOI")
.withValue(ConvertOperators.valueOf("updatedById").convertToObjectId())
.build()
val lookupCreatedBy = lookup("users", "createdByOI", "_id", "createdBy")
val unwindCreatedBy = unwind("createdBy")
val lookupUpdatedBy = lookup("users", "updatedByOI", "_id", "updatedBy")
val unwindUpdatedBy = unwind("updatedBy")
val matchCriteria = mutableListOf<Criteria>()
matchCriteria.add(
Criteria().orOperator(
Criteria.where("ownerId").`is`(user.id),
Criteria.where("participantsIds").`is`(user.id)
)
)
matchCriteria.add(Criteria.where("isDeleted").`is`(false))
val matchStage = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
return listOf(addFieldsAsOJ, lookupCreatedBy, unwindCreatedBy, lookupUpdatedBy, unwindUpdatedBy, matchStage)
}
private fun ownerAndParticipantsLookups(): List<AggregationOperation>{
val addOwnerAsOJ = addFields()
.addField("ownerIdAsObjectId")
.withValue(ConvertOperators.valueOf("ownerId").convertToObjectId())
.addField("participantsIdsAsObjectId")
.withValue(
VariableOperators.Map.itemsOf("participantsIds")
.`as`("id")
.andApply(
ConvertOperators.valueOf("$\$id").convertToObjectId()
)
)
.build()
val lookupOwner = lookup("users", "ownerIdAsObjectId", "_id", "owner")
val unwindOwner = unwind("owner")
val lookupUsers = lookup("users", "participantsIdsAsObjectId", "_id", "participants")
return listOf(addOwnerAsOJ, lookupOwner, unwindOwner, lookupUsers)
}
override suspend fun checkSpace(spaceId: String): Space {
val user = authService.getSecurityUser()
val space = getSpace(spaceId)
// Проверяем доступ пользователя к пространству
return if (space.participants!!.none { it.id.toString() == user.id }) {
throw IllegalArgumentException("User does not have access to this Space")
} else space
}
override suspend fun getSpaces(): List<Space> {
val user = authService.getSecurityUser()
val basicAggregation = basicAggregation(user)
val ownerAndParticipantsLookup = ownerAndParticipantsLookups()
val sort = sort(Sort.by(Sort.Direction.DESC, "createdAt"))
val aggregation = newAggregation(
*basicAggregation.toTypedArray(),
*ownerAndParticipantsLookup.toTypedArray(),
sort,
)
return mongoTemplate.aggregate(aggregation, "spaces", Space::class.java).collectList().awaitSingle()
}
override suspend fun getSpace(id: String): Space {
val user = authService.getSecurityUser()
val basicAggregation = basicAggregation(user)
val ownerAndParticipantsLookup = ownerAndParticipantsLookups()
val aggregation = newAggregation(
*basicAggregation.toTypedArray(),
*ownerAndParticipantsLookup.toTypedArray(),
)
return mongoTemplate.aggregate(aggregation, "spaces", Space::class.java).awaitFirstOrNull()
?: throw NotFoundException("Space not found")
}
override suspend fun createSpace(space: SpaceDTO.CreateSpaceDTO): Space {
val owner = authService.getSecurityUser()
val createdSpace = Space(
name = space.name,
ownerId = owner.id!!,
participantsIds = listOf(owner.id!!),
)
createdSpace.owner = owner
createdSpace.participants?.toMutableList()?.add(owner)
val savedSpace = spaceRepo.save(createdSpace).awaitSingle()
return savedSpace
}
override suspend fun updateSpace(spaceId: String, space: SpaceDTO.UpdateSpaceDTO): Space {
val existingSpace = spaceRepo.findById(spaceId).awaitFirstOrNull() ?: throw NotFoundException("Space not found")
val updatedSpace = existingSpace.copy(
name = space.name,
)
updatedSpace.owner = existingSpace.owner
updatedSpace.participants = existingSpace.participants
return spaceRepo.save(updatedSpace).awaitFirst()
}
override suspend fun deleteSpace(spaceId: String) {
val space = spaceRepo.findById(spaceId).awaitFirstOrNull() ?: throw NotFoundException("Space not found")
space.isDeleted = true
spaceRepo.save(space).awaitFirst()
}
}