diff --git a/sql/group.sql b/sql/group.sql index d2f25e6..467b488 100644 --- a/sql/group.sql +++ b/sql/group.sql @@ -46,3 +46,25 @@ CREATE TABLE `group_user_score` CREATE UNIQUE INDEX uidx__group_user_id ON group_user_score (group_user_id); CREATE INDEX idx__group_id__group_user_id ON group_user_score (group_id, group_user_id); CREATE INDEX idx__uid ON group_user_score (uid); + +-- 그룹 태그 +CREATE TABLE `group_tag` +( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'group tag id', + `name` varchar(30) NOT NULL COMMENT '태그명', + `created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '생성일', + `modified_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일', + PRIMARY KEY (`id`) +) ENGINE = InnoDB AUTO_INCREMENT=200000 DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT='그룹 태그'; +CREATE UNIQUE INDEX uidx__group_tag_name ON group_tag (name); + +-- 그룹 태그 매핑 +CREATE TABLE `group_tag_map` +( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'group tag map id', + `group_id` bigint NOT NULL COMMENT 'group id', + `tag_id` bigint NOT NULL COMMENT 'group tag id', + `created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '생성일', + `modified_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일', + PRIMARY KEY (`id`) +) ENGINE = InnoDB AUTO_INCREMENT=200000 DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT='그룹 태그 매핑'; \ No newline at end of file diff --git a/src/main/kotlin/com/hero/alignlab/config/database/QueryDslConfig.kt b/src/main/kotlin/com/hero/alignlab/config/database/QueryDslConfig.kt new file mode 100644 index 0000000..8f2aa3f --- /dev/null +++ b/src/main/kotlin/com/hero/alignlab/config/database/QueryDslConfig.kt @@ -0,0 +1,16 @@ +package com.hero.alignlab.config.database + +import com.querydsl.jpa.impl.JPAQueryFactory +import jakarta.persistence.EntityManager +import jakarta.persistence.PersistenceContext +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class QueryDslConfig { + @PersistenceContext(name = "heroEntityManager") + private lateinit var entityManager: EntityManager + + @Bean + fun jpaQueryFactory(): JPAQueryFactory = JPAQueryFactory(entityManager) +} \ No newline at end of file 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 91db601..b373325 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 @@ -7,11 +7,15 @@ import com.hero.alignlab.common.model.HeroPageRequest import com.hero.alignlab.config.database.TransactionTemplates import com.hero.alignlab.domain.auth.model.AuthUser import com.hero.alignlab.domain.group.domain.Group +import com.hero.alignlab.domain.group.domain.GroupTag import com.hero.alignlab.domain.group.domain.GroupUser import com.hero.alignlab.domain.group.domain.GroupUserScore import com.hero.alignlab.domain.group.model.CreateGroupContext +import com.hero.alignlab.domain.group.model.UpdateGroupContext import com.hero.alignlab.domain.group.model.request.CheckGroupRegisterRequest import com.hero.alignlab.domain.group.model.request.CreateGroupRequest +import com.hero.alignlab.domain.group.model.request.CreateGroupTagRequest +import com.hero.alignlab.domain.group.model.request.UpdateGroupRequest import com.hero.alignlab.domain.group.model.response.* import com.hero.alignlab.domain.pose.application.PoseSnapshotService import com.hero.alignlab.domain.pose.domain.vo.PoseType.Companion.BAD_POSE @@ -33,6 +37,7 @@ class GroupFacade( private val groupService: GroupService, private val groupUserService: GroupUserService, private val groupUserScoreService: GroupUserScoreService, + private val groupTagService: GroupTagService, private val userInfoService: UserInfoService, private val txTemplates: TransactionTemplates, private val publisher: ApplicationEventPublisher, @@ -54,19 +59,42 @@ class GroupFacade( val group = CreateGroupContext(user, request).create() - val createdGroup = createGroup(user, group) + txTemplates.writer.executes { + val createdGroup = createGroup(user, group) + val createdGroupTags = createGroupTag(createdGroup.id, request.tagNames) - CreateGroupResponse.from(createdGroup) + CreateGroupResponse.from(createdGroup, createdGroupTags) + } } } fun createGroup(user: AuthUser, group: Group): Group { + val createdGroup = groupService.saveSync(group) + + publisher.publishEvent(CreateGroupEvent(createdGroup)) + + return createdGroup + } + + fun createGroupTag(groupId: Long, tagNames: List?): List { + return if (!tagNames.isNullOrEmpty()) { + groupTagService.validateGroupTag(tagNames) + groupTagService.saveSync(CreateGroupTagRequest(groupId, tagNames)) + } else emptyList() + } + + suspend fun updateGroup(user: AuthUser, groupId: Long, request: UpdateGroupRequest): UpdateGroupResponse { + val group = groupService.findByIdAndOwnerUidOrThrow(groupId, user.uid) return txTemplates.writer.executes { - val createdGroup = groupService.saveSync(group) + val updatedGroup = groupService.saveSync(UpdateGroupContext(group, request).update()) - publisher.publishEvent(CreateGroupEvent(createdGroup)) + val updatedGroupTag = if (!request.tagNames.isNullOrEmpty()) { + groupTagService.validateGroupTag(request.tagNames) + groupTagService.deleteSyncGroupId(groupId) + groupTagService.saveSync(CreateGroupTagRequest(groupId, request.tagNames)) + } else emptyList() - createdGroup + UpdateGroupResponse.from(updatedGroup, updatedGroupTag) } } @@ -90,6 +118,7 @@ class GroupFacade( txTemplates.writer.executes { groupUserScoreService.deleteAllByUid(uid) + groupTagService.deleteSyncGroupId(groupId) } } @@ -188,8 +217,9 @@ class GroupFacade( } ) { group, groupUserScore -> val ownerGroupUser = userInfoService.getUserByIdOrThrow(group.ownerUid) + val tags = groupTagService.findByGroupId(groupId) - GetGroupResponse.from(group, ownerGroupUser.nickname).run { + GetGroupResponse.from(group, ownerGroupUser.nickname, tags).run { when (group.ownerUid == user.uid) { true -> this false -> this.copy(joinCode = null) @@ -222,8 +252,8 @@ class GroupFacade( } } - suspend fun searchGroup(user: AuthUser, pageRequest: HeroPageRequest): Page { - val groups = groupService.findAll(pageRequest.toDefault()) + suspend fun searchGroup(user: AuthUser, tagName: String?, pageRequest: HeroPageRequest): Page { + val groups = groupService.findByTagNameAndPage(tagName, pageRequest.toDefault()) val groupUserByUid = groups.content.map { group -> group.id } .run { groupUserService.findByUidAndGroupIdIn(user.uid, this) } 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 f5f1524..da37edb 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 @@ -1,13 +1,7 @@ package com.hero.alignlab.domain.group.application -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.domain.Group import com.hero.alignlab.domain.group.infrastructure.GroupRepository -import com.hero.alignlab.domain.group.model.UpdateGroupContext -import com.hero.alignlab.domain.group.model.request.UpdateGroupRequest -import com.hero.alignlab.domain.group.model.response.UpdateGroupResponse import com.hero.alignlab.exception.ErrorCode import com.hero.alignlab.exception.NotFoundException import kotlinx.coroutines.Dispatchers @@ -21,18 +15,7 @@ import org.springframework.transaction.annotation.Transactional @Service class GroupService( private val groupRepository: GroupRepository, - private val txTemplates: TransactionTemplates, ) { - suspend fun updateGroup(user: AuthUser, id: Long, request: UpdateGroupRequest): UpdateGroupResponse { - val group = findByIdAndOwnerUidOrThrow(id, user.uid) - - val updatedGroup = txTemplates.writer.executes { - groupRepository.save(UpdateGroupContext(group, request).update()) - } - - return UpdateGroupResponse.from(updatedGroup) - } - suspend fun existsByName(name: String): Boolean { return withContext(Dispatchers.IO) { groupRepository.existsByName(name) @@ -72,9 +55,9 @@ class GroupService( return groupRepository.save(group) } - suspend fun findAll(pageable: Pageable): Page { + suspend fun findByTagNameAndPage(tagName: String?, pageable: Pageable): Page { return withContext(Dispatchers.IO) { - groupRepository.findAll(pageable) + groupRepository.findByTagNameAndPage(tagName, pageable) } } diff --git a/src/main/kotlin/com/hero/alignlab/domain/group/application/GroupTagService.kt b/src/main/kotlin/com/hero/alignlab/domain/group/application/GroupTagService.kt new file mode 100644 index 0000000..06a1da1 --- /dev/null +++ b/src/main/kotlin/com/hero/alignlab/domain/group/application/GroupTagService.kt @@ -0,0 +1,56 @@ +package com.hero.alignlab.domain.group.application + +import com.hero.alignlab.domain.group.domain.GroupTag +import com.hero.alignlab.domain.group.domain.GroupTagMap +import com.hero.alignlab.domain.group.infrastructure.GroupTagMapRepository +import com.hero.alignlab.domain.group.infrastructure.GroupTagRepository +import com.hero.alignlab.domain.group.model.request.CreateGroupTagRequest +import com.hero.alignlab.exception.ErrorCode +import com.hero.alignlab.exception.InvalidRequestException +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +class GroupTagService( + private val groupTagRepository: GroupTagRepository, + private val groupTagMapRepository: GroupTagMapRepository, +) { + @Transactional + fun saveSync(request: CreateGroupTagRequest): List { + return request.tagNames.map { tagName -> + findOrCreateTag(tagName).also { tag -> + groupTagMapRepository.save(GroupTagMap(groupId = request.groupId, tagId = tag.id)) + } + } + } + + @Transactional + fun deleteSyncGroupId(groupId: Long) { + groupTagMapRepository.deleteByGroupId(groupId) + } + + suspend fun findByGroupId(groupId: Long): List { + return withContext(Dispatchers.IO) { + val tagIds = groupTagMapRepository.findByGroupId(groupId) + .map { it.tagId } + groupTagRepository.findByIdIn(tagIds) + } + } + + private fun findOrCreateTag(tagName: String): GroupTag { + return groupTagRepository.findByName(tagName) + .firstOrNull() ?: groupTagRepository.save(GroupTag(name = tagName)) + } + + fun validateGroupTag(tagNames: List) { + if (tagNames.size > 3) { + throw InvalidRequestException(ErrorCode.OVER_COUNT_GROUP_TAG_ERROR) + } + + if (tagNames.size != tagNames.distinct().size) { + throw InvalidRequestException(ErrorCode.DUPLICATE_GROUP_TAG_NAME_ERROR) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/hero/alignlab/domain/group/domain/GroupTag.kt b/src/main/kotlin/com/hero/alignlab/domain/group/domain/GroupTag.kt new file mode 100644 index 0000000..85fc777 --- /dev/null +++ b/src/main/kotlin/com/hero/alignlab/domain/group/domain/GroupTag.kt @@ -0,0 +1,15 @@ +package com.hero.alignlab.domain.group.domain + +import com.hero.alignlab.domain.common.domain.BaseEntity +import jakarta.persistence.* + +@Entity +@Table(name = "`group_tag`") +data class GroupTag( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long = -1, + + @Column(name = "name") + var name: String, +) : BaseEntity() \ No newline at end of file diff --git a/src/main/kotlin/com/hero/alignlab/domain/group/domain/GroupTagMap.kt b/src/main/kotlin/com/hero/alignlab/domain/group/domain/GroupTagMap.kt new file mode 100644 index 0000000..55bf409 --- /dev/null +++ b/src/main/kotlin/com/hero/alignlab/domain/group/domain/GroupTagMap.kt @@ -0,0 +1,18 @@ +package com.hero.alignlab.domain.group.domain + +import com.hero.alignlab.domain.common.domain.BaseEntity +import jakarta.persistence.* + +@Entity +@Table(name = "`group_tag_map`") +data class GroupTagMap ( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long = -1, + + @Column(name = "group_id") + val groupId: Long, + + @Column(name = "tag_id") + val tagId: Long +) : BaseEntity() \ No newline at end of file diff --git a/src/main/kotlin/com/hero/alignlab/domain/group/infrastructure/GroupQRepository.kt b/src/main/kotlin/com/hero/alignlab/domain/group/infrastructure/GroupQRepository.kt new file mode 100644 index 0000000..bff8386 --- /dev/null +++ b/src/main/kotlin/com/hero/alignlab/domain/group/infrastructure/GroupQRepository.kt @@ -0,0 +1,9 @@ +package com.hero.alignlab.domain.group.infrastructure + +import com.hero.alignlab.domain.group.domain.Group +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable + +interface GroupQRepository { + fun findByTagNameAndPage(tagName: String?, pageable: Pageable): Page +} \ No newline at end of file diff --git a/src/main/kotlin/com/hero/alignlab/domain/group/infrastructure/GroupQRepositoryImpl.kt b/src/main/kotlin/com/hero/alignlab/domain/group/infrastructure/GroupQRepositoryImpl.kt new file mode 100644 index 0000000..883d4fc --- /dev/null +++ b/src/main/kotlin/com/hero/alignlab/domain/group/infrastructure/GroupQRepositoryImpl.kt @@ -0,0 +1,55 @@ +package com.hero.alignlab.domain.group.infrastructure + +import com.hero.alignlab.domain.group.domain.Group +import com.hero.alignlab.domain.group.domain.QGroup.group +import com.hero.alignlab.domain.group.domain.QGroupTag.groupTag +import com.hero.alignlab.domain.group.domain.QGroupTagMap.groupTagMap +import com.querydsl.core.types.OrderSpecifier +import com.querydsl.core.types.dsl.ComparableExpressionBase +import com.querydsl.jpa.impl.JPAQueryFactory +import org.springframework.data.domain.Page +import org.springframework.data.domain.PageImpl +import org.springframework.data.domain.Pageable + +class GroupQRepositoryImpl( + private val queryFactory: JPAQueryFactory, +) : GroupQRepository { + override fun findByTagNameAndPage(tagName: String?, pageable: Pageable): Page { + val groups = queryFactory + .selectDistinct(group) + .from(group) + .leftJoin(groupTagMap).on(group.id.eq(groupTagMap.groupId)) + .leftJoin(groupTag).on(groupTagMap.tagId.eq(groupTag.id)) + .where( + tagName?.takeIf { it.isNotEmpty() }?.let { groupTag.name.contains(it) } + ) + .offset(pageable.offset) + .limit(pageable.pageSize.toLong()) + .orderBy(getGroupOrderSpecifier(pageable)) + .fetch() + + val count = queryFactory + .select(group.countDistinct()) + .from(group) + .leftJoin(groupTagMap).on(group.id.eq(groupTagMap.groupId)) + .leftJoin(groupTag).on(groupTagMap.tagId.eq(groupTag.id)) + .where( + tagName?.takeIf { it.isNotEmpty() }?.let { groupTag.name.contains(it) } + ) + .orderBy(getGroupOrderSpecifier(pageable)) + .fetchOne() ?: 0L + + return PageImpl(groups, pageable, count) + } + + private fun getGroupOrderSpecifier(pageable: Pageable): OrderSpecifier?>? { + val order = pageable.sort.firstOrNull() ?: return null + val orderSpecifier: ComparableExpressionBase> = when (order.property) { + "createdAt" -> group.createdAt + "name" -> group.name + "ownerUid" -> group.ownerUid + else -> group.createdAt + } + return if (order.isDescending) orderSpecifier.desc() else orderSpecifier.asc() + } +} \ No newline at end of file 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 6463acf..77cf701 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 @@ -8,7 +8,7 @@ import java.time.LocalDateTime @Repository @Transactional(readOnly = true) -interface GroupRepository : JpaRepository { +interface GroupRepository : JpaRepository, GroupQRepository { fun existsByName(name: String): Boolean fun findByIdAndOwnerUid(id: Long, ownerUid: Long): Group? diff --git a/src/main/kotlin/com/hero/alignlab/domain/group/infrastructure/GroupTagMapRepository.kt b/src/main/kotlin/com/hero/alignlab/domain/group/infrastructure/GroupTagMapRepository.kt new file mode 100644 index 0000000..12fdf91 --- /dev/null +++ b/src/main/kotlin/com/hero/alignlab/domain/group/infrastructure/GroupTagMapRepository.kt @@ -0,0 +1,11 @@ +package com.hero.alignlab.domain.group.infrastructure + +import com.hero.alignlab.domain.group.domain.GroupTagMap +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository + +@Repository +interface GroupTagMapRepository : JpaRepository { + fun findByGroupId(groupId: Long): MutableList + fun deleteByGroupId(groupId: Long) +} \ No newline at end of file diff --git a/src/main/kotlin/com/hero/alignlab/domain/group/infrastructure/GroupTagRepository.kt b/src/main/kotlin/com/hero/alignlab/domain/group/infrastructure/GroupTagRepository.kt new file mode 100644 index 0000000..216a344 --- /dev/null +++ b/src/main/kotlin/com/hero/alignlab/domain/group/infrastructure/GroupTagRepository.kt @@ -0,0 +1,12 @@ +package com.hero.alignlab.domain.group.infrastructure + +import com.hero.alignlab.domain.group.domain.GroupTag +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository + +@Repository +interface GroupTagRepository : JpaRepository { + fun findByName(name: String): List + + fun findByIdIn(ids: List): List +} \ No newline at end of file diff --git a/src/main/kotlin/com/hero/alignlab/domain/group/model/request/CreateGroupRequest.kt b/src/main/kotlin/com/hero/alignlab/domain/group/model/request/CreateGroupRequest.kt index 8b21684..9ba24d8 100644 --- a/src/main/kotlin/com/hero/alignlab/domain/group/model/request/CreateGroupRequest.kt +++ b/src/main/kotlin/com/hero/alignlab/domain/group/model/request/CreateGroupRequest.kt @@ -11,4 +11,6 @@ data class CreateGroupRequest( val joinCode: String?, /** 정원, 기본값 30 */ val userCapacity: Int?, + /** 태그 리스트, 최대 3개 */ + val tagNames: List?, ) diff --git a/src/main/kotlin/com/hero/alignlab/domain/group/model/request/CreateGroupTagRequest.kt b/src/main/kotlin/com/hero/alignlab/domain/group/model/request/CreateGroupTagRequest.kt new file mode 100644 index 0000000..04ef3f0 --- /dev/null +++ b/src/main/kotlin/com/hero/alignlab/domain/group/model/request/CreateGroupTagRequest.kt @@ -0,0 +1,8 @@ +package com.hero.alignlab.domain.group.model.request + +data class CreateGroupTagRequest( + /** 그룹명, 중복 불가능 */ + val groupId: Long, + /** 태그 리스트, 최대 3개 */ + val tagNames: List, +) \ No newline at end of file diff --git a/src/main/kotlin/com/hero/alignlab/domain/group/model/request/GroupTagRequest.kt b/src/main/kotlin/com/hero/alignlab/domain/group/model/request/GroupTagRequest.kt new file mode 100644 index 0000000..c9480dd --- /dev/null +++ b/src/main/kotlin/com/hero/alignlab/domain/group/model/request/GroupTagRequest.kt @@ -0,0 +1,8 @@ +package com.hero.alignlab.domain.group.model.request + +data class GroupTagRequest( + /** 태그 ID */ + val tagId: Long, + /** 태그명 */ + val tagName: String, +) 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 index 29ab4b9..20e4ab9 100644 --- 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 @@ -11,4 +11,6 @@ data class UpdateGroupRequest( val joinCode: String?, /** 정원, 기본값 30 */ val userCapacity: Int?, + /** 수정할 태그 리스트 */ + val tagNames: List?, ) diff --git a/src/main/kotlin/com/hero/alignlab/domain/group/model/response/CreateGroupResponse.kt b/src/main/kotlin/com/hero/alignlab/domain/group/model/response/CreateGroupResponse.kt index 492b423..0392df3 100644 --- a/src/main/kotlin/com/hero/alignlab/domain/group/model/response/CreateGroupResponse.kt +++ b/src/main/kotlin/com/hero/alignlab/domain/group/model/response/CreateGroupResponse.kt @@ -1,6 +1,7 @@ package com.hero.alignlab.domain.group.model.response import com.hero.alignlab.domain.group.domain.Group +import com.hero.alignlab.domain.group.domain.GroupTag data class CreateGroupResponse( /** group id */ @@ -9,13 +10,16 @@ data class CreateGroupResponse( val name: String, /** 그룹 설명 */ val description: String?, + /** 그룹 태그 리스트 */ + val tags: List?, ) { companion object { - fun from(group: Group): CreateGroupResponse { + fun from(group: Group, tags: List): CreateGroupResponse { return CreateGroupResponse( id = group.id, name = group.name, description = group.description, + tags = tags.map { GroupTagResponse(it.id, it.name) }, ) } } 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 index fb9540a..48931d4 100644 --- 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 @@ -1,6 +1,7 @@ package com.hero.alignlab.domain.group.model.response import com.hero.alignlab.domain.group.domain.Group +import com.hero.alignlab.domain.group.domain.GroupTag data class GetGroupResponse( val id: Long, @@ -16,10 +17,12 @@ data class GetGroupResponse( val userCount: Int, /** 그룹 정원 */ val userCapacity: Int, - val ranks: List? = null + val ranks: List? = null, + /** 그룹 태그 리스트 */ + val tags: List?, ) { companion object { - fun from(group: Group, ownerName: String): GetGroupResponse { + fun from(group: Group, ownerName: String, tags: List): GetGroupResponse { return GetGroupResponse( id = group.id, name = group.name, @@ -29,7 +32,8 @@ data class GetGroupResponse( isHidden = group.isHidden, joinCode = group.joinCode, userCount = group.userCount, - userCapacity = group.userCapacity + userCapacity = group.userCapacity, + tags = tags.map { GroupTagResponse(it.id, it.name) }, ) } } diff --git a/src/main/kotlin/com/hero/alignlab/domain/group/model/response/GroupTagResponse.kt b/src/main/kotlin/com/hero/alignlab/domain/group/model/response/GroupTagResponse.kt new file mode 100644 index 0000000..e1e2c2f --- /dev/null +++ b/src/main/kotlin/com/hero/alignlab/domain/group/model/response/GroupTagResponse.kt @@ -0,0 +1,8 @@ +package com.hero.alignlab.domain.group.model.response + +data class GroupTagResponse( + /** 태그 ID */ + val tagId: Long, + /** 태그명 */ + val tagName: String, +) 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 index 6e03dfe..e0c4cfa 100644 --- 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 @@ -1,6 +1,7 @@ package com.hero.alignlab.domain.group.model.response import com.hero.alignlab.domain.group.domain.Group +import com.hero.alignlab.domain.group.domain.GroupTag data class UpdateGroupResponse( /** group id */ @@ -9,13 +10,16 @@ data class UpdateGroupResponse( val name: String, /** 그룹 설명 */ val description: String?, + /** 그룹 태그 리스트 */ + val tags: List?, ) { companion object { - fun from(group: Group): UpdateGroupResponse { + fun from(group: Group, tags: List): UpdateGroupResponse { return UpdateGroupResponse( id = group.id, name = group.name, description = group.description, + tags = tags.map { GroupTagResponse(it.id, it.name) }, ) } } 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 d78eec1..1c9c1ee 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 @@ -51,9 +51,10 @@ class GroupResource( @GetMapping("/api/v1/groups") suspend fun searchGroups( user: AuthUser, + @RequestParam(required = false) tagName: String?, @ParameterObject pageRequest: HeroPageRequest, ): PageResponse { - return groupFacade.searchGroup(user, pageRequest).wrapPage() + return groupFacade.searchGroup(user, tagName, pageRequest).wrapPage() } @Operation(summary = "그룹 생성") @@ -72,9 +73,9 @@ class GroupResource( @PathVariable id: Long, @RequestBody request: UpdateGroupRequest ): ResponseEntity> { - return groupService.updateGroup( + return groupFacade.updateGroup( user = user, - id = id, + groupId = id, request = request ).wrapOk() } diff --git a/src/main/kotlin/com/hero/alignlab/exception/ErrorCode.kt b/src/main/kotlin/com/hero/alignlab/exception/ErrorCode.kt index 212bf4a..1ebe7ac 100644 --- a/src/main/kotlin/com/hero/alignlab/exception/ErrorCode.kt +++ b/src/main/kotlin/com/hero/alignlab/exception/ErrorCode.kt @@ -44,7 +44,10 @@ enum class ErrorCode(val status: HttpStatus, val description: String) { INVALID_JOIN_CODE_ERROR(HttpStatus.BAD_REQUEST, "비밀번호의 조건이 부합하지 않습니다."), NOT_CONTAINS_GROUP_USER_ERROR(HttpStatus.BAD_REQUEST, "그룹원이 아닙니다."), NOT_FOUND_GROUP_ID_ERROR(HttpStatus.NOT_FOUND, "group Id를 찾을 수 없습니다."), + NOT_FOUND_GROUP_TAG_ERROR(HttpStatus.NOT_FOUND, "그룹 태그 정보를 찾을 수 없습니다."), OVER_RANGE_GROUP_NAME_ERROR(HttpStatus.BAD_REQUEST, "그룹명은 16글자까지만 입력할 수 있습니다."), + OVER_COUNT_GROUP_TAG_ERROR(HttpStatus.BAD_REQUEST, "그룹 태그는 최대 3개까지 입력 가능합니다."), + DUPLICATE_GROUP_TAG_NAME_ERROR(HttpStatus.BAD_REQUEST, "중복된 태그명이 있습니다."), /** Group User Error Code */ DUPLICATE_GROUP_JOIN_ERROR(HttpStatus.BAD_REQUEST, "한개의 그룹만 참여 가능합니다."),