-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[DPMBE-58] 약속 생성, 삭제, 수정 비즈니스 로직을 작성한다 #72
Changes from 13 commits
f002bb4
c980e99
320f682
a14f8ed
bd61605
c661811
196d09b
a8a2b83
c2a100a
182db30
b5cf25e
272c696
fddcf98
8f8ba6d
602343c
5300f13
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.depromeet.whatnow.api.promise.annotation | ||
|
||
@Retention(AnnotationRetention.RUNTIME) | ||
@Target(AnnotationTarget.FUNCTION) | ||
annotation class RequiresMainUser |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package com.depromeet.whatnow.api.promise.annotation | ||
|
||
import com.depromeet.whatnow.config.security.SecurityUtils | ||
import com.depromeet.whatnow.domains.promise.adaptor.PromiseAdaptor | ||
import com.depromeet.whatnow.domains.promise.exception.NotHostException | ||
import org.aspectj.lang.ProceedingJoinPoint | ||
import org.aspectj.lang.annotation.Around | ||
import org.aspectj.lang.annotation.Aspect | ||
import org.springframework.stereotype.Component | ||
|
||
@Aspect | ||
@Component | ||
class RequiresMainUserAspect( | ||
val promiseAdaptor: PromiseAdaptor, | ||
) { | ||
@Around("@annotation(requiresMainUser)") | ||
fun validateMainUserAccess(joinPoint: ProceedingJoinPoint, requiresMainUser: RequiresMainUser): Any? { | ||
val userId = SecurityUtils.currentUserId | ||
val promiseId = joinPoint.args[0] as Long | ||
val promise = promiseAdaptor.queryPromise(promiseId) | ||
if (userId == promise.mainUserId) { | ||
return joinPoint.proceed() | ||
} else { | ||
throw NotHostException() | ||
} | ||
} | ||
} | ||
Comment on lines
+15
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 안에서 근데이건 나중에 고민해 보셔도 좋을 듯 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AOP 안에서 캐싱하는거 되게 좋은 전략인 것 같아요! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package com.depromeet.whatnow.api.promise.controller | ||
|
||
import com.depromeet.whatnow.api.promise.dto.PromiseDto | ||
import com.depromeet.whatnow.api.promise.dto.PromiseFindDto | ||
import com.depromeet.whatnow.api.promise.dto.PromiseRequest | ||
import com.depromeet.whatnow.api.promise.dto.PromiseSplitedByPromiseTypeDto | ||
import com.depromeet.whatnow.api.promise.usecase.PromiseReadUseCase | ||
import com.depromeet.whatnow.api.promise.usecase.PromiseRegisterUseCase | ||
import com.depromeet.whatnow.common.vo.PlaceVo | ||
import io.swagger.v3.oas.annotations.Operation | ||
import io.swagger.v3.oas.annotations.security.SecurityRequirement | ||
import io.swagger.v3.oas.annotations.tags.Tag | ||
import org.springframework.format.annotation.DateTimeFormat | ||
import org.springframework.web.bind.annotation.DeleteMapping | ||
import org.springframework.web.bind.annotation.GetMapping | ||
import org.springframework.web.bind.annotation.PathVariable | ||
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.RestController | ||
import java.time.LocalDateTime | ||
|
||
@RestController | ||
@RequestMapping("/v1") | ||
@Tag(name = "3. [약속]") | ||
@SecurityRequirement(name = "access-token") | ||
class PromiseController( | ||
val promiseRegisterUseCase: PromiseRegisterUseCase, | ||
val promiseReadUseCase: PromiseReadUseCase, | ||
) { | ||
// 나의 약속 전부 조회 | ||
@Operation(summary = "나의 약속 전부 조회", description = "유저의 약속 전부 조회 (단, 예정된 약속과 지난 약속을 구분해서 조회") | ||
@GetMapping("/promises/users") | ||
fun findByPromiseByUser(): List<PromiseSplitedByPromiseTypeDto> { | ||
return promiseReadUseCase.findPromiseByUserIdSeparatedType() | ||
} | ||
|
||
@Operation(summary = "월단위 약속 조회", description = "유저의 월간 약속 조회 (단, 예정된 약속과 지난 약속을 구분없이 조회)") | ||
@GetMapping("/promises/users/year-month/{year-month}") | ||
fun findByPromiseByUserAndYearMonth(@PathVariable(value = "year-month") yearMonth: String): List<PromiseFindDto> { | ||
return promiseReadUseCase.findPromiseByUserIdYearMonth(yearMonth) | ||
} | ||
|
||
@Operation(summary = "약속(promise) 생성", description = "약속을 생성합니다.") | ||
@PostMapping("/promises") | ||
fun createPromise(@RequestBody promiseRequest: PromiseRequest): PromiseDto { | ||
return promiseRegisterUseCase.createPromise(promiseRequest) | ||
} | ||
|
||
// 1. 약속 장소 변경 | ||
@Operation(summary = "약속(promise) 장소 수정", description = "약속 장소를 변경합니다.") | ||
@PutMapping("/promises/{promise-id}/location") | ||
fun updatePromiseLocation(@PathVariable(value = "promise-id") promiseId: Long, @RequestBody meetPlace: PlaceVo): PromiseDto { | ||
return promiseRegisterUseCase.updatePromiseMeetPlace(promiseId, meetPlace) | ||
} | ||
|
||
@Operation(summary = "약속(promise)시간 수정", description = "약속을 수정합니다. (약속 제목, 약속 장소, 약속 시간)") | ||
@PutMapping("/promises/{promise-id}/end-times/{end-time}") | ||
fun updatePromiseEndTime( | ||
@PathVariable(value = "promise-id") promiseId: Long, | ||
@RequestBody | ||
@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") | ||
endTime: LocalDateTime, | ||
): PromiseDto { | ||
return promiseRegisterUseCase.updatePromiseEndTime(promiseId, endTime) | ||
} | ||
|
||
// 약속 취소 | ||
@Operation(summary = "약속 취소", description = "약속을 취소합니다.") | ||
@DeleteMapping("/promises/{promise-id}/user-id/{user-id}") | ||
fun deletePromise(@PathVariable(value = "promise-id") promiseId: Long) { | ||
promiseRegisterUseCase.deletePromise(promiseId) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package com.depromeet.whatnow.api.promise.dto | ||
|
||
import com.depromeet.whatnow.common.vo.PlaceVo | ||
import com.depromeet.whatnow.domains.promise.domain.Promise | ||
import java.time.LocalDateTime | ||
|
||
data class PromiseDto( | ||
val title: String, | ||
val mainUserId: Long, | ||
val meetPlace: PlaceVo?, | ||
val endTime: LocalDateTime, | ||
) { | ||
companion object { | ||
fun from(p: Promise?): PromiseDto { | ||
// Check if p is null and handle the null case appropriately | ||
if (p == null) { | ||
// Return a default or placeholder value for PromiseDto | ||
return PromiseDto("", 1L, null, LocalDateTime.now()) | ||
} | ||
|
||
// Process non-null Promise object | ||
return PromiseDto(p.title, p.mainUserId, p.meetPlace, p.endTime) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package com.depromeet.whatnow.api.promise.dto | ||
|
||
import com.depromeet.whatnow.common.vo.UserInfoVo | ||
import com.depromeet.whatnow.domains.promise.domain.Promise | ||
import java.time.LocalDateTime | ||
|
||
data class PromiseFindDto( | ||
val title: String, | ||
val address: String, | ||
val endTime: LocalDateTime, | ||
val users: List<UserInfoVo> = listOf(), | ||
) { | ||
companion object { | ||
fun from(promise: Promise, users: List<UserInfoVo>): PromiseFindDto { | ||
val userValues = listOf<UserInfoVo>() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. listOf 가 mutable 한가요? |
||
for (user in users) { | ||
userValues.plus(user) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. listOf가 이뮤터블해서 plus 메소드 내부 동작이 ArrayList를 기존크기 + 1로 새로 할당해서 값을 추가하네용 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. plus 연산은 mutableList 도 동일한 원리로 동작합니다 그래서 addAll 로 변환해서 작성했습니다 ! |
||
return PromiseFindDto( | ||
promise.title, | ||
promise.meetPlace!!.address, | ||
promise.endTime, | ||
userValues, | ||
) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.depromeet.whatnow.api.promise.dto | ||
|
||
import com.depromeet.whatnow.common.vo.PlaceVo | ||
import java.time.LocalDateTime | ||
|
||
data class PromiseRequest( | ||
val title: String, | ||
val mainUserId: Long, | ||
val meetPlace: PlaceVo, | ||
val endTime: LocalDateTime, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.depromeet.whatnow.api.promise.dto | ||
|
||
import com.depromeet.whatnow.common.vo.UserInfoVo | ||
import com.depromeet.whatnow.domains.promise.domain.Promise | ||
|
||
data class PromiseSplitedByPromiseTypeDto( | ||
val promiseType: String, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기서 string 을 내려주지말고 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 찬진님 저도 이부분 보면서 공감되는데요, |
||
val promiseList: List<PromiseFindDto> = listOf(), | ||
) { | ||
fun addPromise(promise: Promise, users: List<UserInfoVo>) { | ||
promiseList.plus(PromiseFindDto.from(promise, users)) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package com.depromeet.whatnow.api.promise.usecase | ||
|
||
import com.depromeet.whatnow.annotation.UseCase | ||
import com.depromeet.whatnow.api.promise.dto.PromiseFindDto | ||
import com.depromeet.whatnow.api.promise.dto.PromiseSplitedByPromiseTypeDto | ||
import com.depromeet.whatnow.common.vo.UserInfoVo | ||
import com.depromeet.whatnow.config.security.SecurityUtils | ||
import com.depromeet.whatnow.domains.promise.adaptor.PromiseAdaptor | ||
import com.depromeet.whatnow.domains.promise.domain.Promise | ||
import com.depromeet.whatnow.domains.promise.domain.PromiseType | ||
import com.depromeet.whatnow.domains.promiseuser.adaptor.PromiseUserAdaptor | ||
import com.depromeet.whatnow.domains.promiseuser.domain.PromiseUser | ||
import com.depromeet.whatnow.domains.user.repository.UserRepository | ||
import java.time.LocalDateTime | ||
import java.time.format.DateTimeFormatter | ||
|
||
@UseCase | ||
class PromiseReadUseCase( | ||
val promiseAdaptor: PromiseAdaptor, | ||
val promiseUserAdaptor: PromiseUserAdaptor, | ||
val userRepository: UserRepository, | ||
) { | ||
/** | ||
* method desc: 유저가 참여한 약속들을 약속 종류(BEFORE, PAST)에 따라 분리해서 조회 | ||
* @param userId: 유저 아이디 | ||
* @return List<PromiseSplitByPromiseTypeDto>: 약속 종류에 따라 분리된 약속들 | ||
*/ | ||
fun findPromiseByUserIdSeparatedType(): List<PromiseSplitedByPromiseTypeDto> { | ||
val userId: Long = SecurityUtils.currentUserId | ||
|
||
val promiseUsers = promiseUserAdaptor.findByUserId(userId) | ||
val promiseSplitByPromiseTypeDto = mutableListOf<PromiseSplitedByPromiseTypeDto>() | ||
|
||
for (promiseUser in promiseUsers) { | ||
val promise = promiseAdaptor.queryPromise(promiseUser.promiseId) | ||
val eachPromiseUsers = promiseUserAdaptor.findByPromiseId(promiseUser.promiseId) | ||
val participant = getParticipantUserInfo(eachPromiseUsers) | ||
val promiseType = if (promise.promiseType == PromiseType.BEFORE) "BEFORE" else "PAST" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Delete 된 상태의 promise도 노출될 가능성이 있지 않을 까요 |
||
val promiseFindDto = PromiseFindDto.from(promise, participant) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 정적 팩터리메소드 |
||
val dto = PromiseSplitedByPromiseTypeDto(promiseType, listOf(promiseFindDto)) | ||
promiseSplitByPromiseTypeDto += dto | ||
} | ||
return promiseSplitByPromiseTypeDto | ||
} | ||
|
||
/** | ||
* method name: findPromiseByUserIdSeparatedWithYearMonth | ||
* description: 유저가 참여한 약속들 중 특정 년월에 해당하는 약속들을 조회한다 | ||
*/ | ||
fun findPromiseByUserIdYearMonth(yearMonth: String): List<PromiseFindDto> { | ||
val userId: Long = SecurityUtils.currentUserId | ||
return findPromisesByUserId(userId) | ||
.filter { isSameYearMonth(it.endTime, yearMonth) } | ||
.map { promise -> | ||
// 약속에 참여한 유저들 | ||
val participants = getParticipantUserInfo(promiseUserAdaptor.findByPromiseId(promise.id!!)) | ||
PromiseFindDto( | ||
title = promise.title, | ||
address = promise.meetPlace!!.address, | ||
endTime = promise.endTime, | ||
users = participants, | ||
) | ||
} | ||
} | ||
|
||
fun findPromisesByUserId(userId: Long): List<Promise> { | ||
val promiseUsers = promiseUserAdaptor.findByUserId(userId) | ||
return promiseUsers.map { promiseAdaptor.queryPromise(it.promiseId) } | ||
} | ||
|
||
private fun getParticipantUserInfo(promiseUsers: List<PromiseUser>): List<UserInfoVo> { | ||
return promiseUsers.mapNotNull { eachUser -> | ||
val user = userRepository.findById(eachUser.userId).orElse(null) | ||
user?.let { UserInfoVo.from(it) } | ||
} | ||
} | ||
|
||
private fun isSameYearMonth(dateTime: LocalDateTime, yearMonth: String): Boolean { | ||
val pattern = DateTimeFormatter.ofPattern("yyyy.MM") | ||
val formattedDateTime = dateTime.format(pattern) | ||
return formattedDateTime == yearMonth | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package com.depromeet.whatnow.api.promise.usecase | ||
|
||
import com.depromeet.whatnow.annotation.UseCase | ||
import com.depromeet.whatnow.api.promise.annotation.RequiresMainUser | ||
import com.depromeet.whatnow.api.promise.dto.PromiseDto | ||
import com.depromeet.whatnow.api.promise.dto.PromiseRequest | ||
import com.depromeet.whatnow.common.vo.PlaceVo | ||
import com.depromeet.whatnow.domains.promise.adaptor.PromiseAdaptor | ||
import com.depromeet.whatnow.domains.promise.domain.Promise | ||
import com.depromeet.whatnow.domains.promise.service.PromiseDomainService | ||
import java.time.LocalDateTime | ||
import javax.transaction.Transactional | ||
|
||
@UseCase | ||
class PromiseRegisterUseCase( | ||
val promiseAdaptor: PromiseAdaptor, | ||
val promiseDomainService: PromiseDomainService, | ||
) { | ||
@Transactional | ||
fun createPromise(promiseRequest: PromiseRequest): PromiseDto { | ||
val promise = promiseDomainService.save( | ||
Promise( | ||
title = promiseRequest.title, | ||
mainUserId = promiseRequest.mainUserId, | ||
meetPlace = promiseRequest.meetPlace, | ||
endTime = promiseRequest.endTime, | ||
), | ||
) | ||
return PromiseDto.from(promise) | ||
} | ||
|
||
@RequiresMainUser | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good |
||
fun updatePromiseMeetPlace(promiseId: Long, meetPlace: PlaceVo): PromiseDto { | ||
val promise = promiseAdaptor.queryPromise(promiseId) | ||
val updatePromise = promiseDomainService.updateMeetPlace(promise, meetPlace) | ||
return PromiseDto.from(updatePromise) | ||
} | ||
|
||
@RequiresMainUser | ||
fun updatePromiseEndTime(promiseId: Long, endTime: LocalDateTime): PromiseDto { | ||
val promise = promiseAdaptor.queryPromise(promiseId) | ||
val updatePromise = promiseDomainService.updateEndTime(promise, endTime) | ||
return PromiseDto.from(updatePromise) | ||
} | ||
|
||
@RequiresMainUser | ||
fun deletePromise(promiseId: Long) { | ||
val promise = promiseAdaptor.queryPromise(promiseId) | ||
promiseDomainService.deletePromise(promise) | ||
} | ||
} |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Promise.validMainUser() 이라는 메서드로 만들면 좋겠군요
NotHostException() 도 저희 입셉션 써주시궁