-
Notifications
You must be signed in to change notification settings - Fork 0
Feat: [FN-302] 그룹 권한 추가 #6
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
base: main
Are you sure you want to change the base?
Changes from all commits
37c76e0
602fa0c
e91d8aa
dab6a72
24ce2d1
3dcb17e
024620d
d8a5ee1
fc02112
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -143,4 +143,6 @@ public ResponseEntity<Void> deleteGroup( | |
|
|
||
| //todo 내가 생성한 그룹 전체 조회 | ||
|
|
||
| //todo 하위 권한 수정 | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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(); | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<GroupMemberRole, List<GroupPermission>> 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<GroupMemberRole, Long> 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<PermissionEntity> 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()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# First, locate and read the ChangeGroupService.java file
find . -name "ChangeGroupService.java" -type fRepository: FlipNoteTeam/FlipNote-Group Length of output: 145 🏁 Script executed: #!/bin/bash
# Find GroupRoleRepositoryPort and GroupRoleRepositoryAdapter files
find . -name "GroupRoleRepository*.java" -type fRepository: FlipNoteTeam/FlipNote-Group Length of output: 325 🏁 Script executed: #!/bin/bash
# Look for the application service directory structure
find . -path "*/flipnote/group/application/service/*" -name "*.java" -type fRepository: FlipNoteTeam/FlipNote-Group Length of output: 368 🏁 Script executed: #!/bin/bash
# Read ChangeGroupService.java to see imports and field declarations
head -30 ./src/main/java/flipnote/group/application/service/ChangeGroupService.javaRepository: FlipNoteTeam/FlipNote-Group Length of output: 1138 🏁 Script executed: #!/bin/bash
# Read GroupRoleRepositoryPort to verify the interface and methods
cat -n ./src/main/java/flipnote/group/application/port/out/GroupRoleRepositoryPort.javaRepository: FlipNoteTeam/FlipNote-Group Length of output: 393 🏁 Script executed: #!/bin/bash
# Read CreateGroupService to compare the correct pattern
head -30 ./src/main/java/flipnote/group/application/service/CreateGroupService.javaRepository: FlipNoteTeam/FlipNote-Group Length of output: 1134 🏁 Script executed: #!/bin/bash
# Check for all adapter imports in the service layer
rg "import flipnote.group.adapter" ./src/main/java/flipnote/group/application/service/ -nRepository: FlipNoteTeam/FlipNote-Group Length of output: 908 Application 서비스 계층에서 포트 인터페이스 대신 구체적인 어댑터를 직접 의존하고 있습니다.
다음과 같이 변경하십시오: ♻️ 포트 인터페이스 사용으로 변경-import flipnote.group.adapter.out.persistence.GroupRoleRepositoryAdapter;
+import flipnote.group.application.port.out.GroupRoleRepositoryPort;- private final GroupRoleRepositoryAdapter groupRoleRepository;
+ private final GroupRoleRepositoryPort groupRoleRepository;🤖 Prompt for AI Agents |
||
| 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; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check GroupRepository usage in service layer files
rg "GroupRepository" --type=java -g '*/application/service/*' -nRepository: FlipNoteTeam/FlipNote-Group Length of output: 219 🏁 Script executed: #!/bin/bash
# Find and examine ChangeGroupService
fd "ChangeGroupService.java" --type fRepository: FlipNoteTeam/FlipNote-Group Length of output: 143 🏁 Script executed: #!/bin/bash
# Find GroupRepositoryPort to verify the abstraction exists
fd "GroupRepositoryPort" --type fRepository: FlipNoteTeam/FlipNote-Group Length of output: 145 🏁 Script executed: #!/bin/bash
# Check the structure of application/service directory
fd . --type f -g "*/application/service/*.java" | head -20Repository: FlipNoteTeam/FlipNote-Group Length of output: 186 🏁 Script executed: #!/bin/bash
# List service files
ls -la src/main/java/flipnote/group/application/service/Repository: FlipNoteTeam/FlipNote-Group Length of output: 472 🏁 Script executed: #!/bin/bash
# Examine ChangeGroupService
cat -n src/main/java/flipnote/group/application/service/ChangeGroupService.javaRepository: FlipNoteTeam/FlipNote-Group Length of output: 1893 🏁 Script executed: #!/bin/bash
# Examine GroupRepositoryPort
cat -n src/main/java/flipnote/group/application/port/out/GroupRepositoryPort.javaRepository: FlipNoteTeam/FlipNote-Group Length of output: 376 🏁 Script executed: #!/bin/bash
# Check FindGroupService
cat -n src/main/java/flipnote/group/application/service/FindGroupService.javaRepository: FlipNoteTeam/FlipNote-Group Length of output: 1667 🏁 Script executed: #!/bin/bash
# Check CreateGroupService to verify the architectural pattern
cat -n src/main/java/flipnote/group/application/service/CreateGroupService.javaRepository: FlipNoteTeam/FlipNote-Group Length of output: 1865 🏁 Script executed: #!/bin/bash
# Check DeleteGroupService as well
cat -n src/main/java/flipnote/group/application/service/DeleteGroupService.javaRepository: FlipNoteTeam/FlipNote-Group Length of output: 1385
🤖 Prompt for AI Agents |
||
| 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)); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
유니크 제약 조건의 컬럼 이름이 실제 컬럼 이름과 불일치합니다.
Line 22의
columnNames = {"group_id", "role"}에서"role"을 참조하지만, Line 37의@Column(name = "group_role")에 의해 실제 DB 컬럼 이름은"group_role"입니다. DDL 생성 시 오류가 발생하거나 제약 조건이 의도대로 동작하지 않습니다.또한 제약 조건 이름
uk_group_roles_group_name에서group_name은 실제 제약 대상과 관련이 없어 혼동됩니다.🐛 수정 제안
Also applies to: 36-38
🤖 Prompt for AI Agents