wishlists + statics + some fixes
This commit is contained in:
@@ -9,7 +9,7 @@ import org.springframework.scheduling.annotation.EnableAsync
|
||||
import org.springframework.scheduling.annotation.EnableScheduling
|
||||
import java.util.TimeZone
|
||||
|
||||
@SpringBootApplication
|
||||
@SpringBootApplication(scanBasePackages = ["space.luminic.budgerapp"])
|
||||
@EnableCaching
|
||||
@EnableAsync
|
||||
@EnableScheduling
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package space.luminic.budgerapp.configs
|
||||
|
||||
import kotlinx.coroutines.reactor.mono
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.http.HttpHeaders
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
||||
@@ -16,8 +15,7 @@ import space.luminic.budgerapp.services.AuthService
|
||||
|
||||
@Component
|
||||
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> {
|
||||
@@ -27,7 +25,10 @@ class BearerTokenFilter(private val authService: AuthService) : SecurityContextS
|
||||
"/api/auth/login",
|
||||
"/api/auth/register",
|
||||
"/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)
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@ class SecurityConfig(
|
||||
.logout { it.disable() }
|
||||
.authorizeExchange {
|
||||
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()
|
||||
}
|
||||
.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
|
||||
class TransactionsMapper : FromDocumentMapper {
|
||||
|
||||
|
||||
override fun fromDocument(document: Document): Transaction {
|
||||
val categoryDocument = document.get("categoryDetails", Document::class.java)
|
||||
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.DateOperators.DateToString
|
||||
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 reactor.core.publisher.Flux
|
||||
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") {
|
||||
budget.categories.firstOrNull { it.category.id == transaction.category.id }
|
||||
?: throw RuntimeException("Budget category not found for expense")
|
||||
?: addCategoryToBudget(transaction.category, budget)
|
||||
|
||||
} else {
|
||||
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(
|
||||
transaction: Transaction,
|
||||
budget: Budget,
|
||||
@@ -826,7 +844,7 @@ class FinancialService(
|
||||
|
||||
|
||||
suspend fun createTransaction(space: Space, transaction: Transaction): Transaction {
|
||||
val securityContextHolder = SecurityContextHolder.getContext()
|
||||
val securityContextHolder = ReactiveSecurityContextHolder.getContext().awaitSingle()
|
||||
val user = userService.getByUserNameWoPass(securityContextHolder.authentication.name)
|
||||
if (space.users.none { it.id.toString() == user.id }) {
|
||||
throw IllegalArgumentException("User does not have access to this Space")
|
||||
@@ -903,7 +921,7 @@ class FinancialService(
|
||||
|
||||
|
||||
suspend fun deleteTransaction(transaction: Transaction) = coroutineScope {
|
||||
transactionsRepo.deleteById(transaction.id!!).awaitSingle()
|
||||
transactionsRepo.deleteById(transaction.id!!).awaitFirstOrNull()
|
||||
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("Вы не можете отменить не свою бронь")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user