Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b9f0e566c |
@@ -9,7 +9,7 @@ import org.springframework.scheduling.annotation.EnableAsync
|
|||||||
import org.springframework.scheduling.annotation.EnableScheduling
|
import org.springframework.scheduling.annotation.EnableScheduling
|
||||||
import java.util.TimeZone
|
import java.util.TimeZone
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication(scanBasePackages = ["space.luminic.budgerapp"])
|
||||||
@EnableCaching
|
@EnableCaching
|
||||||
@EnableAsync
|
@EnableAsync
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package space.luminic.budgerapp.configs
|
package space.luminic.budgerapp.configs
|
||||||
|
|
||||||
import kotlinx.coroutines.reactor.mono
|
import kotlinx.coroutines.reactor.mono
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.springframework.http.HttpHeaders
|
import org.springframework.http.HttpHeaders
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
||||||
@@ -16,8 +15,7 @@ import space.luminic.budgerapp.services.AuthService
|
|||||||
|
|
||||||
@Component
|
@Component
|
||||||
class BearerTokenFilter(private val authService: AuthService) : SecurityContextServerWebExchangeWebFilter() {
|
class BearerTokenFilter(private val authService: AuthService) : SecurityContextServerWebExchangeWebFilter() {
|
||||||
private val logger = LoggerFactory.getLogger(BearerTokenFilter::class.java)
|
// private val logger = LoggerFactory.getLogger(BearerTokenFilter::class.java)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
|
override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
|
||||||
@@ -27,7 +25,10 @@ class BearerTokenFilter(private val authService: AuthService) : SecurityContextS
|
|||||||
"/api/auth/login",
|
"/api/auth/login",
|
||||||
"/api/auth/register",
|
"/api/auth/register",
|
||||||
"/api/auth/tgLogin"
|
"/api/auth/tgLogin"
|
||||||
) || exchange.request.path.value().startsWith("/api/actuator")
|
) || exchange.request.path.value().startsWith("/api/actuator") || exchange.request.path.value()
|
||||||
|
.startsWith("/api/static/")
|
||||||
|
|| exchange.request.path.value()
|
||||||
|
.startsWith("/api/wishlistexternal/")
|
||||||
) {
|
) {
|
||||||
return chain.filter(exchange)
|
return chain.filter(exchange)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ class SecurityConfig(
|
|||||||
.logout { it.disable() }
|
.logout { it.disable() }
|
||||||
.authorizeExchange {
|
.authorizeExchange {
|
||||||
it.pathMatchers(HttpMethod.POST, "/auth/login", "/auth/register", "/auth/tgLogin").permitAll()
|
it.pathMatchers(HttpMethod.POST, "/auth/login", "/auth/register", "/auth/tgLogin").permitAll()
|
||||||
it.pathMatchers("/actuator/**").permitAll()
|
it.pathMatchers("/actuator/**", "/static/**").permitAll()
|
||||||
|
it.pathMatchers("/wishlistexternal/**").permitAll()
|
||||||
it.anyExchange().authenticated()
|
it.anyExchange().authenticated()
|
||||||
}
|
}
|
||||||
.addFilterAt(
|
.addFilterAt(
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package space.luminic.budgerapp.configs
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class StorageConfig(@Value("\${storage.location}") location: String) {
|
||||||
|
|
||||||
|
val rootLocation: Path = Paths.get(location)
|
||||||
|
|
||||||
|
init {
|
||||||
|
Files.createDirectories(rootLocation) // Создаем папку, если её нет
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package space.luminic.budgerapp.controllers
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.springframework.core.io.PathResource
|
||||||
|
import org.springframework.core.io.Resource
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.http.MediaType
|
||||||
|
import org.springframework.http.ResponseEntity
|
||||||
|
import org.springframework.http.codec.multipart.FilePart
|
||||||
|
import org.springframework.web.bind.annotation.*
|
||||||
|
import space.luminic.budgerapp.configs.StorageConfig
|
||||||
|
import space.luminic.budgerapp.models.NotFoundException
|
||||||
|
import space.luminic.budgerapp.services.StaticService
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/static/{spaceId}/wishlists/{wishListItemId}")
|
||||||
|
class ImageController(private val staticService: StaticService, private val storageConfig: StorageConfig) {
|
||||||
|
|
||||||
|
|
||||||
|
@GetMapping("/{resourceId}")
|
||||||
|
suspend fun downloadFile(
|
||||||
|
@PathVariable spaceId: String,
|
||||||
|
@PathVariable wishListItemId: String,
|
||||||
|
@PathVariable resourceId: String
|
||||||
|
): ResponseEntity<Resource> {
|
||||||
|
return withContext(Dispatchers.IO) {
|
||||||
|
// val filePath = staticService.generatePathToFile(spaceId, wishListItemId, resourceId)
|
||||||
|
val filePath =
|
||||||
|
Paths.get(storageConfig.rootLocation.toString(), spaceId, "wishlists", wishListItemId, resourceId)
|
||||||
|
if (!Files.exists(filePath) || !Files.isReadable(filePath)) {
|
||||||
|
throw NotFoundException("File $filePath not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
val resource = PathResource(filePath)
|
||||||
|
val contentType = Files.probeContentType(filePath) ?: "application/octet-stream"
|
||||||
|
|
||||||
|
ResponseEntity.ok()
|
||||||
|
.contentType(MediaType.parseMediaType(contentType))
|
||||||
|
.body(resource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
suspend fun addImage(
|
||||||
|
@PathVariable spaceId: String,
|
||||||
|
@PathVariable wishListItemId: String,
|
||||||
|
@RequestPart("file") file: FilePart,
|
||||||
|
@RequestHeader("Content-Length") contentLength: Long,
|
||||||
|
): ResponseEntity<String> {
|
||||||
|
val maxSize = 5L * 1024 * 1024 // 5MB
|
||||||
|
|
||||||
|
|
||||||
|
println(file.headers().contentType)
|
||||||
|
if (contentLength > maxSize) {
|
||||||
|
return ResponseEntity
|
||||||
|
.status(HttpStatus.PAYLOAD_TOO_LARGE)
|
||||||
|
.body("Размер файла превышает 5MB")
|
||||||
|
}
|
||||||
|
return ResponseEntity.ok(staticService.saveFile(spaceId, wishListItemId, file))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package space.luminic.budgerapp.controllers
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.*
|
||||||
|
import space.luminic.budgerapp.models.WishList
|
||||||
|
import space.luminic.budgerapp.models.WishListItem
|
||||||
|
import space.luminic.budgerapp.services.SpaceService
|
||||||
|
import space.luminic.budgerapp.services.WishListService
|
||||||
|
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/spaces/{spaceId}/wishlists")
|
||||||
|
class WishListController(
|
||||||
|
private val wishListService: WishListService,
|
||||||
|
private val spaceService: SpaceService
|
||||||
|
) {
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
suspend fun findWishList(@PathVariable spaceId: String): List<WishList> {
|
||||||
|
spaceService.isValidRequest(spaceId)
|
||||||
|
return wishListService.findWishLists(spaceId)
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{wishListId}")
|
||||||
|
suspend fun getWishList(@PathVariable spaceId: String, @PathVariable wishListId: String): WishList {
|
||||||
|
spaceService.isValidRequest(spaceId)
|
||||||
|
return wishListService.getList(wishListId)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
suspend fun createWishList(@PathVariable spaceId: String, @RequestBody wishList: WishList): WishList {
|
||||||
|
val space = spaceService.isValidRequest(spaceId)
|
||||||
|
return wishListService.createWishList(space, wishList)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{wishListId}")
|
||||||
|
suspend fun updateWishList(
|
||||||
|
@PathVariable spaceId: String,
|
||||||
|
@PathVariable wishListId: String,
|
||||||
|
@RequestBody wishList: WishList
|
||||||
|
): WishList {
|
||||||
|
spaceService.isValidRequest(spaceId)
|
||||||
|
return wishListService.updateWishListInfo(wishList)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{wishListId}/items/{itemId}")
|
||||||
|
suspend fun updateWishListItem(
|
||||||
|
@PathVariable spaceId: String,
|
||||||
|
@PathVariable wishListId: String,
|
||||||
|
@PathVariable itemId: String,
|
||||||
|
@RequestBody wishListItem: WishListItem
|
||||||
|
): WishList {
|
||||||
|
spaceService.isValidRequest(spaceId)
|
||||||
|
return wishListService.updateWishListItemInfo(wishListId, wishListItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{wishListId}/items")
|
||||||
|
suspend fun addItemToWishList(
|
||||||
|
@PathVariable spaceId: String,
|
||||||
|
@PathVariable wishListId: String,
|
||||||
|
@RequestBody wishListItem: WishListItem
|
||||||
|
): WishList {
|
||||||
|
spaceService.isValidRequest(spaceId)
|
||||||
|
return wishListService.addItemToWishList(wishListId, wishListItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{wishListId}/items/{wishListItemId}")
|
||||||
|
suspend fun removeItemFromWishList(
|
||||||
|
@PathVariable spaceId: String,
|
||||||
|
@PathVariable wishListId: String,
|
||||||
|
@PathVariable wishListItemId: String
|
||||||
|
): WishList {
|
||||||
|
spaceService.isValidRequest(spaceId)
|
||||||
|
return wishListService.removeItemFromWishList(wishListId, wishListItemId)
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{wishListId}")
|
||||||
|
suspend fun deleteWishList(@PathVariable spaceId: String, @PathVariable wishListId: String) {
|
||||||
|
spaceService.isValidRequest(spaceId)
|
||||||
|
wishListService.deleteWishList(wishListId)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{wishListId}/{wishlistItemId}/reserve/_cancel")
|
||||||
|
suspend fun cancelReserve(
|
||||||
|
@PathVariable spaceId: String, @PathVariable wishListId: String,
|
||||||
|
@PathVariable wishlistItemId: String
|
||||||
|
) : WishList {
|
||||||
|
spaceService.isValidRequest(spaceId)
|
||||||
|
return wishListService.cancelReserve(wishListId, wishlistItemId)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package space.luminic.budgerapp.controllers
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.*
|
||||||
|
import space.luminic.budgerapp.models.Reserve
|
||||||
|
import space.luminic.budgerapp.models.WishList
|
||||||
|
import space.luminic.budgerapp.models.WishListItem
|
||||||
|
import space.luminic.budgerapp.services.WishlistExternalService
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/wishlistexternal/{wishListId}")
|
||||||
|
class WishlistExternalController(private val wishlistExternalService: WishlistExternalService) {
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
suspend fun getWishListInfo(@PathVariable wishListId: String): WishList {
|
||||||
|
return wishlistExternalService.getWishListInfo(wishListId)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{wishlistItemId}/reserve/_create")
|
||||||
|
suspend fun reserveItem(
|
||||||
|
@PathVariable wishListId: String,
|
||||||
|
@PathVariable wishlistItemId: String,
|
||||||
|
@RequestBody reservedBy: Reserve
|
||||||
|
): WishListItem {
|
||||||
|
return wishlistExternalService.reserveWishlistItem(wishListId, wishlistItemId, reservedBy)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{wishlistItemId}/reserve/_cancel")
|
||||||
|
suspend fun cancelReserve(
|
||||||
|
@PathVariable wishListId: String,
|
||||||
|
@PathVariable wishlistItemId: String,
|
||||||
|
@RequestBody reservedBy: Reserve
|
||||||
|
): WishListItem {
|
||||||
|
return wishlistExternalService.cancelReserve(wishListId, wishlistItemId, reservedBy)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,8 +8,6 @@ import java.time.ZoneId
|
|||||||
|
|
||||||
@Component
|
@Component
|
||||||
class TransactionsMapper : FromDocumentMapper {
|
class TransactionsMapper : FromDocumentMapper {
|
||||||
|
|
||||||
|
|
||||||
override fun fromDocument(document: Document): Transaction {
|
override fun fromDocument(document: Document): Transaction {
|
||||||
val categoryDocument = document.get("categoryDetails", Document::class.java)
|
val categoryDocument = document.get("categoryDetails", Document::class.java)
|
||||||
val categoryTypeDocument = categoryDocument["type"] as Document
|
val categoryTypeDocument = categoryDocument["type"] as Document
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package space.luminic.budgerapp.mappers
|
||||||
|
|
||||||
|
import org.bson.Document
|
||||||
|
import org.bson.types.ObjectId
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
import space.luminic.budgerapp.models.*
|
||||||
|
import java.time.ZoneId
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class WishListMapper : FromDocumentMapper {
|
||||||
|
override fun fromDocument(document: Document): WishList {
|
||||||
|
val spaceDoc = document.get("spaceDetails", Document::class.java)
|
||||||
|
val ownerDoc = document.get("ownerDetails", Document::class.java)
|
||||||
|
val itemsDocList = document.getList("itemsDetails", Document::class.java).orEmpty()
|
||||||
|
return WishList(
|
||||||
|
id = document.get("_id", ObjectId::class.java).toString(),
|
||||||
|
name = document.get("name", String::class.java),
|
||||||
|
description = document.get("description", String::class.java),
|
||||||
|
space = Space(id = spaceDoc.getObjectId("_id").toString()),
|
||||||
|
isPrivate = document.getBoolean("isPrivate"),
|
||||||
|
owner = User(
|
||||||
|
ownerDoc.getObjectId("_id").toString(),
|
||||||
|
firstName = ownerDoc.getString("firstName").toString()
|
||||||
|
),
|
||||||
|
items = itemsDocList.map {
|
||||||
|
val reserveDoc = it.get("reservedBy", Document::class.java)
|
||||||
|
WishListItem(
|
||||||
|
id = it.getObjectId("_id").toString(),
|
||||||
|
name = it.getString("name"),
|
||||||
|
description = it.getString("description"),
|
||||||
|
price = it.getDouble("price"),
|
||||||
|
link = it.getString("link"),
|
||||||
|
images = it.getList("images", String::class.java),
|
||||||
|
reservedBy = if (reserveDoc != null) Reserve(
|
||||||
|
reserveDoc.getString("aid"),
|
||||||
|
reserveDoc.getString("name")
|
||||||
|
) else null,
|
||||||
|
updatedAt = it.getDate("updatedAt").toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(),
|
||||||
|
createdAt = it.getDate("createdAt").toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()
|
||||||
|
)
|
||||||
|
}.toMutableList(),
|
||||||
|
updatedAt = document.getDate("updatedAt").toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(),
|
||||||
|
createdAt = document.getDate("createdAt").toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/main/kotlin/space/luminic/budgerapp/models/WishList.kt
Normal file
39
src/main/kotlin/space/luminic/budgerapp/models/WishList.kt
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
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.time.LocalDateTime
|
||||||
|
|
||||||
|
@Document(collection = "wishlists")
|
||||||
|
data class WishList(
|
||||||
|
@Id val id: String? = null,
|
||||||
|
@DBRef var space: Space? = null,
|
||||||
|
var name: String,
|
||||||
|
var description: String,
|
||||||
|
var isPrivate: Boolean,
|
||||||
|
@DBRef var owner: User? = null,
|
||||||
|
@DBRef var items: MutableList<WishListItem> = mutableListOf(),
|
||||||
|
var updatedAt: LocalDateTime = LocalDateTime.now(),
|
||||||
|
val createdAt: LocalDateTime = LocalDateTime.now()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@Document(collection = "wishlistItems")
|
||||||
|
data class WishListItem(
|
||||||
|
@Id val id: String? = null,
|
||||||
|
var name: String,
|
||||||
|
var description: String,
|
||||||
|
var price: Double,
|
||||||
|
var link: String,
|
||||||
|
var images: MutableList<String> = mutableListOf(),
|
||||||
|
var reservedBy: Reserve? = null,
|
||||||
|
var updatedAt: LocalDateTime = LocalDateTime.now(),
|
||||||
|
var createdAt: LocalDateTime = LocalDateTime.now()
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Reserve(
|
||||||
|
val aid: String,
|
||||||
|
val name: String? = null,
|
||||||
|
|
||||||
|
)
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package space.luminic.budgerapp.repos
|
||||||
|
|
||||||
|
import org.springframework.data.mongodb.repository.ReactiveMongoRepository
|
||||||
|
import org.springframework.stereotype.Repository
|
||||||
|
import space.luminic.budgerapp.models.WishList
|
||||||
|
import space.luminic.budgerapp.models.WishListItem
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
interface WishListRepo : ReactiveMongoRepository<WishList, String> {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
interface WishListItemRepo : ReactiveMongoRepository<WishListItem, String> {}
|
||||||
@@ -23,7 +23,7 @@ import org.springframework.data.mongodb.core.ReactiveMongoTemplate
|
|||||||
import org.springframework.data.mongodb.core.aggregation.Aggregation.*
|
import org.springframework.data.mongodb.core.aggregation.Aggregation.*
|
||||||
import org.springframework.data.mongodb.core.aggregation.DateOperators.DateToString
|
import org.springframework.data.mongodb.core.aggregation.DateOperators.DateToString
|
||||||
import org.springframework.data.mongodb.core.query.Criteria
|
import org.springframework.data.mongodb.core.query.Criteria
|
||||||
import org.springframework.security.core.context.SecurityContextHolder
|
import org.springframework.security.core.context.ReactiveSecurityContextHolder
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import reactor.core.publisher.Flux
|
import reactor.core.publisher.Flux
|
||||||
import reactor.core.publisher.Mono
|
import reactor.core.publisher.Mono
|
||||||
@@ -169,16 +169,34 @@ class FinancialService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findBudgetCategory(transaction: Transaction, budget: Budget): BudgetCategory {
|
private suspend fun findBudgetCategory(transaction: Transaction, budget: Budget): BudgetCategory {
|
||||||
return if (transaction.category.type.code == "EXPENSE") {
|
return if (transaction.category.type.code == "EXPENSE") {
|
||||||
budget.categories.firstOrNull { it.category.id == transaction.category.id }
|
budget.categories.firstOrNull { it.category.id == transaction.category.id }
|
||||||
?: throw RuntimeException("Budget category not found for expense")
|
?: addCategoryToBudget(transaction.category, budget)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
budget.incomeCategories.firstOrNull { it.category.id == transaction.category.id }
|
budget.incomeCategories.firstOrNull { it.category.id == transaction.category.id }
|
||||||
?: throw RuntimeException("Budget category not found for income")
|
?:addCategoryToBudget(transaction.category, budget)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun addCategoryToBudget(category: Category, budget: Budget): BudgetCategory {
|
||||||
|
val sums = getBudgetSumsByCategory(category.id!!, budget)
|
||||||
|
val categoryBudget = BudgetCategory(
|
||||||
|
currentSpent = sums.getDouble("instantAmount"),
|
||||||
|
currentPlanned = sums.getDouble("plannedAmount"),
|
||||||
|
currentLimit = sums.getDouble("plannedAmount"),
|
||||||
|
category = category
|
||||||
|
)
|
||||||
|
if (category.type.code == "EXPENSE") {
|
||||||
|
budget.categories.add(categoryBudget)
|
||||||
|
} else {
|
||||||
|
budget.incomeCategories.add(categoryBudget)
|
||||||
|
}
|
||||||
|
budgetRepo.save(budget).awaitSingle()
|
||||||
|
return categoryBudget
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun updateBudgetCategory(
|
private suspend fun updateBudgetCategory(
|
||||||
transaction: Transaction,
|
transaction: Transaction,
|
||||||
budget: Budget,
|
budget: Budget,
|
||||||
@@ -826,7 +844,7 @@ class FinancialService(
|
|||||||
|
|
||||||
|
|
||||||
suspend fun createTransaction(space: Space, transaction: Transaction): Transaction {
|
suspend fun createTransaction(space: Space, transaction: Transaction): Transaction {
|
||||||
val securityContextHolder = SecurityContextHolder.getContext()
|
val securityContextHolder = ReactiveSecurityContextHolder.getContext().awaitSingle()
|
||||||
val user = userService.getByUserNameWoPass(securityContextHolder.authentication.name)
|
val user = userService.getByUserNameWoPass(securityContextHolder.authentication.name)
|
||||||
if (space.users.none { it.id.toString() == user.id }) {
|
if (space.users.none { it.id.toString() == user.id }) {
|
||||||
throw IllegalArgumentException("User does not have access to this Space")
|
throw IllegalArgumentException("User does not have access to this Space")
|
||||||
@@ -903,7 +921,7 @@ class FinancialService(
|
|||||||
|
|
||||||
|
|
||||||
suspend fun deleteTransaction(transaction: Transaction) = coroutineScope {
|
suspend fun deleteTransaction(transaction: Transaction) = coroutineScope {
|
||||||
transactionsRepo.deleteById(transaction.id!!).awaitSingle()
|
transactionsRepo.deleteById(transaction.id!!).awaitFirstOrNull()
|
||||||
launch { updateBudgetOnDelete(transaction) }
|
launch { updateBudgetOnDelete(transaction) }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package space.luminic.budgerapp.services
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.reactor.awaitSingleOrNull
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.springframework.http.codec.multipart.FilePart
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import space.luminic.budgerapp.configs.StorageConfig
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Paths
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class StaticService(private val storageConfig: StorageConfig) {
|
||||||
|
|
||||||
|
suspend fun saveFile(spaceId: String, wishListItemId: String, filePart: FilePart): String {
|
||||||
|
|
||||||
|
val folder = Paths.get(storageConfig.rootLocation.toString(), spaceId, "wishlists", wishListItemId)
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
Files.createDirectories(folder)
|
||||||
|
}
|
||||||
|
val filename = UUID.randomUUID().toString().split("-")[0] + "." + filePart.filename().split(".").last()
|
||||||
|
val filePath =
|
||||||
|
folder.resolve(filename)
|
||||||
|
filePart.transferTo(filePath).awaitSingleOrNull()
|
||||||
|
return filePath.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
package space.luminic.budgerapp.services
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.toList
|
||||||
|
import kotlinx.coroutines.reactive.asFlow
|
||||||
|
import kotlinx.coroutines.reactor.awaitSingle
|
||||||
|
import kotlinx.coroutines.reactor.awaitSingleOrNull
|
||||||
|
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.aggregation.AggregationOperation
|
||||||
|
import org.springframework.data.mongodb.core.query.Criteria
|
||||||
|
import org.springframework.security.core.context.ReactiveSecurityContextHolder
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import space.luminic.budgerapp.mappers.WishListMapper
|
||||||
|
import space.luminic.budgerapp.models.NotFoundException
|
||||||
|
import space.luminic.budgerapp.models.Space
|
||||||
|
import space.luminic.budgerapp.models.WishList
|
||||||
|
import space.luminic.budgerapp.models.WishListItem
|
||||||
|
import space.luminic.budgerapp.repos.WishListItemRepo
|
||||||
|
import space.luminic.budgerapp.repos.WishListRepo
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class WishListService(
|
||||||
|
private val reactiveMongoTemplate: ReactiveMongoTemplate,
|
||||||
|
private val wishListMapper: WishListMapper,
|
||||||
|
private val wishListRepo: WishListRepo,
|
||||||
|
private val wishListItemRepo: WishListItemRepo,
|
||||||
|
private val userService: UserService
|
||||||
|
) {
|
||||||
|
private fun getLookupsAndUnwinds(): List<AggregationOperation> {
|
||||||
|
val aggregationOperation = mutableListOf<AggregationOperation>()
|
||||||
|
aggregationOperation.add(lookup("spaces", "space.\$id", "_id", "spaceDetails"))
|
||||||
|
aggregationOperation.add(unwind("spaceDetails"))
|
||||||
|
aggregationOperation.add(lookup("users", "owner.\$id", "_id", "ownerDetails"))
|
||||||
|
aggregationOperation.add(unwind("ownerDetails"))
|
||||||
|
aggregationOperation.add(lookup("wishlistItems", "items.\$id", "_id", "itemsDetails"))
|
||||||
|
return aggregationOperation
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun findWishLists(spaceId: String): List<WishList> {
|
||||||
|
val user = userService.getByUserNameWoPass(
|
||||||
|
ReactiveSecurityContextHolder.getContext().awaitSingle().authentication.name
|
||||||
|
)
|
||||||
|
val match = match(
|
||||||
|
Criteria.where("spaceDetails._id").`is`(ObjectId(spaceId))
|
||||||
|
.andOperator(
|
||||||
|
Criteria.where("ownerDetails._id").`is`(ObjectId(user.id))
|
||||||
|
.orOperator(Criteria.where("isPrivate").`is`(false))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val aggregation = newAggregation(*(getLookupsAndUnwinds().toTypedArray()), match)
|
||||||
|
return reactiveMongoTemplate.aggregate(aggregation, "wishlists", Document::class.java)
|
||||||
|
.asFlow().map {
|
||||||
|
wishListMapper.fromDocument(it)
|
||||||
|
}.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getList(listId: String): WishList {
|
||||||
|
val user = userService.getByUserNameWoPass(
|
||||||
|
ReactiveSecurityContextHolder.getContext().awaitSingle().authentication.name
|
||||||
|
)
|
||||||
|
val match = match(
|
||||||
|
Criteria.where("_id").`is`(ObjectId(listId))
|
||||||
|
.andOperator(Criteria.where("ownerDetails._id").`is`(ObjectId(user.id)))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
val aggregation = newAggregation(*(getLookupsAndUnwinds().toTypedArray()), match)
|
||||||
|
// val aggregation = newAggregation(lookupSpace, unwindSpace, lookupOwner, unwindOwner, lookupItems, match)
|
||||||
|
return reactiveMongoTemplate.aggregate(aggregation, "wishlists", Document::class.java)
|
||||||
|
.next().map {
|
||||||
|
wishListMapper.fromDocument(it)
|
||||||
|
}.awaitSingleOrNull() ?: throw NotFoundException("WishList with id $listId does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun createWishList(space: Space, wishList: WishList): WishList {
|
||||||
|
val user = userService.getByUserNameWoPass(
|
||||||
|
ReactiveSecurityContextHolder.getContext().awaitSingle().authentication.name
|
||||||
|
)
|
||||||
|
wishList.owner = user
|
||||||
|
wishList.space = space
|
||||||
|
return wishListRepo.save(wishList).awaitSingle()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun updateWishListInfo(wishList: WishList): WishList {
|
||||||
|
val oldStateOfWishList = getList(wishList.id!!)
|
||||||
|
val newStateOfWishList = oldStateOfWishList.copy(
|
||||||
|
name = wishList.name,
|
||||||
|
description = wishList.description,
|
||||||
|
isPrivate = wishList.isPrivate,
|
||||||
|
updatedAt = LocalDateTime.now()
|
||||||
|
)
|
||||||
|
return wishListRepo.save(newStateOfWishList).awaitSingle()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun updateWishListItemInfo(wishListId: String, wishListItem: WishListItem): WishList {
|
||||||
|
wishListItemRepo.save(wishListItem).awaitSingle()
|
||||||
|
return getList(wishListId)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun addItemToWishList(wishListId: String, item: WishListItem): WishList {
|
||||||
|
val wishList = getList(wishListId)
|
||||||
|
val savedItem = wishListItemRepo.save(item).awaitSingle()
|
||||||
|
wishList.items.add(savedItem)
|
||||||
|
return wishListRepo.save(wishList).awaitSingle()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun removeItemFromWishList(wishListId: String, itemId: String): WishList {
|
||||||
|
val wishList = getList(wishListId)
|
||||||
|
return if (wishList.items.removeIf { it.id == itemId }) wishListRepo.save(wishList).awaitSingle() else wishList
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun deleteWishList(wishListId: String) {
|
||||||
|
wishListRepo.deleteById(wishListId).awaitSingleOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun cancelReserve(wishListId: String, wishlistItemId: String): WishList {
|
||||||
|
val wishList = getList(wishListId)
|
||||||
|
val wishlistItem = wishList.items.first { it.id == wishlistItemId }
|
||||||
|
return if (wishlistItem.reservedBy != null) {
|
||||||
|
wishlistItem.reservedBy = null
|
||||||
|
wishListItemRepo.save(wishlistItem).awaitSingle()
|
||||||
|
wishList
|
||||||
|
} else wishList
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package space.luminic.budgerapp.services
|
||||||
|
|
||||||
|
import kotlinx.coroutines.reactor.awaitSingle
|
||||||
|
import kotlinx.coroutines.reactor.awaitSingleOrNull
|
||||||
|
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.stereotype.Service
|
||||||
|
import space.luminic.budgerapp.mappers.WishListMapper
|
||||||
|
import space.luminic.budgerapp.models.NotFoundException
|
||||||
|
import space.luminic.budgerapp.models.Reserve
|
||||||
|
import space.luminic.budgerapp.models.WishList
|
||||||
|
import space.luminic.budgerapp.models.WishListItem
|
||||||
|
import space.luminic.budgerapp.repos.WishListItemRepo
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class WishlistExternalService(
|
||||||
|
private val reactiveMongoTemplate: ReactiveMongoTemplate,
|
||||||
|
private val wishListMapper: WishListMapper,
|
||||||
|
private val wishListItemRepo: WishListItemRepo
|
||||||
|
) {
|
||||||
|
suspend fun getWishListInfo(wishListId: String): WishList {
|
||||||
|
val lookupSpace = lookup("spaces", "space.\$id", "_id", "spaceDetails")
|
||||||
|
val unwindSpace = unwind("spaceDetails")
|
||||||
|
val lookupOwner = lookup("users", "owner.\$id", "_id", "ownerDetails")
|
||||||
|
val unwindOwner = unwind("ownerDetails")
|
||||||
|
val lookupItems = lookup("wishlistItems", "items.\$id", "_id", "itemsDetails")
|
||||||
|
val match = match(
|
||||||
|
Criteria.where("_id").`is`(ObjectId(wishListId))
|
||||||
|
)
|
||||||
|
|
||||||
|
val aggregation = newAggregation(lookupSpace, unwindSpace, lookupOwner, unwindOwner, lookupItems, match)
|
||||||
|
return reactiveMongoTemplate.aggregate(aggregation, "wishlists", Document::class.java)
|
||||||
|
.next().map {
|
||||||
|
wishListMapper.fromDocument(it)
|
||||||
|
}.awaitSingleOrNull() ?: throw NotFoundException("WishList with id $wishListId does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun reserveWishlistItem(wishListId: String, wishlistItemId: String, reservedBy: Reserve): WishListItem {
|
||||||
|
val wishlist = getWishListInfo(wishListId)
|
||||||
|
val wishlistItem = wishlist.items.first { wishlistItem -> wishlistItem.id == wishlistItemId }
|
||||||
|
return if (wishlistItem.reservedBy == null) {
|
||||||
|
wishlistItem.reservedBy = reservedBy
|
||||||
|
wishlistItem.updatedAt = LocalDateTime.now()
|
||||||
|
wishListItemRepo.save(wishlistItem).awaitSingle()
|
||||||
|
} else throw IllegalArgumentException("Wishlist item already reserved")
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun cancelReserve(wishListId: String, wishlistItemId: String, reservedBy: Reserve): WishListItem {
|
||||||
|
val wishlist = getWishListInfo(wishListId)
|
||||||
|
val wishlistItem = wishlist.items.first { wishlistItem -> wishlistItem.id == wishlistItemId }
|
||||||
|
return if (wishlistItem.reservedBy?.aid == reservedBy.aid) {
|
||||||
|
wishlistItem.reservedBy = null
|
||||||
|
wishListItemRepo.save(wishlistItem).awaitSingle()
|
||||||
|
} else if (wishlistItem.reservedBy == null) {
|
||||||
|
wishlistItem
|
||||||
|
} else throw IllegalArgumentException("Вы не можете отменить не свою бронь")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,12 @@ logging.level.org.mongodb.driver.protocol.command = INFO
|
|||||||
server.compression.enabled=true
|
server.compression.enabled=true
|
||||||
server.compression.mime-types=application/json
|
server.compression.mime-types=application/json
|
||||||
|
|
||||||
|
# ???????????? ?????? ????? (?? ????????? 1 ??)
|
||||||
|
spring.servlet.multipart.max-file-size=10MB
|
||||||
|
spring.servlet.multipart.max-request-size=10MB
|
||||||
|
|
||||||
|
storage.location: static
|
||||||
|
|
||||||
|
|
||||||
# Expose prometheus, health, and info endpoints
|
# Expose prometheus, health, and info endpoints
|
||||||
#management.endpoints.web.exposure.include=prometheus,health,info
|
#management.endpoints.web.exposure.include=prometheus,health,info
|
||||||
|
|||||||
Reference in New Issue
Block a user