diff --git a/src/main/java/flipnote/group/adapter/in/web/GroupController.java b/src/main/java/flipnote/group/adapter/in/web/GroupController.java index 8856c06..cd0ed6a 100644 --- a/src/main/java/flipnote/group/adapter/in/web/GroupController.java +++ b/src/main/java/flipnote/group/adapter/in/web/GroupController.java @@ -143,4 +143,6 @@ public ResponseEntity deleteGroup( //todo 내가 생성한 그룹 전체 조회 + //todo 하위 권한 수정 + } diff --git a/src/main/java/flipnote/group/adapter/out/entity/GroupMemberEntity.java b/src/main/java/flipnote/group/adapter/out/entity/GroupMemberEntity.java index 8161510..ea59dba 100644 --- a/src/main/java/flipnote/group/adapter/out/entity/GroupMemberEntity.java +++ b/src/main/java/flipnote/group/adapter/out/entity/GroupMemberEntity.java @@ -40,42 +40,27 @@ public class GroupMemberEntity extends BaseEntity { @Column(name = "user_id", nullable = false) private Long userId; - @Enumerated(EnumType.STRING) - @Column(nullable = false) - private GroupMemberRole role; + @Column(name = "group_role_id", nullable = false) + private Long groupRoleId; @Builder - private GroupMemberEntity(Long groupId, Long userId, GroupMemberRole role) { + private GroupMemberEntity(Long groupId, Long userId, Long groupRoleId) { this.groupId = groupId; this.userId = userId; - this.role = (role != null) ? role : GroupMemberRole.MEMBER; + this.groupRoleId = groupRoleId; } /** - * 오너인 경우 + * 멤버 생성 * @param groupId * @param userId * @return */ - public static GroupMemberEntity createOwner(Long groupId, Long userId) { + public static GroupMemberEntity create(Long groupId, Long userId, Long groupRoleId) { return GroupMemberEntity.builder() .groupId(groupId) .userId(userId) - .role(GroupMemberRole.OWNER) - .build(); - } - - /** - * 오너가 아닌 경우 - * @param groupId - * @param userId - * @return - */ - public static GroupMemberEntity join(Long groupId, Long userId) { - return GroupMemberEntity.builder() - .groupId(groupId) - .userId(userId) - .role(GroupMemberRole.MEMBER) + .groupRoleId(groupRoleId) .build(); } } diff --git a/src/main/java/flipnote/group/adapter/out/entity/PermissionEntity.java b/src/main/java/flipnote/group/adapter/out/entity/PermissionEntity.java new file mode 100644 index 0000000..530cf0e --- /dev/null +++ b/src/main/java/flipnote/group/adapter/out/entity/PermissionEntity.java @@ -0,0 +1,51 @@ +package flipnote.group.adapter.out.entity; + +import flipnote.group.domain.model.permission.GroupPermission; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Table(name = "group_role_permissions", + uniqueConstraints = @UniqueConstraint( + name = "uk_grp_role_perm", + columnNames = {"group_role_id", "permission"} + ) +) +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class PermissionEntity { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "group_role_id", nullable = false) + private Long groupRoleId; + + @Enumerated(EnumType.STRING) + @Column(nullable = false, length = 50) + private GroupPermission permission; + + @Builder + private PermissionEntity(Long groupRoleId, GroupPermission permission) { + this.groupRoleId = groupRoleId; + this.permission = permission; + } + + public static PermissionEntity create(Long groupRoleId, GroupPermission permission) { + return PermissionEntity.builder() + .groupRoleId(groupRoleId) + .permission(permission) + .build(); + } +} diff --git a/src/main/java/flipnote/group/adapter/out/entity/RoleEntity.java b/src/main/java/flipnote/group/adapter/out/entity/RoleEntity.java new file mode 100644 index 0000000..58bf25f --- /dev/null +++ b/src/main/java/flipnote/group/adapter/out/entity/RoleEntity.java @@ -0,0 +1,53 @@ +package flipnote.group.adapter.out.entity; + +import flipnote.group.domain.model.member.GroupMemberRole; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Table(name = "group_roles", + uniqueConstraints = @UniqueConstraint( + name = "uk_group_roles_group_name", + columnNames = {"group_id", "role"} + ) +) +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class RoleEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "group_id", nullable = false) + private Long groupId; + + @Enumerated(EnumType.STRING) + @Column(nullable = false, length = 50, name = "group_role") + private GroupMemberRole role; + + @Builder + private RoleEntity(Long groupId, GroupMemberRole role) { + this.groupId = groupId; + this.role = role; + } + + public static RoleEntity create(Long groupId, GroupMemberRole groupMemberRole) { + return RoleEntity.builder() + .groupId(groupId) + .role(groupMemberRole) + .build(); + } +} + diff --git a/src/main/java/flipnote/group/adapter/out/persistence/GroupMemberRepositoryAdapter.java b/src/main/java/flipnote/group/adapter/out/persistence/GroupMemberRepositoryAdapter.java index ad903b3..3763430 100644 --- a/src/main/java/flipnote/group/adapter/out/persistence/GroupMemberRepositoryAdapter.java +++ b/src/main/java/flipnote/group/adapter/out/persistence/GroupMemberRepositoryAdapter.java @@ -15,12 +15,23 @@ public class GroupMemberRepositoryAdapter implements GroupMemberRepositoryPort { private final GroupMemberRepository groupMemberRepository; /** - * 오너일 경우 + * 그룹 멤버 저장 * @param groupId * @param userId */ @Override - public void saveOwner(Long groupId, Long userId) { - groupMemberRepository.save(GroupMemberMapper.createOwner(groupId, userId)); + public void save(Long groupId, Long userId, Long roleId) { + groupMemberRepository.save(GroupMemberMapper.create(groupId, userId, roleId)); + } + + /** + * 유저가 그룹 내에 있는지 체크 + * @param groupId + * @param userId + * @return + */ + @Override + public boolean existsUserInGroup(Long groupId, Long userId) { + return groupMemberRepository.existsByGroupIdAndUserId(groupId, userId); } } diff --git a/src/main/java/flipnote/group/adapter/out/persistence/GroupRoleRepositoryAdapter.java b/src/main/java/flipnote/group/adapter/out/persistence/GroupRoleRepositoryAdapter.java new file mode 100644 index 0000000..ca757da --- /dev/null +++ b/src/main/java/flipnote/group/adapter/out/persistence/GroupRoleRepositoryAdapter.java @@ -0,0 +1,90 @@ +package flipnote.group.adapter.out.persistence; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Repository; + +import flipnote.group.adapter.out.entity.PermissionEntity; +import flipnote.group.adapter.out.entity.RoleEntity; +import flipnote.group.application.port.out.GroupRoleRepositoryPort; +import flipnote.group.domain.model.member.GroupMemberRole; +import flipnote.group.domain.model.permission.GroupPermission; +import flipnote.group.infrastructure.persistence.jpa.GroupMemberRepository; +import flipnote.group.infrastructure.persistence.jpa.GroupRolePermissionRepository; +import flipnote.group.infrastructure.persistence.jpa.GroupRoleRepository; +import lombok.RequiredArgsConstructor; + +@Repository +@RequiredArgsConstructor +public class GroupRoleRepositoryAdapter implements GroupRoleRepositoryPort { + + private final GroupRoleRepository groupRoleRepository; + private final GroupRolePermissionRepository groupRolePermissionRepository; + private final GroupMemberRepository groupMemberRepository; + + private static final Map> DEFAULT_PERMS_BY_ROLE = + Map.of( + GroupMemberRole.OWNER, List.of( + GroupPermission.KICK, + GroupPermission.JOIN_REQUEST_MANAGE + ), + GroupMemberRole.HEAD_MANAGER, List.of( + GroupPermission.KICK, + GroupPermission.JOIN_REQUEST_MANAGE + ), + GroupMemberRole.MANAGER, List.of( + GroupPermission.KICK, + GroupPermission.JOIN_REQUEST_MANAGE + ), + GroupMemberRole.MEMBER, List.of() + ); + + /** + * 그룹 생성시 역할도 추가 + * @param groupId + * @return + */ + @Override + public Long create(Long groupId) { + // 오너 역할 생성 + Map roleIdByRole = Arrays.stream(new GroupMemberRole[]{ + GroupMemberRole.OWNER, + GroupMemberRole.HEAD_MANAGER, + GroupMemberRole.MANAGER, + GroupMemberRole.MEMBER + }) + .collect(java.util.stream.Collectors.toMap( + role -> role, + role -> groupRoleRepository.save(RoleEntity.create(groupId, role)).getId() + )); + + // 역할별 기본 권한 세팅 (role-permission 매핑 생성) + List perms = DEFAULT_PERMS_BY_ROLE.entrySet().stream() + .flatMap(e -> e.getValue().stream() + .map(p -> PermissionEntity.create(roleIdByRole.get(e.getKey()), p)) + ) + .toList(); + + groupRolePermissionRepository.saveAll(perms); + + // 그룹 생성자에게 OWNER roleId 리턴 (바깥에서 group_members 생성할 때 사용) + return roleIdByRole.get(GroupMemberRole.OWNER); + } + + /** + * 해당 유저가 그룹 내에 역할인지 확인 + * 오너 여부에서 사용 + * @param userId + * @param groupId + * @param groupMemberRole + * @return + */ + @Override + public boolean checkRole(Long userId, Long groupId, GroupMemberRole groupMemberRole) { + RoleEntity roleEntity = groupRoleRepository.findByGroupIdAndRole(groupId, groupMemberRole); + + return groupMemberRepository.existsByUserIdAndGroupRoleId(userId, roleEntity.getId()); + } +} diff --git a/src/main/java/flipnote/group/adapter/out/persistence/mapper/GroupMemberMapper.java b/src/main/java/flipnote/group/adapter/out/persistence/mapper/GroupMemberMapper.java index 5d1e4b7..4dbacc2 100644 --- a/src/main/java/flipnote/group/adapter/out/persistence/mapper/GroupMemberMapper.java +++ b/src/main/java/flipnote/group/adapter/out/persistence/mapper/GroupMemberMapper.java @@ -10,11 +10,7 @@ @Component @NoArgsConstructor(access = AccessLevel.PRIVATE) public class GroupMemberMapper { - public static GroupMemberEntity createOwner(Long groupId, Long userId) { - return GroupMemberEntity.builder() - .groupId(groupId) - .userId(userId) - .role(GroupMemberRole.OWNER) - .build(); + public static GroupMemberEntity create(Long groupId, Long userId, Long roleId) { + return GroupMemberEntity.create(groupId, userId, roleId); } } diff --git a/src/main/java/flipnote/group/application/port/out/GroupMemberRepositoryPort.java b/src/main/java/flipnote/group/application/port/out/GroupMemberRepositoryPort.java index 5858851..5f93ec1 100644 --- a/src/main/java/flipnote/group/application/port/out/GroupMemberRepositoryPort.java +++ b/src/main/java/flipnote/group/application/port/out/GroupMemberRepositoryPort.java @@ -1,5 +1,7 @@ package flipnote.group.application.port.out; public interface GroupMemberRepositoryPort { - void saveOwner(Long groupId, Long userId); + void save(Long groupId, Long userId, Long roleId); + + boolean existsUserInGroup(Long groupId, Long userId); } diff --git a/src/main/java/flipnote/group/application/port/out/GroupRoleRepositoryPort.java b/src/main/java/flipnote/group/application/port/out/GroupRoleRepositoryPort.java new file mode 100644 index 0000000..bb61247 --- /dev/null +++ b/src/main/java/flipnote/group/application/port/out/GroupRoleRepositoryPort.java @@ -0,0 +1,9 @@ +package flipnote.group.application.port.out; + +import flipnote.group.domain.model.member.GroupMemberRole; + +public interface GroupRoleRepositoryPort { + Long create(Long groupId); + + boolean checkRole(Long userId, Long groupId, GroupMemberRole groupMemberRole); +} diff --git a/src/main/java/flipnote/group/application/service/ChangeGroupService.java b/src/main/java/flipnote/group/application/service/ChangeGroupService.java index d72fecb..948e34a 100644 --- a/src/main/java/flipnote/group/application/service/ChangeGroupService.java +++ b/src/main/java/flipnote/group/application/service/ChangeGroupService.java @@ -1,15 +1,15 @@ package flipnote.group.application.service; -import java.util.Optional; - import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import flipnote.group.adapter.out.entity.GroupEntity; +import flipnote.group.adapter.out.persistence.GroupRoleRepositoryAdapter; import flipnote.group.adapter.out.persistence.mapper.GroupMapper; import flipnote.group.application.port.in.ChangeGroupUseCase; import flipnote.group.application.port.in.command.ChangeGroupCommand; import flipnote.group.application.port.in.result.ChangeGroupResult; +import flipnote.group.domain.model.member.GroupMemberRole; import flipnote.group.infrastructure.persistence.jpa.GroupRepository; import lombok.RequiredArgsConstructor; @@ -18,6 +18,7 @@ public class ChangeGroupService implements ChangeGroupUseCase { private final GroupRepository jpaGroupRepository; + private final GroupRoleRepositoryAdapter groupRoleRepository; /** * 그룹 수정 @@ -32,6 +33,13 @@ public ChangeGroupResult change(ChangeGroupCommand cmd) { () -> new IllegalArgumentException("group not Exists") ); + //오너 인지 확인 + boolean isOwner = groupRoleRepository.checkRole(cmd.userId(), entity.getId(), GroupMemberRole.OWNER); + + if(!isOwner) { + throw new IllegalArgumentException("not owner"); + } + entity.change(cmd); return new ChangeGroupResult(GroupMapper.toDomain(entity)); diff --git a/src/main/java/flipnote/group/application/service/CreateGroupService.java b/src/main/java/flipnote/group/application/service/CreateGroupService.java index 88bd774..89db017 100644 --- a/src/main/java/flipnote/group/application/service/CreateGroupService.java +++ b/src/main/java/flipnote/group/application/service/CreateGroupService.java @@ -9,6 +9,7 @@ import flipnote.group.application.port.in.result.CreateGroupResult; import flipnote.group.application.port.out.GroupMemberRepositoryPort; import flipnote.group.application.port.out.GroupRepositoryPort; +import flipnote.group.application.port.out.GroupRoleRepositoryPort; import flipnote.group.domain.model.group.Group; import lombok.RequiredArgsConstructor; @@ -18,6 +19,7 @@ public class CreateGroupService implements CreateGroupUseCase { private final GroupRepositoryPort groupRepository; private final GroupMemberRepositoryPort groupMemberRepository; + private final GroupRoleRepositoryPort groupRoleRepository; /** * 그룹 생성 @@ -33,9 +35,12 @@ public CreateGroupResult create(CreateGroupCommand cmd) { //그룹 도메인 -> 엔티티 변환 후 저장 Long groupId = groupRepository.saveNewGroup(domainGroup); + + //그룹 역할 생성 + Long roleId = groupRoleRepository.create(groupId); - //그룹 멤버 저장 - groupMemberRepository.saveOwner(groupId, cmd.userId()); + //생성자 오너 역할로 저장 + groupMemberRepository.save(groupId, cmd.userId(), roleId); return new CreateGroupResult(groupId); } diff --git a/src/main/java/flipnote/group/application/service/DeleteGroupService.java b/src/main/java/flipnote/group/application/service/DeleteGroupService.java index 41af347..5c6ea6e 100644 --- a/src/main/java/flipnote/group/application/service/DeleteGroupService.java +++ b/src/main/java/flipnote/group/application/service/DeleteGroupService.java @@ -6,6 +6,8 @@ import flipnote.group.application.port.in.DeleteGroupUseCase; import flipnote.group.application.port.in.command.DeleteGroupCommand; import flipnote.group.application.port.out.GroupRepositoryPort; +import flipnote.group.application.port.out.GroupRoleRepositoryPort; +import flipnote.group.domain.model.member.GroupMemberRole; import lombok.RequiredArgsConstructor; @Service @@ -13,10 +15,20 @@ public class DeleteGroupService implements DeleteGroupUseCase { private final GroupRepositoryPort groupRepository; + private final GroupRoleRepositoryPort groupRoleRepository; @Override @Transactional public void deleteGroup(DeleteGroupCommand cmd) { + + //오너 인지 확인 + boolean isOwner = groupRoleRepository.checkRole(cmd.userId(), cmd.groupId(), GroupMemberRole.OWNER); + + //오너가 아닐 경우 에러 발생 + if(!isOwner) { + throw new IllegalArgumentException("not owner"); + } + groupRepository.delete(cmd.groupId()); } diff --git a/src/main/java/flipnote/group/application/service/FindGroupService.java b/src/main/java/flipnote/group/application/service/FindGroupService.java index 700ec9c..3e0673d 100644 --- a/src/main/java/flipnote/group/application/service/FindGroupService.java +++ b/src/main/java/flipnote/group/application/service/FindGroupService.java @@ -9,6 +9,7 @@ import flipnote.group.application.port.in.FindGroupUseCase; import flipnote.group.application.port.in.command.FindGroupCommand; import flipnote.group.application.port.in.result.FindGroupResult; +import flipnote.group.application.port.out.GroupMemberRepositoryPort; import flipnote.group.application.port.out.GroupRepositoryPort; import flipnote.group.domain.model.group.Group; import lombok.RequiredArgsConstructor; @@ -18,6 +19,7 @@ public class FindGroupService implements FindGroupUseCase { private final GroupRepositoryPort groupRepository; + private final GroupMemberRepositoryPort groupMemberRepository; /** * 하나의 그룹에 대한 정보 조회 @@ -27,6 +29,13 @@ public class FindGroupService implements FindGroupUseCase { @Override public FindGroupResult findGroup(FindGroupCommand cmd) { + // 유저가 그룹 내에 존재하는지 확인 + boolean isMember = groupMemberRepository.existsUserInGroup(cmd.groupId(), cmd.userId()); + + if(!isMember) { + throw new IllegalArgumentException("user not in Group"); + } + Group group = groupRepository.findById(cmd.groupId()); return new FindGroupResult(group); diff --git a/src/main/java/flipnote/group/domain/model/member/GroupMemberRole.java b/src/main/java/flipnote/group/domain/model/member/GroupMemberRole.java index 4ca3784..e707111 100644 --- a/src/main/java/flipnote/group/domain/model/member/GroupMemberRole.java +++ b/src/main/java/flipnote/group/domain/model/member/GroupMemberRole.java @@ -1,5 +1,5 @@ package flipnote.group.domain.model.member; public enum GroupMemberRole { - OWNER, HEAD_MANAGER, MANAGER, STAFF, MEMBER + OWNER, HEAD_MANAGER, MANAGER, MEMBER } diff --git a/src/main/java/flipnote/group/domain/model/permission/.gitkeep b/src/main/java/flipnote/group/domain/model/permission/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/flipnote/group/domain/model/permission/GroupPermission.java b/src/main/java/flipnote/group/domain/model/permission/GroupPermission.java new file mode 100644 index 0000000..0a68e81 --- /dev/null +++ b/src/main/java/flipnote/group/domain/model/permission/GroupPermission.java @@ -0,0 +1,5 @@ +package flipnote.group.domain.model.permission; + +public enum GroupPermission { + KICK, JOIN_REQUEST_MANAGE +} diff --git a/src/main/java/flipnote/group/infrastructure/persistence/jpa/GroupMemberRepository.java b/src/main/java/flipnote/group/infrastructure/persistence/jpa/GroupMemberRepository.java index 4da80d7..b0294a5 100644 --- a/src/main/java/flipnote/group/infrastructure/persistence/jpa/GroupMemberRepository.java +++ b/src/main/java/flipnote/group/infrastructure/persistence/jpa/GroupMemberRepository.java @@ -5,4 +5,7 @@ import flipnote.group.adapter.out.entity.GroupMemberEntity; public interface GroupMemberRepository extends JpaRepository { + boolean existsByUserIdAndGroupRoleId(Long userId, Long id); + + boolean existsByGroupIdAndUserId(Long groupId, Long userId); } diff --git a/src/main/java/flipnote/group/infrastructure/persistence/jpa/GroupRolePermissionRepository.java b/src/main/java/flipnote/group/infrastructure/persistence/jpa/GroupRolePermissionRepository.java new file mode 100644 index 0000000..c935cca --- /dev/null +++ b/src/main/java/flipnote/group/infrastructure/persistence/jpa/GroupRolePermissionRepository.java @@ -0,0 +1,8 @@ +package flipnote.group.infrastructure.persistence.jpa; + +import org.springframework.data.jpa.repository.JpaRepository; + +import flipnote.group.adapter.out.entity.PermissionEntity; + +public interface GroupRolePermissionRepository extends JpaRepository { +} diff --git a/src/main/java/flipnote/group/infrastructure/persistence/jpa/GroupRoleRepository.java b/src/main/java/flipnote/group/infrastructure/persistence/jpa/GroupRoleRepository.java new file mode 100644 index 0000000..cc6bd33 --- /dev/null +++ b/src/main/java/flipnote/group/infrastructure/persistence/jpa/GroupRoleRepository.java @@ -0,0 +1,10 @@ +package flipnote.group.infrastructure.persistence.jpa; + +import org.springframework.data.jpa.repository.JpaRepository; + +import flipnote.group.adapter.out.entity.RoleEntity; +import flipnote.group.domain.model.member.GroupMemberRole; + +public interface GroupRoleRepository extends JpaRepository { + RoleEntity findByGroupIdAndRole(Long groupId, GroupMemberRole groupMemberRole); +}