Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,26 @@ class GatheringQueryService(
proposalService.getGatheringProposalStat(query.gatheringId, query.memberId)

val gatheringMemberIds = gatheringMemberService.getGatheringMemberIds(query.gatheringId)
val gathering = gatheringRepository.findByIdOrThrow(query.gatheringId)

val members = memberService.getMemberOverviews(gatheringMemberIds).map {
GatheringResponse.GatheringDetail.MemberOverview(it.memberId, it.memberName)
GatheringResponse.GatheringDetail.MemberOverview(
memberId = it.memberId,
memberName = it.memberName,
isMe = it.memberId == query.memberId,
isHost = gathering.isHost(it.memberId)
)
}

val gathering = gatheringRepository.findByIdOrThrow(query.gatheringId)

return GatheringResponse.GatheringDetail(
gatheringId = gathering.id,
gatheringIntervalDays = gathering.intervalDays,
gatheringName = gathering.name,
lastPushRelativeTime = RelativeTime.from(gathering.lastPushedAt ?: gathering.createdAt),
sentProposalCount = proposalStat.sentCount,
receivedProposalCount = proposalStat.receivedCount,
members = members
members = members,
isHost = gathering.isHost(query.memberId)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package nexters.tuk.application.gathering

import nexters.tuk.application.gathering.dto.request.GatheringCommand
import nexters.tuk.application.gathering.dto.response.GatheringResponse
import nexters.tuk.contract.BaseException
import nexters.tuk.contract.ErrorType
import nexters.tuk.domain.gathering.Gathering
import nexters.tuk.domain.gathering.GatheringRepository
import nexters.tuk.domain.gathering.findByIdOrThrow
Expand Down Expand Up @@ -37,6 +39,9 @@ class GatheringService(
@Transactional
fun updateGathering(command: GatheringCommand.Update): GatheringResponse.Simple {
val gathering = gatheringRepository.findByIdOrThrow(command.gatheringId)

validateHostPermission(gathering = gathering, memberId = command.memberId)

gathering.update(command)

return GatheringResponse.Simple(
Expand All @@ -45,4 +50,22 @@ class GatheringService(
intervalDays = gathering.intervalDays,
)
}

private fun validateHostPermission(
gathering: Gathering,
memberId: Long,
) {
if (!gathering.isHost(memberId)) {
throw BaseException(ErrorType.BAD_REQUEST, "수정 권한이 없습니다.")
}
}

@Transactional
fun deleteGathering(command: GatheringCommand.Delete) {
val gathering = gatheringRepository.findByIdOrThrow(command.gatheringId)

validateHostPermission(gathering = gathering, command.memberId)

gathering.delete()
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package nexters.tuk.application.gathering.dto.request

import io.swagger.v3.oas.annotations.media.Schema


class GatheringCommand {
data class Generate(
Expand All @@ -11,12 +9,13 @@ class GatheringCommand {
val tags: List<Long>,
)

data class JoinGathering(
data class Delete(
val memberId: Long,
val gatheringId: Long,
)

data class Update(
val memberId: Long,
val gatheringId: Long,
val gatheringIntervalDays: Long,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,26 +32,20 @@ class GatheringResponse {
}

data class GatheringDetail(
@Schema(description = "모임 id")
val gatheringId: Long,
@Schema(description = "모임 주기")
val gatheringIntervalDays: Long,
@Schema(description = "모임명")
val gatheringName: String,
@Schema(description = "상대 시간 타입 - \"오늘\", \"n일 전\", \"n주 전\", \"n개월 전\", \"n년 전\" ")
val lastPushRelativeTime: RelativeTime,
@Schema(description = "보낸 제안 수")
val sentProposalCount: Int,
@Schema(description = "받은 제안 수")
val receivedProposalCount: Int,
@Schema(description = "모임원")
val members: List<MemberOverview>,
val isHost: Boolean
) {
data class MemberOverview(
@Schema(description = "사용자 id")
val memberId: Long,
@Schema(description = "사용자 명")
val memberName: String,
val isMe: Boolean,
val isHost: Boolean,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ class GatheringController(
val response = gatheringService.updateGathering(
GatheringCommand.Update(
gatheringId = gatheringId,
gatheringIntervalDays = request.gatheringIntervalDays
gatheringIntervalDays = request.gatheringIntervalDays,
memberId = memberId
)
)

Expand Down Expand Up @@ -89,4 +90,19 @@ class GatheringController(

return ApiResponse.ok(response)
}

@DeleteMapping("/{gatheringId}")
override fun deleteGathering(
@Authenticated memberId: Long,
@PathVariable("gatheringId") gatheringId: Long
): ApiResponse<Unit> {
gatheringService.deleteGathering(
GatheringCommand.Delete(
memberId = memberId,
gatheringId = gatheringId
)
)

return ApiResponse.ok()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ class GatheringDto {
@Schema(description = "모임 주기 (일 단위)")
val gatheringIntervalDays: Long,
) {
fun toCommand(gatheringId: Long): GatheringCommand.Update {
fun toCommand(gatheringId: Long, memberId: Long): GatheringCommand.Update {
return GatheringCommand.Update(
memberId = memberId,
gatheringId = gatheringId,
gatheringIntervalDays = gatheringIntervalDays
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,9 @@ interface GatheringSpec {
fun getGatheringName(
gatheringId: Long
): ApiResponse<GatheringResponse.GatheringName>

@Operation(
summary = "모임 삭제",
)
fun deleteGathering(memberId: Long, gatheringId: Long): ApiResponse<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class GatheringQueryServiceIntegrationTest @Autowired constructor(
}

@Test
fun `모임 상세 정보를 정상적으로 조회한다`() {
fun `호스트가 모임 상세를 조회할 때 모든 정보가 올바르게 설정된다`() {
// given
val host = memberFixture.createMember(socialId = "host", email = "[email protected]")
val member1 = memberFixture.createMember(socialId = "member1", email = "[email protected]")
Expand Down Expand Up @@ -130,17 +130,31 @@ class GatheringQueryServiceIntegrationTest @Autowired constructor(
// when
val result = gatheringQueryService.getGatheringDetail(query)

// then
// then - 기본 정보
assertThat(result.gatheringId).isEqualTo(gathering.id)
assertThat(result.gatheringIntervalDays).isEqualTo(gathering.intervalDays)
assertThat(result.gatheringName).isEqualTo("테스트 모임")
assertThat(result.lastPushRelativeTime).isNotNull()
assertThat(result.sentProposalCount).isEqualTo(2)
assertThat(result.receivedProposalCount).isEqualTo(1)
assertThat(result.members).hasSize(3)

val memberNames = result.members.map { it.memberName }
assertThat(memberNames).containsExactlyInAnyOrder("테스트사용자", "테스트사용자", "테스트사용자")
assertThat(result.isHost).isTrue()

// then - isMe와 isHost 검증
val hostMember = result.members.find { it.memberId == host.id }
assertThat(hostMember).isNotNull
assertThat(hostMember!!.isMe).isTrue()
assertThat(hostMember.isHost).isTrue()

val member1Info = result.members.find { it.memberId == member1.id }
assertThat(member1Info).isNotNull
assertThat(member1Info!!.isMe).isFalse()
assertThat(member1Info.isHost).isFalse()

val member2Info = result.members.find { it.memberId == member2.id }
assertThat(member2Info).isNotNull
assertThat(member2Info!!.isMe).isFalse()
assertThat(member2Info.isHost).isFalse()
}

@Test
Expand Down Expand Up @@ -200,7 +214,7 @@ class GatheringQueryServiceIntegrationTest @Autowired constructor(
}

@Test
fun `혼자만 있는 모임의 상세 정보를 조회한다`() {
fun `혼자만 있는 모임에서 호스트의 모든 정보가 올바르게 설정된다`() {
// given
val host = memberFixture.createMember(socialId = "host", email = "[email protected]")

Expand All @@ -212,16 +226,22 @@ class GatheringQueryServiceIntegrationTest @Autowired constructor(
// when
val result = gatheringQueryService.getGatheringDetail(query)

// then
// then - 기본 정보
assertThat(result.gatheringId).isEqualTo(gathering.id)
assertThat(result.gatheringIntervalDays).isEqualTo(gathering.intervalDays)
assertThat(result.gatheringName).isEqualTo("혼자 모임")
assertThat(result.lastPushRelativeTime).isNotNull()
assertThat(result.sentProposalCount).isEqualTo(0)
assertThat(result.receivedProposalCount).isEqualTo(0)
assertThat(result.members).hasSize(1)
assertThat(result.members.first().memberName).isEqualTo("테스트사용자")
assertThat(result.members.first().memberId).isEqualTo(host.id)
assertThat(result.isHost).isTrue()

// then - isMe와 isHost 검증
val hostMember = result.members.first()
assertThat(hostMember.memberId).isEqualTo(host.id)
assertThat(hostMember.memberName).isEqualTo("테스트사용자")
assertThat(hostMember.isMe).isTrue()
assertThat(hostMember.isHost).isTrue()
}

@Test
Expand Down Expand Up @@ -286,4 +306,43 @@ class GatheringQueryServiceIntegrationTest @Autowired constructor(
val actualNames = result.gatheringOverviews.map { it.gatheringName }
assertThat(actualNames).containsExactlyInAnyOrderElementsOf(expectedNames)
}


@Test
fun `일반 멤버가 모임 상세를 조회할 때 isMe와 isHost가 올바르게 설정된다`() {
// given
val host = memberFixture.createMember(socialId = "host", email = "[email protected]")
val member1 = memberFixture.createMember(socialId = "member1", email = "[email protected]")
val member2 = memberFixture.createMember(socialId = "member2", email = "[email protected]")

val gathering = gatheringFixture.createGathering(host, "테스트 모임")
gatheringMemberRepository.save(GatheringMember.registerMember(gathering, host.id))
gatheringMemberRepository.save(GatheringMember.registerMember(gathering, member1.id))
gatheringMemberRepository.save(GatheringMember.registerMember(gathering, member2.id))

val query = GatheringQuery.GatheringDetail(member1.id, gathering.id)

// when
val result = gatheringQueryService.getGatheringDetail(query)

// then
assertThat(result.isHost).isFalse()
assertThat(result.members).hasSize(3)

val hostMember = result.members.find { it.memberId == host.id }
assertThat(hostMember).isNotNull
assertThat(hostMember!!.isMe).isFalse()
assertThat(hostMember.isHost).isTrue()

val member1Info = result.members.find { it.memberId == member1.id }
assertThat(member1Info).isNotNull
assertThat(member1Info!!.isMe).isTrue()
assertThat(member1Info.isHost).isFalse()

val member2Info = result.members.find { it.memberId == member2.id }
assertThat(member2Info).isNotNull
assertThat(member2Info!!.isMe).isFalse()
assertThat(member2Info.isHost).isFalse()
}

}
Loading
Loading