From e33fcc3863425c7459eb0ce5c9cbdd17c9194d14 Mon Sep 17 00:00:00 2001 From: hyuuuun Date: Mon, 23 Mar 2026 21:35:42 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix=20:=20isPublic=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../permission/mapper/FamilyLineMapper.java | 6 ++ .../service/MemberPermissionServiceImpl.java | 15 ++++ .../mapper/permission/FamilyLineMapper.xml | 19 ++++ .../MemberPermissionServiceImplTest.java | 89 ++++++++++++++++++- 4 files changed, 127 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/pooli/permission/mapper/FamilyLineMapper.java b/src/main/java/com/pooli/permission/mapper/FamilyLineMapper.java index fbdd288f..573b3124 100644 --- a/src/main/java/com/pooli/permission/mapper/FamilyLineMapper.java +++ b/src/main/java/com/pooli/permission/mapper/FamilyLineMapper.java @@ -21,4 +21,10 @@ public interface FamilyLineMapper { // '특정 회선이 속한 가족 그룹'에 속한 모든 회선 식별자 조회 List findAllFamilyIdByLineId(Long lineId); + + // 비공개 권한 해제 시 공개 상태를 true로 복구 (단건) + void forcePublicByLineId(Long lineId); + + // 비공개 권한 해제 시 공개 상태를 true로 일괄 복구 + void forcePublicByLineIds(@Param("lineIds") List lineIds); } diff --git a/src/main/java/com/pooli/permission/service/MemberPermissionServiceImpl.java b/src/main/java/com/pooli/permission/service/MemberPermissionServiceImpl.java index 9c67e69d..3c19d916 100644 --- a/src/main/java/com/pooli/permission/service/MemberPermissionServiceImpl.java +++ b/src/main/java/com/pooli/permission/service/MemberPermissionServiceImpl.java @@ -99,6 +99,11 @@ public MemberPermissionResDto updateMemberPermission(Long familyId, Long lineId, .isEnable(reqDto.getIsEnable()) .build()); + // 비공개 권한(permission_id=2)을 비활성화하면 공개 상태를 강제로 true로 복구 + if (reqDto.getPermissionId() == 2 && Boolean.FALSE.equals(reqDto.getIsEnable())) { + familyLineMapper.forcePublicByLineId(lineId); + } + return permissionLineMapper.findByFamilyIdAndLineId(familyId, lineId).stream() .filter(p -> p.getPermissionId().equals(reqDto.getPermissionId())) .findFirst() @@ -143,6 +148,16 @@ public MemberPermissionListResDto bulkUpdateMemberPermissions(Long lineId, List< permissionLineMapper.bulkUpsert(reqList); + // 비공개 권한(permission_id=2)을 비활성화한 회선만 추려서 공개 상태를 강제로 true로 복구 + List lineIdsToRestore = reqList.stream() + .filter(r -> r.getPermissionId() == 2 && Boolean.FALSE.equals(r.getIsEnable())) + .map(MemberPermissionBulkUpsertReqDto::getLineId) + .distinct() + .toList(); + if (!lineIdsToRestore.isEmpty()) { + familyLineMapper.forcePublicByLineIds(lineIdsToRestore); + } + reqList.stream() .map(MemberPermissionBulkUpsertReqDto::getLineId) .distinct() diff --git a/src/main/resources/mapper/permission/FamilyLineMapper.xml b/src/main/resources/mapper/permission/FamilyLineMapper.xml index e8aee402..40bc9ba0 100644 --- a/src/main/resources/mapper/permission/FamilyLineMapper.xml +++ b/src/main/resources/mapper/permission/FamilyLineMapper.xml @@ -41,6 +41,25 @@ ); + + + UPDATE FAMILY_LINE + SET is_public = TRUE, + updated_at = NOW() + WHERE line_id = #{lineId} + + + + + UPDATE FAMILY_LINE + SET is_public = TRUE, + updated_at = NOW() + WHERE line_id IN + + #{lineId} + + + UPDATE FAMILY_LINE diff --git a/src/test/java/com/pooli/permission/service/MemberPermissionServiceImplTest.java b/src/test/java/com/pooli/permission/service/MemberPermissionServiceImplTest.java index 55d3df2c..0f1135f6 100644 --- a/src/test/java/com/pooli/permission/service/MemberPermissionServiceImplTest.java +++ b/src/test/java/com/pooli/permission/service/MemberPermissionServiceImplTest.java @@ -201,6 +201,45 @@ void updateMemberPermission_success_returnsUpdatedPermission() { assertThat(captor.getValue().getIsEnable()).isTrue(); } + @Test + @DisplayName("단건 변경: 비공개 권한(permission_id=2)을 비활성화하면 공개 상태가 복구된다") + void updateMemberPermission_privacyPermissionDisabled_restoresVisibility() { + AuthUserDetails admin = userWithRole(99L, "ROLE_ADMIN"); + MemberPermissionUpsertReqDto req = updateReq(2, false); + + when(familyLineMapper.findByFamilyIdAndLineId(1L, 20L)) + .thenReturn(Optional.of(familyLine(1L, 20L, FamilyRole.MEMBER))); + when(permissionMapper.findById(2)).thenReturn(Optional.of(permission(2))); + when(permissionLineMapper.findByFamilyIdAndLineId(1L, 20L)) + .thenReturn(List.of(memberPermission(20L, 2, false))); + + MemberPermissionResDto result = + memberPermissionService.updateMemberPermission(1L, 20L, req, admin); + + assertThat(result.getLineId()).isEqualTo(20L); + assertThat(result.getPermissionId()).isEqualTo(2); + assertThat(result.getIsEnable()).isFalse(); + + verify(familyLineMapper).forcePublicByLineId(20L); + } + + @Test + @DisplayName("단건 변경: 비공개 권한이 아닌 다른 권한을 꺼도 공개 복구가 호출되지 않는다") + void updateMemberPermission_nonPrivacyPermission_doesNotRestoreVisibility() { + AuthUserDetails admin = userWithRole(99L, "ROLE_ADMIN"); + MemberPermissionUpsertReqDto req = updateReq(1, false); + + when(familyLineMapper.findByFamilyIdAndLineId(1L, 20L)) + .thenReturn(Optional.of(familyLine(1L, 20L, FamilyRole.MEMBER))); + when(permissionMapper.findById(1)).thenReturn(Optional.of(permission(1))); + when(permissionLineMapper.findByFamilyIdAndLineId(1L, 20L)) + .thenReturn(List.of(memberPermission(20L, 1, false))); + + memberPermissionService.updateMemberPermission(1L, 20L, req, admin); + + verify(familyLineMapper, never()).forcePublicByLineId(anyLong()); + } + @Test @DisplayName("구성원 권한 변경 시 가족-회선 매핑이 없으면 PERMISSION-4404를 반환한다") void updateMemberPermission_familyLineMissing_throwsFamilyLineMappingNotFound() { @@ -299,6 +338,52 @@ void bulkUpdateMemberPermissions_ownerLineIdIgnored_usesSessionLineIdAndDistinct assertThat(lineIdCaptor.getAllValues()).containsExactlyInAnyOrder(20L, 30L); } + @Test + @DisplayName("일괄 변경: 비공개 권한(permission_id=2)을 비활성화하면 해당 회선만 공개 복구된다") + void bulkUpdateMemberPermissions_privacyPermissionDisabled_restoresVisibility() { + AuthUserDetails admin = userWithRole(50L, "ROLE_ADMIN"); + List reqList = List.of( + req(20L, 2, false), + req(30L, 1, true) + ); + + when(familyLineMapper.findByLineId(50L)).thenReturn(Optional.of(familyLine(1L, 50L, FamilyRole.OWNER))); + when(familyLineMapper.findByFamilyIdAndLineId(1L, 20L)) + .thenReturn(Optional.of(familyLine(1L, 20L, FamilyRole.MEMBER))); + when(familyLineMapper.findByFamilyIdAndLineId(1L, 30L)) + .thenReturn(Optional.of(familyLine(1L, 30L, FamilyRole.MEMBER))); + when(permissionMapper.findById(anyInt())) + .thenAnswer(invocation -> Optional.of(permission(invocation.getArgument(0)))); + when(permissionLineMapper.findByFamilyId(1L)).thenReturn(List.of()); + + memberPermissionService.bulkUpdateMemberPermissions(50L, reqList, admin); + + verify(familyLineMapper).forcePublicByLineIds(List.of(20L)); + } + + @Test + @DisplayName("일괄 변경: 비공개 권한이 아닌 다른 권한만 변경하면 공개 복구가 호출되지 않는다") + void bulkUpdateMemberPermissions_nonPrivacyPermission_doesNotRestoreVisibility() { + AuthUserDetails admin = userWithRole(50L, "ROLE_ADMIN"); + List reqList = List.of( + req(20L, 1, false), + req(30L, 1, true) + ); + + when(familyLineMapper.findByLineId(50L)).thenReturn(Optional.of(familyLine(1L, 50L, FamilyRole.OWNER))); + when(familyLineMapper.findByFamilyIdAndLineId(1L, 20L)) + .thenReturn(Optional.of(familyLine(1L, 20L, FamilyRole.MEMBER))); + when(familyLineMapper.findByFamilyIdAndLineId(1L, 30L)) + .thenReturn(Optional.of(familyLine(1L, 30L, FamilyRole.MEMBER))); + when(permissionMapper.findById(anyInt())) + .thenAnswer(invocation -> Optional.of(permission(invocation.getArgument(0)))); + when(permissionLineMapper.findByFamilyId(1L)).thenReturn(List.of()); + + memberPermissionService.bulkUpdateMemberPermissions(50L, reqList, admin); + + verify(familyLineMapper, never()).forcePublicByLineIds(anyList()); + } + @Test @DisplayName("일괄 변경 시 빈 목록이면 업데이트/알람 없이 현재 상태를 반환한다") void bulkUpdateMemberPermissions_emptyReqList_returnsCurrentState() { @@ -338,7 +423,7 @@ void bulkUpdateMemberPermissions_permissionNotFound_throwsPermissionNotFound() { } @Test - @DisplayName("bulk update: member line not in family returns PERMISSION-4404") + @DisplayName("일괄 변경 시 가족-회선 매핑이 없으면 PERMISSION-4404를 반환한다") void bulkUpdateMemberPermissions_familyLineMissing_throwsFamilyLineMappingNotFound() { AuthUserDetails admin = userWithRole(50L, "ROLE_ADMIN"); List reqList = List.of(req(20L, 1, true)); @@ -358,7 +443,7 @@ void bulkUpdateMemberPermissions_familyLineMissing_throwsFamilyLineMappingNotFou } @Test - @DisplayName("getMemberPermissions: non-owner user returns COMMON:4302") + @DisplayName("구성원 권한 조회 시 OWNER가 아니면 COMMON:4302를 반환한다") void getMemberPermissions_nonOwner_throwsLineOwnershipForbidden() { AuthUserDetails member = userWithRole(10L, "ROLE_FAMILY_MEMBER"); when(familyLineMapper.findByLineId(20L)).thenReturn(Optional.of(familyLine(1L, 20L, FamilyRole.MEMBER))); From 007ffb7f462b721eb23abd21e644f5732b05c7e9 Mon Sep 17 00:00:00 2001 From: hyuuuun Date: Mon, 23 Mar 2026 22:32:55 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix=20:=20=EB=B0=B0=ED=8F=AC=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20SKIP=5FTRAFFIC=5FDEPLOY=3Dtrue=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?traffic=20=EB=B6=80=EB=B6=84=20skip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gradle.yml | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 7ced53d5..816eaf7a 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -102,18 +102,15 @@ jobs: run: | aws s3 cp revision.zip "s3://${{ secrets.CODEDEPLOY_S3_BUCKET }}/release/${GITHUB_SHA}.zip" - # 12) CodeDeploy 배포 트리거 (release -> traffic 순차) - - name: Create CodeDeploy deployments (sequential) + # 12) release 배포는 항상 수행 + - name: Create CodeDeploy release deployment run: | APP_NAME="${{ secrets.CODEDEPLOY_APP_NAME }}" BUCKET="${{ secrets.CODEDEPLOY_S3_BUCKET }}" S3_KEY="release/${GITHUB_SHA}.zip" - # 새 시크릿이 없으면 기본 그룹명을 사용 RELEASE_GROUP="${{ secrets.CODEDEPLOY_RELEASE_DEPLOYMENT_GROUP }}" - TRAFFIC_GROUP="${{ secrets.CODEDEPLOY_TRAFFIC_DEPLOYMENT_GROUP }}" : "${RELEASE_GROUP:=pooli-release-group}" - : "${TRAFFIC_GROUP:=pooli-traffic-group}" echo "release group: ${RELEASE_GROUP}" RELEASE_DEPLOY_ID=$(aws deploy create-deployment \ @@ -128,6 +125,22 @@ jobs: echo "release deployment id: ${RELEASE_DEPLOY_ID}" aws deploy wait deployment-successful --deployment-id "${RELEASE_DEPLOY_ID}" + # 13) 운영 토글이 켜져 있으면 traffic 배포 생략 + - name: Skip CodeDeploy traffic deployment + if: ${{ vars.SKIP_TRAFFIC_DEPLOY == 'true' }} + run: echo "SKIP_TRAFFIC_DEPLOY=true, skipping traffic deployment" + + # 14) 운영 토글이 꺼져 있으면 traffic 배포 수행 + - name: Create CodeDeploy traffic deployment + if: ${{ vars.SKIP_TRAFFIC_DEPLOY != 'true' }} + run: | + APP_NAME="${{ secrets.CODEDEPLOY_APP_NAME }}" + BUCKET="${{ secrets.CODEDEPLOY_S3_BUCKET }}" + S3_KEY="release/${GITHUB_SHA}.zip" + + TRAFFIC_GROUP="${{ secrets.CODEDEPLOY_TRAFFIC_DEPLOYMENT_GROUP }}" + : "${TRAFFIC_GROUP:=pooli-traffic-group}" + echo "traffic group: ${TRAFFIC_GROUP}" TRAFFIC_DEPLOY_ID=$(aws deploy create-deployment \ --application-name "${APP_NAME}" \