fix recurrents
This commit is contained in:
@@ -26,7 +26,7 @@ class BearerTokenFilter(private val authService: AuthService) : SecurityContextS
|
||||
override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
|
||||
val token = exchange.request.headers.getFirst(HttpHeaders.AUTHORIZATION)?.removePrefix("Bearer ")
|
||||
|
||||
if (exchange.request.path.value() in listOf("/api/auth/login","/api/auth/register") || exchange.request.path.value()
|
||||
if (exchange.request.path.value() in listOf("/api/auth/login","/api/auth/register", "/api/auth/tgLogin") || exchange.request.path.value()
|
||||
.startsWith("/api/actuator")
|
||||
) {
|
||||
return chain.filter(exchange)
|
||||
|
||||
@@ -25,7 +25,7 @@ class SecurityConfig(
|
||||
|
||||
.logout { it.disable() }
|
||||
.authorizeExchange {
|
||||
it.pathMatchers(HttpMethod.POST, "/auth/login", "/auth/register").permitAll()
|
||||
it.pathMatchers(HttpMethod.POST, "/auth/login", "/auth/register", "/auth/tgLogin").permitAll()
|
||||
it.pathMatchers("/actuator/**").permitAll()
|
||||
it.anyExchange().authenticated()
|
||||
}
|
||||
|
||||
@@ -27,6 +27,11 @@ class AuthController(
|
||||
return authService.register(request.username, request.password, request.firstName)
|
||||
}
|
||||
|
||||
@PostMapping("/tgLogin")
|
||||
fun tgLogin(@RequestHeader("X-Tg-Id") tgId: String): Mono<Map<String, String>> {
|
||||
return authService.tgLogin(tgId).map { token -> mapOf("token" to token) }
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/me")
|
||||
fun getMe(@RequestHeader("Authorization") token: String): Mono<User> {
|
||||
|
||||
@@ -87,7 +87,9 @@ class SpaceController(
|
||||
//
|
||||
@GetMapping("/{spaceId}/budgets")
|
||||
fun getBudgets(@PathVariable spaceId: String): Mono<List<Budget>> {
|
||||
return financialService.getBudgets(spaceId)
|
||||
return spaceService.isValidRequest(spaceId).flatMap {
|
||||
financialService.getBudgets(spaceId)
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/{spaceId}/budgets/{id}")
|
||||
@@ -104,7 +106,7 @@ class SpaceController(
|
||||
@RequestBody budgetCreationDTO: BudgetCreationDTO,
|
||||
): Mono<Budget> {
|
||||
return spaceService.isValidRequest(spaceId).flatMap {
|
||||
financialService.createBudget(spaceId, budgetCreationDTO.budget, budgetCreationDTO.createRecurrent)
|
||||
financialService.createBudget(it, budgetCreationDTO.budget, budgetCreationDTO.createRecurrent)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -124,7 +126,7 @@ class SpaceController(
|
||||
@RequestBody limit: LimitValue,
|
||||
): Mono<BudgetCategory> {
|
||||
return spaceService.isValidRequest(spaceId).flatMap {
|
||||
financialService.setCategoryLimit(it.id!!,budgetId, catId, limit.limit)
|
||||
financialService.setCategoryLimit(it.id!!, budgetId, catId, limit.limit)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -177,7 +179,7 @@ class SpaceController(
|
||||
@PostMapping("/{spaceId}/transactions")
|
||||
fun createTransaction(@PathVariable spaceId: String, @RequestBody transaction: Transaction): Mono<Transaction> {
|
||||
return spaceService.isValidRequest(spaceId).flatMap {
|
||||
financialService.createTransaction(spaceId, transaction)
|
||||
financialService.createTransaction(it, transaction)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -219,7 +221,7 @@ class SpaceController(
|
||||
}
|
||||
|
||||
@GetMapping("/{spaceId}/categories/types")
|
||||
fun getCategoriesTypes(): ResponseEntity<Any> {
|
||||
fun getCategoriesTypes(@PathVariable spaceId: String): ResponseEntity<Any> {
|
||||
return try {
|
||||
ResponseEntity.ok(categoryService.getCategoryTypes())
|
||||
} catch (e: Exception) {
|
||||
@@ -238,15 +240,43 @@ class SpaceController(
|
||||
|
||||
}
|
||||
|
||||
@PutMapping("/{spaceId}/{categoryId}")
|
||||
fun editCategory(@PathVariable categoryId: String, @RequestBody category: Category): Mono<Category> {
|
||||
return categoryService.editCategory(category)
|
||||
@PutMapping("/{spaceId}/categories/{categoryId}")
|
||||
fun editCategory(
|
||||
@PathVariable categoryId: String,
|
||||
@RequestBody category: Category,
|
||||
@PathVariable spaceId: String
|
||||
): Mono<Category> {
|
||||
return spaceService.isValidRequest(spaceId).flatMap {
|
||||
categoryService.editCategory(it, category)
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{spaceId}/categories/{categoryId}")
|
||||
fun deleteCategory(@PathVariable categoryId: String, @PathVariable spaceId: String): Mono<String> {
|
||||
return spaceService.isValidRequest(spaceId).flatMap {
|
||||
categoryService.deleteCategory(it, categoryId)
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/by-month")
|
||||
fun getCategoriesSumsByMonths(): Mono<List<Document>> {
|
||||
return financialService.getCategorySumsPipeline(LocalDate.of(2024, 8, 1), LocalDate.of(2025, 1, 12))
|
||||
@GetMapping("/{spaceId}/categories/tags")
|
||||
fun getTags(@PathVariable spaceId: String): Mono<List<Tag>> {
|
||||
return spaceService.isValidRequest(spaceId).flatMap {
|
||||
spaceService.getTags(it)
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/{spaceId}/categories/tags")
|
||||
fun createTags(@PathVariable spaceId: String, @RequestBody tag: Tag): Mono<Tag> {
|
||||
return spaceService.isValidRequest(spaceId).flatMap {
|
||||
spaceService.createTag(it, tag)
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{spaceId}/categories/tags/{tagId}")
|
||||
fun deleteTags(@PathVariable spaceId: String, @PathVariable tagId: String): Mono<Void> {
|
||||
return spaceService.isValidRequest(spaceId).flatMap {
|
||||
spaceService.deleteTag(it, tagId)
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/{spaceId}/analytics/by-month")
|
||||
@@ -262,7 +292,7 @@ class SpaceController(
|
||||
@GetMapping("/{spaceId}/recurrents")
|
||||
fun getRecurrents(@PathVariable spaceId: String): Mono<List<Recurrent>> {
|
||||
return spaceService.isValidRequest(spaceId).flatMap {
|
||||
recurrentService.getRecurrents(it)
|
||||
recurrentService.getRecurrents(it.id!!)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,8 +332,8 @@ class SpaceController(
|
||||
}
|
||||
}
|
||||
|
||||
// @GetMapping("/regen")
|
||||
// fun regenSpaces(): Mono<List<Space>> {
|
||||
// return spaceService.regenSpaces()
|
||||
// }
|
||||
@GetMapping("/regen")
|
||||
fun regenSpaces(): Mono<Category> {
|
||||
return spaceService.regenSpaceCategory()
|
||||
}
|
||||
}
|
||||
@@ -23,86 +23,86 @@ import space.luminic.budgerapp.services.FinancialService
|
||||
@RequestMapping("/transactions")
|
||||
class TransactionController(private val financialService: FinancialService) {
|
||||
|
||||
|
||||
@GetMapping
|
||||
fun getTransactions(
|
||||
@RequestParam spaceId: String,
|
||||
@RequestParam(value = "transaction_type") transactionType: String? = null,
|
||||
@RequestParam(value = "category_type") categoryType: String? = null,
|
||||
@RequestParam(value = "user_id") userId: String? = null,
|
||||
@RequestParam(value = "is_child") isChild: Boolean? = null,
|
||||
@RequestParam(value = "limit") limit: Int = 10,
|
||||
@RequestParam(value = "offset") offset: Int = 0
|
||||
): ResponseEntity<Any> {
|
||||
try {
|
||||
return ResponseEntity.ok(
|
||||
financialService.getTransactions(
|
||||
spaceId = spaceId,
|
||||
transactionType = transactionType,
|
||||
categoryType = categoryType,
|
||||
userId = userId,
|
||||
isChild = isChild,
|
||||
limit = limit,
|
||||
offset = offset
|
||||
)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
fun getTransaction(@PathVariable id: String): ResponseEntity<Any> {
|
||||
try {
|
||||
return ResponseEntity.ok(financialService.getTransactionById(id))
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return ResponseEntity(e.message, HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
fun createTransaction(@RequestParam spaceId: String, @RequestBody transaction: Transaction): ResponseEntity<Any> {
|
||||
try {
|
||||
return ResponseEntity.ok(financialService.createTransaction(spaceId,transaction))
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return ResponseEntity(e.message, HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
fun editTransaction(@PathVariable id: String, @RequestBody transaction: Transaction): ResponseEntity<Any> {
|
||||
try {
|
||||
return ResponseEntity.ok(financialService.editTransaction(transaction))
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return ResponseEntity(e.message, HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
||||
|
||||
// @DeleteMapping("/{id}")
|
||||
// fun deleteTransaction(@PathVariable id: String): Mono<Void> {
|
||||
//
|
||||
// return financialService.deleteTransaction(id)
|
||||
// @GetMapping
|
||||
// fun getTransactions(
|
||||
// @RequestParam spaceId: String,
|
||||
// @RequestParam(value = "transaction_type") transactionType: String? = null,
|
||||
// @RequestParam(value = "category_type") categoryType: String? = null,
|
||||
// @RequestParam(value = "user_id") userId: String? = null,
|
||||
// @RequestParam(value = "is_child") isChild: Boolean? = null,
|
||||
// @RequestParam(value = "limit") limit: Int = 10,
|
||||
// @RequestParam(value = "offset") offset: Int = 0
|
||||
// ): ResponseEntity<Any> {
|
||||
// try {
|
||||
// return ResponseEntity.ok(
|
||||
// financialService.getTransactions(
|
||||
// spaceId = spaceId,
|
||||
// transactionType = transactionType,
|
||||
// categoryType = categoryType,
|
||||
// userId = userId,
|
||||
// isChild = isChild,
|
||||
// limit = limit,
|
||||
// offset = offset
|
||||
// )
|
||||
// )
|
||||
// } catch (e: Exception) {
|
||||
// e.printStackTrace()
|
||||
// return ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @GetMapping("/{id}")
|
||||
// fun getTransaction(@PathVariable id: String): ResponseEntity<Any> {
|
||||
// try {
|
||||
// return ResponseEntity.ok(financialService.getTransactionById(id))
|
||||
// } catch (e: Exception) {
|
||||
// e.printStackTrace()
|
||||
// return ResponseEntity(e.message, HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @PostMapping
|
||||
// fun createTransaction(@RequestParam spaceId: String, @RequestBody transaction: Transaction): ResponseEntity<Any> {
|
||||
// try {
|
||||
// return ResponseEntity.ok(financialService.createTransaction(spaceId,transaction))
|
||||
// } catch (e: Exception) {
|
||||
// e.printStackTrace()
|
||||
// return ResponseEntity(e.message, HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
//
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @PutMapping("/{id}")
|
||||
// fun editTransaction(@PathVariable id: String, @RequestBody transaction: Transaction): ResponseEntity<Any> {
|
||||
// try {
|
||||
// return ResponseEntity.ok(financialService.editTransaction(transaction))
|
||||
// } catch (e: Exception) {
|
||||
// e.printStackTrace()
|
||||
// return ResponseEntity(e.message, HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//// @DeleteMapping("/{id}")
|
||||
//// fun deleteTransaction(@PathVariable id: String): Mono<Void> {
|
||||
////
|
||||
//// return financialService.deleteTransaction(id)
|
||||
////
|
||||
//// }
|
||||
//
|
||||
//
|
||||
// @GetMapping("/{id}/child")
|
||||
// fun getChildTransactions(@PathVariable id: String): ResponseEntity<Any> {
|
||||
// return ResponseEntity.ok(financialService.getChildTransaction(id))
|
||||
// }
|
||||
//
|
||||
// @GetMapping("/avg-by-category")
|
||||
// fun getAvgSums(): ResponseEntity<Any> {
|
||||
// return ResponseEntity.ok(financialService.getAverageSpendingByCategory())
|
||||
// }
|
||||
|
||||
|
||||
@GetMapping("/{id}/child")
|
||||
fun getChildTransactions(@PathVariable id: String): ResponseEntity<Any> {
|
||||
return ResponseEntity.ok(financialService.getChildTransaction(id))
|
||||
}
|
||||
|
||||
@GetMapping("/avg-by-category")
|
||||
fun getAvgSums(): ResponseEntity<Any> {
|
||||
return ResponseEntity.ok(financialService.getAverageSpendingByCategory())
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/types")
|
||||
@GetMapping("/types/")
|
||||
fun getTypes(): ResponseEntity<Any> {
|
||||
return try {
|
||||
ResponseEntity.ok(financialService.getTransactionTypes())
|
||||
|
||||
@@ -7,7 +7,9 @@ import space.luminic.budgerapp.models.*
|
||||
import java.time.ZoneId
|
||||
|
||||
@Component
|
||||
class BudgetMapper : FromDocumentMapper {
|
||||
class BudgetMapper(private val categoryMapper: CategoryMapper) : FromDocumentMapper {
|
||||
|
||||
|
||||
override fun fromDocument(document: Document): Budget {
|
||||
return Budget(
|
||||
id = document.getObjectId("_id").toString(),
|
||||
@@ -20,16 +22,8 @@ class BudgetMapper : FromDocumentMapper {
|
||||
it.getObjectId("_id").toString() == cat.get("category", DBRef::class.java).id.toString()
|
||||
}
|
||||
BudgetCategory(
|
||||
category = Category(
|
||||
id = cat.get("category", DBRef::class.java).id.toString(),
|
||||
type = CategoryType(
|
||||
categoryDetailed.get("type", Document::class.java).getString("code"),
|
||||
categoryDetailed.get("type", Document::class.java).getString("name")
|
||||
),
|
||||
name = categoryDetailed.getString("name"),
|
||||
description = categoryDetailed.getString("description"),
|
||||
icon = categoryDetailed.getString("icon")
|
||||
), currentLimit = cat.getDouble("currentLimit")
|
||||
category = categoryMapper.fromDocument(categoryDetailed),
|
||||
currentLimit = cat.getDouble("currentLimit")
|
||||
)
|
||||
}.toMutableList(),
|
||||
incomeCategories = document.getList("incomeCategories", Document::class.java).map { cat ->
|
||||
@@ -38,16 +32,8 @@ class BudgetMapper : FromDocumentMapper {
|
||||
it.getObjectId("_id").toString() == cat.get("category", DBRef::class.java).id.toString()
|
||||
}
|
||||
BudgetCategory(
|
||||
category = Category(
|
||||
id = cat.get("category", DBRef::class.java).id.toString(),
|
||||
type = CategoryType(
|
||||
categoryDetailed.get("type", Document::class.java).getString("code"),
|
||||
categoryDetailed.get("type", Document::class.java).getString("name")
|
||||
),
|
||||
name = categoryDetailed.getString("name"),
|
||||
description = categoryDetailed.getString("description"),
|
||||
icon = categoryDetailed.getString("icon")
|
||||
), currentLimit = cat.getDouble("currentLimit")
|
||||
category = categoryMapper.fromDocument(categoryDetailed),
|
||||
currentLimit = cat.getDouble("currentLimit")
|
||||
)
|
||||
}.toMutableList()
|
||||
)
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package space.luminic.budgerapp.mappers
|
||||
|
||||
import org.bson.Document
|
||||
import org.bson.types.ObjectId
|
||||
import org.springframework.stereotype.Component
|
||||
import space.luminic.budgerapp.models.Category
|
||||
import space.luminic.budgerapp.models.CategoryTag
|
||||
import space.luminic.budgerapp.models.CategoryType
|
||||
import space.luminic.budgerapp.models.Space
|
||||
|
||||
|
||||
@Component
|
||||
class CategoryMapper : FromDocumentMapper {
|
||||
override fun fromDocument(document: Document): Category {
|
||||
val categoryTypeDocument = document["type"] as Document
|
||||
val spaceDocument = document.get("spaceDetails", Document::class.java) ?: Document()
|
||||
val spaceId = spaceDocument.get("_id", String::class.java)?:null
|
||||
val tags = document.getList("tags", Document::class.java) ?: emptyList()
|
||||
|
||||
val categoryTags = tags.map { tag ->
|
||||
CategoryTag(tag.getString("code"), tag.getString("name"))
|
||||
}.toMutableSet()
|
||||
return Category(
|
||||
id = (document["_id"] as ObjectId).toString(),
|
||||
space = Space(id = spaceId),
|
||||
type = CategoryType(
|
||||
categoryTypeDocument["code"] as String,
|
||||
categoryTypeDocument["name"] as String
|
||||
),
|
||||
name = document["name"] as String,
|
||||
description = document["description"] as String,
|
||||
icon = document["icon"] as String,
|
||||
tags = categoryTags,
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package space.luminic.budgerapp.mappers
|
||||
|
||||
import org.bson.Document
|
||||
import org.springframework.stereotype.Component
|
||||
import space.luminic.budgerapp.models.Category
|
||||
import space.luminic.budgerapp.models.CategoryType
|
||||
import space.luminic.budgerapp.models.Recurrent
|
||||
import space.luminic.budgerapp.models.Space
|
||||
|
||||
@Component
|
||||
class RecurrentMapper: FromDocumentMapper {
|
||||
override fun fromDocument(document: Document): Recurrent {
|
||||
val categoryDoc = document.get("categoryDetails", Document::class.java)
|
||||
val categoryTypeDoc = categoryDoc.get("type", Document::class.java)
|
||||
val spaceDocument = document.get("spaceDetails", Document::class.java)
|
||||
return Recurrent(
|
||||
id = document.getObjectId("_id").toString(),
|
||||
space = null,
|
||||
atDay = document.getInteger("atDay"),
|
||||
category = Category(
|
||||
id = categoryDoc.getObjectId("_id").toString(),
|
||||
space = null,
|
||||
type = CategoryType(categoryTypeDoc.getString("code"), categoryTypeDoc.getString("name")),
|
||||
name = categoryDoc.getString("name"),
|
||||
description = categoryDoc.getString("description"),
|
||||
icon = categoryDoc.getString("icon"),
|
||||
),
|
||||
name = document.getString("name"),
|
||||
description = document.getString("description"),
|
||||
amount = document.getInteger("amount"),
|
||||
createdAt = document.getDate("createdAt"),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -11,9 +11,10 @@ data class Category(
|
||||
val id: String? = null,
|
||||
@DBRef var space: Space? = null,
|
||||
var type: CategoryType,
|
||||
val name: String,
|
||||
val description: String? = null,
|
||||
val icon: String? = null
|
||||
var name: String,
|
||||
var description: String? = null,
|
||||
var icon: String? = null,
|
||||
var tags: MutableSet<CategoryTag> = mutableSetOf(),
|
||||
)
|
||||
|
||||
|
||||
@@ -21,3 +22,28 @@ data class CategoryType(
|
||||
val code: String,
|
||||
val name: String? = null
|
||||
)
|
||||
|
||||
data class CategoryTag (
|
||||
val code: String,
|
||||
val name: String,
|
||||
|
||||
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as CategoryTag
|
||||
|
||||
if (code != other.code) return false
|
||||
if (name != other.name) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = code.hashCode()
|
||||
result = 31 * result + name.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
16
src/main/kotlin/space/luminic/budgerapp/models/Tag.kt
Normal file
16
src/main/kotlin/space/luminic/budgerapp/models/Tag.kt
Normal file
@@ -0,0 +1,16 @@
|
||||
package space.luminic.budgerapp.models
|
||||
|
||||
import org.springframework.data.annotation.Id
|
||||
import org.springframework.data.mongodb.core.mapping.DBRef
|
||||
import org.springframework.data.mongodb.core.mapping.Document
|
||||
|
||||
|
||||
|
||||
|
||||
@Document(collection = "tags")
|
||||
data class Tag(
|
||||
@Id var id: String? = null,
|
||||
@DBRef var space: Space? = null,
|
||||
var code: String,
|
||||
var name: String,
|
||||
)
|
||||
7
src/main/kotlin/space/luminic/budgerapp/repos/TagRepo.kt
Normal file
7
src/main/kotlin/space/luminic/budgerapp/repos/TagRepo.kt
Normal file
@@ -0,0 +1,7 @@
|
||||
package space.luminic.budgerapp.repos
|
||||
|
||||
import org.springframework.data.mongodb.repository.ReactiveMongoRepository
|
||||
import space.luminic.budgerapp.models.Tag
|
||||
|
||||
interface TagRepo : ReactiveMongoRepository<Tag, String> {
|
||||
}
|
||||
@@ -14,4 +14,6 @@ interface UserRepo : ReactiveMongoRepository<User, String> {
|
||||
fun findByUsernameWOPassword(username: String): Mono<User>
|
||||
|
||||
fun findByUsername(username: String): Mono<User>
|
||||
|
||||
fun findByTgId(id: String): Mono<User>
|
||||
}
|
||||
@@ -43,6 +43,25 @@ class AuthService(
|
||||
}
|
||||
}
|
||||
|
||||
fun tgLogin(tgId: String): Mono<String> {
|
||||
return userRepository.findByTgId(tgId)
|
||||
.switchIfEmpty(Mono.error(AuthException("Invalid credentials")))
|
||||
.flatMap { user ->
|
||||
println("here")
|
||||
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()
|
||||
)
|
||||
).thenReturn(token)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun register(username: String, password: String, firstName: String): Mono<User> {
|
||||
return userRepository.findByUsername(username)
|
||||
.flatMap<User> { Mono.error(IllegalArgumentException("User with username '$username' already exists")) } // Ошибка, если пользователь уже существует
|
||||
|
||||
@@ -8,7 +8,6 @@ import org.springframework.cache.annotation.CacheEvict
|
||||
import org.springframework.cache.annotation.Cacheable
|
||||
import org.springframework.context.ApplicationEventPublisher
|
||||
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.query.Criteria
|
||||
@@ -16,72 +15,102 @@ import org.springframework.data.mongodb.core.query.isEqualTo
|
||||
import org.springframework.stereotype.Service
|
||||
import reactor.core.publisher.Flux
|
||||
import reactor.core.publisher.Mono
|
||||
import space.luminic.budgerapp.mappers.CategoryMapper
|
||||
import space.luminic.budgerapp.models.*
|
||||
import space.luminic.budgerapp.repos.CategoryRepo
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import java.time.LocalTime
|
||||
import java.time.ZoneId
|
||||
import java.time.ZoneOffset
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
|
||||
@Service
|
||||
class CategoryService(
|
||||
private val categoryRepo: CategoryRepo,
|
||||
private val financialService: FinancialService,
|
||||
private val mongoTemplate: ReactiveMongoTemplate,
|
||||
private val eventPublisher: ApplicationEventPublisher
|
||||
) {
|
||||
private val eventPublisher: ApplicationEventPublisher,
|
||||
private val categoryMapper: CategoryMapper,
|
||||
|
||||
) {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(javaClass)
|
||||
|
||||
|
||||
fun getCategory(id: String): Mono<Category> {
|
||||
return categoryRepo.findById(id)
|
||||
}
|
||||
|
||||
|
||||
// @Cacheable("categories")
|
||||
fun getCategories(spaceId: String, type: String? = null, sortBy: String, direction: String): Mono<List<Category>> {
|
||||
fun findCategory(
|
||||
space: Space? = null,
|
||||
id: String? = null,
|
||||
name: String? = null,
|
||||
tagCode: String? = null
|
||||
): Mono<Category> {
|
||||
val lookupSpaces = lookup("spaces", "space.\$id", "_id", "spaceDetails")
|
||||
val unwindSpace = unwind("spaceDetails")
|
||||
val matchCriteria = mutableListOf<Criteria>()
|
||||
|
||||
// Добавляем фильтры
|
||||
matchCriteria.add(Criteria.where("spaceDetails._id").`is`(ObjectId(spaceId)))
|
||||
type?.let { matchCriteria.add(Criteria.where("type.code").isEqualTo(it)) }
|
||||
matchCriteria.add(Criteria.where("spaceDetails._id").`is`(ObjectId(space!!.id)))
|
||||
id?.let { matchCriteria.add(Criteria.where("_id").`is`(ObjectId(id))) }
|
||||
name?.let { matchCriteria.add(Criteria.where("name").isEqualTo(it.trim())) }
|
||||
tagCode?.let { matchCriteria.add(Criteria.where("tags.code").`is`(it)) }
|
||||
|
||||
val match = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
|
||||
|
||||
|
||||
// val project = project("_id", "type", "name", "description", "icon")
|
||||
|
||||
val aggregationBuilder = mutableListOf(
|
||||
lookupSpaces,
|
||||
unwindSpace,
|
||||
match.takeIf { matchCriteria.isNotEmpty() },
|
||||
).filterNotNull()
|
||||
|
||||
val aggregation = newAggregation(aggregationBuilder)
|
||||
return mongoTemplate.aggregate(
|
||||
aggregation, "categories", Document::class.java
|
||||
).next()
|
||||
.map { doc ->
|
||||
categoryMapper.fromDocument(doc)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun getCategories(
|
||||
spaceId: String,
|
||||
type: String? = null,
|
||||
sortBy: String,
|
||||
direction: String,
|
||||
tagCode: String? = null
|
||||
): Mono<List<Category>> {
|
||||
val lookupSpaces = lookup("spaces", "space.\$id", "_id", "spaceDetails")
|
||||
val unwindSpace = unwind("spaceDetails")
|
||||
val matchCriteria = mutableListOf<Criteria>()
|
||||
// Добавляем фильтры
|
||||
matchCriteria.add(Criteria.where("spaceDetails._id").`is`(ObjectId(spaceId)))
|
||||
type?.let { matchCriteria.add(Criteria.where("type.code").isEqualTo(it)) }
|
||||
tagCode?.let { matchCriteria.add(Criteria.where("tags.code").`is`(it)) }
|
||||
|
||||
val match = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
|
||||
|
||||
val sort = sort(Sort.by(direction, sortBy))
|
||||
|
||||
val lookupSpaces = lookup("spaces", "space.\$id", "_id", "spaceDetails")
|
||||
val project = project("_id", "type", "name", "description", "icon")
|
||||
// val project = project("_id", "type", "name", "description", "icon")
|
||||
|
||||
val aggregationBuilder = mutableListOf(
|
||||
|
||||
lookupSpaces,
|
||||
unwindSpace,
|
||||
match.takeIf { matchCriteria.isNotEmpty() },
|
||||
project,
|
||||
// project,
|
||||
sort,
|
||||
).filterNotNull()
|
||||
|
||||
val aggregation = newAggregation(aggregationBuilder)
|
||||
logger.error("STARTED")
|
||||
return mongoTemplate.aggregate(
|
||||
aggregation, "categories", Category::class.java
|
||||
aggregation, "categories", Document::class.java
|
||||
)
|
||||
.collectList() // Преобразуем Flux<Transaction> в Mono<List<Transaction>>
|
||||
.map { it.toMutableList() }
|
||||
.map { docs ->
|
||||
docs.map { doc ->
|
||||
categoryMapper.fromDocument(doc)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Cacheable("getAllCategories")
|
||||
fun getCategories2(type: String? = null, sortBy: String, direction: String): Mono<List<Category>> {
|
||||
return categoryRepo.findAll(Sort.by(if (direction == "ASC") Direction.ASC else Direction.DESC, sortBy))
|
||||
.collectList()
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Cacheable("categoryTypes")
|
||||
@@ -93,681 +122,44 @@ class CategoryService(
|
||||
}
|
||||
|
||||
|
||||
|
||||
@CacheEvict(cacheNames = ["getAllCategories"], allEntries = true)
|
||||
fun editCategory(category: Category): Mono<Category> {
|
||||
return categoryRepo.findById(category.id!!) // Возвращаем Mono<Category>
|
||||
fun editCategory(space: Space, category: Category): Mono<Category> {
|
||||
return findCategory(space, id = category.id) // Возвращаем Mono<Category>
|
||||
.flatMap { oldCategory ->
|
||||
if (oldCategory.type.code != category.type.code) {
|
||||
return@flatMap Mono.error<Category>(IllegalArgumentException("You cannot change category type"))
|
||||
}
|
||||
category.space = space
|
||||
categoryRepo.save(category) // Сохраняем категорию, если тип не изменился
|
||||
}
|
||||
}
|
||||
|
||||
@CacheEvict(cacheNames = ["getAllCategories"], allEntries = true)
|
||||
// fun deleteCategory(categoryId: String): Mono<String> {
|
||||
// return categoryRepo.findById(categoryId).switchIfEmpty(
|
||||
// Mono.error(IllegalArgumentException("Category with id: $categoryId not found"))
|
||||
// ).flatMap {
|
||||
// financialService.getTransactions(categoryId = categoryId)
|
||||
// .flatMapMany { transactions ->
|
||||
// categoryRepo.findByName("Другое").switchIfEmpty(
|
||||
// categoryRepo.save(
|
||||
// Category(
|
||||
// type = CategoryType("EXPENSE", "Траты"),
|
||||
// name = "Другое",
|
||||
// description = "Категория для других трат",
|
||||
// icon = "🚮"
|
||||
// )
|
||||
// )
|
||||
// ).flatMapMany { category ->
|
||||
// Flux.fromIterable(transactions).flatMap { transaction ->
|
||||
// transaction.category = category // Присваиваем конкретный объект категории
|
||||
// financialService.editTransaction(transaction) // Сохраняем изменения
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// .then(categoryRepo.deleteById(categoryId)) // Удаляем старую категорию
|
||||
// .thenReturn(categoryId) // Возвращаем удалённую категорию
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
fun getBudgetCategories(dateFrom: LocalDate, dateTo: LocalDate): Mono<Map<String, Map<String, Double>>> {
|
||||
val pipeline = listOf(
|
||||
Document(
|
||||
"\$lookup",
|
||||
Document("from", "transactions")
|
||||
.append(
|
||||
"let",
|
||||
Document("categoryId", "\$_id")
|
||||
)
|
||||
.append(
|
||||
"pipeline", listOf(
|
||||
Document(
|
||||
"\$match",
|
||||
Document(
|
||||
"\$expr",
|
||||
Document(
|
||||
"\$and", listOf(
|
||||
Document("\$eq", listOf("\$category.\$id", "\$\$categoryId")),
|
||||
Document(
|
||||
"\$gte", listOf(
|
||||
"\$date",
|
||||
Date.from(
|
||||
LocalDateTime.of(dateFrom, LocalTime.MIN)
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.withZoneSameInstant(ZoneOffset.UTC).toInstant()
|
||||
),
|
||||
)
|
||||
),
|
||||
Document(
|
||||
"\$lte", listOf(
|
||||
"\$date",
|
||||
Date.from(
|
||||
LocalDateTime.of(dateTo, LocalTime.MIN)
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.withZoneSameInstant(ZoneOffset.UTC).toInstant()
|
||||
fun deleteCategory(space: Space, categoryId: String): Mono<String> {
|
||||
return findCategory(space, id = categoryId).switchIfEmpty(
|
||||
Mono.error(IllegalArgumentException("Category with id: $categoryId not found"))
|
||||
).flatMap {
|
||||
financialService.getTransactions(space.id!!, categoryId = categoryId)
|
||||
.flatMapMany { transactions ->
|
||||
findCategory(space, name = "Другое").switchIfEmpty(
|
||||
categoryRepo.save(
|
||||
Category(
|
||||
space = space,
|
||||
type = CategoryType("EXPENSE", "Траты"),
|
||||
name = "Другое",
|
||||
description = "Категория для других трат",
|
||||
icon = "🚮"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
Document(
|
||||
"\$group",
|
||||
Document("_id", "\$type.code")
|
||||
.append(
|
||||
"totalAmount",
|
||||
Document("\$sum", "\$amount")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.append("as", "transactionSums")
|
||||
),
|
||||
Document(
|
||||
"\$project",
|
||||
Document("_id", 1L)
|
||||
.append(
|
||||
"plannedAmount",
|
||||
Document(
|
||||
"\$arrayElemAt", listOf(
|
||||
Document(
|
||||
"\$filter",
|
||||
Document("input", "\$transactionSums")
|
||||
.append("as", "sum")
|
||||
.append(
|
||||
"cond",
|
||||
Document("\$eq", listOf("\$\$sum._id", "PLANNED"))
|
||||
)
|
||||
), 0L
|
||||
)
|
||||
)
|
||||
)
|
||||
.append(
|
||||
"instantAmount",
|
||||
Document(
|
||||
"\$arrayElemAt", listOf(
|
||||
Document(
|
||||
"\$filter",
|
||||
Document("input", "\$transactionSums")
|
||||
.append("as", "sum")
|
||||
.append(
|
||||
"cond",
|
||||
Document("\$eq", listOf("\$\$sum._id", "INSTANT"))
|
||||
)
|
||||
), 0L
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
Document(
|
||||
"\$addFields",
|
||||
Document(
|
||||
"plannedAmount",
|
||||
Document("\$ifNull", listOf("\$plannedAmount.totalAmount", 0.0))
|
||||
)
|
||||
.append(
|
||||
"instantAmount",
|
||||
Document("\$ifNull", listOf("\$instantAmount.totalAmount", 0.0))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
// Анализ плана выполнения (вывод для отладки)
|
||||
// getCategoriesExplainReactive(pipeline)
|
||||
// .doOnNext { explainResult ->
|
||||
// logger.info("Explain Result: ${explainResult.toJson()}")
|
||||
// }
|
||||
// .subscribe() // Этот вызов лучше оставить только для отладки
|
||||
//
|
||||
|
||||
|
||||
return mongoTemplate.getCollection("categories")
|
||||
.flatMapMany { it.aggregate(pipeline) }
|
||||
.collectList()
|
||||
.flatMap { result ->
|
||||
val categories = result.associate { document ->
|
||||
val id = document["_id"].toString()
|
||||
val values = mapOf(
|
||||
"plannedAmount" to (document["plannedAmount"] as Double? ?: 0.0),
|
||||
"instantAmount" to (document["instantAmount"] as Double? ?: 0.0)
|
||||
)
|
||||
id to values
|
||||
}
|
||||
|
||||
Mono.just(categories)
|
||||
).flatMapMany { category ->
|
||||
Flux.fromIterable(transactions).flatMap { transaction ->
|
||||
transaction.category = category // Присваиваем конкретный объект категории
|
||||
financialService.editTransaction(transaction) // Сохраняем изменения
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun getCategoryTransactionPipeline(dateFrom: LocalDate, dateTo: LocalDate): Mono<MutableList<BudgetCategory>> {
|
||||
val pipeline = listOf(
|
||||
Document("\$match", Document("type.code", "EXPENSE")),
|
||||
Document(
|
||||
"\$lookup",
|
||||
Document("from", "transactions")
|
||||
.append(
|
||||
"let",
|
||||
Document("categoryId", "\$_id")
|
||||
)
|
||||
.append(
|
||||
"pipeline", listOf(
|
||||
Document(
|
||||
"\$match",
|
||||
Document(
|
||||
"\$expr",
|
||||
Document(
|
||||
"\$and", listOf(
|
||||
Document("\$eq", listOf("\$category.\$id", "\$\$categoryId")),
|
||||
Document(
|
||||
"\$gte", listOf(
|
||||
"\$date",
|
||||
Date.from(
|
||||
LocalDateTime.of(dateFrom, LocalTime.MIN)
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.withZoneSameInstant(ZoneOffset.UTC).toInstant()
|
||||
)
|
||||
)
|
||||
),
|
||||
Document(
|
||||
"\$lt", listOf(
|
||||
"\$date",
|
||||
Date.from(
|
||||
LocalDateTime.of(dateTo, LocalTime.MIN)
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.withZoneSameInstant(ZoneOffset.UTC).toInstant()
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
Document(
|
||||
"\$group",
|
||||
Document("_id", "\$type.code")
|
||||
.append(
|
||||
"totalAmount",
|
||||
Document("\$sum", "\$amount")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.append("as", "transactionSums")
|
||||
),
|
||||
Document(
|
||||
"\$project",
|
||||
Document("_id", 1L)
|
||||
.append("type", 1L)
|
||||
.append("name", 1L)
|
||||
.append("description", 1L)
|
||||
.append("icon", 1L)
|
||||
.append(
|
||||
"plannedAmount",
|
||||
Document(
|
||||
"\$arrayElemAt", listOf(
|
||||
Document(
|
||||
"\$filter",
|
||||
Document("input", "\$transactionSums")
|
||||
.append("as", "sum")
|
||||
.append(
|
||||
"cond",
|
||||
Document("\$eq", listOf("\$\$sum._id", "PLANNED"))
|
||||
)
|
||||
), 0.0
|
||||
)
|
||||
)
|
||||
)
|
||||
.append(
|
||||
"instantAmount",
|
||||
Document(
|
||||
"\$arrayElemAt", listOf(
|
||||
Document(
|
||||
"\$filter",
|
||||
Document("input", "\$transactionSums")
|
||||
.append("as", "sum")
|
||||
.append(
|
||||
"cond",
|
||||
Document("\$eq", listOf("\$\$sum._id", "INSTANT"))
|
||||
)
|
||||
), 0.0
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
Document(
|
||||
"\$addFields",
|
||||
Document(
|
||||
"plannedAmount",
|
||||
Document("\$ifNull", listOf("\$plannedAmount.totalAmount", 0.0))
|
||||
)
|
||||
.append(
|
||||
"instantAmount",
|
||||
Document("\$ifNull", listOf("\$instantAmount.totalAmount", 0.0))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
return mongoTemplate.getCollection("categories")
|
||||
.flatMapMany { it.aggregate(pipeline, Document::class.java) }
|
||||
.map { document ->
|
||||
val catType = document["type"] as Document
|
||||
BudgetCategory(
|
||||
currentSpent = document["instantAmount"] as Double,
|
||||
currentLimit = document["plannedAmount"] as Double,
|
||||
currentPlanned = document["plannedAmount"] as Double,
|
||||
category = Category(
|
||||
document["_id"].toString(),
|
||||
type = CategoryType(catType["code"] as String, catType["name"] as String),
|
||||
name = document["name"] as String,
|
||||
description = document["description"] as String,
|
||||
icon = document["icon"] as String
|
||||
)
|
||||
)
|
||||
}
|
||||
.collectList()
|
||||
.map { it.toMutableList() }
|
||||
.then(categoryRepo.deleteById(categoryId)) // Удаляем старую категорию
|
||||
.thenReturn(categoryId) // Возвращаем удалённую категорию
|
||||
}
|
||||
|
||||
|
||||
fun getCategorySumsPipeline(dateFrom: LocalDate, dateTo: LocalDate): Mono<List<Document>> {
|
||||
val pipeline = listOf(
|
||||
Document(
|
||||
"\$lookup",
|
||||
Document("from", "categories")
|
||||
.append("localField", "category.\$id")
|
||||
.append("foreignField", "_id")
|
||||
.append("as", "categoryDetails")
|
||||
),
|
||||
Document("\$unwind", "\$categoryDetails"),
|
||||
Document(
|
||||
"\$match",
|
||||
Document(
|
||||
"date",
|
||||
Document(
|
||||
"\$gte", Date.from(
|
||||
LocalDateTime.of(dateFrom, LocalTime.MIN)
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.withZoneSameInstant(ZoneOffset.UTC).toInstant()
|
||||
)
|
||||
)
|
||||
.append(
|
||||
"\$lte", LocalDateTime.of(dateTo, LocalTime.MIN)
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.withZoneSameInstant(ZoneOffset.UTC).toInstant()
|
||||
)
|
||||
)
|
||||
),
|
||||
Document(
|
||||
"\$group",
|
||||
Document(
|
||||
"_id",
|
||||
Document("categoryId", "\$categoryDetails._id")
|
||||
.append("categoryName", "\$categoryDetails.name")
|
||||
.append("year", Document("\$year", "\$date"))
|
||||
.append("month", Document("\$month", "\$date"))
|
||||
)
|
||||
.append("totalAmount", Document("\$sum", "\$amount"))
|
||||
),
|
||||
Document(
|
||||
"\$group",
|
||||
Document("_id", "\$_id.categoryId")
|
||||
.append("categoryName", Document("\$first", "\$_id.categoryName"))
|
||||
.append(
|
||||
"monthlyData",
|
||||
Document(
|
||||
"\$push",
|
||||
Document(
|
||||
"month",
|
||||
Document(
|
||||
"\$concat", listOf(
|
||||
Document("\$toString", "\$_id.year"), "-",
|
||||
Document(
|
||||
"\$cond", listOf(
|
||||
Document("\$lt", listOf("\$_id.month", 10L)),
|
||||
Document(
|
||||
"\$concat", listOf(
|
||||
"0",
|
||||
Document("\$toString", "\$_id.month")
|
||||
)
|
||||
),
|
||||
Document("\$toString", "\$_id.month")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.append("totalAmount", "\$totalAmount")
|
||||
)
|
||||
)
|
||||
),
|
||||
Document(
|
||||
"\$addFields",
|
||||
Document(
|
||||
"completeMonthlyData",
|
||||
Document(
|
||||
"\$map",
|
||||
Document("input", Document("\$range", listOf(0L, 6L)))
|
||||
.append("as", "offset")
|
||||
.append(
|
||||
"in",
|
||||
Document(
|
||||
"month",
|
||||
Document(
|
||||
"\$dateToString",
|
||||
Document("format", "%Y-%m")
|
||||
.append(
|
||||
"date",
|
||||
Document(
|
||||
"\$dateAdd",
|
||||
Document("startDate", java.util.Date(1754006400000L))
|
||||
.append("unit", "month")
|
||||
.append(
|
||||
"amount",
|
||||
Document("\$multiply", listOf("\$\$offset", 1L))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.append(
|
||||
"totalAmount",
|
||||
Document(
|
||||
"\$let",
|
||||
Document(
|
||||
"vars",
|
||||
Document(
|
||||
"matched",
|
||||
Document(
|
||||
"\$arrayElemAt", listOf(
|
||||
Document(
|
||||
"\$filter",
|
||||
Document("input", "\$monthlyData")
|
||||
.append("as", "data")
|
||||
.append(
|
||||
"cond",
|
||||
Document(
|
||||
"\$eq", listOf(
|
||||
"\$\$data.month",
|
||||
Document(
|
||||
"\$dateToString",
|
||||
Document("format", "%Y-%m")
|
||||
.append(
|
||||
"date",
|
||||
Document(
|
||||
"\$dateAdd",
|
||||
Document(
|
||||
"startDate",
|
||||
java.util.Date(
|
||||
1733011200000L
|
||||
)
|
||||
)
|
||||
.append(
|
||||
"unit",
|
||||
"month"
|
||||
)
|
||||
.append(
|
||||
"amount",
|
||||
Document(
|
||||
"\$multiply",
|
||||
listOf(
|
||||
"\$\$offset",
|
||||
1L
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
), 0L
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.append(
|
||||
"in",
|
||||
Document("\$ifNull", listOf("\$\$matched.totalAmount", 0L))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
Document(
|
||||
"\$project",
|
||||
Document("_id", 0L)
|
||||
.append("categoryId", "\$_id")
|
||||
.append("categoryName", "\$categoryName")
|
||||
.append("monthlyData", "\$completeMonthlyData")
|
||||
)
|
||||
)
|
||||
|
||||
return mongoTemplate.getCollection("transactions")
|
||||
.flatMapMany { it.aggregate(pipeline) }
|
||||
.map {
|
||||
it["categoryId"] = it["categoryId"].toString()
|
||||
it
|
||||
}
|
||||
.collectList()
|
||||
}
|
||||
|
||||
fun getCategorySummaries(dateFrom: LocalDate): Mono<List<Document>> {
|
||||
val sixMonthsAgo = Date.from(
|
||||
LocalDateTime.of(dateFrom, LocalTime.MIN)
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.withZoneSameInstant(ZoneOffset.UTC).toInstant()
|
||||
) // Пример даты, можно заменить на вычисляемую
|
||||
|
||||
val aggregation = listOf(
|
||||
// 1. Фильтр за последние 6 месяцев
|
||||
Document(
|
||||
"\$match",
|
||||
Document("date", Document("\$gte", sixMonthsAgo).append("\$lt", Date())).append("type.code", "INSTANT")
|
||||
),
|
||||
|
||||
// 2. Группируем по категории + (год, месяц)
|
||||
Document(
|
||||
"\$group", Document(
|
||||
"_id", Document("category", "\$category.\$id")
|
||||
.append("year", Document("\$year", "\$date"))
|
||||
.append("month", Document("\$month", "\$date"))
|
||||
)
|
||||
.append("totalAmount", Document("\$sum", "\$amount"))
|
||||
),
|
||||
|
||||
// 3. Подтягиваем информацию о категории
|
||||
Document(
|
||||
"\$lookup", Document("from", "categories")
|
||||
.append("localField", "_id.category")
|
||||
.append("foreignField", "_id")
|
||||
.append("as", "categoryInfo")
|
||||
),
|
||||
|
||||
// 4. Распаковываем массив категорий
|
||||
Document("\$unwind", "\$categoryInfo"),
|
||||
|
||||
// 5. Фильтруем по типу категории (EXPENSE)
|
||||
Document("\$match", Document("categoryInfo.type.code", "EXPENSE")),
|
||||
|
||||
// 6. Группируем обратно по категории, собирая все (год, месяц, total)
|
||||
Document(
|
||||
"\$group", Document("_id", "\$_id.category")
|
||||
.append("categoryName", Document("\$first", "\$categoryInfo.name"))
|
||||
.append("categoryType", Document("\$first", "\$categoryInfo.type.code"))
|
||||
.append("categoryIcon", Document("\$first", "\$categoryInfo.icon"))
|
||||
.append(
|
||||
"monthlySums", Document(
|
||||
"\$push", Document("year", "\$_id.year")
|
||||
.append("month", "\$_id.month")
|
||||
.append("total", "\$totalAmount")
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
// 7. Формируем единый массив из 6 элементов:
|
||||
// - каждый элемент = {year, month, total},
|
||||
// - если нет записей за месяц, ставим total=0
|
||||
Document(
|
||||
"\$project", Document("categoryName", 1)
|
||||
.append("categoryType", 1)
|
||||
.append("categoryIcon", 1)
|
||||
.append(
|
||||
"monthlySums", Document(
|
||||
"\$map", Document("input", Document("\$range", listOf(0, 6)))
|
||||
.append("as", "i")
|
||||
.append(
|
||||
"in", Document(
|
||||
"\$let", Document(
|
||||
"vars", Document(
|
||||
"subDate", Document(
|
||||
"\$dateSubtract", Document("startDate", Date())
|
||||
.append("unit", "month")
|
||||
.append("amount", "$\$i")
|
||||
)
|
||||
)
|
||||
)
|
||||
.append(
|
||||
"in", Document("year", Document("\$year", "$\$subDate"))
|
||||
.append("month", Document("\$month", "$\$subDate"))
|
||||
.append(
|
||||
"total", Document(
|
||||
"\$ifNull", listOf(
|
||||
Document(
|
||||
"\$getField", Document("field", "total")
|
||||
.append(
|
||||
"input", Document(
|
||||
"\$arrayElemAt", listOf(
|
||||
Document(
|
||||
"\$filter",
|
||||
Document(
|
||||
"input",
|
||||
"\$monthlySums"
|
||||
)
|
||||
.append("as", "ms")
|
||||
.append(
|
||||
"cond", Document(
|
||||
"\$and", listOf(
|
||||
Document(
|
||||
"\$eq",
|
||||
listOf(
|
||||
"$\$ms.year",
|
||||
Document(
|
||||
"\$year",
|
||||
"$\$subDate"
|
||||
)
|
||||
)
|
||||
),
|
||||
Document(
|
||||
"\$eq",
|
||||
listOf(
|
||||
"$\$ms.month",
|
||||
Document(
|
||||
"\$month",
|
||||
"$\$subDate"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
), 0.0
|
||||
)
|
||||
)
|
||||
)
|
||||
), 0.0
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
// 8. Сортируем результат по имени категории
|
||||
Document("\$sort", Document("categoryName", 1))
|
||||
)
|
||||
|
||||
// Выполняем агрегацию
|
||||
return mongoTemplate.getCollection("transactions")
|
||||
.flatMapMany { it.aggregate(aggregation) }
|
||||
.map { document ->
|
||||
// Преобразуем _id в строку
|
||||
document["_id"] = document["_id"].toString()
|
||||
|
||||
// Получаем monthlySums и приводим к изменяемому списку
|
||||
val monthlySums = (document["monthlySums"] as? List<*>)?.map { monthlySum ->
|
||||
if (monthlySum is Document) {
|
||||
// Создаем копию Document, чтобы избежать изменений в исходном списке
|
||||
Document(monthlySum).apply {
|
||||
// Добавляем поле date
|
||||
val date = LocalDate.of(getInteger("year"), getInteger("month"), 1)
|
||||
this["date"] = date
|
||||
}
|
||||
} else {
|
||||
monthlySum
|
||||
}
|
||||
}?.toMutableList()
|
||||
|
||||
// Сортируем monthlySums по полю date
|
||||
val sortedMonthlySums = monthlySums?.sortedBy { (it as? Document)?.get("date") as? LocalDate }
|
||||
|
||||
// Рассчитываем разницу между текущим и предыдущим месяцем
|
||||
var previousMonthSum = 0.0
|
||||
sortedMonthlySums?.forEach { monthlySum ->
|
||||
if (monthlySum is Document) {
|
||||
val currentMonthSum = monthlySum.getDouble("total") ?: 0.0
|
||||
|
||||
// Рассчитываем разницу в процентах
|
||||
val difference = if (previousMonthSum != 0.0 && currentMonthSum != 0.0) {
|
||||
(((currentMonthSum - previousMonthSum) / previousMonthSum) * 100).toInt()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
// Добавляем поле difference
|
||||
monthlySum["difference"] = difference
|
||||
|
||||
// Обновляем previousMonthSum для следующей итерации
|
||||
previousMonthSum = currentMonthSum
|
||||
}
|
||||
}
|
||||
|
||||
// Обновляем документ с отсортированными и обновленными monthlySums
|
||||
document["monthlySums"] = sortedMonthlySums
|
||||
document
|
||||
}
|
||||
.collectList()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ class FinancialService(
|
||||
val recurrentService: RecurrentService,
|
||||
val userService: UserService,
|
||||
val reactiveMongoTemplate: ReactiveMongoTemplate,
|
||||
private val spaceService: SpaceService,
|
||||
private val categoryRepo: CategoryRepo,
|
||||
val transactionsMapper: TransactionsMapper,
|
||||
val budgetMapper: BudgetMapper
|
||||
@@ -197,34 +196,9 @@ class FinancialService(
|
||||
Sort.by(it.order, it.by)
|
||||
} ?: Sort.by(Direction.DESC, "dateFrom")
|
||||
|
||||
return ReactiveSecurityContextHolder.getContext().map { it.authentication }.flatMap { authentication ->
|
||||
val username = authentication.name
|
||||
spaceService.getSpace(spaceId)
|
||||
.switchIfEmpty(Mono.error(IllegalArgumentException("Space not found for $spaceId")))
|
||||
.flatMap { space ->
|
||||
userService.getByUsername(username)
|
||||
.switchIfEmpty(Mono.error(IllegalArgumentException("User not found for username: $username")))
|
||||
.flatMap { user ->
|
||||
val userIds = space.users.mapNotNull { it.id }
|
||||
if (user.id !in userIds) {
|
||||
Mono.error(IllegalArgumentException("User cannot access this Space"))
|
||||
} else {
|
||||
val spaceObjectId = try {
|
||||
ObjectId(space.id!!) // Преобразуем строку в ObjectId
|
||||
} catch (e: IllegalArgumentException) {
|
||||
return@flatMap Mono.error(IllegalArgumentException("Invalid Space ID format: ${space.id}"))
|
||||
return findProjectedBudgets(ObjectId(spaceId), sort)
|
||||
}
|
||||
|
||||
println("Space ID type: ${spaceObjectId::class.java}, value: $spaceObjectId")
|
||||
// Применяем сортировку к запросу
|
||||
findProjectedBudgets(spaceObjectId, sort)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun findProjectedBudgets(spaceId: ObjectId, sortRequested: Sort? = null): Mono<List<Budget>> {
|
||||
val lookupCategories = lookup("categories", "categories.category.\$id", "_id", "categoriesDetails")
|
||||
@@ -380,10 +354,10 @@ class FinancialService(
|
||||
}
|
||||
|
||||
|
||||
fun createBudget(spaceId: String, budget: Budget, createRecurrent: Boolean): Mono<Budget> {
|
||||
return Mono.zip(getBudgetByDate(budget.dateFrom, spaceId).map { Optional.ofNullable(it) }
|
||||
fun createBudget(space: Space, budget: Budget, createRecurrent: Boolean): Mono<Budget> {
|
||||
return Mono.zip(getBudgetByDate(budget.dateFrom, space.id!!).map { Optional.ofNullable(it) }
|
||||
.switchIfEmpty(Mono.just(Optional.empty())),
|
||||
getBudgetByDate(budget.dateTo, spaceId).map { Optional.ofNullable(it) }
|
||||
getBudgetByDate(budget.dateTo, space.id!!).map { Optional.ofNullable(it) }
|
||||
.switchIfEmpty(Mono.just(Optional.empty()))).flatMap { tuple ->
|
||||
val startBudget = tuple.t1.orElse(null)
|
||||
val endBudget = tuple.t2.orElse(null)
|
||||
@@ -394,17 +368,7 @@ class FinancialService(
|
||||
}
|
||||
|
||||
// Получаем Space по spaceId
|
||||
spaceService.getSpace(spaceId)
|
||||
.switchIfEmpty(Mono.error(IllegalArgumentException("Space not found for $spaceId"))).flatMap { space ->
|
||||
// Проверяем, входит ли пользователь в этот Space
|
||||
ReactiveSecurityContextHolder.getContext().flatMap { securityContext ->
|
||||
val username = securityContext.authentication.name
|
||||
userService.getByUsername(username)
|
||||
.switchIfEmpty(Mono.error(IllegalArgumentException("User not found for username: $username")))
|
||||
.flatMap { user ->
|
||||
if (space.users.none { it.id == user.id }) {
|
||||
return@flatMap Mono.error<Budget>(IllegalArgumentException("User does not have access to this space"))
|
||||
}
|
||||
|
||||
|
||||
// Присваиваем Space бюджету
|
||||
budget.space = space
|
||||
@@ -419,7 +383,7 @@ class FinancialService(
|
||||
// Создаем бюджет после возможного создания рекуррентных транзакций
|
||||
recurrentsCreation.then(
|
||||
getCategoryTransactionPipeline(
|
||||
spaceId,
|
||||
space.id!!,
|
||||
budget.dateFrom,
|
||||
budget.dateTo
|
||||
).flatMap { categories ->
|
||||
@@ -433,7 +397,7 @@ class FinancialService(
|
||||
}.subscribe()
|
||||
}).then(
|
||||
getCategoryTransactionPipeline(
|
||||
spaceId,
|
||||
space.id!!,
|
||||
budget.dateFrom,
|
||||
budget.dateTo,
|
||||
"INCOME"
|
||||
@@ -449,9 +413,7 @@ class FinancialService(
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun getBudgetByDate(date: LocalDate, spaceId: String): Mono<Budget> {
|
||||
return budgetRepo.findByDateFromLessThanEqualAndDateToGreaterThanEqualAndSpace(date, date, ObjectId(spaceId))
|
||||
@@ -665,17 +627,6 @@ class FinancialService(
|
||||
limit: Int? = null,
|
||||
offset: Int? = null,
|
||||
): Mono<MutableList<Transaction>> {
|
||||
return ReactiveSecurityContextHolder.getContext().map { it.authentication }.flatMap { authentication ->
|
||||
val username = authentication.name
|
||||
spaceService.getSpace(spaceId)
|
||||
.switchIfEmpty(Mono.error(IllegalArgumentException("Space not found for $spaceId")))
|
||||
.flatMap { space ->
|
||||
userService.getByUsername(username)
|
||||
.switchIfEmpty(Mono.error(IllegalArgumentException("User not found for username: $username")))
|
||||
.flatMap { user ->
|
||||
if (space.users.none { it.id.toString() == user.id }) {
|
||||
return@flatMap Mono.error<MutableList<Transaction>>(IllegalArgumentException("User does not have access to this Space"))
|
||||
}
|
||||
|
||||
val matchCriteria = mutableListOf<Criteria>()
|
||||
|
||||
@@ -726,7 +677,7 @@ class FinancialService(
|
||||
|
||||
val aggregation = newAggregation(aggregationBuilder)
|
||||
|
||||
return@flatMap reactiveMongoTemplate.aggregate(
|
||||
return reactiveMongoTemplate.aggregate(
|
||||
aggregation, "transactions", Document::class.java
|
||||
).collectList().map { docs ->
|
||||
|
||||
@@ -737,9 +688,7 @@ class FinancialService(
|
||||
test
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun getTransactionByParentId(
|
||||
parentId: String
|
||||
@@ -832,12 +781,9 @@ class FinancialService(
|
||||
}
|
||||
|
||||
|
||||
fun createTransaction(spaceId: String, transaction: Transaction): Mono<Transaction> {
|
||||
fun createTransaction(space: Space, transaction: Transaction): Mono<Transaction> {
|
||||
return ReactiveSecurityContextHolder.getContext().map { it.authentication }.flatMap { authentication ->
|
||||
val username = authentication.name
|
||||
spaceService.getSpace(spaceId)
|
||||
.switchIfEmpty(Mono.error(IllegalArgumentException("Space not found for $spaceId")))
|
||||
.flatMap { space ->
|
||||
userService.getByUsername(username)
|
||||
.switchIfEmpty(Mono.error(IllegalArgumentException("User not found for username: $username")))
|
||||
.flatMap { user ->
|
||||
@@ -854,7 +800,6 @@ class FinancialService(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@CacheEvict(cacheNames = ["transactions", "budgets"], allEntries = true)
|
||||
@@ -1669,7 +1614,7 @@ class FinancialService(
|
||||
Document("\$unwind", "\$categoryInfo"),
|
||||
|
||||
// 5. Фильтруем по типу категории (EXPENSE)
|
||||
Document("\$match", Document("categoryInfo.type.code", "EXPENSE")),
|
||||
// Document("\$match", Document("categoryInfo.type.code", "EXPENSE")),
|
||||
|
||||
// 6. Группируем обратно по категории, собирая все (год, месяц, total)
|
||||
Document(
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package space.luminic.budgerapp.services
|
||||
|
||||
|
||||
|
||||
import org.bson.Document
|
||||
import org.bson.types.ObjectId
|
||||
import org.slf4j.LoggerFactory
|
||||
@@ -16,11 +15,11 @@ import org.springframework.security.core.context.ReactiveSecurityContextHolder
|
||||
import org.springframework.stereotype.Service
|
||||
import reactor.core.publisher.Flux
|
||||
import reactor.core.publisher.Mono
|
||||
import space.luminic.budgerapp.mappers.RecurrentMapper
|
||||
import space.luminic.budgerapp.models.*
|
||||
import space.luminic.budgerapp.repos.RecurrentRepo
|
||||
import space.luminic.budgerapp.repos.TransactionRepo
|
||||
import java.time.YearMonth
|
||||
import java.time.ZoneId
|
||||
|
||||
|
||||
@Service
|
||||
@@ -29,45 +28,28 @@ class RecurrentService(
|
||||
private val recurrentRepo: RecurrentRepo,
|
||||
private val transactionRepo: TransactionRepo,
|
||||
private val userService: UserService,
|
||||
private val spaceService: SpaceService,
|
||||
private val recurrentMapper: RecurrentMapper,
|
||||
) {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(javaClass)
|
||||
|
||||
|
||||
fun getRecurrents(space: Space): Mono<List<Recurrent>> {
|
||||
fun getRecurrents(spaceId: String): Mono<List<Recurrent>> {
|
||||
val lookupCategories = lookup("categories", "category.\$id", "_id", "categoryDetails")
|
||||
val unwindCategory = unwind("categoryDetails")
|
||||
|
||||
val lookupSpace = lookup("spaces", "space.\$id", "_id", "spaceDetails")
|
||||
val unwindSpace = unwind("spaceDetails")
|
||||
val matchStage = match(Criteria.where("spaceDetails._id").`is`(ObjectId(space.id)))
|
||||
val matchStage = match(Criteria.where("spaceDetails._id").`is`(ObjectId(spaceId)))
|
||||
|
||||
val sort =sort(Sort.by(Direction.ASC, "atDay"))
|
||||
val sort = sort(Sort.by(Direction.ASC, "atDay"))
|
||||
val aggregation =
|
||||
newAggregation(lookupCategories, unwindCategory,lookupSpace, unwindSpace, matchStage, sort)
|
||||
newAggregation(lookupCategories, unwindCategory, lookupSpace, unwindSpace, matchStage, sort)
|
||||
// Запрос рекуррентных платежей
|
||||
return reactiveMongoTemplate.aggregate(aggregation, "recurrents", Document::class.java).collectList().map { docs ->
|
||||
return reactiveMongoTemplate.aggregate(aggregation, "recurrents", Document::class.java).collectList()
|
||||
.map { docs ->
|
||||
docs.map { doc ->
|
||||
val categoryDoc = doc.get("categoryDetails", Document::class.java)
|
||||
val categoryTypeDoc = categoryDoc.get("type", Document::class.java)
|
||||
Recurrent(
|
||||
id = doc.getObjectId("_id").toString(),
|
||||
space = space,
|
||||
atDay = doc.getInteger("atDay"),
|
||||
category = Category(
|
||||
id = categoryDoc.getObjectId("_id").toString(),
|
||||
space = space,
|
||||
type = CategoryType(categoryTypeDoc.getString("code"), categoryTypeDoc.getString("name")),
|
||||
name = categoryDoc.getString("name"),
|
||||
description = categoryDoc.getString("description"),
|
||||
icon = categoryDoc.getString("icon"),
|
||||
),
|
||||
name = doc.getString("name"),
|
||||
description = doc.getString("description"),
|
||||
amount = doc.getInteger("amount"),
|
||||
createdAt = doc.getDate("createdAt"),
|
||||
)
|
||||
recurrentMapper.fromDocument(doc)
|
||||
}.toList()
|
||||
}
|
||||
}
|
||||
@@ -107,7 +89,7 @@ class RecurrentService(
|
||||
.switchIfEmpty(Mono.error(IllegalArgumentException("User not found for username: $username")))
|
||||
}
|
||||
.flatMapMany { user ->
|
||||
getRecurrents(space) // Теперь это Mono<List<Recurrent>>
|
||||
getRecurrents(space.id!!) // Теперь это Mono<List<Recurrent>>
|
||||
.flatMapMany { Flux.fromIterable(it) } // Преобразуем List<Recurrent> в Flux<Recurrent>
|
||||
.map { recurrent ->
|
||||
// Определяем дату транзакции
|
||||
@@ -115,9 +97,11 @@ class RecurrentService(
|
||||
recurrent.atDay in budget.dateFrom.dayOfMonth..daysInCurrentMonth -> {
|
||||
currentYearMonth.atDay(recurrent.atDay)
|
||||
}
|
||||
|
||||
recurrent.atDay < budget.dateFrom.dayOfMonth -> {
|
||||
currentYearMonth.atDay(recurrent.atDay).plusMonths(1)
|
||||
}
|
||||
|
||||
else -> {
|
||||
val extraDays = recurrent.atDay - daysInCurrentMonth
|
||||
currentYearMonth.plusMonths(1).atDay(extraDays)
|
||||
@@ -154,17 +138,7 @@ class RecurrentService(
|
||||
return recurrentRepo.deleteById(id)
|
||||
}
|
||||
|
||||
fun regenRecurrents(): Mono<List<Recurrent>> {
|
||||
return recurrentRepo.findAll()
|
||||
.flatMap { recurrent ->
|
||||
spaceService.getSpace("67af3c0f652da946a7dd9931")
|
||||
.flatMap { space ->
|
||||
recurrent.space = space
|
||||
recurrentRepo.save(recurrent) // Сохраняем и возвращаем сохраненный объект
|
||||
}
|
||||
}
|
||||
.collectList() // Собираем результаты в список
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,14 +1,19 @@
|
||||
package space.luminic.budgerapp.services
|
||||
|
||||
import org.bson.Document
|
||||
import org.bson.types.ObjectId
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.*
|
||||
import org.springframework.data.mongodb.core.query.Criteria
|
||||
import org.springframework.data.mongodb.core.query.Query
|
||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder
|
||||
import org.springframework.stereotype.Service
|
||||
import reactor.core.publisher.Flux
|
||||
import reactor.core.publisher.Mono
|
||||
import space.luminic.budgerapp.models.Category
|
||||
import space.luminic.budgerapp.models.Space
|
||||
import space.luminic.budgerapp.models.SpaceInvite
|
||||
import space.luminic.budgerapp.models.Tag
|
||||
import space.luminic.budgerapp.repos.*
|
||||
import java.time.LocalDateTime
|
||||
import java.util.UUID
|
||||
@@ -22,7 +27,11 @@ class SpaceService(
|
||||
private val reactiveMongoTemplate: ReactiveMongoTemplate,
|
||||
private val categoryRepo: CategoryRepo,
|
||||
private val recurrentRepo: RecurrentRepo,
|
||||
private val transactionRepo: TransactionRepo
|
||||
private val transactionRepo: TransactionRepo,
|
||||
private val financialService: FinancialService,
|
||||
private val categoryService: CategoryService,
|
||||
private val recurrentService: RecurrentService,
|
||||
private val tagRepo: TagRepo
|
||||
) {
|
||||
|
||||
fun isValidRequest(spaceId: String): Mono<Space> {
|
||||
@@ -101,27 +110,29 @@ class SpaceService(
|
||||
val objectId = ObjectId(space.id)
|
||||
|
||||
return Mono.`when`(
|
||||
budgetRepo.findBySpaceId(objectId)
|
||||
.flatMap { budgetRepo.delete(it) }
|
||||
financialService.findProjectedBudgets(objectId)
|
||||
.flatMapMany { Flux.fromIterable(it) }
|
||||
.flatMap { budgetRepo.deleteById(it.id!!) }
|
||||
.then(),
|
||||
|
||||
transactionRepo.findBySpaceId(objectId)
|
||||
.flatMap { transactionRepo.delete(it) }
|
||||
financialService.getTransactions(objectId.toString())
|
||||
.flatMapMany { Flux.fromIterable(it) }
|
||||
.flatMap { transactionRepo.deleteById(it.id!!) }
|
||||
.then(),
|
||||
|
||||
categoryRepo.findBySpaceId(objectId)
|
||||
.flatMap { categoryRepo.delete(it) }
|
||||
categoryService.getCategories(objectId.toString(), null, "name", "ASC")
|
||||
.flatMapMany { Flux.fromIterable(it) }
|
||||
.flatMap { categoryRepo.deleteById(it.id!!) }
|
||||
.then(),
|
||||
|
||||
recurrentRepo.findRecurrentsBySpaceId(objectId)
|
||||
.flatMap { recurrentRepo.delete(it) }
|
||||
recurrentService.getRecurrents(objectId.toString())
|
||||
.flatMapMany { Flux.fromIterable(it) }
|
||||
.flatMap { recurrentRepo.deleteById(it.id!!) }
|
||||
.then()
|
||||
).then(spaceRepo.deleteById(space.id!!)) // Исправлено: удаление по ID
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
fun createInviteSpace(spaceId: String): Mono<SpaceInvite> {
|
||||
return ReactiveSecurityContextHolder.getContext()
|
||||
.map { it.authentication }
|
||||
@@ -247,6 +258,106 @@ class SpaceService(
|
||||
}
|
||||
}
|
||||
|
||||
fun findTag(space: Space, tagCode: String): Mono<Tag> {
|
||||
val lookupSpaces = lookup("spaces", "space.\$id", "_id", "spaceDetails")
|
||||
val unwindSpace = unwind("spaceDetails")
|
||||
val matchCriteria = mutableListOf<Criteria>()
|
||||
// Добавляем фильтры
|
||||
matchCriteria.add(Criteria.where("spaceDetails._id").`is`(ObjectId(space.id)))
|
||||
matchCriteria.add(Criteria.where("code").`is`(tagCode))
|
||||
val match = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
|
||||
val aggregationBuilder = mutableListOf(
|
||||
lookupSpaces,
|
||||
unwindSpace,
|
||||
match.takeIf { matchCriteria.isNotEmpty() },
|
||||
).filterNotNull()
|
||||
|
||||
val aggregation = newAggregation(aggregationBuilder)
|
||||
return reactiveMongoTemplate.aggregate(
|
||||
aggregation, "tags", Document::class.java
|
||||
).next()
|
||||
.map { doc ->
|
||||
Tag(
|
||||
id = doc.getObjectId("_id").toString(),
|
||||
space = Space(id = doc.get("spaceDetails", Document::class.java).getObjectId("_id").toString()),
|
||||
code = doc.getString("code"),
|
||||
name = doc.getString("name")
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun createTag(space: Space, tag: Tag): Mono<Tag> {
|
||||
tag.space = space
|
||||
return findTag(space, tag.code)
|
||||
.flatMap { existingTag ->
|
||||
Mono.error<Tag>(IllegalArgumentException("Tag with code ${existingTag.code} already exists"))
|
||||
}
|
||||
.switchIfEmpty(tagRepo.save(tag))
|
||||
}
|
||||
|
||||
fun deleteTag(space: Space, tagCode: String): Mono<Void> {
|
||||
return findTag(space, tagCode)
|
||||
.switchIfEmpty(Mono.error(IllegalArgumentException("Tag with code $tagCode not found")))
|
||||
.flatMap { tag ->
|
||||
categoryService.getCategories(space.id!!, sortBy = "name", direction = "ASC", tagCode = tag.code)
|
||||
.flatMapMany { cats ->
|
||||
Flux.fromIterable(cats)
|
||||
.map { cat ->
|
||||
cat.tags.removeIf { it.code == tagCode } // Изменяем список тегов
|
||||
cat
|
||||
}
|
||||
.flatMap { categoryRepo.save(it) } // Сохраняем обновлённые категории
|
||||
}
|
||||
.then(tagRepo.deleteById(tag.id!!)) // Удаляем тег только после обновления категорий
|
||||
}
|
||||
}
|
||||
|
||||
fun getTags(space: Space): Mono<List<Tag>> {
|
||||
val lookupSpaces = lookup("spaces", "space.\$id", "_id", "spaceDetails")
|
||||
val unwindSpace = unwind("spaceDetails")
|
||||
val matchCriteria = mutableListOf<Criteria>()
|
||||
// Добавляем фильтры
|
||||
matchCriteria.add(Criteria.where("spaceDetails._id").`is`(ObjectId(space.id)))
|
||||
val match = match(Criteria().andOperator(*matchCriteria.toTypedArray()))
|
||||
val aggregationBuilder = mutableListOf(
|
||||
|
||||
lookupSpaces,
|
||||
unwindSpace,
|
||||
match.takeIf { matchCriteria.isNotEmpty() },
|
||||
).filterNotNull()
|
||||
|
||||
val aggregation = newAggregation(aggregationBuilder)
|
||||
return reactiveMongoTemplate.aggregate(
|
||||
aggregation, "tags", Document::class.java
|
||||
)
|
||||
.collectList() // Преобразуем Flux<Transaction> в Mono<List<Transaction>>
|
||||
.map { docs ->
|
||||
docs.map { doc ->
|
||||
Tag(
|
||||
id = doc.getObjectId("_id").toString(),
|
||||
space = Space(id = doc.get("spaceDetails", Document::class.java).getObjectId("_id").toString()),
|
||||
code = doc.getString("code"),
|
||||
name = doc.getString("name")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun regenSpaceCategory(): Mono<Category> {
|
||||
return getSpace("67af3c0f652da946a7dd9931")
|
||||
.flatMap { space ->
|
||||
categoryService.findCategory(id= "677bc767c7857460a491bd4f")
|
||||
.flatMap { category -> // заменил map на flatMap
|
||||
category.space = space
|
||||
category.name = "Сбережения"
|
||||
category.description = "Отчисления в накопления или инвестиционные счета"
|
||||
category.icon = "💰"
|
||||
categoryRepo.save(category) // теперь возвращаем Mono<Category>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fun regenSpaces(): Mono<List<Space>> {
|
||||
// return spaceRepo.findAll()
|
||||
// .flatMap { space ->
|
||||
|
||||
Reference in New Issue
Block a user