diff --git a/src/main/kotlin/com/hero/alignlab/domain/group/application/GroupFacade.kt b/src/main/kotlin/com/hero/alignlab/domain/group/application/GroupFacade.kt index c835d0d..83f49bd 100644 --- a/src/main/kotlin/com/hero/alignlab/domain/group/application/GroupFacade.kt +++ b/src/main/kotlin/com/hero/alignlab/domain/group/application/GroupFacade.kt @@ -4,9 +4,11 @@ import arrow.fx.coroutines.parZip import com.hero.alignlab.common.extension.executes import com.hero.alignlab.config.database.TransactionTemplates import com.hero.alignlab.domain.auth.model.AuthUser +import com.hero.alignlab.domain.group.model.response.GetGroupResponse import com.hero.alignlab.domain.group.model.response.JoinGroupResponse import com.hero.alignlab.exception.ErrorCode import com.hero.alignlab.exception.InvalidRequestException +import com.hero.alignlab.exception.NotFoundException import org.springframework.stereotype.Service @Service @@ -71,4 +73,22 @@ class GroupFacade( ) } } + + suspend fun getGroup(user: AuthUser, groupId: Long): GetGroupResponse { + return parZip( + { groupService.findByIdOrThrow(groupId) }, + { groupUserService.existsByGroupIdAndUid(groupId, user.uid) }, + ) { group, includeGroup -> + if (!includeGroup) { + throw NotFoundException(ErrorCode.NOT_FOUND_GROUP_ERROR) + } + + GetGroupResponse.from(group).run { + when (group.ownerUid == user.uid) { + true -> this + false -> this.copy(joinCode = null) + } + } + } + } } diff --git a/src/main/kotlin/com/hero/alignlab/domain/group/application/GroupService.kt b/src/main/kotlin/com/hero/alignlab/domain/group/application/GroupService.kt index de0a5ed..05357e2 100644 --- a/src/main/kotlin/com/hero/alignlab/domain/group/application/GroupService.kt +++ b/src/main/kotlin/com/hero/alignlab/domain/group/application/GroupService.kt @@ -6,7 +6,9 @@ import com.hero.alignlab.domain.auth.model.AuthUser import com.hero.alignlab.domain.group.domain.Group import com.hero.alignlab.domain.group.infrastructure.GroupRepository import com.hero.alignlab.domain.group.model.request.CreateGroupRequest +import com.hero.alignlab.domain.group.model.request.UpdateGroupRequest import com.hero.alignlab.domain.group.model.response.CreateGroupResponse +import com.hero.alignlab.domain.group.model.response.UpdateGroupResponse import com.hero.alignlab.event.model.CreateGroupEvent import com.hero.alignlab.exception.ErrorCode import com.hero.alignlab.exception.InvalidRequestException @@ -48,12 +50,37 @@ class GroupService( return CreateGroupResponse.from(createdGroup) } + suspend fun updateGroup(user: AuthUser, id: Long, request: UpdateGroupRequest): UpdateGroupResponse { + val group = findByIdAndOwnerUidOrThrow(id, user.uid) + + val updatedGroup = txTemplates.writer.executes { + group.apply { + this.name = request.name + this.description = request.description + this.isHidden = request.isHidden + this.joinCode = request.joinCode ?: UUID.randomUUID().toString().replace("-", "") + }.run { groupRepository.save(this) } + } + + return UpdateGroupResponse.from(updatedGroup) + } + suspend fun existsByName(name: String): Boolean { return withContext(Dispatchers.IO) { groupRepository.existsByName(name) } } + suspend fun findByIdAndOwnerUidOrThrow(id: Long, ownerUid: Long): Group { + return findByIdAndOwnerUid(id, ownerUid) ?: throw NotFoundException(ErrorCode.NOT_FOUND_GROUP_ERROR) + } + + suspend fun findByIdAndOwnerUid(id: Long, ownerUid: Long): Group? { + return withContext(Dispatchers.IO) { + groupRepository.findByIdAndOwnerUid(id, ownerUid) + } + } + suspend fun findByIdOrThrow(id: Long): Group { return findByIdOrNull(id) ?: throw NotFoundException(ErrorCode.NOT_FOUND_GROUP_ERROR) } diff --git a/src/main/kotlin/com/hero/alignlab/domain/group/domain/Group.kt b/src/main/kotlin/com/hero/alignlab/domain/group/domain/Group.kt index b63f85b..3090458 100644 --- a/src/main/kotlin/com/hero/alignlab/domain/group/domain/Group.kt +++ b/src/main/kotlin/com/hero/alignlab/domain/group/domain/Group.kt @@ -11,17 +11,17 @@ data class Group( val id: Long = -1, @Column(name = "name") - val name: String, + var name: String, @Column(name = "description") - val description: String?, + var description: String?, @Column(name = "owner_uid") var ownerUid: Long, @Column(name = "is_hidden") - val isHidden: Boolean, + var isHidden: Boolean, @Column(name = "join_code") - val joinCode: String + var joinCode: String ) : BaseEntity() diff --git a/src/main/kotlin/com/hero/alignlab/domain/group/infrastructure/GroupRepository.kt b/src/main/kotlin/com/hero/alignlab/domain/group/infrastructure/GroupRepository.kt index 73baa85..e5c460c 100644 --- a/src/main/kotlin/com/hero/alignlab/domain/group/infrastructure/GroupRepository.kt +++ b/src/main/kotlin/com/hero/alignlab/domain/group/infrastructure/GroupRepository.kt @@ -9,4 +9,6 @@ import org.springframework.transaction.annotation.Transactional @Transactional(readOnly = true) interface GroupRepository : JpaRepository { fun existsByName(name: String): Boolean + + fun findByIdAndOwnerUid(id: Long, ownerUid: Long): Group? } diff --git a/src/main/kotlin/com/hero/alignlab/domain/group/model/request/UpdateGroupRequest.kt b/src/main/kotlin/com/hero/alignlab/domain/group/model/request/UpdateGroupRequest.kt new file mode 100644 index 0000000..eda5f98 --- /dev/null +++ b/src/main/kotlin/com/hero/alignlab/domain/group/model/request/UpdateGroupRequest.kt @@ -0,0 +1,12 @@ +package com.hero.alignlab.domain.group.model.request + +data class UpdateGroupRequest( + /** 그룹명, 중복 불가능 */ + val name: String, + /** 그룹 설명 */ + val description: String?, + /** 숨김 여부 */ + val isHidden: Boolean, + /** 참여 코드, 미입력시 자동 생성 */ + val joinCode: String?, +) diff --git a/src/main/kotlin/com/hero/alignlab/domain/group/model/response/GetGroupResponse.kt b/src/main/kotlin/com/hero/alignlab/domain/group/model/response/GetGroupResponse.kt new file mode 100644 index 0000000..c916f55 --- /dev/null +++ b/src/main/kotlin/com/hero/alignlab/domain/group/model/response/GetGroupResponse.kt @@ -0,0 +1,26 @@ +package com.hero.alignlab.domain.group.model.response + +import com.hero.alignlab.domain.group.domain.Group + +data class GetGroupResponse( + val id: Long, + val name: String, + val description: String?, + val ownerUid: Long, + val isHidden: Boolean, + /** 그룹장만 조회 가능 */ + val joinCode: String?, +) { + companion object { + fun from(group: Group): GetGroupResponse { + return GetGroupResponse( + group.id, + group.name, + group.description, + group.ownerUid, + group.isHidden, + group.joinCode + ) + } + } +} diff --git a/src/main/kotlin/com/hero/alignlab/domain/group/model/response/UpdateGroupResponse.kt b/src/main/kotlin/com/hero/alignlab/domain/group/model/response/UpdateGroupResponse.kt new file mode 100644 index 0000000..6e03dfe --- /dev/null +++ b/src/main/kotlin/com/hero/alignlab/domain/group/model/response/UpdateGroupResponse.kt @@ -0,0 +1,23 @@ +package com.hero.alignlab.domain.group.model.response + +import com.hero.alignlab.domain.group.domain.Group + +data class UpdateGroupResponse( + /** group id */ + val id: Long, + /** 그룹명 */ + val name: String, + /** 그룹 설명 */ + val description: String?, +) { + companion object { + fun from(group: Group): UpdateGroupResponse { + return UpdateGroupResponse( + id = group.id, + name = group.name, + description = group.description, + ) + } + } +} + diff --git a/src/main/kotlin/com/hero/alignlab/domain/group/resource/GroupResource.kt b/src/main/kotlin/com/hero/alignlab/domain/group/resource/GroupResource.kt index 6a3b38b..c2c4a01 100644 --- a/src/main/kotlin/com/hero/alignlab/domain/group/resource/GroupResource.kt +++ b/src/main/kotlin/com/hero/alignlab/domain/group/resource/GroupResource.kt @@ -7,6 +7,7 @@ import com.hero.alignlab.domain.auth.model.AuthUser import com.hero.alignlab.domain.group.application.GroupFacade import com.hero.alignlab.domain.group.application.GroupService import com.hero.alignlab.domain.group.model.request.CreateGroupRequest +import com.hero.alignlab.domain.group.model.request.UpdateGroupRequest import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.tags.Tag import org.springframework.http.MediaType @@ -19,6 +20,17 @@ class GroupResource( private val groupService: GroupService, private val groupFacade: GroupFacade, ) { + /** + * - 속해 있는 그룹의 정보를 조회할 수 있다. + * - 그룹장의 경우, joinCode를 조회할 수 있다.(그룹원은 조회 불가.) + */ + @Operation(summary = "그룹 조회") + @GetMapping("/api/v1/groups/{id}") + suspend fun getGroup( + user: AuthUser, + @PathVariable id: Long + ) = groupFacade.getGroup(user, id).wrapOk() + @Operation(summary = "그룹 생성") @PostMapping("/api/v1/groups") suspend fun createGroup( @@ -26,13 +38,28 @@ class GroupResource( @RequestBody request: CreateGroupRequest ) = groupService.createGroup(user, request).wrapCreated() + @Operation(summary = "그룹 수정하기") + @PutMapping("/api/v1/groups/{id}") + suspend fun updateGroup( + user: AuthUser, + @PathVariable id: Long, + @RequestBody request: UpdateGroupRequest + ) = groupService.updateGroup( + user = user, + id = id, + request = request + ).wrapOk() + @Operation(summary = "그룹 탈퇴하기") - @PostMapping("/api/v1/groups/{id}/withdraw") + @DeleteMapping("/api/v1/groups/{id}/withdraw") suspend fun withdrawGroup( user: AuthUser, @PathVariable id: Long ) = groupFacade.withdraw(user, id).wrapVoid() + /** + * - 숨김처리된 그룹의 경우, joinCode가 입력되어야 접속 가능하다. + */ @Operation(summary = "그룹 들어가기") @PostMapping("/api/v1/groups/{groupId}/join") suspend fun joinGroup(