diff --git a/build.gradle.kts b/build.gradle.kts index 37db6a7..7acef27 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -38,10 +38,11 @@ dependencies { implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.springframework.boot:spring-boot-starter-validation") implementation("commons-logging:commons-logging:1.3.4") - implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.boot:spring-boot-starter-cache") implementation("org.springframework.boot:spring-boot-starter-security") + implementation ("org.springframework.boot:spring-boot-starter-actuator") + implementation("io.jsonwebtoken:jjwt-api:0.11.5") implementation("io.jsonwebtoken:jjwt-impl:0.11.5") @@ -53,10 +54,11 @@ dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:1.7.3") implementation("org.springframework.boot:spring-boot-starter-data-mongodb-reactive") implementation("com.google.code.gson:gson") + implementation("io.micrometer:micrometer-registry-prometheus") + - runtimeOnly("org.postgresql:postgresql") compileOnly("org.projectlombok:lombok") annotationProcessor("org.projectlombok:lombok") testImplementation("org.springframework.boot:spring-boot-starter-test") @@ -73,3 +75,5 @@ kotlin { tasks.withType { useJUnitPlatform() } + + diff --git a/src/main/kotlin/space/luminic/budgerapp/configs/BearerTokenFilter.kt b/src/main/kotlin/space/luminic/budgerapp/configs/BearerTokenFilter.kt index 8b8296b..842421a 100644 --- a/src/main/kotlin/space/luminic/budgerapp/configs/BearerTokenFilter.kt +++ b/src/main/kotlin/space/luminic/budgerapp/configs/BearerTokenFilter.kt @@ -26,7 +26,10 @@ class BearerTokenFilter(private val authService: AuthService) : SecurityContextS override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono { val token = exchange.request.headers.getFirst(HttpHeaders.AUTHORIZATION)?.removePrefix("Bearer ") - if (exchange.request.path.value() == "/api/auth/login"){ + logger.info("here ${exchange.request.path.value()}") + if (exchange.request.path.value() == "/api/auth/login" || exchange.request.path.value() + .startsWith("/api/actuator") + ) { return chain.filter(exchange) } @@ -52,8 +55,6 @@ class BearerTokenFilter(private val authService: AuthService) : SecurityContextS } - - } diff --git a/src/main/kotlin/space/luminic/budgerapp/configs/CommonConfig.kt b/src/main/kotlin/space/luminic/budgerapp/configs/CommonConfig.kt new file mode 100644 index 0000000..ff0b04e --- /dev/null +++ b/src/main/kotlin/space/luminic/budgerapp/configs/CommonConfig.kt @@ -0,0 +1,12 @@ +package space.luminic.budgerapp.configs + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +//@Configuration +//class CommonConfig { +// @Bean +// fun httpTraceRepository(): HttpTraceRepository { +// return InMemoryHttpTraceRepository() +// } +//} \ No newline at end of file diff --git a/src/main/kotlin/space/luminic/budgerapp/controllers/BudgetController.kt b/src/main/kotlin/space/luminic/budgerapp/controllers/BudgetController.kt index b227a66..9ace580 100644 --- a/src/main/kotlin/space/luminic/budgerapp/controllers/BudgetController.kt +++ b/src/main/kotlin/space/luminic/budgerapp/controllers/BudgetController.kt @@ -35,7 +35,7 @@ class BudgetController( @GetMapping("/{id}") fun getBudget(@PathVariable id: String): Mono { - + logger.info("here }") return budgetService.getBudget(id) } diff --git a/src/main/kotlin/space/luminic/budgerapp/controllers/CategoriesController.kt b/src/main/kotlin/space/luminic/budgerapp/controllers/CategoriesController.kt index 33dfd4e..7de8a32 100644 --- a/src/main/kotlin/space/luminic/budgerapp/controllers/CategoriesController.kt +++ b/src/main/kotlin/space/luminic/budgerapp/controllers/CategoriesController.kt @@ -1,5 +1,6 @@ package space.luminic.budgerapp.controllers +import org.bson.Document import org.slf4j.LoggerFactory import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity @@ -10,25 +11,33 @@ import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.PutMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController import org.springframework.web.client.HttpClientErrorException import reactor.core.publisher.Mono +import space.luminic.budgerapp.models.BudgetCategory import space.luminic.budgerapp.models.Category +import space.luminic.budgerapp.services.BudgetService import space.luminic.budgerapp.services.CategoryService +import java.time.LocalDate @RestController @RequestMapping("/categories") class CategoriesController( - private val categoryService: CategoryService + private val categoryService: CategoryService, + private val budgetService: BudgetService ) { private val logger = LoggerFactory.getLogger(javaClass) @GetMapping() - fun getCategories(): Mono> { - return categoryService.getCategories() + fun getCategories( + @RequestParam("sort") sortBy: String = "name", + @RequestParam("direction") direction: String = "ASC" + ): Mono> { + return categoryService.getCategories(sortBy, direction) } @@ -57,4 +66,19 @@ class CategoriesController( return categoryService.deleteCategory(categoryId) } + @GetMapping("/test") + fun test(): Mono> { + var dateFrom = LocalDate.parse("2025-01-10") + var dateTo = LocalDate.parse("2025-02-09") + + return categoryService.getCategoryTransactionPipeline(dateFrom, dateTo) + + } + + + @GetMapping("/by-month") + fun getCategoriesSumsByMonths(): Mono> { + return categoryService.getCategorySumsPipeline(LocalDate.of(2024, 8, 1), LocalDate.of(2025, 1, 12)) + } + } \ No newline at end of file diff --git a/src/main/kotlin/space/luminic/budgerapp/models/Budget.kt b/src/main/kotlin/space/luminic/budgerapp/models/Budget.kt index 8a4bc51..7d71ed4 100644 --- a/src/main/kotlin/space/luminic/budgerapp/models/Budget.kt +++ b/src/main/kotlin/space/luminic/budgerapp/models/Budget.kt @@ -3,9 +3,9 @@ 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 -import java.math.BigDecimal import java.time.LocalDate import java.time.LocalDateTime +import kotlin.collections.mutableListOf data class BudgetDTO( @@ -19,7 +19,7 @@ data class BudgetDTO( var categories: MutableList = mutableListOf(), var transactions: MutableList? = null, -) + ) @Document("budgets") diff --git a/src/main/kotlin/space/luminic/budgerapp/repos/BudgetRepo.kt b/src/main/kotlin/space/luminic/budgerapp/repos/BudgetRepo.kt index b144ca8..03c548a 100644 --- a/src/main/kotlin/space/luminic/budgerapp/repos/BudgetRepo.kt +++ b/src/main/kotlin/space/luminic/budgerapp/repos/BudgetRepo.kt @@ -14,6 +14,6 @@ interface BudgetRepo: ReactiveMongoRepository { override fun findAll(sort: Sort): Flux - fun findByDateFromLessThanEqualAndDateToGreaterThan(dateOne: LocalDate, dateTwo: LocalDate): Mono + fun findByDateFromLessThanEqualAndDateToGreaterThanEqual(dateOne: LocalDate, dateTwo: LocalDate): Mono } \ No newline at end of file diff --git a/src/main/kotlin/space/luminic/budgerapp/repos/CategoryRepo.kt b/src/main/kotlin/space/luminic/budgerapp/repos/CategoryRepo.kt index 979010d..c894dd5 100644 --- a/src/main/kotlin/space/luminic/budgerapp/repos/CategoryRepo.kt +++ b/src/main/kotlin/space/luminic/budgerapp/repos/CategoryRepo.kt @@ -10,4 +10,6 @@ interface CategoryRepo : ReactiveMongoRepository { fun findByName(name: String): Mono + + } \ No newline at end of file diff --git a/src/main/kotlin/space/luminic/budgerapp/services/BudgetService.kt b/src/main/kotlin/space/luminic/budgerapp/services/BudgetService.kt index a0758c1..a240c35 100644 --- a/src/main/kotlin/space/luminic/budgerapp/services/BudgetService.kt +++ b/src/main/kotlin/space/luminic/budgerapp/services/BudgetService.kt @@ -1,6 +1,7 @@ package space.luminic.budgerapp.services + import org.slf4j.LoggerFactory import org.springframework.cache.annotation.CacheEvict import org.springframework.cache.annotation.Cacheable @@ -59,27 +60,32 @@ class BudgetService( TransactionEventType.DELETE -> updateBudgetOnDelete(event) } } + // runBlocking(Dispatchers.IO) { // updateBudgetWarns( -// budget = budgetRepo.findByDateFromLessThanEqualAndDateToGreaterThan( -// event.newTransaction.date.toLocalDate(), event.newTransaction.date.toLocalDate() -// ) +// budgetRepo.findByDateFromLessThanEqualAndDateToGreaterThan( +// event.newTransaction.date, event.newTransaction.date. +// ).map{it} // ) // } } fun updateBudgetOnCreate(event: TransactionEvent) { - budgetRepo.findByDateFromLessThanEqualAndDateToGreaterThan( + budgetRepo.findByDateFromLessThanEqualAndDateToGreaterThanEqual( event.newTransaction.date, event.newTransaction.date ).flatMap { budget -> - categoryService.getBudgetCategories(budget.dateFrom, budget.dateTo).flatMap { categories -> + val categories = categoryService.getBudgetCategories(budget.dateFrom, budget.dateTo) + logger.info(categories.toString()) + categories.flatMap { categories -> val updatedCategories = when (event.newTransaction.type.code) { "PLANNED" -> Flux.fromIterable(budget.categories) .map { category -> categories[category.category.id]?.let { data -> - category.currentSpent = data["instantAmount"] ?: 0.0 - category.currentPlanned = data["plannedAmount"] ?: 0.0 - category.currentLimit += event.newTransaction.amount + if (category.category.id == event.newTransaction.category.id) { + category.currentSpent = data["instantAmount"] ?: 0.0 + category.currentPlanned = data["plannedAmount"] ?: 0.0 + category.currentLimit += event.newTransaction.amount + } } category }.collectList() @@ -87,8 +93,10 @@ class BudgetService( "INSTANT" -> Flux.fromIterable(budget.categories) .map { category -> categories[category.category.id]?.let { data -> - category.currentSpent = data["instantAmount"] ?: 0.0 - category.currentPlanned = data["plannedAmount"] ?: 0.0 + if (category.category.id == event.newTransaction.category.id) { + category.currentSpent = data["instantAmount"] ?: 0.0 + category.currentPlanned = data["plannedAmount"] ?: 0.0 + } } category }.collectList() @@ -107,13 +115,13 @@ class BudgetService( fun updateBudgetOnEdit(event: TransactionEvent) { - budgetRepo.findByDateFromLessThanEqualAndDateToGreaterThan( + budgetRepo.findByDateFromLessThanEqualAndDateToGreaterThanEqual( event.oldTransaction.date, event.oldTransaction.date ).switchIfEmpty( Mono.error(BudgetNotFoundException("old budget cannot be null")) ).then().subscribe() - budgetRepo.findByDateFromLessThanEqualAndDateToGreaterThan( + budgetRepo.findByDateFromLessThanEqualAndDateToGreaterThanEqual( event.newTransaction.date, event.newTransaction.date ).flatMap { budget -> categoryService.getBudgetCategories(budget.dateFrom, budget.dateTo).flatMap { categories -> @@ -134,8 +142,10 @@ class BudgetService( "INSTANT" -> Flux.fromIterable(budget.categories) .map { category -> categories[category.category.id]?.let { data -> - category.currentSpent = data["instantAmount"] ?: 0.0 - category.currentPlanned = data["plannedAmount"] ?: 0.0 + if (category.category.id == event.newTransaction.category.id) { + category.currentSpent = data["instantAmount"] ?: 0.0 + category.currentPlanned = data["plannedAmount"] ?: 0.0 + } } category }.collectList() @@ -154,7 +164,7 @@ class BudgetService( fun updateBudgetOnDelete(event: TransactionEvent) { - budgetRepo.findByDateFromLessThanEqualAndDateToGreaterThan( + budgetRepo.findByDateFromLessThanEqualAndDateToGreaterThanEqual( event.newTransaction.date, event.newTransaction.date ).flatMap { budget -> categoryService.getBudgetCategories(budget.dateFrom, budget.dateTo).flatMap { categories -> @@ -162,9 +172,11 @@ class BudgetService( "PLANNED" -> Flux.fromIterable(budget.categories) .map { category -> categories[category.category.id]?.let { data -> - category.currentSpent = data["instantAmount"] ?: 0.0 - category.currentPlanned = data["plannedAmount"] ?: 0.0 - category.currentLimit += event.newTransaction.amount + if (category.category.id == event.newTransaction.category.id) { + category.currentSpent = data["instantAmount"] ?: 0.0 + category.currentPlanned = data["plannedAmount"] ?: 0.0 + category.currentLimit += event.newTransaction.amount + } } category }.collectList() @@ -172,8 +184,10 @@ class BudgetService( "INSTANT" -> Flux.fromIterable(budget.categories) .map { category -> categories[category.category.id]?.let { data -> - category.currentSpent = data["instantAmount"] ?: 0.0 - category.currentPlanned = data["plannedAmount"] ?: 0.0 + if (category.category.id == event.newTransaction.category.id) { + category.currentSpent = data["instantAmount"] ?: 0.0 + category.currentPlanned = data["plannedAmount"] ?: 0.0 + } } category }.collectList() @@ -206,6 +220,7 @@ class BudgetService( // @Cacheable("budgets", key = "#id") fun getBudget(id: String): Mono { + logger.info("here b") return budgetRepo.findById(id) .flatMap { budget -> val budgetDTO = BudgetDTO( @@ -243,6 +258,7 @@ class BudgetService( budgetDTO.plannedExpenses = transactions["plannedExpenses"] as MutableList budgetDTO.plannedIncomes = transactions["plannedIncomes"] as MutableList budgetDTO.transactions = transactions["instantTransactions"] as MutableList + logger.info("here e") budgetDTO } @@ -301,7 +317,7 @@ class BudgetService( fun getBudgetByDate(date: LocalDate): Mono { - return budgetRepo.findByDateFromLessThanEqualAndDateToGreaterThan(date, date).switchIfEmpty(Mono.empty()) + return budgetRepo.findByDateFromLessThanEqualAndDateToGreaterThanEqual(date, date).switchIfEmpty(Mono.empty()) } // fun getBudgetCategorySQL(id: Int): List? { @@ -360,7 +376,7 @@ class BudgetService( @CacheEvict(cacheNames = ["budgets", "budgetsList"], allEntries = true) fun setCategoryLimit(budgetId: String, catId: String, limit: Double): Mono { return budgetRepo.findById(budgetId).flatMap { budget -> - val catEdit = budget.categories.firstOrNull { it.category.id == catId } + val catEdit = budget.categories.firstOrNull { it.category?.id == catId } ?: return@flatMap Mono.error(Exception("Category not found in the budget")) transactionService.calcTransactionsSum(budget, catId, "PLANNED").flatMap { catPlanned -> @@ -378,9 +394,6 @@ class BudgetService( } - - - fun getWarns(budgetId: String, isHide: Boolean? = null): Mono> { return warnRepo.findAllByBudgetIdAndIsHide(budgetId, isHide == true).collectList() } @@ -469,10 +482,10 @@ class BudgetService( ): Flux { val warnsForCategory = mutableListOf>() - val averageSum = averageSums[category.category.id] ?: 0.0 + val averageSum = averageSums[category.category?.id] ?: 0.0 val categorySpentRatioInAvgIncome = if (averageIncome > 0.0) averageSum / averageIncome else 0.0 val projectedAvailableSum = currentBudgetIncome * categorySpentRatioInAvgIncome - val contextAtAvg = "category${category.category.id}atbudget${finalBudget.id}lessavg" + val contextAtAvg = "category${category.category?.id}atbudget${finalBudget.id}lessavg" val lowSavingContext = "savingValueLess10atBudget${finalBudget.id}" if (averageSum > category.currentLimit) { @@ -482,11 +495,11 @@ class BudgetService( Warn( serenity = WarnSerenity.MAIN, message = PushMessage( - title = "Внимание на ${category.category.name}!", + title = "Внимание на ${category.category?.name}!", body = "Лимит меньше средних трат (Среднее: ${averageSum.toInt()} ₽ Текущий лимит: ${category.currentLimit.toInt()} ₽)." + "\nСредняя доля данной категории в доходах: ${(categorySpentRatioInAvgIncome * 100).toInt()}%." + "\nПроецируется на текущие поступления: ${projectedAvailableSum.toInt()} ₽", - icon = category.category.icon + icon = category.category?.icon ), budgetId = finalBudget.id!!, context = contextAtAvg, @@ -499,7 +512,7 @@ class BudgetService( warnRepo.findWarnByContext(contextAtAvg).flatMap { warnRepo.delete(it).then(Mono.empty()) } } - if (category.category.id == "675850148198643f121e466a") { + if (category.category?.id == "675850148198643f121e466a") { val savingRatio = if (plannedIncome > 0.0) category.currentLimit / plannedIncome else 0.0 if (savingRatio < 0.1) { val warnMono = warnRepo.findWarnByContext(lowSavingContext) @@ -512,7 +525,7 @@ class BudgetService( body = "Текущие плановые сбережения равны ${plannedSaving.toInt()} (${ (savingRatio * 100).toInt() }%)! Исправьте!", - icon = category.category.icon + icon = category.category!!.icon ), budgetId = finalBudget.id!!, context = lowSavingContext, diff --git a/src/main/kotlin/space/luminic/budgerapp/services/CategoryService.kt b/src/main/kotlin/space/luminic/budgerapp/services/CategoryService.kt index 50f29c8..0909b95 100644 --- a/src/main/kotlin/space/luminic/budgerapp/services/CategoryService.kt +++ b/src/main/kotlin/space/luminic/budgerapp/services/CategoryService.kt @@ -5,6 +5,8 @@ import org.bson.Document import org.slf4j.LoggerFactory import org.springframework.cache.annotation.CacheEvict import org.springframework.cache.annotation.Cacheable +import org.springframework.data.domain.Sort +import org.springframework.data.domain.Sort.Direction import org.springframework.data.mongodb.core.ReactiveMongoTemplate import org.springframework.stereotype.Service import reactor.core.publisher.Flux @@ -30,9 +32,16 @@ class CategoryService( private val logger = LoggerFactory.getLogger(javaClass) + fun getCategory(id: String): Mono { + return categoryRepo.findById(id) + } + @Cacheable("getAllCategories") - fun getCategories(): Mono> { - return categoryRepo.findAll().collectList() + fun getCategories(sortBy: String, direction: String): Mono> { + return categoryRepo.findAll(Sort.by(if (direction == "ASC") Direction.ASC else Direction.DESC, sortBy)) + .collectList() + + } @Cacheable("categoryTypes") @@ -43,12 +52,12 @@ class CategoryService( return types } - @CacheEvict(cacheNames = ["getAllCategories"],allEntries = true) + @CacheEvict(cacheNames = ["getAllCategories"], allEntries = true) fun createCategory(category: Category): Mono { return categoryRepo.save(category) } - @CacheEvict(cacheNames = ["getAllCategories"],allEntries = true) + @CacheEvict(cacheNames = ["getAllCategories"], allEntries = true) fun editCategory(category: Category): Mono { return categoryRepo.findById(category.id!!) // Возвращаем Mono .flatMap { oldCategory -> @@ -59,7 +68,7 @@ class CategoryService( } } - @CacheEvict(cacheNames = ["getAllCategories"],allEntries = true) + @CacheEvict(cacheNames = ["getAllCategories"], allEntries = true) fun deleteCategory(categoryId: String): Mono { return categoryRepo.findById(categoryId).switchIfEmpty( Mono.error(IllegalArgumentException("Category with id: $categoryId not found")) @@ -75,9 +84,9 @@ class CategoryService( icon = "🚮" ) ) - ).flatMapMany { newCategory -> + ).flatMapMany { Category -> Flux.fromIterable(transactions).flatMap { transaction -> - transaction.category = newCategory // Присваиваем конкретный объект категории + transaction.category = Category // Присваиваем конкретный объект категории transactionService.editTransaction(transaction) // Сохраняем изменения } } @@ -118,7 +127,7 @@ class CategoryService( ) ), Document( - "\$lt", listOf( + "\$lte", listOf( "\$date", Date.from( LocalDateTime.of(dateTo, LocalTime.MIN) @@ -339,7 +348,7 @@ class CategoryService( currentPlanned = document["plannedAmount"] as Double, category = Category( document["_id"].toString(), - CategoryType(catType["code"] as String, catType["name"] as String), + 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 @@ -351,4 +360,190 @@ class CategoryService( } + fun getCategorySumsPipeline(dateFrom: LocalDate, dateTo: LocalDate): Mono> { + 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(dateTo, 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() + } + + } diff --git a/src/main/resources/application-dev-local.properties b/src/main/resources/application-dev-local.properties new file mode 100644 index 0000000..4c1fd8d --- /dev/null +++ b/src/main/resources/application-dev-local.properties @@ -0,0 +1,27 @@ +spring.application.name=budger-app + +spring.data.mongodb.host=localhost +spring.data.mongodb.port=27018 +spring.data.mongodb.database=budger-app +#spring.data.mongodb.username=budger-app +#spring.data.mongodb.password=BA1q2w3e4r! +#spring.data.mongodb.authentication-database=admin + + +management.endpoints.web.exposure.include=* +management.endpoint.health.show-details=always + + + +spring.datasource.url=jdbc:postgresql://213.183.51.243/familybudget_app +spring.datasource.username=familybudget_app +spring.datasource.password=FB1q2w3e4r! + + +# ??????? JDBC +spring.datasource.driver-class-name=org.postgresql.Driver + +# ????????? Hibernate +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect +spring.jpa.hibernate.ddl-auto=none + diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties index 75bdc84..c606d9f 100644 --- a/src/main/resources/application-dev.properties +++ b/src/main/resources/application-dev.properties @@ -1,11 +1,11 @@ spring.application.name=budger-app -spring.data.mongodb.host=213.226.71.138 +spring.data.mongodb.host=77.222.32.64 spring.data.mongodb.port=27017 spring.data.mongodb.database=budger-app -spring.data.mongodb.username=budger-app -spring.data.mongodb.password=BA1q2w3e4r! -spring.data.mongodb.authentication-database=admin +#spring.data.mongodb.username=budger-app +#spring.data.mongodb.password=BA1q2w3e4r! +#spring.data.mongodb.authentication-database=admin management.endpoints.web.exposure.include=* diff --git a/src/main/resources/application-prod.properties b/src/main/resources/application-prod.properties index e797ccc..69e8322 100644 --- a/src/main/resources/application-prod.properties +++ b/src/main/resources/application-prod.properties @@ -1,25 +1,20 @@ spring.application.name=budger-app -spring.data.mongodb.host=127.0.0.1 -spring.data.mongodb.port=27017 -spring.data.mongodb.database=budger-app -spring.data.mongodb.username=budger-app -spring.data.mongodb.password=BA1q2w3e4r! -spring.data.mongodb.authentication-database=admin + +spring.data.mongodb.uri=mongodb://budger-app:BA1q2w3e4r!@luminic.space:27017/budger-app?authSource=admin&minPoolSize=10&maxPoolSize=100 -spring.datasource.url=jdbc:postgresql://213.183.51.243/familybudget_app -spring.datasource.username=familybudget_app -spring.datasource.password=FB1q2w3e4r! +logging.level.org.springframework.web=DEBUG +logging.level.org.springframework.data = DEBUG +logging.level.org.springframework.data.mongodb.core.ReactiveMongoTemplate=DEBUG +logging.level.org.springframework.security = DEBUG +logging.level.org.springframework.data.mongodb.code = DEBUG +logging.level.org.springframework.web.reactive=DEBUG + +#management.endpoints.web.exposure.include=* +#management.endpoint.metrics.access=read_only - -logging.level.org.springframework.web=INFO -logging.level.org.springframework.data = INFO -logging.level.org.springframework.data.mongodb.core.ReactiveMongoTemplate=INFO -logging.level.org.springframework.security = INFO -logging.level.org.springframework.data.mongodb.code = INFO -logging.level.org.springframework.web.reactive=INFO diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 4debb45..b804bb6 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -4,7 +4,7 @@ server.port=8082 #server.servlet.context-path=/api spring.webflux.base-path=/api -spring.profiles.active=dev +spring.profiles.active=prod spring.main.web-application-type=reactive @@ -20,10 +20,10 @@ server.compression.enabled=true server.compression.mime-types=application/json -# ??????? JDBC -spring.datasource.driver-class-name=org.postgresql.Driver +# Expose prometheus, health, and info endpoints +#management.endpoints.web.exposure.include=prometheus,health,info +management.endpoints.web.exposure.include=* -# ????????? Hibernate -spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect -spring.jpa.hibernate.ddl-auto=none +# Enable Prometheus metrics export +management.prometheus.metrics.export.enabled=true