init
This commit is contained in:
@@ -1,14 +0,0 @@
|
||||
package space.luminic.finance.services
|
||||
|
||||
import space.luminic.finance.dtos.AccountDTO
|
||||
import space.luminic.finance.models.Account
|
||||
import space.luminic.finance.models.Transaction
|
||||
|
||||
interface AccountService {
|
||||
suspend fun getAccounts(spaceId: String): List<Account>
|
||||
suspend fun getAccount(spaceId: String, accountId: String): Account
|
||||
suspend fun getAccountTransactions(spaceId: String, accountId: String): List<Transaction>
|
||||
suspend fun createAccount(spaceId: String, account: AccountDTO.CreateAccountDTO): Account
|
||||
suspend fun updateAccount(spaceId: String, account: AccountDTO.UpdateAccountDTO): Account
|
||||
suspend fun deleteAccount(spaceId: String, accountId: String)
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
package space.luminic.finance.services
|
||||
|
||||
import kotlinx.coroutines.reactive.awaitSingle
|
||||
import org.bson.Document
|
||||
import org.bson.types.ObjectId
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.addFields
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.lookup
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.match
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.unwind
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationOperation
|
||||
import org.springframework.data.mongodb.core.aggregation.ConvertOperators
|
||||
import org.springframework.data.mongodb.core.aggregation.LookupOperation
|
||||
import org.springframework.data.mongodb.core.query.Criteria
|
||||
import org.springframework.stereotype.Service
|
||||
import space.luminic.finance.dtos.AccountDTO
|
||||
import space.luminic.finance.models.Account
|
||||
import space.luminic.finance.models.Transaction
|
||||
import space.luminic.finance.repos.AccountRepo
|
||||
|
||||
@Service
|
||||
class AccountServiceImpl(
|
||||
private val accountRepo: AccountRepo,
|
||||
private val mongoTemplate: ReactiveMongoTemplate,
|
||||
private val spaceService: SpaceService,
|
||||
private val transactionService: TransactionService
|
||||
): AccountService {
|
||||
|
||||
private fun basicAggregation(spaceId: String): 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.where("spaceId").`is`(spaceId))
|
||||
matchCriteria.add(Criteria.where("isDeleted").`is`(false))
|
||||
val matchStage = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
|
||||
|
||||
return listOf(addFieldsAsOJ, lookupCreatedBy, unwindCreatedBy, lookupUpdatedBy, unwindUpdatedBy, matchStage)
|
||||
}
|
||||
|
||||
|
||||
override suspend fun getAccounts(spaceId: String): List<Account> {
|
||||
val basicAggregation = basicAggregation(spaceId)
|
||||
val aggregation = newAggregation(*basicAggregation.toTypedArray())
|
||||
return mongoTemplate.aggregate(aggregation, "accounts", Account::class.java)
|
||||
.collectList()
|
||||
.awaitSingle()
|
||||
}
|
||||
|
||||
override suspend fun getAccount(
|
||||
spaceId: String,
|
||||
accountId: String
|
||||
): Account {
|
||||
val basicAggregation = basicAggregation(spaceId)
|
||||
val matchStage = match (Criteria.where("_id").`is`(ObjectId(accountId)))
|
||||
val aggregation = newAggregation(matchStage, *basicAggregation.toTypedArray())
|
||||
return mongoTemplate.aggregate(aggregation, "accounts", Account::class.java)
|
||||
.awaitSingle()
|
||||
}
|
||||
|
||||
override suspend fun getAccountTransactions(
|
||||
spaceId: String,
|
||||
accountId: String
|
||||
): List<Transaction> {
|
||||
val space = spaceService.checkSpace(spaceId)
|
||||
val filter = TransactionService.TransactionsFilter(
|
||||
accountId = accountId,
|
||||
)
|
||||
return transactionService.getTransactions(spaceId, filter, "date", "ASC")
|
||||
}
|
||||
|
||||
override suspend fun createAccount(
|
||||
spaceId: String,
|
||||
account: AccountDTO.CreateAccountDTO
|
||||
): Account {
|
||||
val createdAccount = Account(
|
||||
type = account.type,
|
||||
spaceId = spaceId,
|
||||
name = account.name,
|
||||
currencyCode = account.currencyCode,
|
||||
amount = account.amount,
|
||||
goalId = account.goalId,
|
||||
)
|
||||
return accountRepo.save(createdAccount).awaitSingle()
|
||||
}
|
||||
|
||||
override suspend fun updateAccount(
|
||||
spaceId: String,
|
||||
account: AccountDTO.UpdateAccountDTO
|
||||
): Account {
|
||||
val existingAccount = getAccount(spaceId, account.id)
|
||||
val newAccount = existingAccount.copy(
|
||||
name = account.name,
|
||||
type = account.type,
|
||||
currencyCode = account.currencyCode,
|
||||
amount = account.amount,
|
||||
goalId = account.goalId,
|
||||
)
|
||||
return accountRepo.save(newAccount).awaitSingle()
|
||||
}
|
||||
|
||||
override suspend fun deleteAccount(spaceId: String, accountId: String) {
|
||||
val existingAccount = getAccount(spaceId, accountId)
|
||||
|
||||
existingAccount.isDeleted = true
|
||||
accountRepo.save(existingAccount).awaitSingle()
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
package space.luminic.finance.services
|
||||
|
||||
import kotlinx.coroutines.reactive.awaitFirstOrNull
|
||||
import kotlinx.coroutines.reactor.awaitSingle
|
||||
import kotlinx.coroutines.reactor.awaitSingleOrNull
|
||||
import org.springframework.cache.annotation.Cacheable
|
||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder
|
||||
import org.springframework.security.core.context.SecurityContextHolder
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
|
||||
import org.springframework.stereotype.Service
|
||||
@@ -13,14 +10,13 @@ import space.luminic.finance.models.Token
|
||||
import space.luminic.finance.models.User
|
||||
import space.luminic.finance.repos.UserRepo
|
||||
import space.luminic.finance.utils.JWTUtil
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
|
||||
|
||||
@Service
|
||||
class AuthService(
|
||||
private val userRepository: UserRepo,
|
||||
private val userRepo: UserRepo,
|
||||
private val tokenService: TokenService,
|
||||
private val jwtUtil: JWTUtil,
|
||||
private val userService: UserService,
|
||||
@@ -28,18 +24,28 @@ class AuthService(
|
||||
) {
|
||||
private val passwordEncoder = BCryptPasswordEncoder()
|
||||
|
||||
suspend fun getSecurityUser(): User {
|
||||
val securityContextHolder = ReactiveSecurityContextHolder.getContext().awaitSingleOrNull()
|
||||
fun getSecurityUser(): User {
|
||||
val securityContextHolder = SecurityContextHolder.getContext()
|
||||
?: throw AuthException("Authentication failed")
|
||||
val authentication = securityContextHolder.authentication
|
||||
|
||||
val username = authentication.name
|
||||
// Получаем пользователя по имени
|
||||
return userService.getByUsername(username)
|
||||
return userService.getById(username.toInt())
|
||||
}
|
||||
|
||||
suspend fun login(username: String, password: String): String {
|
||||
val user = userRepository.findByUsername(username).awaitFirstOrNull()
|
||||
fun getSecurityUserId(): Int {
|
||||
val securityContextHolder = SecurityContextHolder.getContext()
|
||||
?: throw AuthException("Authentication failed")
|
||||
val authentication = securityContextHolder.authentication
|
||||
|
||||
val username = authentication.name
|
||||
// Получаем пользователя по имени
|
||||
return username.toInt()
|
||||
}
|
||||
|
||||
fun login(username: String, password: String): String {
|
||||
val user = userRepo.findByUsername(username)
|
||||
?: throw UsernameNotFoundException("Пользователь не найден")
|
||||
return if (passwordEncoder.matches(password, user.password)) {
|
||||
val token = jwtUtil.generateToken(user.username)
|
||||
@@ -47,10 +53,7 @@ class AuthService(
|
||||
tokenService.saveToken(
|
||||
token = token,
|
||||
username = username,
|
||||
expiresAt = LocalDateTime.ofInstant(
|
||||
expireAt.toInstant(),
|
||||
ZoneId.systemDefault()
|
||||
)
|
||||
expiresAt = expireAt.toInstant()
|
||||
)
|
||||
token
|
||||
} else {
|
||||
@@ -58,26 +61,23 @@ class AuthService(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun tgLogin(tgId: String): String {
|
||||
fun tgLogin(tgId: String): String {
|
||||
val user =
|
||||
userRepository.findByTgId(tgId).awaitSingleOrNull() ?: throw UsernameNotFoundException("Пользователь не найден")
|
||||
userRepo.findByTgId(tgId) ?: throw UsernameNotFoundException("Пользователь не найден")
|
||||
|
||||
val token = jwtUtil.generateToken(user.username)
|
||||
val expireAt = Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 10)
|
||||
tokenService.saveToken(
|
||||
token = token,
|
||||
username = user.username,
|
||||
expiresAt = LocalDateTime.ofInstant(
|
||||
expireAt.toInstant(),
|
||||
ZoneId.systemDefault()
|
||||
)
|
||||
expiresAt = expireAt.toInstant()
|
||||
)
|
||||
return token
|
||||
|
||||
}
|
||||
|
||||
suspend fun register(username: String, password: String, firstName: String): User {
|
||||
val user = userRepository.findByUsername(username).awaitSingleOrNull()
|
||||
fun register(username: String, password: String, firstName: String): User {
|
||||
val user = userRepo.findByUsername(username)
|
||||
if (user == null) {
|
||||
var newUser = User(
|
||||
username = username,
|
||||
@@ -85,18 +85,18 @@ class AuthService(
|
||||
firstName = firstName,
|
||||
roles = mutableListOf("USER")
|
||||
)
|
||||
newUser = userRepository.save(newUser).awaitSingle()
|
||||
newUser = userRepo.save(newUser)
|
||||
return newUser
|
||||
} else throw IllegalArgumentException("Пользователь уже зарегистрирован")
|
||||
}
|
||||
|
||||
|
||||
@Cacheable(cacheNames = ["tokens"], key = "#token")
|
||||
suspend fun isTokenValid(token: String): User {
|
||||
val tokenDetails = tokenService.getToken(token).awaitFirstOrNull() ?: throw AuthException("Токен не валиден")
|
||||
fun isTokenValid(token: String): User {
|
||||
val tokenDetails = tokenService.getToken(token)
|
||||
when {
|
||||
tokenDetails.status == Token.TokenStatus.ACTIVE && tokenDetails.expiresAt.isAfter(LocalDateTime.now()) -> {
|
||||
return userService.getByUsername(tokenDetails.username)
|
||||
tokenDetails.status == Token.TokenStatus.ACTIVE && tokenDetails.expiresAt.isAfter(Instant.now()) -> {
|
||||
return tokenDetails.user
|
||||
}
|
||||
|
||||
else -> {
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package space.luminic.finance.services
|
||||
|
||||
import space.luminic.finance.dtos.BudgetDTO.*
|
||||
import space.luminic.finance.models.Budget
|
||||
import space.luminic.finance.models.Transaction
|
||||
|
||||
interface BudgetService {
|
||||
|
||||
suspend fun getBudgets(spaceId: String, sortBy: String, sortDirection: String): List<Budget>
|
||||
suspend fun getBudget(spaceId: String, budgetId: String): Budget
|
||||
suspend fun getBudgetTransactions(spaceId: String, budgetId: String): List<Transaction>
|
||||
suspend fun createBudget(spaceId: String, type: Budget.BudgetType, budgetDto: CreateBudgetDTO): Budget
|
||||
suspend fun updateBudget(spaceId: String, budgetDto: UpdateBudgetDTO): Budget
|
||||
suspend fun deleteBudget(spaceId: String, budgetId: String)
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
package space.luminic.finance.services
|
||||
|
||||
import kotlinx.coroutines.reactive.awaitFirstOrNull
|
||||
import kotlinx.coroutines.reactive.awaitSingle
|
||||
import org.bson.Document
|
||||
import org.bson.types.ObjectId
|
||||
import org.springframework.data.domain.Sort
|
||||
import org.springframework.data.domain.Sort.Direction
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.*
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.sort
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationOperation
|
||||
import org.springframework.data.mongodb.core.aggregation.ConvertOperators
|
||||
import org.springframework.data.mongodb.core.aggregation.LookupOperation
|
||||
import org.springframework.data.mongodb.core.aggregation.SetOperation.set
|
||||
import org.springframework.data.mongodb.core.aggregation.UnsetOperation.unset
|
||||
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.BudgetDTO
|
||||
import space.luminic.finance.models.Budget
|
||||
import space.luminic.finance.models.NotFoundException
|
||||
import space.luminic.finance.models.Transaction
|
||||
import space.luminic.finance.repos.BudgetRepo
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Service
|
||||
class BudgetServiceImpl(
|
||||
private val budgetRepo: BudgetRepo,
|
||||
private val authService: AuthService,
|
||||
private val categoryService: CategoryService,
|
||||
private val mongoTemplate: ReactiveMongoTemplate,
|
||||
private val spaceService: SpaceService,
|
||||
private val transactionService: TransactionService,
|
||||
) : BudgetService {
|
||||
|
||||
private fun basicAggregation(spaceId: String): List<AggregationOperation> {
|
||||
|
||||
val unwindCategories = unwind("categories", true)
|
||||
val setCategoryIdOI = set("categories.categoryIdOI")
|
||||
.toValue(ConvertOperators.valueOf("categories.categoryId").convertToObjectId())
|
||||
val lookupCategory = lookup(
|
||||
"categories", // from
|
||||
"categories.categoryIdOI", // localField
|
||||
"_id", // foreignField
|
||||
"joinedCategory" // as
|
||||
)
|
||||
val unwindJoinedCategory = unwind("joinedCategory", true)
|
||||
val setEmbeddedCategory = set("categories.category").toValue("\$joinedCategory")
|
||||
val unsetTemps = unset("joinedCategory", "categories.categoryIdOI")
|
||||
val groupBack: AggregationOperation = AggregationOperation {
|
||||
Document(
|
||||
"\$group", Document()
|
||||
.append("_id", "\$_id")
|
||||
.append("doc", Document("\$first", "\$\$ROOT"))
|
||||
.append("categories", Document("\$push", "\$categories"))
|
||||
)
|
||||
}
|
||||
val setDocCategories: AggregationOperation = AggregationOperation {
|
||||
Document("\$set", Document("doc.categories", "\$categories"))
|
||||
}
|
||||
val replaceRootDoc = replaceRoot("doc")
|
||||
|
||||
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.where("spaceId").`is`(spaceId))
|
||||
matchCriteria.add(Criteria.where("isDeleted").`is`(false))
|
||||
val matchStage = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
|
||||
|
||||
return listOf(matchStage,
|
||||
unwindCategories,
|
||||
setCategoryIdOI,
|
||||
lookupCategory,
|
||||
unwindJoinedCategory,
|
||||
setEmbeddedCategory,
|
||||
unsetTemps,
|
||||
groupBack,
|
||||
setDocCategories,
|
||||
replaceRootDoc,
|
||||
addFieldsAsOJ,
|
||||
lookupCreatedBy,
|
||||
unwindCreatedBy,
|
||||
lookupUpdatedBy,
|
||||
unwindUpdatedBy)
|
||||
}
|
||||
|
||||
|
||||
override suspend fun getBudgets(spaceId: String, sortBy: String, sortDirection: String): List<Budget> {
|
||||
|
||||
require(spaceId.isNotBlank()) { "Space ID must not be blank" }
|
||||
|
||||
val allowedSortFields = setOf("dateFrom", "dateTo", "amount", "categoryName", "createdAt")
|
||||
require(sortBy in allowedSortFields) { "Invalid sort field: $sortBy" }
|
||||
|
||||
val direction = when (sortDirection.uppercase()) {
|
||||
"ASC" -> Direction.ASC
|
||||
"DESC" -> Direction.DESC
|
||||
else -> throw IllegalArgumentException("Sort direction must be 'ASC' or 'DESC'")
|
||||
}
|
||||
val sort = sort(Sort.by(direction, sortBy))
|
||||
val basicAggregation = basicAggregation(spaceId)
|
||||
val aggregation =
|
||||
newAggregation(
|
||||
*basicAggregation.toTypedArray(),
|
||||
sort
|
||||
)
|
||||
|
||||
return mongoTemplate.aggregate(aggregation, "budgets", Budget::class.java)
|
||||
.collectList()
|
||||
.awaitSingle()
|
||||
|
||||
}
|
||||
|
||||
override suspend fun getBudget(spaceId: String, budgetId: String): Budget {
|
||||
val basicAggregation = basicAggregation(spaceId)
|
||||
val matchStage = match(Criteria.where("_id").`is`(ObjectId(budgetId)))
|
||||
val aggregation = newAggregation(matchStage, *basicAggregation.toTypedArray(), )
|
||||
return mongoTemplate.aggregate(aggregation, "budgets", Budget::class.java).awaitFirstOrNull()
|
||||
?: throw NotFoundException("Budget not found")
|
||||
|
||||
}
|
||||
|
||||
override suspend fun getBudgetTransactions(
|
||||
spaceId: String,
|
||||
budgetId: String
|
||||
): List<Transaction> {
|
||||
spaceService.checkSpace(spaceId)
|
||||
val budget = getBudget(spaceId, budgetId)
|
||||
val filter = TransactionService.TransactionsFilter(
|
||||
dateFrom = budget.dateFrom,
|
||||
dateTo = budget.dateTo
|
||||
)
|
||||
return transactionService.getTransactions(spaceId, filter, "date", "ASC")
|
||||
|
||||
}
|
||||
|
||||
|
||||
override suspend fun createBudget(
|
||||
spaceId: String,
|
||||
type: Budget.BudgetType,
|
||||
budgetDto: BudgetDTO.CreateBudgetDTO
|
||||
): Budget {
|
||||
val user = authService.getSecurityUser()
|
||||
val categories = categoryService.getCategories(spaceId)
|
||||
val budget = Budget(
|
||||
spaceId = spaceId,
|
||||
type = type,
|
||||
name = budgetDto.name,
|
||||
description = budgetDto.description,
|
||||
categories = categories.map { Budget.BudgetCategory(it.id!!, BigDecimal.ZERO) },
|
||||
dateFrom = budgetDto.dateFrom,
|
||||
dateTo = budgetDto.dateTo
|
||||
)
|
||||
return budgetRepo.save(budget).awaitSingle()
|
||||
}
|
||||
|
||||
override suspend fun updateBudget(
|
||||
spaceId: String,
|
||||
budgetDto: BudgetDTO.UpdateBudgetDTO
|
||||
): Budget {
|
||||
val budget = getBudget(spaceId, budgetDto.id)
|
||||
budgetDto.name?.let { name -> budget.name = name }
|
||||
budgetDto.description?.let { description -> budget.description = description }
|
||||
budgetDto.dateFrom?.let { dateFrom -> budget.dateFrom = dateFrom }
|
||||
budgetDto.dateTo?.let { dateTo -> budget.dateTo = dateTo }
|
||||
|
||||
return budgetRepo.save(budget).awaitSingle()
|
||||
}
|
||||
|
||||
override suspend fun deleteBudget(spaceId: String, budgetId: String) {
|
||||
val budget = getBudget(spaceId, budgetId)
|
||||
budget.isDeleted = true
|
||||
budgetRepo.save(budget).awaitSingle()
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,13 @@
|
||||
package space.luminic.finance.services
|
||||
|
||||
import space.luminic.finance.dtos.BudgetDTO
|
||||
import space.luminic.finance.dtos.CategoryDTO
|
||||
import space.luminic.finance.models.Category
|
||||
import space.luminic.finance.models.Space
|
||||
|
||||
interface CategoryService {
|
||||
suspend fun getCategories(spaceId: String): List<Category>
|
||||
suspend fun getCategory(spaceId: String, id: String): Category
|
||||
suspend fun createCategory(spaceId: String, category: CategoryDTO.CreateCategoryDTO): Category
|
||||
suspend fun updateCategory(spaceId: String,category: CategoryDTO.UpdateCategoryDTO): Category
|
||||
suspend fun deleteCategory(spaceId: String, id: String)
|
||||
suspend fun createCategoriesForSpace(spaceId: String): List<Category>
|
||||
fun getCategories(spaceId: Int): List<Category>
|
||||
fun getCategory(spaceId: Int, id: Int): Category
|
||||
fun createCategory(spaceId: Int, category: CategoryDTO.CreateCategoryDTO): Category
|
||||
fun createEtalonCategoriesForSpace(spaceId: Int): List<Category>
|
||||
fun updateCategory(spaceId: Int,categoryId:Int, category: CategoryDTO.UpdateCategoryDTO): Category
|
||||
fun deleteCategory(spaceId: Int, id: Int)
|
||||
}
|
||||
@@ -1,108 +1,105 @@
|
||||
package space.luminic.finance.services
|
||||
|
||||
import kotlinx.coroutines.reactive.awaitSingle
|
||||
import org.bson.types.ObjectId
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.addFields
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.lookup
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.match
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.unwind
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationOperation
|
||||
import org.springframework.data.mongodb.core.aggregation.ConvertOperators
|
||||
import org.springframework.data.mongodb.core.query.Criteria
|
||||
import org.springframework.jdbc.core.JdbcTemplate
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Propagation
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import space.luminic.finance.dtos.CategoryDTO
|
||||
import space.luminic.finance.models.Category
|
||||
import space.luminic.finance.models.Space
|
||||
import space.luminic.finance.models.NotFoundException
|
||||
import space.luminic.finance.repos.CategoryEtalonRepo
|
||||
import space.luminic.finance.repos.CategoryRepo
|
||||
import space.luminic.finance.repos.SpaceRepo
|
||||
|
||||
|
||||
@Service
|
||||
class CategoryServiceImpl(
|
||||
private val categoryRepo: CategoryRepo,
|
||||
private val categoryEtalonRepo: CategoryEtalonRepo,
|
||||
private val reactiveMongoTemplate: ReactiveMongoTemplate,
|
||||
private val authService: AuthService,
|
||||
private val spaceRepo: SpaceRepo,
|
||||
private val categoriesRepo: CategoryRepo,
|
||||
private val categoriesEtalonRepo: CategoryEtalonRepo,
|
||||
private val authService: AuthService
|
||||
) : CategoryService {
|
||||
|
||||
private fun basicAggregation(spaceId: String): 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.where("spaceId").`is`(spaceId))
|
||||
matchCriteria.add(Criteria.where("isDeleted").`is`(false))
|
||||
val matchStage = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
|
||||
|
||||
return listOf(addFieldsAsOJ, lookupCreatedBy, unwindCreatedBy, lookupUpdatedBy, unwindUpdatedBy, matchStage)
|
||||
override fun getCategories(spaceId: Int): List<Category> {
|
||||
val userId = authService.getSecurityUserId()
|
||||
val space = spaceRepo.findSpaceById(spaceId, userId)
|
||||
return categoriesRepo.findBySpaceId(spaceId)
|
||||
}
|
||||
|
||||
override suspend fun getCategories(spaceId: String): List<Category> {
|
||||
val basicAggregation = basicAggregation(spaceId)
|
||||
val aggregation = newAggregation(*basicAggregation.toTypedArray())
|
||||
return reactiveMongoTemplate.aggregate(aggregation, "categories", Category::class.java).collectList().awaitSingle()
|
||||
override fun getCategory(spaceId: Int, id: Int): Category {
|
||||
val userId = authService.getSecurityUserId()
|
||||
val space = spaceRepo.findSpaceById(spaceId, userId)
|
||||
return categoriesRepo.findBySpaceIdAndId(spaceId, id)
|
||||
?: throw NotFoundException("Category with id $id not found")
|
||||
}
|
||||
|
||||
override suspend fun getCategory(spaceId: String, id: String): Category {
|
||||
val basicAggregation = basicAggregation(spaceId)
|
||||
val match = match(Criteria.where("_id").`is`(ObjectId(id)))
|
||||
val aggregation = newAggregation(*basicAggregation.toTypedArray(), match)
|
||||
return reactiveMongoTemplate.aggregate(aggregation, "categories", Category::class.java).awaitSingle()
|
||||
}
|
||||
|
||||
|
||||
override suspend fun createCategory(
|
||||
spaceId: String,
|
||||
@Transactional
|
||||
override fun createCategory(
|
||||
spaceId: Int,
|
||||
category: CategoryDTO.CreateCategoryDTO
|
||||
): Category {
|
||||
val createdCategory = Category(
|
||||
spaceId = spaceId,
|
||||
type = category.type,
|
||||
val userId = authService.getSecurityUserId()
|
||||
val space = spaceRepo.findSpaceById(spaceId, userId)
|
||||
val newCategory = Category(
|
||||
space = space,
|
||||
name = category.name,
|
||||
icon = category.icon
|
||||
)
|
||||
return categoryRepo.save(createdCategory).awaitSingle()
|
||||
}
|
||||
|
||||
override suspend fun updateCategory(
|
||||
spaceId: String,
|
||||
category: CategoryDTO.UpdateCategoryDTO
|
||||
): Category {
|
||||
val existingCategory = getCategory(spaceId, category.id)
|
||||
val updatedCategory = existingCategory.copy(
|
||||
type = category.type,
|
||||
name = category.name,
|
||||
description = category.description,
|
||||
icon = category.icon,
|
||||
)
|
||||
return categoryRepo.save(updatedCategory).awaitSingle()
|
||||
return categoriesRepo.create(newCategory, userId)
|
||||
}
|
||||
|
||||
override suspend fun deleteCategory(spaceId: String, id: String) {
|
||||
val existingCategory = getCategory(spaceId, id)
|
||||
existingCategory.isDeleted = true
|
||||
categoryRepo.save(existingCategory).awaitSingle()
|
||||
}
|
||||
|
||||
override suspend fun createCategoriesForSpace(spaceId: String): List<Category> {
|
||||
val etalonCategories = categoryEtalonRepo.findAll().collectList().awaitSingle()
|
||||
val toCreate = etalonCategories.map {
|
||||
Category(
|
||||
spaceId = spaceId,
|
||||
type = it.type,
|
||||
name = it.name,
|
||||
icon = it.icon
|
||||
@Transactional(propagation = Propagation.NESTED)
|
||||
override fun createEtalonCategoriesForSpace(
|
||||
spaceId: Int
|
||||
): List<Category> {
|
||||
val userId = authService.getSecurityUserId()
|
||||
val space = spaceRepo.findSpaceById(spaceId, userId)
|
||||
val categories = categoriesEtalonRepo.findAll()
|
||||
val newCategories = mutableListOf<Category>()
|
||||
categories.forEach { category ->
|
||||
newCategories.add(
|
||||
categoriesRepo.create(
|
||||
Category(
|
||||
space = space,
|
||||
name = category.name,
|
||||
description = category.description,
|
||||
type = category.type,
|
||||
icon = category.icon,
|
||||
), userId
|
||||
)
|
||||
)
|
||||
}
|
||||
return categoryRepo.saveAll(toCreate).collectList().awaitSingle()
|
||||
|
||||
return newCategories
|
||||
}
|
||||
@Transactional
|
||||
override fun updateCategory(
|
||||
spaceId: Int,
|
||||
categoryId: Int,
|
||||
category: CategoryDTO.UpdateCategoryDTO
|
||||
): Category {
|
||||
val userId = authService.getSecurityUserId()
|
||||
val space = spaceRepo.findSpaceById(spaceId, userId)
|
||||
val existingCategory = getCategory(spaceId, categoryId)
|
||||
val newCategory = Category(
|
||||
id = existingCategory.id,
|
||||
space = space,
|
||||
name = category.name,
|
||||
description = category.description,
|
||||
type = category.type,
|
||||
icon = category.icon,
|
||||
isDeleted = existingCategory.isDeleted,
|
||||
createdBy = existingCategory.createdBy,
|
||||
createdAt = existingCategory.createdAt,
|
||||
)
|
||||
return categoriesRepo.update(newCategory, userId)
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun deleteCategory(spaceId: Int, id: Int) {
|
||||
val userId = authService.getSecurityUserId()
|
||||
val space = spaceRepo.findSpaceById(spaceId, userId)
|
||||
categoriesRepo.delete(id)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
//package space.luminic.finance.services
|
||||
//
|
||||
//import kotlinx.coroutines.reactive.awaitSingle
|
||||
//import org.bson.types.ObjectId
|
||||
//import org.springframework.data.mongodb.core.ReactiveMongoTemplate
|
||||
//import org.springframework.data.mongodb.core.aggregation.Aggregation.addFields
|
||||
//import org.springframework.data.mongodb.core.aggregation.Aggregation.lookup
|
||||
//import org.springframework.data.mongodb.core.aggregation.Aggregation.match
|
||||
//import org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation
|
||||
//import org.springframework.data.mongodb.core.aggregation.Aggregation.unwind
|
||||
//import org.springframework.data.mongodb.core.aggregation.AggregationOperation
|
||||
//import org.springframework.data.mongodb.core.aggregation.ConvertOperators
|
||||
//import org.springframework.data.mongodb.core.query.Criteria
|
||||
//import org.springframework.stereotype.Service
|
||||
//import space.luminic.finance.dtos.CategoryDTO
|
||||
//import space.luminic.finance.models.Category
|
||||
//import space.luminic.finance.repos.CategoryEtalonRepo
|
||||
//import space.luminic.finance.repos.CategoryRepo
|
||||
//
|
||||
//@Service
|
||||
//class CategoryServiceMongoImpl(
|
||||
// private val categoryRepo: CategoryRepo,
|
||||
// private val categoryEtalonRepo: CategoryEtalonRepo,
|
||||
// private val reactiveMongoTemplate: ReactiveMongoTemplate,
|
||||
// private val authService: AuthService,
|
||||
//) : CategoryService {
|
||||
//
|
||||
// private fun basicAggregation(spaceId: String): 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.where("spaceId").`is`(spaceId))
|
||||
// matchCriteria.add(Criteria.where("isDeleted").`is`(false))
|
||||
// val matchStage = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
|
||||
//
|
||||
// return listOf(addFieldsAsOJ, lookupCreatedBy, unwindCreatedBy, lookupUpdatedBy, unwindUpdatedBy, matchStage)
|
||||
// }
|
||||
//
|
||||
// override suspend fun getCategories(spaceId: String): List<Category> {
|
||||
// val basicAggregation = basicAggregation(spaceId)
|
||||
// val aggregation = newAggregation(*basicAggregation.toTypedArray())
|
||||
// return reactiveMongoTemplate.aggregate(aggregation, "categories", Category::class.java).collectList().awaitSingle()
|
||||
// }
|
||||
//
|
||||
// override suspend fun getCategory(spaceId: String, id: String): Category {
|
||||
// val basicAggregation = basicAggregation(spaceId)
|
||||
// val match = match(Criteria.where("_id").`is`(ObjectId(id)))
|
||||
// val aggregation = newAggregation(*basicAggregation.toTypedArray(), match)
|
||||
// return reactiveMongoTemplate.aggregate(aggregation, "categories", Category::class.java).awaitSingle()
|
||||
// }
|
||||
//
|
||||
//
|
||||
// override suspend fun createCategory(
|
||||
// spaceId: String,
|
||||
// category: CategoryDTO.CreateCategoryDTO
|
||||
// ): Category {
|
||||
// val createdCategory = Category(
|
||||
// spaceId = spaceId,
|
||||
// type = category.type,
|
||||
// name = category.name,
|
||||
// icon = category.icon
|
||||
// )
|
||||
// return categoryRepo.save(createdCategory).awaitSingle()
|
||||
// }
|
||||
//
|
||||
// override suspend fun updateCategory(
|
||||
// spaceId: String,
|
||||
// category: CategoryDTO.UpdateCategoryDTO
|
||||
// ): Category {
|
||||
// val existingCategory = getCategory(spaceId, category.id)
|
||||
// val updatedCategory = existingCategory.copy(
|
||||
// type = category.type,
|
||||
// name = category.name,
|
||||
// icon = category.icon,
|
||||
// )
|
||||
// return categoryRepo.save(updatedCategory).awaitSingle()
|
||||
// }
|
||||
//
|
||||
// override suspend fun deleteCategory(spaceId: String, id: String) {
|
||||
// val existingCategory = getCategory(spaceId, id)
|
||||
// existingCategory.isDeleted = true
|
||||
// categoryRepo.save(existingCategory).awaitSingle()
|
||||
// }
|
||||
//
|
||||
// override suspend fun createCategoriesForSpace(spaceId: String): List<Category> {
|
||||
// val etalonCategories = categoryEtalonRepo.findAll().collectList().awaitSingle()
|
||||
// val toCreate = etalonCategories.map {
|
||||
// Category(
|
||||
// spaceId = spaceId,
|
||||
// type = it.type,
|
||||
// name = it.name,
|
||||
// icon = it.icon
|
||||
// )
|
||||
// }
|
||||
// return categoryRepo.saveAll(toCreate).collectList().awaitSingle()
|
||||
// }
|
||||
//
|
||||
//
|
||||
//}
|
||||
@@ -1,16 +0,0 @@
|
||||
package space.luminic.finance.services
|
||||
|
||||
import kotlinx.coroutines.reactor.mono
|
||||
import org.springframework.data.domain.ReactiveAuditorAware
|
||||
import org.springframework.stereotype.Component
|
||||
import reactor.core.publisher.Mono
|
||||
|
||||
@Component
|
||||
class CoroutineAuditorAware(
|
||||
private val authService: AuthService
|
||||
) : ReactiveAuditorAware<String> {
|
||||
override fun getCurrentAuditor(): Mono<String> =
|
||||
mono {
|
||||
authService.getSecurityUser().id!!
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,11 @@ import space.luminic.finance.models.CurrencyRate
|
||||
|
||||
interface CurrencyService {
|
||||
|
||||
suspend fun getCurrencies(): List<Currency>
|
||||
suspend fun getCurrency(currencyCode: String): Currency
|
||||
suspend fun createCurrency(currency: CurrencyDTO): Currency
|
||||
suspend fun updateCurrency(currency: CurrencyDTO): Currency
|
||||
suspend fun deleteCurrency(currencyCode: String)
|
||||
suspend fun createCurrencyRate(currencyCode: String): CurrencyRate
|
||||
fun getCurrencies(): List<Currency>
|
||||
fun getCurrency(currencyCode: String): Currency
|
||||
fun createCurrency(currency: CurrencyDTO): Currency
|
||||
fun updateCurrency(currency: CurrencyDTO): Currency
|
||||
fun deleteCurrency(currencyCode: String)
|
||||
fun createCurrencyRate(currencyCode: String): CurrencyRate
|
||||
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
package space.luminic.finance.services
|
||||
|
||||
import kotlinx.coroutines.reactive.awaitSingle
|
||||
import org.springframework.stereotype.Service
|
||||
import space.luminic.finance.dtos.CurrencyDTO
|
||||
import space.luminic.finance.models.Currency
|
||||
import space.luminic.finance.models.CurrencyRate
|
||||
import space.luminic.finance.repos.CurrencyRateRepo
|
||||
import space.luminic.finance.models.NotFoundException
|
||||
import space.luminic.finance.repos.CurrencyRepo
|
||||
import java.math.BigDecimal
|
||||
import java.time.LocalDate
|
||||
@@ -13,39 +12,39 @@ import java.time.LocalDate
|
||||
@Service
|
||||
class CurrencyServiceImpl(
|
||||
private val currencyRepo: CurrencyRepo,
|
||||
private val currencyRateRepo: CurrencyRateRepo
|
||||
) : CurrencyService {
|
||||
|
||||
override suspend fun getCurrencies(): List<Currency> {
|
||||
return currencyRepo.findAll().collectList().awaitSingle()
|
||||
override fun getCurrencies(): List<Currency> {
|
||||
return currencyRepo.findAll()
|
||||
}
|
||||
|
||||
override suspend fun getCurrency(currencyCode: String): Currency {
|
||||
return currencyRepo.findById(currencyCode).awaitSingle()
|
||||
override fun getCurrency(currencyCode: String): Currency {
|
||||
return currencyRepo.findByCode(currencyCode)
|
||||
?: throw NotFoundException("Currency code $currencyCode not found")
|
||||
}
|
||||
|
||||
override suspend fun createCurrency(currency: CurrencyDTO): Currency {
|
||||
val createdCurrency = Currency(currency.code, currency.name, currency.symbol)
|
||||
return currencyRepo.save(createdCurrency).awaitSingle()
|
||||
override fun createCurrency(currency: CurrencyDTO): Currency {
|
||||
val currency = Currency(currency.code, currency.name, currency.code)
|
||||
return currencyRepo.save(currency)
|
||||
}
|
||||
|
||||
override suspend fun updateCurrency(currency: CurrencyDTO): Currency {
|
||||
val existingCurrency = currencyRepo.findById(currency.code).awaitSingle()
|
||||
val newCurrency = existingCurrency.copy(name = currency.name, symbol = currency.symbol)
|
||||
return currencyRepo.save(newCurrency).awaitSingle()
|
||||
}
|
||||
|
||||
override suspend fun deleteCurrency(currencyCode: String) {
|
||||
currencyRepo.deleteById(currencyCode).awaitSingle()
|
||||
}
|
||||
|
||||
override suspend fun createCurrencyRate(currencyCode: String): CurrencyRate {
|
||||
return currencyRateRepo.save(
|
||||
CurrencyRate(
|
||||
currencyCode = currencyCode,
|
||||
rate = BigDecimal(12.0),
|
||||
date = LocalDate.now(),
|
||||
override fun updateCurrency(currency: CurrencyDTO): Currency {
|
||||
getCurrency(currency.code)
|
||||
val updatedCurrency =
|
||||
Currency(
|
||||
code = currency.code,
|
||||
name = currency.name,
|
||||
symbol = currency.symbol
|
||||
)
|
||||
).awaitSingle()
|
||||
return currencyRepo.save(updatedCurrency)
|
||||
}
|
||||
|
||||
override fun deleteCurrency(currencyCode: String) {
|
||||
currencyRepo.delete(currencyCode)
|
||||
}
|
||||
|
||||
override fun createCurrencyRate(currencyCode: String): CurrencyRate {
|
||||
print("createCurrencyRate")
|
||||
val currency = getCurrency(currencyCode)
|
||||
return CurrencyRate(currency = currency, rate = BigDecimal.ZERO, date = LocalDate.now())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//package space.luminic.finance.services
|
||||
//
|
||||
//import kotlinx.coroutines.reactive.awaitSingle
|
||||
//import org.springframework.stereotype.Service
|
||||
//import space.luminic.finance.dtos.CurrencyDTO
|
||||
//import space.luminic.finance.models.Currency
|
||||
//import space.luminic.finance.models.CurrencyRate
|
||||
//import space.luminic.finance.repos.CurrencyRateRepo
|
||||
//import space.luminic.finance.repos.CurrencyRepo
|
||||
//import java.math.BigDecimal
|
||||
//import java.time.LocalDate
|
||||
//
|
||||
//@Service
|
||||
//class CurrencyServiceMongoImpl(
|
||||
// private val currencyRepo: CurrencyRepo,
|
||||
// private val currencyRateRepo: CurrencyRateRepo
|
||||
//) : CurrencyService {
|
||||
//
|
||||
// override suspend fun getCurrencies(): List<Currency> {
|
||||
// return currencyRepo.findAll().collectList().awaitSingle()
|
||||
// }
|
||||
//
|
||||
// override suspend fun getCurrency(currencyCode: String): Currency {
|
||||
// return currencyRepo.findById(currencyCode).awaitSingle()
|
||||
// }
|
||||
//
|
||||
// override suspend fun createCurrency(currency: CurrencyDTO): Currency {
|
||||
// val createdCurrency = Currency(currency.code, currency.name, currency.symbol)
|
||||
// return currencyRepo.save(createdCurrency).awaitSingle()
|
||||
// }
|
||||
//
|
||||
// override suspend fun updateCurrency(currency: CurrencyDTO): Currency {
|
||||
// val existingCurrency = currencyRepo.findById(currency.code).awaitSingle()
|
||||
// val newCurrency = existingCurrency.copy(name = currency.name, symbol = currency.symbol)
|
||||
// return currencyRepo.save(newCurrency).awaitSingle()
|
||||
// }
|
||||
//
|
||||
// override suspend fun deleteCurrency(currencyCode: String) {
|
||||
// currencyRepo.deleteById(currencyCode).awaitSingle()
|
||||
// }
|
||||
//
|
||||
// override suspend fun createCurrencyRate(currencyCode: String): CurrencyRate {
|
||||
// return currencyRateRepo.save(
|
||||
// CurrencyRate(
|
||||
// currencyCode = currencyCode,
|
||||
// rate = BigDecimal(12.0),
|
||||
// date = LocalDate.now(),
|
||||
// )
|
||||
// ).awaitSingle()
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,22 @@
|
||||
package space.luminic.finance.services
|
||||
|
||||
import space.luminic.finance.dtos.GoalDTO
|
||||
import space.luminic.finance.models.Goal
|
||||
|
||||
interface GoalService {
|
||||
fun findAllBySpaceId(spaceId: Int): List<Goal>
|
||||
fun findBySpaceIdAndId(spaceId: Int, id: Int): Goal
|
||||
fun create(spaceId: Int,goal: GoalDTO.CreateGoalDTO): Int
|
||||
fun update(spaceId: Int, goalId: Int, goal: GoalDTO.UpdateGoalDTO)
|
||||
fun delete(spaceId: Int, id: Int)
|
||||
|
||||
fun getComponents(spaceId: Int, goalId: Int): List<Goal.GoalComponent>
|
||||
fun getComponent(spaceId: Int, goalId: Int, id: Int): Goal.GoalComponent?
|
||||
fun createComponent(spaceId: Int, goalId: Int, component: Goal.GoalComponent): Int
|
||||
fun updateComponent(spaceId: Int, goalId: Int, component: Goal.GoalComponent)
|
||||
fun deleteComponent(spaceId: Int, goalId: Int, id: Int)
|
||||
|
||||
fun assignTransaction(spaceId: Int, goalId: Int, transactionId: Int)
|
||||
fun refuseTransaction(spaceId: Int,goalId: Int, transactionId: Int)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package space.luminic.finance.services
|
||||
|
||||
import org.springframework.stereotype.Service
|
||||
import space.luminic.finance.dtos.GoalDTO
|
||||
import space.luminic.finance.models.Goal
|
||||
import space.luminic.finance.models.NotFoundException
|
||||
import space.luminic.finance.repos.GoalRepo
|
||||
import space.luminic.finance.repos.SpaceRepo
|
||||
import space.luminic.finance.repos.TransactionRepo
|
||||
|
||||
@Service
|
||||
class GoalServiceImpl(
|
||||
private val goalRepo: GoalRepo,
|
||||
private val spaceRepo: SpaceRepo,
|
||||
private val authService: AuthService,
|
||||
private val transactionRepo: TransactionRepo
|
||||
) : GoalService {
|
||||
override fun findAllBySpaceId(spaceId: Int): List<Goal> {
|
||||
val userId = authService.getSecurityUserId()
|
||||
spaceRepo.findSpaceById(spaceId, userId)
|
||||
return goalRepo.findAllBySpaceId(spaceId)
|
||||
}
|
||||
|
||||
override fun findBySpaceIdAndId(spaceId: Int, id: Int): Goal {
|
||||
val userId = authService.getSecurityUserId()
|
||||
spaceRepo.findSpaceById(spaceId, userId)
|
||||
return goalRepo.findBySpaceIdAndId(spaceId, userId) ?: throw NotFoundException("Goal $id not found")
|
||||
}
|
||||
|
||||
override fun create(spaceId: Int, goal: GoalDTO.CreateGoalDTO): Int {
|
||||
val userId = authService.getSecurityUserId()
|
||||
spaceRepo.findSpaceById(spaceId, userId)
|
||||
val creatingGoal = Goal(
|
||||
type = goal.type,
|
||||
name = goal.name,
|
||||
amount = goal.amount,
|
||||
untilDate = goal.date
|
||||
)
|
||||
return goalRepo.create(creatingGoal, userId)
|
||||
}
|
||||
|
||||
override fun update(spaceId: Int, goalId: Int, goal: GoalDTO.UpdateGoalDTO) {
|
||||
val userId = authService.getSecurityUserId()
|
||||
spaceRepo.findSpaceById(spaceId, userId)
|
||||
val existingGoal =
|
||||
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
|
||||
val updatedGoal = existingGoal.copy(
|
||||
type = goal.type,
|
||||
name = goal.name,
|
||||
description = goal.description,
|
||||
amount = goal.amount,
|
||||
untilDate = goal.date
|
||||
)
|
||||
goalRepo.update(updatedGoal, userId)
|
||||
}
|
||||
|
||||
override fun delete(spaceId: Int, id: Int) {
|
||||
goalRepo.delete(spaceId, id)
|
||||
}
|
||||
|
||||
override fun getComponents(
|
||||
spaceId: Int,
|
||||
goalId: Int
|
||||
): List<Goal.GoalComponent> {
|
||||
val userId = authService.getSecurityUserId()
|
||||
spaceRepo.findSpaceById(spaceId, userId)
|
||||
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
|
||||
return goalRepo.getComponents(spaceId, goalId)
|
||||
}
|
||||
|
||||
override fun getComponent(
|
||||
spaceId: Int,
|
||||
goalId: Int,
|
||||
id: Int
|
||||
): Goal.GoalComponent? {
|
||||
val userId = authService.getSecurityUserId()
|
||||
spaceRepo.findSpaceById(spaceId, userId)
|
||||
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
|
||||
return goalRepo.getComponent(spaceId, goalId, id)
|
||||
}
|
||||
|
||||
override fun createComponent(
|
||||
spaceId: Int,
|
||||
goalId: Int,
|
||||
component: Goal.GoalComponent
|
||||
): Int {
|
||||
val userId = authService.getSecurityUserId()
|
||||
spaceRepo.findSpaceById(spaceId, userId)
|
||||
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
|
||||
return goalRepo.createComponent(goalId, component, userId)
|
||||
}
|
||||
|
||||
override fun updateComponent(
|
||||
spaceId: Int,
|
||||
goalId: Int,
|
||||
component: Goal.GoalComponent
|
||||
) {
|
||||
val userId = authService.getSecurityUserId()
|
||||
spaceRepo.findSpaceById(spaceId, userId)
|
||||
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
|
||||
val existingComponent = goalRepo.getComponent(spaceId, goalId, component.id!!)
|
||||
?: throw NotFoundException("Component $goalId not found")
|
||||
val updatedComponent = existingComponent.copy(
|
||||
name = component.name,
|
||||
amount = component.amount,
|
||||
isDone = component.isDone,
|
||||
date = component.date
|
||||
)
|
||||
goalRepo.updateComponent(goalId, updatedComponent.id!!, updatedComponent, userId)
|
||||
}
|
||||
|
||||
override fun deleteComponent(spaceId: Int, goalId: Int, id: Int) {
|
||||
val userId = authService.getSecurityUserId()
|
||||
spaceRepo.findSpaceById(spaceId, userId)
|
||||
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
|
||||
goalRepo.getComponent(spaceId, goalId, id) ?: throw NotFoundException("Component $goalId not found")
|
||||
goalRepo.deleteComponent(goalId, id)
|
||||
}
|
||||
|
||||
override fun assignTransaction(spaceId: Int, goalId: Int, transactionId: Int) {
|
||||
val userId = authService.getSecurityUserId()
|
||||
spaceRepo.findSpaceById(spaceId, userId)
|
||||
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
|
||||
transactionRepo.findBySpaceIdAndId(spaceId, transactionId) ?: throw NotFoundException(
|
||||
"Transaction $transactionId not found"
|
||||
)
|
||||
goalRepo.assignTransaction(goalId, transactionId)
|
||||
|
||||
}
|
||||
|
||||
override fun refuseTransaction(spaceId: Int, goalId: Int, transactionId: Int) {
|
||||
val userId = authService.getSecurityUserId()
|
||||
spaceRepo.findSpaceById(spaceId, userId)
|
||||
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
|
||||
transactionRepo.findBySpaceIdAndId(spaceId, transactionId) ?: throw NotFoundException(
|
||||
"Transaction $transactionId not found"
|
||||
)
|
||||
goalRepo.refuseTransaction(goalId, transactionId)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package space.luminic.finance.services
|
||||
|
||||
import space.luminic.finance.dtos.RecurrentOperationDTO
|
||||
import space.luminic.finance.models.RecurrentOperation
|
||||
|
||||
|
||||
interface RecurrentOperationService {
|
||||
fun findBySpaceId(spaceId: Int): List<RecurrentOperation>
|
||||
fun findBySpaceIdAndId(spaceId: Int, id: Int): RecurrentOperation
|
||||
fun create(spaceId: Int, operation: RecurrentOperationDTO.CreateRecurrentOperationDTO): Int
|
||||
fun update(spaceId: Int, operationId: Int, operation: RecurrentOperationDTO.UpdateRecurrentOperationDTO)
|
||||
fun delete(spaceId: Int, id: Int)
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package space.luminic.finance.services
|
||||
|
||||
import org.springframework.stereotype.Service
|
||||
import space.luminic.finance.dtos.RecurrentOperationDTO
|
||||
import space.luminic.finance.models.NotFoundException
|
||||
import space.luminic.finance.models.RecurrentOperation
|
||||
import space.luminic.finance.repos.RecurrentOperationRepo
|
||||
import space.luminic.finance.repos.SpaceRepo
|
||||
|
||||
@Service
|
||||
class RecurrentOperationServiceImpl(
|
||||
private val authService: AuthService,
|
||||
private val spaceRepo: SpaceRepo,
|
||||
private val recurrentOperationRepo: RecurrentOperationRepo,
|
||||
private val categoryService: CategoryService
|
||||
): RecurrentOperationService {
|
||||
override fun findBySpaceId(spaceId: Int): List<RecurrentOperation> {
|
||||
val userId = authService.getSecurityUserId()
|
||||
spaceRepo.findSpaceById(spaceId, userId)
|
||||
return recurrentOperationRepo.findAllBySpaceId(spaceId)
|
||||
}
|
||||
|
||||
override fun findBySpaceIdAndId(
|
||||
spaceId: Int,
|
||||
id: Int
|
||||
): RecurrentOperation {
|
||||
val userId = authService.getSecurityUserId()
|
||||
spaceRepo.findSpaceById(spaceId, userId)
|
||||
return recurrentOperationRepo.findBySpaceIdAndId(spaceId, id) ?: throw NotFoundException("Cannot find recurrent operation with id ${id}")
|
||||
}
|
||||
|
||||
override fun create(spaceId: Int, operation: RecurrentOperationDTO.CreateRecurrentOperationDTO): Int {
|
||||
val userId = authService.getSecurityUserId()
|
||||
val space = spaceRepo.findSpaceById(spaceId, userId) ?: throw NotFoundException("Cannot find space with id ${spaceId}")
|
||||
val category = categoryService.getCategory(spaceId, operation.categoryId)
|
||||
val creatingOperation = RecurrentOperation(
|
||||
space = space,
|
||||
category = category,
|
||||
name = operation.name,
|
||||
amount = operation.amount,
|
||||
date = operation.date
|
||||
)
|
||||
return recurrentOperationRepo.create(creatingOperation, userId)
|
||||
}
|
||||
|
||||
override fun update(spaceId: Int, operationId: Int, operation: RecurrentOperationDTO.UpdateRecurrentOperationDTO) {
|
||||
val userId = authService.getSecurityUserId()
|
||||
spaceRepo.findSpaceById(spaceId, userId)
|
||||
val newCategory = categoryService.getCategory(spaceId, operation.categoryId)
|
||||
val existingOperation = recurrentOperationRepo.findBySpaceIdAndId(spaceId,operationId ) ?: throw NotFoundException("Cannot find operation with id $operationId")
|
||||
val updatedOperation = existingOperation.copy(
|
||||
category = newCategory,
|
||||
name = operation.name,
|
||||
amount = operation.amount,
|
||||
date = operation.date
|
||||
)
|
||||
recurrentOperationRepo.update(updatedOperation, userId)
|
||||
|
||||
}
|
||||
|
||||
override fun delete(spaceId: Int, id: Int) {
|
||||
val userId = authService.getSecurityUserId()
|
||||
spaceRepo.findSpaceById(spaceId, userId)
|
||||
recurrentOperationRepo.delete(id)
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,15 @@
|
||||
package space.luminic.finance.services
|
||||
|
||||
import space.luminic.finance.dtos.SpaceDTO
|
||||
import space.luminic.finance.models.Budget
|
||||
import space.luminic.finance.models.Space
|
||||
|
||||
interface SpaceService {
|
||||
|
||||
suspend fun checkSpace(spaceId: String): Space
|
||||
suspend fun getSpaces(): List<Space>
|
||||
suspend fun getSpace(id: String): Space
|
||||
suspend fun createSpace(space: SpaceDTO.CreateSpaceDTO): Space
|
||||
suspend fun updateSpace(spaceId: String, space: SpaceDTO.UpdateSpaceDTO): Space
|
||||
suspend fun deleteSpace(spaceId: String)
|
||||
fun checkSpace(spaceId: Int): Space
|
||||
fun getSpaces(): List<Space>
|
||||
fun getSpace(id: Int): Space
|
||||
fun createSpace(space: SpaceDTO.CreateSpaceDTO): Int
|
||||
fun updateSpace(spaceId: Int, space: SpaceDTO.UpdateSpaceDTO): Int
|
||||
fun deleteSpace(spaceId: Int)
|
||||
|
||||
}
|
||||
@@ -1,149 +1,75 @@
|
||||
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.cache.annotation.CacheEvict
|
||||
import org.springframework.cache.annotation.Cacheable
|
||||
import org.springframework.jdbc.core.JdbcTemplate
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
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,
|
||||
private val categoryService: CategoryService
|
||||
) : 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)
|
||||
override fun checkSpace(spaceId: Int): Space {
|
||||
return getSpace(spaceId)
|
||||
}
|
||||
|
||||
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)
|
||||
// @Cacheable(cacheNames = ["spaces"])
|
||||
override fun getSpaces(): List<Space> {
|
||||
val user = authService.getSecurityUserId()
|
||||
val spaces = spaceRepo.findSpacesAvailableForUser(user)
|
||||
return spaces
|
||||
}
|
||||
|
||||
override suspend fun checkSpace(spaceId: String): Space {
|
||||
override fun getSpace(id: Int): Space {
|
||||
val user = authService.getSecurityUserId()
|
||||
val space = spaceRepo.findSpaceById(id, user) ?: throw NotFoundException("Space with id $id not found")
|
||||
return space
|
||||
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun createSpace(space: SpaceDTO.CreateSpaceDTO): Int {
|
||||
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(
|
||||
val creatingSpace = 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()
|
||||
owner = user,
|
||||
participants = setOf(user)
|
||||
)
|
||||
val userId = authService.getSecurityUserId()
|
||||
val savedSpace = spaceRepo.create(creatingSpace, userId)
|
||||
if (space.createBasicCategories) {
|
||||
categoryService.createEtalonCategoriesForSpace(savedSpace)
|
||||
}
|
||||
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(
|
||||
@Transactional
|
||||
override fun updateSpace(
|
||||
spaceId: Int,
|
||||
space: SpaceDTO.UpdateSpaceDTO
|
||||
): Int {
|
||||
val userId = authService.getSecurityUserId()
|
||||
val existingSpace = getSpace(spaceId)
|
||||
val updatedSpace = Space(
|
||||
id = existingSpace.id,
|
||||
name = space.name,
|
||||
)
|
||||
updatedSpace.owner = existingSpace.owner
|
||||
updatedSpace.participants = existingSpace.participants
|
||||
return spaceRepo.save(updatedSpace).awaitFirst()
|
||||
owner = existingSpace.owner,
|
||||
participants = existingSpace.participants,
|
||||
isDeleted = existingSpace.isDeleted,
|
||||
createdBy = existingSpace.createdBy,
|
||||
createdAt = existingSpace.createdAt,
|
||||
)
|
||||
return spaceRepo.update(updatedSpace, userId)
|
||||
}
|
||||
|
||||
override suspend fun deleteSpace(spaceId: String) {
|
||||
val space = spaceRepo.findById(spaceId).awaitFirstOrNull() ?: throw NotFoundException("Space not found")
|
||||
space.isDeleted = true
|
||||
spaceRepo.save(space).awaitFirst()
|
||||
@Transactional
|
||||
override fun deleteSpace(spaceId: Int) {
|
||||
spaceRepo.delete(spaceId)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
//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 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.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.NotFoundException
|
||||
//import space.luminic.finance.models.Space
|
||||
//import space.luminic.finance.models.User
|
||||
//import space.luminic.finance.repos.SpaceRepo
|
||||
//
|
||||
//@Service
|
||||
//class SpaceServiceMongoImpl(
|
||||
// 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()
|
||||
// }
|
||||
//}
|
||||
@@ -1,113 +1,111 @@
|
||||
package space.luminic.finance.services
|
||||
|
||||
|
||||
import com.interaso.webpush.VapidKeys
|
||||
import com.interaso.webpush.WebPushService
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.reactive.awaitSingle
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.bson.types.ObjectId
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.dao.DuplicateKeyException
|
||||
import org.springframework.stereotype.Service
|
||||
import space.luminic.finance.models.PushMessage
|
||||
import space.luminic.finance.models.Subscription
|
||||
import space.luminic.finance.models.SubscriptionDTO
|
||||
import space.luminic.finance.models.User
|
||||
import space.luminic.finance.repos.SubscriptionRepo
|
||||
import space.luminic.finance.services.VapidConstants.VAPID_PRIVATE_KEY
|
||||
import space.luminic.finance.services.VapidConstants.VAPID_PUBLIC_KEY
|
||||
import space.luminic.finance.services.VapidConstants.VAPID_SUBJECT
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.jvm.javaClass
|
||||
import kotlin.text.orEmpty
|
||||
|
||||
object VapidConstants {
|
||||
const val VAPID_PUBLIC_KEY =
|
||||
"BKmMyBUhpkcmzYWcYsjH_spqcy0zf_8eVtZo60f7949TgLztCmv3YD0E_vtV2dTfECQ4sdLdPK3ICDcyOkCqr84"
|
||||
const val VAPID_PRIVATE_KEY = "YeJH_0LhnVYN6RdxMidgR6WMYlpGXTJS3HjT9V3NSGI"
|
||||
const val VAPID_SUBJECT = "mailto:voroninvyu@gmail.com"
|
||||
}
|
||||
|
||||
@Service
|
||||
class SubscriptionService(private val subscriptionRepo: SubscriptionRepo) {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(javaClass)
|
||||
private val pushService =
|
||||
WebPushService(
|
||||
subject = VAPID_SUBJECT,
|
||||
vapidKeys = VapidKeys.fromUncompressedBytes(VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY)
|
||||
)
|
||||
|
||||
|
||||
suspend fun sendToSpaceOwner(ownerId: String, message: PushMessage) = coroutineScope {
|
||||
val ownerTokens = subscriptionRepo.findByUserIdAndIsActive(ObjectId(ownerId)).collectList().awaitSingle()
|
||||
|
||||
ownerTokens.forEach { token ->
|
||||
launch(Dispatchers.IO) { // Теперь мы точно в корутин скоупе
|
||||
try {
|
||||
sendNotification(token.endpoint, token.p256dh, token.auth, message)
|
||||
} catch (e: Exception) {
|
||||
logger.error("Ошибка при отправке уведомления: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
suspend fun sendNotification(endpoint: String, p256dh: String, auth: String, payload: PushMessage) {
|
||||
try {
|
||||
pushService.send(
|
||||
payload = Json.encodeToString(payload),
|
||||
endpoint = endpoint,
|
||||
p256dh = p256dh,
|
||||
auth = auth
|
||||
)
|
||||
logger.info("Уведомление успешно отправлено на endpoint: $endpoint")
|
||||
|
||||
} catch (e: Exception) {
|
||||
logger.error("Ошибка при отправке уведомления на endpoint $endpoint: ${e.message}")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
suspend fun sendToAll(payload: PushMessage) {
|
||||
|
||||
subscriptionRepo.findAll().collectList().awaitSingle().forEach { sub ->
|
||||
|
||||
try {
|
||||
sendNotification(sub.endpoint, sub.p256dh, sub.auth, payload)
|
||||
} catch (e: Exception) {
|
||||
sub.isActive = false
|
||||
subscriptionRepo.save(sub).awaitSingle()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
suspend fun subscribe(subscriptionDTO: SubscriptionDTO, user: User): String {
|
||||
val subscription = Subscription(
|
||||
id = null,
|
||||
user = user,
|
||||
endpoint = subscriptionDTO.endpoint,
|
||||
auth = subscriptionDTO.keys["auth"].orEmpty(),
|
||||
p256dh = subscriptionDTO.keys["p256dh"].orEmpty(),
|
||||
isActive = true
|
||||
)
|
||||
|
||||
return try {
|
||||
val savedSubscription = subscriptionRepo.save(subscription).awaitSingle()
|
||||
"Subscription created with ID: ${savedSubscription.id}"
|
||||
} catch (e: DuplicateKeyException) {
|
||||
logger.info("Subscription already exists. Skipping.")
|
||||
"Subscription already exists. Skipping."
|
||||
} catch (e: Exception) {
|
||||
logger.error("Error while saving subscription: ${e.message}")
|
||||
throw kotlin.RuntimeException("Error while saving subscription")
|
||||
}
|
||||
}
|
||||
}
|
||||
//package space.luminic.finance.services
|
||||
//
|
||||
//
|
||||
//import com.interaso.webpush.VapidKeys
|
||||
//import com.interaso.webpush.WebPushService
|
||||
//import kotlinx.coroutines.Dispatchers
|
||||
//import kotlinx.coroutines.coroutineScope
|
||||
//import kotlinx.coroutines.launch
|
||||
//import kotlinx.coroutines.reactive.awaitSingle
|
||||
//import kotlinx.serialization.encodeToString
|
||||
//import kotlinx.serialization.json.Json
|
||||
//import org.slf4j.LoggerFactory
|
||||
//import org.springframework.dao.DuplicateKeyException
|
||||
//import org.springframework.stereotype.Service
|
||||
//import space.luminic.finance.models.PushMessage
|
||||
//import space.luminic.finance.models.Subscription
|
||||
//import space.luminic.finance.models.User
|
||||
//import space.luminic.finance.repos.SubscriptionRepo
|
||||
//import space.luminic.finance.services.VapidConstants.VAPID_PRIVATE_KEY
|
||||
//import space.luminic.finance.services.VapidConstants.VAPID_PUBLIC_KEY
|
||||
//import space.luminic.finance.services.VapidConstants.VAPID_SUBJECT
|
||||
//import kotlin.collections.forEach
|
||||
//import kotlin.jvm.javaClass
|
||||
//import kotlin.text.orEmpty
|
||||
//
|
||||
//object VapidConstants {
|
||||
// const val VAPID_PUBLIC_KEY =
|
||||
// "BKmMyBUhpkcmzYWcYsjH_spqcy0zf_8eVtZo60f7949TgLztCmv3YD0E_vtV2dTfECQ4sdLdPK3ICDcyOkCqr84"
|
||||
// const val VAPID_PRIVATE_KEY = "YeJH_0LhnVYN6RdxMidgR6WMYlpGXTJS3HjT9V3NSGI"
|
||||
// const val VAPID_SUBJECT = "mailto:voroninvyu@gmail.com"
|
||||
//}
|
||||
//
|
||||
//@Service
|
||||
//class SubscriptionService(private val subscriptionRepo: SubscriptionRepo) {
|
||||
//
|
||||
// private val logger = LoggerFactory.getLogger(javaClass)
|
||||
// private val pushService =
|
||||
// WebPushService(
|
||||
// subject = VAPID_SUBJECT,
|
||||
// vapidKeys = VapidKeys.fromUncompressedBytes(VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY)
|
||||
// )
|
||||
//
|
||||
//
|
||||
// suspend fun sendToSpaceOwner(ownerId: String, message: PushMessage) = coroutineScope {
|
||||
// val ownerTokens = subscriptionRepo.findByUserIdAndIsActive(ObjectId(ownerId)).collectList().awaitSingle()
|
||||
//
|
||||
// ownerTokens.forEach { token ->
|
||||
// launch(Dispatchers.IO) { // Теперь мы точно в корутин скоупе
|
||||
// try {
|
||||
// sendNotification(token.endpoint, token.p256dh, token.auth, message)
|
||||
// } catch (e: Exception) {
|
||||
// logger.error("Ошибка при отправке уведомления: ${e.message}", e)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// suspend fun sendNotification(endpoint: String, p256dh: String, auth: String, payload: PushMessage) {
|
||||
// try {
|
||||
// pushService.send(
|
||||
// payload = Json.encodeToString(payload),
|
||||
// endpoint = endpoint,
|
||||
// p256dh = p256dh,
|
||||
// auth = auth
|
||||
// )
|
||||
// logger.info("Уведомление успешно отправлено на endpoint: $endpoint")
|
||||
//
|
||||
// } catch (e: Exception) {
|
||||
// logger.error("Ошибка при отправке уведомления на endpoint $endpoint: ${e.message}")
|
||||
// throw e
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// suspend fun sendToAll(payload: PushMessage) {
|
||||
//
|
||||
// subscriptionRepo.findAll().collectList().awaitSingle().forEach { sub ->
|
||||
//
|
||||
// try {
|
||||
// sendNotification(sub.endpoint, sub.p256dh, sub.auth, payload)
|
||||
// } catch (e: Exception) {
|
||||
// sub.isActive = false
|
||||
// subscriptionRepo.save(sub).awaitSingle()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// suspend fun subscribe(subscriptionDTO: SubscriptionDTO, user: User): String {
|
||||
// val subscription = Subscription(
|
||||
// id = null,
|
||||
// user = user,
|
||||
// endpoint = subscriptionDTO.endpoint,
|
||||
// auth = subscriptionDTO.keys["auth"].orEmpty(),
|
||||
// p256dh = subscriptionDTO.keys["p256dh"].orEmpty(),
|
||||
// isActive = true
|
||||
// )
|
||||
//
|
||||
// return try {
|
||||
// val savedSubscription = subscriptionRepo.save(subscription).awaitSingle()
|
||||
// "Subscription created with ID: ${savedSubscription.id}"
|
||||
// } catch (e: DuplicateKeyException) {
|
||||
// logger.info("Subscription already exists. Skipping.")
|
||||
// "Subscription already exists. Skipping."
|
||||
// } catch (e: Exception) {
|
||||
// logger.error("Error while saving subscription: ${e.message}")
|
||||
// throw kotlin.RuntimeException("Error while saving subscription")
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -1,43 +1,51 @@
|
||||
package space.luminic.finance.services
|
||||
|
||||
import kotlinx.coroutines.reactor.awaitSingle
|
||||
import org.springframework.cache.annotation.CacheEvict
|
||||
import org.springframework.stereotype.Service
|
||||
import reactor.core.publisher.Mono
|
||||
import space.luminic.finance.configs.AuthException
|
||||
import space.luminic.finance.models.Token
|
||||
import space.luminic.finance.models.Token.TokenStatus
|
||||
import space.luminic.finance.repos.TokenRepo
|
||||
import java.time.LocalDateTime
|
||||
import java.time.Instant
|
||||
|
||||
@Service
|
||||
class TokenService(private val tokenRepository: TokenRepo) {
|
||||
class TokenService(
|
||||
private val userService: UserService,
|
||||
private val tokenRepo: TokenRepo) {
|
||||
|
||||
@CacheEvict("tokens", allEntries = true)
|
||||
suspend fun saveToken(token: String, username: String, expiresAt: LocalDateTime): Token {
|
||||
fun saveToken(token: String, username: String, expiresAt: Instant): Token {
|
||||
val user = userService.getByUsername(username)
|
||||
val newToken = Token(
|
||||
token = token,
|
||||
username = username,
|
||||
issuedAt = LocalDateTime.now(),
|
||||
user = user,
|
||||
issuedAt = Instant.now(),
|
||||
expiresAt = expiresAt
|
||||
)
|
||||
return tokenRepository.save(newToken).awaitSingle()
|
||||
return tokenRepo.create(newToken)
|
||||
}
|
||||
|
||||
fun getToken(token: String): Mono<Token> {
|
||||
return tokenRepository.findByToken(token)
|
||||
fun getToken(token: String): Token {
|
||||
return tokenRepo.findByToken(token) ?: throw AuthException("Токен не валиден")
|
||||
}
|
||||
|
||||
|
||||
fun revokeToken(token: String) {
|
||||
val tokenDetail =
|
||||
tokenRepository.findByToken(token).block()!!
|
||||
val updatedToken = tokenDetail.copy(status = TokenStatus.REVOKED)
|
||||
tokenRepository.save(updatedToken).block()
|
||||
val tokenDetail = getToken(token)
|
||||
val updatedToken = Token(
|
||||
id = tokenDetail.id,
|
||||
token = tokenDetail.token,
|
||||
user = tokenDetail.user,
|
||||
status = TokenStatus.REVOKED,
|
||||
issuedAt = tokenDetail.issuedAt,
|
||||
expiresAt = tokenDetail.expiresAt
|
||||
)
|
||||
tokenRepo.update(updatedToken)
|
||||
}
|
||||
|
||||
|
||||
@CacheEvict("tokens", allEntries = true)
|
||||
fun deleteExpiredTokens() {
|
||||
tokenRepository.deleteByExpiresAtBefore(LocalDateTime.now())
|
||||
tokenRepo.deleteByExpiresAtBefore(Instant.now())
|
||||
}
|
||||
}
|
||||
@@ -7,14 +7,13 @@ import java.time.LocalDate
|
||||
interface TransactionService {
|
||||
|
||||
data class TransactionsFilter(
|
||||
val accountId: String,
|
||||
val dateFrom: LocalDate? = null,
|
||||
val dateTo: LocalDate? = null,
|
||||
)
|
||||
|
||||
suspend fun getTransactions(spaceId: String, filter: TransactionsFilter, sortBy: String, sortDirection: String): List<Transaction>
|
||||
suspend fun getTransaction(spaceId: String, transactionId: String): Transaction
|
||||
suspend fun createTransaction(spaceId: String, transaction: TransactionDTO.CreateTransactionDTO): Transaction
|
||||
suspend fun updateTransaction(spaceId: String, transaction: TransactionDTO.UpdateTransactionDTO): Transaction
|
||||
suspend fun deleteTransaction(spaceId: String, transactionId: String)
|
||||
fun getTransactions(spaceId: Int, filter: TransactionsFilter, sortBy: String, sortDirection: String): List<Transaction>
|
||||
fun getTransaction(spaceId: Int, transactionId: Int): Transaction
|
||||
fun createTransaction(spaceId: Int, transaction: TransactionDTO.CreateTransactionDTO): Int
|
||||
fun updateTransaction(spaceId: Int, transactionId: Int, transaction: TransactionDTO.UpdateTransactionDTO): Int
|
||||
fun deleteTransaction(spaceId: Int, transactionId: Int)
|
||||
}
|
||||
@@ -1,20 +1,5 @@
|
||||
package space.luminic.finance.services
|
||||
|
||||
import kotlinx.coroutines.reactive.awaitFirstOrNull
|
||||
import kotlinx.coroutines.reactive.awaitSingle
|
||||
import org.bson.types.ObjectId
|
||||
import org.springframework.data.domain.Sort
|
||||
import org.springframework.data.domain.Sort.Direction
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.addFields
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.lookup
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.match
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.sort
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.unwind
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationOperation
|
||||
import org.springframework.data.mongodb.core.aggregation.ConvertOperators
|
||||
import org.springframework.data.mongodb.core.query.Criteria
|
||||
import org.springframework.stereotype.Service
|
||||
import space.luminic.finance.dtos.TransactionDTO
|
||||
import space.luminic.finance.models.NotFoundException
|
||||
@@ -23,163 +8,89 @@ import space.luminic.finance.repos.TransactionRepo
|
||||
|
||||
@Service
|
||||
class TransactionServiceImpl(
|
||||
private val mongoTemplate: ReactiveMongoTemplate,
|
||||
private val transactionRepo: TransactionRepo,
|
||||
private val spaceService: SpaceService,
|
||||
private val categoryService: CategoryService,
|
||||
private val transactionRepo: TransactionRepo,
|
||||
private val authService: AuthService,
|
||||
) : TransactionService {
|
||||
|
||||
|
||||
private fun basicAggregation(spaceId: String): List<AggregationOperation> {
|
||||
val addFieldsOI = addFields()
|
||||
.addField("createdByOI")
|
||||
.withValue(ConvertOperators.valueOf("createdById").convertToObjectId())
|
||||
.addField("updatedByOI")
|
||||
.withValue(ConvertOperators.valueOf("updatedById").convertToObjectId())
|
||||
.addField("fromAccountIdOI")
|
||||
.withValue(ConvertOperators.valueOf("fromAccountId").convertToObjectId())
|
||||
.addField("toAccountIdOI")
|
||||
.withValue(ConvertOperators.valueOf("toAccountId").convertToObjectId())
|
||||
.addField("categoryIdOI")
|
||||
.withValue(ConvertOperators.valueOf("categoryId").convertToObjectId())
|
||||
.build()
|
||||
|
||||
val lookupFromAccount = lookup("accounts", "fromAccountIdOI", "_id", "fromAccount")
|
||||
val unwindFromAccount = unwind("fromAccount")
|
||||
val lookupToAccount = lookup("accounts", "toAccountIdOI", "_id", "toAccount")
|
||||
val unwindToAccount = unwind("toAccount", true)
|
||||
|
||||
val lookupCategory = lookup("categories", "categoryIdOI", "_id", "category")
|
||||
val unwindCategory = unwind("category")
|
||||
|
||||
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.where("spaceId").`is`(spaceId))
|
||||
val matchStage = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
|
||||
|
||||
return listOf(
|
||||
matchStage,
|
||||
addFieldsOI,
|
||||
lookupFromAccount,
|
||||
unwindFromAccount,
|
||||
lookupToAccount,
|
||||
unwindToAccount,
|
||||
lookupCategory,
|
||||
unwindCategory,
|
||||
lookupCreatedBy,
|
||||
unwindCreatedBy,
|
||||
lookupUpdatedBy,
|
||||
unwindUpdatedBy
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getTransactions(
|
||||
spaceId: String,
|
||||
override fun getTransactions(
|
||||
spaceId: Int,
|
||||
filter: TransactionService.TransactionsFilter,
|
||||
sortBy: String,
|
||||
sortDirection: String
|
||||
): List<Transaction> {
|
||||
val allowedSortFields = setOf("date", "amount", "category.name", "createdAt")
|
||||
require(sortBy in allowedSortFields) { "Invalid sort field: $sortBy" }
|
||||
|
||||
val direction = when (sortDirection.uppercase()) {
|
||||
"ASC" -> Direction.ASC
|
||||
"DESC" -> Direction.DESC
|
||||
else -> throw IllegalArgumentException("Sort direction must be 'ASC' or 'DESC'")
|
||||
}
|
||||
val basicAggregation = basicAggregation(spaceId)
|
||||
|
||||
val sort = sort(Sort.by(direction, sortBy))
|
||||
val matchCriteria = mutableListOf<Criteria>()
|
||||
filter.dateFrom?.let { matchCriteria.add(Criteria.where("date").gte(it)) }
|
||||
filter.dateTo?.let { matchCriteria.add(Criteria.where("date").lte(it)) }
|
||||
val matchStage = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
|
||||
val aggregation =
|
||||
newAggregation(
|
||||
matchStage,
|
||||
*basicAggregation.toTypedArray(),
|
||||
sort
|
||||
)
|
||||
|
||||
return mongoTemplate.aggregate(aggregation, "transactions", Transaction::class.java)
|
||||
.collectList()
|
||||
.awaitSingle()
|
||||
return transactionRepo.findAllBySpaceId(spaceId)
|
||||
}
|
||||
|
||||
override suspend fun getTransaction(
|
||||
spaceId: String,
|
||||
transactionId: String
|
||||
override fun getTransaction(
|
||||
spaceId: Int,
|
||||
transactionId: Int
|
||||
): Transaction {
|
||||
val matchCriteria = mutableListOf<Criteria>()
|
||||
matchCriteria.add(Criteria.where("spaceId").`is`(spaceId))
|
||||
matchCriteria.add(Criteria.where("_id").`is`(ObjectId(transactionId)))
|
||||
val matchStage = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
|
||||
|
||||
val aggregation =
|
||||
newAggregation(
|
||||
matchStage,
|
||||
)
|
||||
return mongoTemplate.aggregate(aggregation, "transactions", Transaction::class.java)
|
||||
.awaitFirstOrNull() ?: throw NotFoundException("Transaction with ID $transactionId not found")
|
||||
spaceService.getSpace(spaceId)
|
||||
return transactionRepo.findBySpaceIdAndId(spaceId, transactionId)
|
||||
?: throw NotFoundException("Transaction with id $transactionId not found")
|
||||
}
|
||||
|
||||
override suspend fun createTransaction(
|
||||
spaceId: String,
|
||||
override fun createTransaction(
|
||||
spaceId: Int,
|
||||
transaction: TransactionDTO.CreateTransactionDTO
|
||||
): Transaction {
|
||||
if (transaction.type == Transaction.TransactionType.TRANSFER && transaction.toAccountId == null) {
|
||||
throw IllegalArgumentException("Cannot create a transaction with type TRANSFER without a toAccountId")
|
||||
}
|
||||
): Int {
|
||||
val userId = authService.getSecurityUserId()
|
||||
val space = spaceService.getSpace(spaceId)
|
||||
val category = categoryService.getCategory(spaceId, transaction.categoryId)
|
||||
if (transaction.type != Transaction.TransactionType.TRANSFER && transaction.type.name != category.type.name) {
|
||||
throw IllegalArgumentException("Transaction type should match with category type")
|
||||
}
|
||||
val transaction = Transaction(
|
||||
spaceId = spaceId,
|
||||
space = space,
|
||||
type = transaction.type,
|
||||
kind = transaction.kind,
|
||||
categoryId = transaction.categoryId,
|
||||
category = category,
|
||||
comment = transaction.comment,
|
||||
amount = transaction.amount,
|
||||
fees = transaction.fees,
|
||||
date = transaction.date,
|
||||
fromAccountId = transaction.fromAccountId,
|
||||
toAccountId = transaction.toAccountId,
|
||||
)
|
||||
return transactionRepo.save(transaction).awaitSingle()
|
||||
return transactionRepo.create(transaction, userId)
|
||||
}
|
||||
|
||||
override suspend fun updateTransaction(
|
||||
spaceId: String,
|
||||
override fun updateTransaction(
|
||||
spaceId: Int,
|
||||
transactionId: Int,
|
||||
transaction: TransactionDTO.UpdateTransactionDTO
|
||||
): Transaction {
|
||||
if (transaction.type == Transaction.TransactionType.TRANSFER && transaction.toAccountId == null) {
|
||||
throw IllegalArgumentException("Cannot edit a transaction with type TRANSFER without a toAccountId")
|
||||
}
|
||||
val exitingTx = getTransaction(spaceId, transaction.id)
|
||||
val transaction = exitingTx.copy(
|
||||
spaceId = exitingTx.spaceId,
|
||||
): Int {
|
||||
val space = spaceService.getSpace(spaceId)
|
||||
val existingTransaction = getTransaction(space.id!!, transactionId)
|
||||
val newCategory = categoryService.getCategory(spaceId, transaction.categoryId)
|
||||
// val id: Int,
|
||||
// val type: TransactionType = TransactionType.EXPENSE,
|
||||
// val kind: TransactionKind = TransactionKind.INSTANT,
|
||||
// val category: Int,
|
||||
// val comment: String,
|
||||
// val amount: BigDecimal,
|
||||
// val fees: BigDecimal = BigDecimal.ZERO,
|
||||
// val date: Instant
|
||||
val updatedTransaction = Transaction(
|
||||
id = existingTransaction.id,
|
||||
space = existingTransaction.space,
|
||||
parent = existingTransaction.parent,
|
||||
type = transaction.type,
|
||||
kind = transaction.kind,
|
||||
categoryId = transaction.category,
|
||||
category = newCategory,
|
||||
comment = transaction.comment,
|
||||
amount = transaction.amount,
|
||||
fees = transaction.fees,
|
||||
date = transaction.date,
|
||||
fromAccountId = transaction.fromAccountId,
|
||||
toAccountId = transaction.toAccountId,
|
||||
isDeleted = existingTransaction.isDeleted,
|
||||
isDone = transaction.isDone,
|
||||
createdBy = existingTransaction.createdBy,
|
||||
createdAt = existingTransaction.createdAt
|
||||
|
||||
)
|
||||
return transactionRepo.save(transaction).awaitSingle()
|
||||
return transactionRepo.update(updatedTransaction)
|
||||
}
|
||||
|
||||
override suspend fun deleteTransaction(spaceId: String, transactionId: String) {
|
||||
val transaction = getTransaction(spaceId, transactionId)
|
||||
transaction.isDeleted = true
|
||||
transactionRepo.save(transaction).awaitSingle()
|
||||
override fun deleteTransaction(spaceId: Int, transactionId: Int) {
|
||||
val space = spaceService.getSpace(spaceId)
|
||||
getTransaction(space.id!!, transactionId)
|
||||
transactionRepo.delete(transactionId)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
//package space.luminic.finance.services
|
||||
//
|
||||
//import kotlinx.coroutines.reactive.awaitFirstOrNull
|
||||
//import kotlinx.coroutines.reactive.awaitSingle
|
||||
//import org.bson.types.ObjectId
|
||||
//import org.springframework.data.domain.Sort
|
||||
//import org.springframework.data.domain.Sort.Direction
|
||||
//import org.springframework.data.mongodb.core.ReactiveMongoTemplate
|
||||
//import org.springframework.data.mongodb.core.aggregation.Aggregation.addFields
|
||||
//import org.springframework.data.mongodb.core.aggregation.Aggregation.lookup
|
||||
//import org.springframework.data.mongodb.core.aggregation.Aggregation.match
|
||||
//import org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation
|
||||
//import org.springframework.data.mongodb.core.aggregation.Aggregation.sort
|
||||
//import org.springframework.data.mongodb.core.aggregation.Aggregation.unwind
|
||||
//import org.springframework.data.mongodb.core.aggregation.AggregationOperation
|
||||
//import org.springframework.data.mongodb.core.aggregation.ConvertOperators
|
||||
//import org.springframework.data.mongodb.core.query.Criteria
|
||||
//import org.springframework.stereotype.Service
|
||||
//import space.luminic.finance.dtos.TransactionDTO
|
||||
//import space.luminic.finance.models.NotFoundException
|
||||
//import space.luminic.finance.models.Transaction
|
||||
//import space.luminic.finance.repos.TransactionRepo
|
||||
//
|
||||
//@Service
|
||||
//class TransactionServiceMongoImpl(
|
||||
// private val mongoTemplate: ReactiveMongoTemplate,
|
||||
// private val transactionRepo: TransactionRepo,
|
||||
// private val categoryService: CategoryService,
|
||||
//) : TransactionService {
|
||||
//
|
||||
//
|
||||
// private fun basicAggregation(spaceId: String): List<AggregationOperation> {
|
||||
// val addFieldsOI = addFields()
|
||||
// .addField("createdByOI")
|
||||
// .withValue(ConvertOperators.valueOf("createdById").convertToObjectId())
|
||||
// .addField("updatedByOI")
|
||||
// .withValue(ConvertOperators.valueOf("updatedById").convertToObjectId())
|
||||
// .addField("fromAccountIdOI")
|
||||
// .withValue(ConvertOperators.valueOf("fromAccountId").convertToObjectId())
|
||||
// .addField("toAccountIdOI")
|
||||
// .withValue(ConvertOperators.valueOf("toAccountId").convertToObjectId())
|
||||
// .addField("categoryIdOI")
|
||||
// .withValue(ConvertOperators.valueOf("categoryId").convertToObjectId())
|
||||
// .build()
|
||||
//
|
||||
// val lookupFromAccount = lookup("accounts", "fromAccountIdOI", "_id", "fromAccount")
|
||||
// val unwindFromAccount = unwind("fromAccount")
|
||||
// val lookupToAccount = lookup("accounts", "toAccountIdOI", "_id", "toAccount")
|
||||
// val unwindToAccount = unwind("toAccount", true)
|
||||
//
|
||||
// val lookupCategory = lookup("categories", "categoryIdOI", "_id", "category")
|
||||
// val unwindCategory = unwind("category")
|
||||
//
|
||||
// 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.where("spaceId").`is`(spaceId))
|
||||
// val matchStage = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
|
||||
//
|
||||
// return listOf(
|
||||
// matchStage,
|
||||
// addFieldsOI,
|
||||
// lookupFromAccount,
|
||||
// unwindFromAccount,
|
||||
// lookupToAccount,
|
||||
// unwindToAccount,
|
||||
// lookupCategory,
|
||||
// unwindCategory,
|
||||
// lookupCreatedBy,
|
||||
// unwindCreatedBy,
|
||||
// lookupUpdatedBy,
|
||||
// unwindUpdatedBy
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// override suspend fun getTransactions(
|
||||
// spaceId: String,
|
||||
// filter: TransactionService.TransactionsFilter,
|
||||
// sortBy: String,
|
||||
// sortDirection: String
|
||||
// ): List<Transaction> {
|
||||
// val allowedSortFields = setOf("date", "amount", "category.name", "createdAt")
|
||||
// require(sortBy in allowedSortFields) { "Invalid sort field: $sortBy" }
|
||||
//
|
||||
// val direction = when (sortDirection.uppercase()) {
|
||||
// "ASC" -> Direction.ASC
|
||||
// "DESC" -> Direction.DESC
|
||||
// else -> throw IllegalArgumentException("Sort direction must be 'ASC' or 'DESC'")
|
||||
// }
|
||||
// val basicAggregation = basicAggregation(spaceId)
|
||||
//
|
||||
// val sort = sort(Sort.by(direction, sortBy))
|
||||
// val matchCriteria = mutableListOf<Criteria>()
|
||||
// filter.dateFrom?.let { matchCriteria.add(Criteria.where("date").gte(it)) }
|
||||
// filter.dateTo?.let { matchCriteria.add(Criteria.where("date").lte(it)) }
|
||||
// val matchStage = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
|
||||
// val aggregation =
|
||||
// newAggregation(
|
||||
// matchStage,
|
||||
// *basicAggregation.toTypedArray(),
|
||||
// sort
|
||||
// )
|
||||
//
|
||||
// return mongoTemplate.aggregate(aggregation, "transactions", Transaction::class.java)
|
||||
// .collectList()
|
||||
// .awaitSingle()
|
||||
// }
|
||||
//
|
||||
// override suspend fun getTransaction(
|
||||
// spaceId: String,
|
||||
// transactionId: String
|
||||
// ): Transaction {
|
||||
// val matchCriteria = mutableListOf<Criteria>()
|
||||
// matchCriteria.add(Criteria.where("spaceId").`is`(spaceId))
|
||||
// matchCriteria.add(Criteria.where("_id").`is`(ObjectId(transactionId)))
|
||||
// val matchStage = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
|
||||
//
|
||||
// val aggregation =
|
||||
// newAggregation(
|
||||
// matchStage,
|
||||
// )
|
||||
// return mongoTemplate.aggregate(aggregation, "transactions", Transaction::class.java)
|
||||
// .awaitFirstOrNull() ?: throw NotFoundException("Transaction with ID $transactionId not found")
|
||||
// }
|
||||
//
|
||||
// override suspend fun createTransaction(
|
||||
// spaceId: String,
|
||||
// transaction: TransactionDTO.CreateTransactionDTO
|
||||
// ): Transaction {
|
||||
// if (transaction.type == Transaction.TransactionType.TRANSFER && transaction.toAccountId == null) {
|
||||
// throw IllegalArgumentException("Cannot create a transaction with type TRANSFER without a toAccountId")
|
||||
// }
|
||||
// val category = categoryService.getCategory(spaceId, transaction.categoryId)
|
||||
// if (transaction.type != Transaction.TransactionType.TRANSFER && transaction.type.name != category.type.name) {
|
||||
// throw IllegalArgumentException("Transaction type should match with category type")
|
||||
// }
|
||||
// val transaction = Transaction(
|
||||
// spaceId = spaceId,
|
||||
// type = transaction.type,
|
||||
// kind = transaction.kind,
|
||||
// categoryId = transaction.categoryId,
|
||||
// comment = transaction.comment,
|
||||
// amount = transaction.amount,
|
||||
// fees = transaction.fees,
|
||||
// date = transaction.date,
|
||||
// fromAccountId = transaction.fromAccountId,
|
||||
// toAccountId = transaction.toAccountId,
|
||||
// )
|
||||
// return transactionRepo.save(transaction).awaitSingle()
|
||||
// }
|
||||
//
|
||||
// override suspend fun updateTransaction(
|
||||
// spaceId: String,
|
||||
// transaction: TransactionDTO.UpdateTransactionDTO
|
||||
// ): Transaction {
|
||||
// if (transaction.type == Transaction.TransactionType.TRANSFER && transaction.toAccountId == null) {
|
||||
// throw IllegalArgumentException("Cannot edit a transaction with type TRANSFER without a toAccountId")
|
||||
// }
|
||||
// val exitingTx = getTransaction(spaceId, transaction.id)
|
||||
// val transaction = exitingTx.copy(
|
||||
// spaceId = exitingTx.spaceId,
|
||||
// type = transaction.type,
|
||||
// kind = transaction.kind,
|
||||
// categoryId = transaction.category,
|
||||
// comment = transaction.comment,
|
||||
// amount = transaction.amount,
|
||||
// fees = transaction.fees,
|
||||
// date = transaction.date,
|
||||
// fromAccountId = transaction.fromAccountId,
|
||||
// toAccountId = transaction.toAccountId,
|
||||
// )
|
||||
// return transactionRepo.save(transaction).awaitSingle()
|
||||
// }
|
||||
//
|
||||
// override suspend fun deleteTransaction(spaceId: String, transactionId: String) {
|
||||
// val transaction = getTransaction(spaceId, transactionId)
|
||||
// transaction.isDeleted = true
|
||||
// transactionRepo.save(transaction).awaitSingle()
|
||||
// }
|
||||
//
|
||||
//
|
||||
//}
|
||||
@@ -1,12 +1,9 @@
|
||||
package space.luminic.finance.services
|
||||
|
||||
|
||||
import kotlinx.coroutines.reactor.awaitSingle
|
||||
import kotlinx.coroutines.reactor.awaitSingleOrNull
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.cache.annotation.Cacheable
|
||||
import org.springframework.stereotype.Service
|
||||
import space.luminic.finance.mappers.UserMapper
|
||||
import space.luminic.finance.models.NotFoundException
|
||||
import space.luminic.finance.models.User
|
||||
import space.luminic.finance.repos.UserRepo
|
||||
@@ -17,36 +14,30 @@ class UserService(val userRepo: UserRepo) {
|
||||
|
||||
|
||||
@Cacheable("users", key = "#username")
|
||||
suspend fun getByUsername(username: String): User {
|
||||
return userRepo.findByUsername(username).awaitSingleOrNull()
|
||||
?: throw NotFoundException("User with username: $username not found")
|
||||
fun getByUsername(username: String): User {
|
||||
return userRepo.findByUsername(username) ?: throw NotFoundException("User with username: $username not found")
|
||||
|
||||
}
|
||||
|
||||
suspend fun getById(id: String): User {
|
||||
return userRepo.findById(id).awaitSingleOrNull()
|
||||
?: throw NotFoundException("User with id: $id not found")
|
||||
fun getById(id: Int): User {
|
||||
return userRepo.findById(id) ?: throw NotFoundException("User with id: $id not found")
|
||||
}
|
||||
|
||||
suspend fun getUserByTelegramId(telegramId: Long): User {
|
||||
return userRepo.findByTgId(telegramId.toString()).awaitSingleOrNull()
|
||||
?: throw NotFoundException("User with telegramId: $telegramId not found")
|
||||
fun getUserByTelegramId(telegramId: Long): User {
|
||||
return userRepo.findByTgId(telegramId.toString())?: throw NotFoundException("User with telegramId: $telegramId not found")
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Cacheable("users", key = "#username")
|
||||
suspend fun getByUserNameWoPass(username: String): User {
|
||||
return userRepo.findByUsernameWOPassword(username).awaitSingleOrNull()
|
||||
?: throw NotFoundException("User with username: $username not found")
|
||||
fun getByUserNameWoPass(username: String): User {
|
||||
return userRepo.findByUsername(username) ?: throw NotFoundException("User with username: $username not found")
|
||||
|
||||
}
|
||||
|
||||
@Cacheable("usersList")
|
||||
suspend fun getUsers(): List<User> {
|
||||
fun getUsers(): List<User> {
|
||||
return userRepo.findAll()
|
||||
.collectList()
|
||||
.awaitSingle()
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user