Skip to content
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

그룹 태그 기능 추가 #26

Merged
merged 2 commits into from
Nov 17, 2024
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
23 changes: 23 additions & 0 deletions sql/group.sql
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,26 @@ 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='그룹 태그 매핑';
CREATE UNIQUE INDEX uidx__group_id__tag_id ON group_tag_map (group_id, tag_id);
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ fun NumberPath<Long>.isEquals(parameter: Long?): BooleanExpression? {
}

fun StringPath.isContains(parameter: String?): BooleanExpression? {
return parameter?.let { param -> this.contains(param) }
return parameter?.takeIf { param -> param.isNotEmpty() }?.let { param -> this.contains(param) }
}

fun NumberPath<Long>.isIn(parameters: Set<Long>?): BooleanExpression? {
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import jakarta.persistence.*
class Discussion(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = -1L,
val id: Long = 0L,

@Column(name = "uid")
val uid: Long,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import jakarta.persistence.*
class DiscussionComment(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = -1L,
val id: Long = 0L,

@Column(name = "uid")
val uid: Long,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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<String>?): List<GroupTag> {
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.deleteGroupTagMapSyncByGroupId(groupId)
groupTagService.saveSync(CreateGroupTagRequest(groupId, request.tagNames))
} else emptyList()

createdGroup
UpdateGroupResponse.from(updatedGroup, updatedGroupTag)
}
}

Expand All @@ -90,6 +118,7 @@ class GroupFacade(

txTemplates.writer.executes {
groupUserScoreService.deleteAllByUid(uid)
groupTagService.deleteGroupTagMapSyncByGroupId(groupId)
}
}

Expand Down Expand Up @@ -185,11 +214,12 @@ class GroupFacade(
.filterNot { groupUserScore -> groupUserScore.score == null }
.sortedBy { groupUserScore -> groupUserScore.score }
.take(5)
}
) { group, groupUserScore ->
},
{ groupTagService.findByGroupId(groupId) },
) { group, groupUserScore, tags ->
val ownerGroupUser = userInfoService.getUserByIdOrThrow(group.ownerUid)

GetGroupResponse.from(group, ownerGroupUser.nickname).run {
GetGroupResponse.of(group, tags, ownerGroupUser.nickname).run {
when (group.ownerUid == user.uid) {
true -> this
false -> this.copy(joinCode = null)
Expand Down Expand Up @@ -222,8 +252,8 @@ class GroupFacade(
}
}

suspend fun searchGroup(user: AuthUser, pageRequest: HeroPageRequest): Page<SearchGroupResponse> {
val groups = groupService.findAll(pageRequest.toDefault())
suspend fun searchGroup(user: AuthUser, tagName: String?, pageRequest: HeroPageRequest): Page<SearchGroupResponse> {
val groups = groupService.findByTagNameAndPage(tagName, pageRequest.toDefault())

val groupUserByUid = groups.content.map { group -> group.id }
.run { groupUserService.findByUidAndGroupIdIn(user.uid, this) }
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -72,9 +55,9 @@ class GroupService(
return groupRepository.save(group)
}

suspend fun findAll(pageable: Pageable): Page<Group> {
suspend fun findByTagNameAndPage(tagName: String?, pageable: Pageable): Page<Group> {
return withContext(Dispatchers.IO) {
groupRepository.findAll(pageable)
groupRepository.findByTagNameAndPage(tagName, pageable)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
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<GroupTag> {
val existsTags = groupTagRepository.findAllByNameIn(request.tagNames)
val existsTagNames = existsTags.map { tag -> tag.name }

val needToCreateTags = request.tagNames
.filterNot { tagName -> existsTagNames.contains(tagName) }
.map { tagName -> GroupTag(name = tagName) }

val createdTags = groupTagRepository.saveAll(needToCreateTags)

(createdTags + existsTags)
.map { tag -> GroupTagMap(groupId = request.groupId, tagId = tag.id) }
.run { groupTagMapRepository.saveAll(this) }

return (createdTags + existsTags)
}

@Transactional
fun deleteGroupTagMapSyncByGroupId(groupId: Long) {
/**
* 왜, QueryDSL로 삭제를 했는지
* https://docs.jboss.org/hibernate/orm/4.2/javadocs/org/hibernate/event/internal/AbstractFlushingEventListener.html
*/
groupTagRepository.deleteGroupTagMapByGroupId(groupId)
}

suspend fun findByGroupId(groupId: Long): List<GroupTag> {
return withContext(Dispatchers.IO) {
groupTagRepository.findByGroupId(groupId)
}
}

fun validateGroupTag(tagNames: List<String>) {
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)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import jakarta.persistence.*
data class Group(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = -1,
val id: Long = 0L,

@Column(name = "name")
var name: String,
Expand Down
15 changes: 15 additions & 0 deletions src/main/kotlin/com/hero/alignlab/domain/group/domain/GroupTag.kt
Original file line number Diff line number Diff line change
@@ -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 = 0L,

@Column(name = "name")
var name: String,
) : BaseEntity()
Original file line number Diff line number Diff line change
@@ -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 = 0L,

@Column(name = "group_id")
val groupId: Long,

@Column(name = "tag_id")
val tagId: Long
) : BaseEntity()
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import jakarta.persistence.*
data class GroupUser(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = -1,
val id: Long = 0L,

@Column(name = "group_id")
val groupId: Long,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import jakarta.persistence.*
class GroupUserScore(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = -1,
val id: Long = 0L,

@Column(name = "group_id")
val groupId: Long,
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Group>
}
Loading
Loading