From 01ffcab97781dd6ff46ec4fd276d4ed9ab0d1008 Mon Sep 17 00:00:00 2001 From: JanooGwan Date: Wed, 8 Apr 2026 21:00:44 +0900 Subject: [PATCH 1/5] =?UTF-8?q?fix:=20=EC=8B=9C=ED=8A=B8=20preview?= =?UTF-8?q?=EB=8A=94=20=EB=93=B1=EB=A1=9D=EB=90=9C=20=EC=8B=9C=ED=8A=B8?= =?UTF-8?q?=EB=A7=8C=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../club/controller/ClubSheetMigrationApi.java | 4 ++-- .../controller/ClubSheetMigrationController.java | 3 +-- .../domain/club/service/SheetImportService.java | 14 +++++++++++--- .../agit/konect/global/code/ApiResponseCode.java | 2 ++ 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/main/java/gg/agit/konect/domain/club/controller/ClubSheetMigrationApi.java b/src/main/java/gg/agit/konect/domain/club/controller/ClubSheetMigrationApi.java index 2028b825f..efb4f8f07 100644 --- a/src/main/java/gg/agit/konect/domain/club/controller/ClubSheetMigrationApi.java +++ b/src/main/java/gg/agit/konect/domain/club/controller/ClubSheetMigrationApi.java @@ -38,14 +38,14 @@ ResponseEntity migrateSheet( @Operation( summary = "시트 불러오기 전 부원 목록을 미리본다", description = """ - 스프레드시트 URL을 읽어 등록 예정인 부원 목록을 JSON으로 반환합니다. + PUT /clubs/{clubId}/sheet 로 AI 분석 및 등록이 완료된 스프레드시트를 읽어 + 등록 예정인 부원 목록을 JSON으로 반환합니다. 이 API는 데이터를 저장하지 않고 미리보기 용도로만 사용합니다. """ ) @PostMapping("/{clubId}/sheet/import/preview") ResponseEntity previewPreMembers( @PathVariable(name = "clubId") Integer clubId, - @Valid @RequestBody SheetImportRequest request, @UserId Integer requesterId ); diff --git a/src/main/java/gg/agit/konect/domain/club/controller/ClubSheetMigrationController.java b/src/main/java/gg/agit/konect/domain/club/controller/ClubSheetMigrationController.java index e7b8c0b56..bcceb2161 100644 --- a/src/main/java/gg/agit/konect/domain/club/controller/ClubSheetMigrationController.java +++ b/src/main/java/gg/agit/konect/domain/club/controller/ClubSheetMigrationController.java @@ -43,11 +43,10 @@ public ResponseEntity migrateSheet( @Override public ResponseEntity previewPreMembers( @PathVariable(name = "clubId") Integer clubId, - @Valid @RequestBody SheetImportRequest request, @UserId Integer requesterId ) { SheetImportPreviewResponse response = sheetImportService.previewPreMembersFromSheet( - clubId, requesterId, request.spreadsheetUrl() + clubId, requesterId ); return ResponseEntity.ok(response); } diff --git a/src/main/java/gg/agit/konect/domain/club/service/SheetImportService.java b/src/main/java/gg/agit/konect/domain/club/service/SheetImportService.java index 42a0975a7..006a61026 100644 --- a/src/main/java/gg/agit/konect/domain/club/service/SheetImportService.java +++ b/src/main/java/gg/agit/konect/domain/club/service/SheetImportService.java @@ -56,12 +56,11 @@ public class SheetImportService { public SheetImportPreviewResponse previewPreMembersFromSheet( Integer clubId, - Integer requesterId, - String spreadsheetUrl + Integer requesterId ) { clubPermissionValidator.validateManagerAccess(clubId, requesterId); - String spreadsheetId = SpreadsheetUrlParser.extractId(spreadsheetUrl); + String spreadsheetId = resolveRegisteredSpreadsheetId(clubId); SheetHeaderMapper.SheetAnalysisResult analysis = sheetHeaderMapper.analyzeAllSheets(spreadsheetId); SheetImportSource source = loadSheetImportSource(spreadsheetId, analysis.memberListMapping()); @@ -77,6 +76,15 @@ public SheetImportPreviewResponse previewPreMembersFromSheet( }); } + private String resolveRegisteredSpreadsheetId(Integer clubId) { + Club club = clubRepository.getById(clubId); + String spreadsheetId = club.getGoogleSheetId(); + if (spreadsheetId == null || spreadsheetId.isBlank()) { + throw CustomException.of(ApiResponseCode.CLUB_SHEET_ANALYSIS_REQUIRED); + } + return spreadsheetId; + } + @Transactional public SheetImportResponse confirmImportPreMembers( Integer clubId, diff --git a/src/main/java/gg/agit/konect/global/code/ApiResponseCode.java b/src/main/java/gg/agit/konect/global/code/ApiResponseCode.java index d62a0fdfa..d2999a5f5 100644 --- a/src/main/java/gg/agit/konect/global/code/ApiResponseCode.java +++ b/src/main/java/gg/agit/konect/global/code/ApiResponseCode.java @@ -49,6 +49,8 @@ public enum ApiResponseCode { INVALID_NOTIFICATION_TOKEN(HttpStatus.BAD_REQUEST, "푸시 알림 토큰이 유효하지 않습니다."), FEE_PAYMENT_IMAGE_REQUIRED(HttpStatus.BAD_REQUEST, "회비 납부가 필요한 동아리입니다. 납부 증빙 사진을 첨부해주세요."), AMBIGUOUS_USER_MATCH(HttpStatus.BAD_REQUEST, "동일한 정보로 식별되는 사용자가 2명 이상입니다. 관리자에게 문의해주세요."), + CLUB_SHEET_ANALYSIS_REQUIRED(HttpStatus.BAD_REQUEST, + "구글 시트 파일에서 동아리 부원을 가져오기 전에 먼저 AI 분석 및 등록이 완료되어야 합니다."), // 401 Unauthorized INVALID_SESSION(HttpStatus.UNAUTHORIZED, "올바르지 않은 인증 정보 입니다."), From cbe5f6b5f5e01111675730fbe58751d2414e6273 Mon Sep 17 00:00:00 2001 From: JanooGwan Date: Wed, 8 Apr 2026 21:01:03 +0900 Subject: [PATCH 2/5] =?UTF-8?q?test:=20=EB=93=B1=EB=A1=9D=EB=90=9C=20?= =?UTF-8?q?=EC=8B=9C=ED=8A=B8=20=EA=B8=B0=EC=A4=80=20preview=20=ED=9D=90?= =?UTF-8?q?=EB=A6=84=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../club/service/SheetImportServiceTest.java | 23 +++++++++++--- .../club/ClubSheetMigrationApiTest.java | 30 ++++++++++++------- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/test/java/gg/agit/konect/domain/club/service/SheetImportServiceTest.java b/src/test/java/gg/agit/konect/domain/club/service/SheetImportServiceTest.java index b54422755..489c48d2e 100644 --- a/src/test/java/gg/agit/konect/domain/club/service/SheetImportServiceTest.java +++ b/src/test/java/gg/agit/konect/domain/club/service/SheetImportServiceTest.java @@ -1,6 +1,7 @@ package gg.agit.konect.domain.club.service; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anySet; @@ -33,6 +34,8 @@ import gg.agit.konect.domain.club.repository.ClubRepository; import gg.agit.konect.domain.user.model.User; import gg.agit.konect.domain.user.repository.UserRepository; +import gg.agit.konect.global.code.ApiResponseCode; +import gg.agit.konect.global.exception.CustomException; import gg.agit.konect.support.ServiceTestSupport; import gg.agit.konect.support.fixture.ClubFixture; import gg.agit.konect.support.fixture.UniversityFixture; @@ -43,8 +46,6 @@ class SheetImportServiceTest extends ServiceTestSupport { private static final Integer CLUB_ID = 1; private static final Integer REQUESTER_ID = 2; private static final String SPREADSHEET_ID = "sheet-id"; - private static final String SPREADSHEET_URL = - "https://docs.google.com/spreadsheets/d/" + SPREADSHEET_ID + "/edit"; @Mock private Sheets googleSheetsService; @@ -88,6 +89,7 @@ class SheetImportServiceTest extends ServiceTestSupport { @Test void previewPreMembersFromSheetReturnsDirectAndPreMembers() throws IOException { Club club = ClubFixture.create(UniversityFixture.create()); + club.updateGoogleSheetId(SPREADSHEET_ID); User directUser = UserFixture.createUser(club.getUniversity(), "Alex Kim", "2021232948"); given(clubRepository.getById(CLUB_ID)).willReturn(club); @@ -115,8 +117,7 @@ void previewPreMembersFromSheetReturnsDirectAndPreMembers() throws IOException { SheetImportPreviewResponse response = sheetImportService.previewPreMembersFromSheet( CLUB_ID, - REQUESTER_ID, - SPREADSHEET_URL + REQUESTER_ID ); assertThat(response.previewCount()).isEqualTo(2); @@ -133,6 +134,20 @@ void previewPreMembersFromSheetReturnsDirectAndPreMembers() throws IOException { .containsExactly(true, true); } + @Test + void previewPreMembersFromSheetThrowsWhenSheetIsNotRegistered() { + Club club = ClubFixture.create(UniversityFixture.create()); + + given(clubRepository.getById(CLUB_ID)).willReturn(club); + + assertThatThrownBy(() -> sheetImportService.previewPreMembersFromSheet(CLUB_ID, REQUESTER_ID)) + .isInstanceOf(CustomException.class) + .extracting(exception -> ((CustomException)exception).getErrorCode()) + .isEqualTo(ApiResponseCode.CLUB_SHEET_ANALYSIS_REQUIRED); + + verifyNoInteractions(googleSheetsService, sheetHeaderMapper); + } + @Test void confirmImportPreMembersImportsOnlyEnabledMembers() { Club club = ClubFixture.create(UniversityFixture.create()); diff --git a/src/test/java/gg/agit/konect/integration/domain/club/ClubSheetMigrationApiTest.java b/src/test/java/gg/agit/konect/integration/domain/club/ClubSheetMigrationApiTest.java index 6ce72c97b..d38987caa 100644 --- a/src/test/java/gg/agit/konect/integration/domain/club/ClubSheetMigrationApiTest.java +++ b/src/test/java/gg/agit/konect/integration/domain/club/ClubSheetMigrationApiTest.java @@ -71,13 +71,10 @@ void previewPreMembersSuccess() throws Exception { given(sheetImportService.previewPreMembersFromSheet( eq(CLUB_ID), - eq(REQUESTER_ID), - eq(SPREADSHEET_URL) + eq(REQUESTER_ID) )).willReturn(response); - SheetImportRequest request = new SheetImportRequest(SPREADSHEET_URL); - - performPost("/clubs/" + CLUB_ID + "/sheet/import/preview", request) + performPost("/clubs/" + CLUB_ID + "/sheet/import/preview") .andExpect(status().isOk()) .andExpect(jsonPath("$.previewCount").value(2)) .andExpect(jsonPath("$.autoRegisteredCount").value(1)) @@ -96,19 +93,32 @@ void previewPreMembersSuccess() throws Exception { void previewPreMembersForbiddenGoogleSheetAccess() throws Exception { given(sheetImportService.previewPreMembersFromSheet( eq(CLUB_ID), - eq(REQUESTER_ID), - eq(SPREADSHEET_URL) + eq(REQUESTER_ID) )).willThrow(CustomException.of(ApiResponseCode.FORBIDDEN_GOOGLE_SHEET_ACCESS)); - SheetImportRequest request = new SheetImportRequest(SPREADSHEET_URL); - - performPost("/clubs/" + CLUB_ID + "/sheet/import/preview", request) + performPost("/clubs/" + CLUB_ID + "/sheet/import/preview") .andExpect(status().isForbidden()) .andExpect(jsonPath("$.code") .value(ApiResponseCode.FORBIDDEN_GOOGLE_SHEET_ACCESS.name())) .andExpect(jsonPath("$.message") .value(ApiResponseCode.FORBIDDEN_GOOGLE_SHEET_ACCESS.getMessage())); } + + @Test + @DisplayName("returns 400 when sheet analysis and registration are not completed") + void previewPreMembersRequiresRegisteredSheet() throws Exception { + given(sheetImportService.previewPreMembersFromSheet( + eq(CLUB_ID), + eq(REQUESTER_ID) + )).willThrow(CustomException.of(ApiResponseCode.CLUB_SHEET_ANALYSIS_REQUIRED)); + + performPost("/clubs/" + CLUB_ID + "/sheet/import/preview") + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code") + .value(ApiResponseCode.CLUB_SHEET_ANALYSIS_REQUIRED.name())) + .andExpect(jsonPath("$.message") + .value(ApiResponseCode.CLUB_SHEET_ANALYSIS_REQUIRED.getMessage())); + } } @Nested From 388bd390b5e19ec11c0b2f19245813cf7f341638 Mon Sep 17 00:00:00 2001 From: JanooGwan Date: Wed, 8 Apr 2026 21:09:26 +0900 Subject: [PATCH 3/5] =?UTF-8?q?refactor:=20=EC=8B=9C=ED=8A=B8=20preview=20?= =?UTF-8?q?=ED=81=B4=EB=9F=BD=20=EC=A4=91=EB=B3=B5=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/club/service/SheetImportService.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/java/gg/agit/konect/domain/club/service/SheetImportService.java b/src/main/java/gg/agit/konect/domain/club/service/SheetImportService.java index 006a61026..c5532823a 100644 --- a/src/main/java/gg/agit/konect/domain/club/service/SheetImportService.java +++ b/src/main/java/gg/agit/konect/domain/club/service/SheetImportService.java @@ -59,13 +59,12 @@ public SheetImportPreviewResponse previewPreMembersFromSheet( Integer requesterId ) { clubPermissionValidator.validateManagerAccess(clubId, requesterId); - - String spreadsheetId = resolveRegisteredSpreadsheetId(clubId); - SheetHeaderMapper.SheetAnalysisResult analysis = - sheetHeaderMapper.analyzeAllSheets(spreadsheetId); - SheetImportSource source = loadSheetImportSource(spreadsheetId, analysis.memberListMapping()); return executeReadOnlyTransaction(() -> { - Club club = clubRepository.getById(clubId); + Club club = resolveRegisteredSpreadsheetId(clubId); + String spreadsheetId = club.getGoogleSheetId(); + SheetHeaderMapper.SheetAnalysisResult analysis = + sheetHeaderMapper.analyzeAllSheets(spreadsheetId); + SheetImportSource source = loadSheetImportSource(spreadsheetId, analysis.memberListMapping()); SheetImportPlan plan = buildImportPlan( clubId, club, @@ -76,13 +75,13 @@ public SheetImportPreviewResponse previewPreMembersFromSheet( }); } - private String resolveRegisteredSpreadsheetId(Integer clubId) { + private Club resolveRegisteredSpreadsheetId(Integer clubId) { Club club = clubRepository.getById(clubId); String spreadsheetId = club.getGoogleSheetId(); if (spreadsheetId == null || spreadsheetId.isBlank()) { throw CustomException.of(ApiResponseCode.CLUB_SHEET_ANALYSIS_REQUIRED); } - return spreadsheetId; + return club; } @Transactional From 5e737e56f0f13e6b74ab7140e76b21025c82e78c Mon Sep 17 00:00:00 2001 From: JanooGwan Date: Wed, 8 Apr 2026 21:48:36 +0900 Subject: [PATCH 4/5] =?UTF-8?q?refactor:=20=EC=8B=9C=ED=8A=B8=20preview?= =?UTF-8?q?=EC=97=90=20=EC=A0=80=EC=9E=A5=EB=90=9C=20=EB=B6=84=EC=84=9D=20?= =?UTF-8?q?=EB=A7=A4=ED=95=91=20=EC=9A=B0=EC=84=A0=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../club/service/SheetImportService.java | 52 +++++++++++++------ .../club/service/SheetImportServiceTest.java | 27 ++++++++-- 2 files changed, 60 insertions(+), 19 deletions(-) diff --git a/src/main/java/gg/agit/konect/domain/club/service/SheetImportService.java b/src/main/java/gg/agit/konect/domain/club/service/SheetImportService.java index c5532823a..c337003d6 100644 --- a/src/main/java/gg/agit/konect/domain/club/service/SheetImportService.java +++ b/src/main/java/gg/agit/konect/domain/club/service/SheetImportService.java @@ -16,6 +16,8 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionTemplate; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.api.services.sheets.v4.Sheets; import com.google.api.services.sheets.v4.model.ValueRange; @@ -53,29 +55,27 @@ public class SheetImportService { private final ChatRoomMembershipService chatRoomMembershipService; private final ClubPermissionValidator clubPermissionValidator; private final PlatformTransactionManager transactionManager; + private final ObjectMapper objectMapper; public SheetImportPreviewResponse previewPreMembersFromSheet( Integer clubId, Integer requesterId ) { clubPermissionValidator.validateManagerAccess(clubId, requesterId); - return executeReadOnlyTransaction(() -> { - Club club = resolveRegisteredSpreadsheetId(clubId); - String spreadsheetId = club.getGoogleSheetId(); - SheetHeaderMapper.SheetAnalysisResult analysis = - sheetHeaderMapper.analyzeAllSheets(spreadsheetId); - SheetImportSource source = loadSheetImportSource(spreadsheetId, analysis.memberListMapping()); - SheetImportPlan plan = buildImportPlan( - clubId, - club, - source.members(), - source.warnings() - ); - return SheetImportPreviewResponse.of(plan.previewMembers(), plan.warnings()); - }); + Club club = resolveClubWithRegisteredSheet(clubId); + String spreadsheetId = club.getGoogleSheetId(); + SheetColumnMapping mapping = resolveRegisteredMemberListMapping(club); + SheetImportSource source = loadSheetImportSource(spreadsheetId, mapping); + SheetImportPlan plan = buildImportPlan( + clubId, + club, + source.members(), + source.warnings() + ); + return SheetImportPreviewResponse.of(plan.previewMembers(), plan.warnings()); } - private Club resolveRegisteredSpreadsheetId(Integer clubId) { + private Club resolveClubWithRegisteredSheet(Integer clubId) { Club club = clubRepository.getById(clubId); String spreadsheetId = club.getGoogleSheetId(); if (spreadsheetId == null || spreadsheetId.isBlank()) { @@ -84,6 +84,28 @@ private Club resolveRegisteredSpreadsheetId(Integer clubId) { return club; } + private SheetColumnMapping resolveRegisteredMemberListMapping(Club club) { + String mappingJson = club.getSheetColumnMapping(); + if (mappingJson == null || mappingJson.isBlank()) { + throw CustomException.of(ApiResponseCode.CLUB_SHEET_ANALYSIS_REQUIRED); + } + + try { + Map raw = objectMapper.readValue(mappingJson, new TypeReference<>() {}); + int dataStartRow = raw.containsKey("dataStartRow") + ? ((Number)raw.get("dataStartRow")).intValue() : 2; + Map fieldMap = new HashMap<>(); + raw.forEach((key, value) -> { + if (!"dataStartRow".equals(key) && value instanceof Number number) { + fieldMap.put(key, number.intValue()); + } + }); + return new SheetColumnMapping(fieldMap, dataStartRow); + } catch (Exception e) { + throw CustomException.of(ApiResponseCode.CLUB_SHEET_ANALYSIS_REQUIRED); + } + } + @Transactional public SheetImportResponse confirmImportPreMembers( Integer clubId, diff --git a/src/test/java/gg/agit/konect/domain/club/service/SheetImportServiceTest.java b/src/test/java/gg/agit/konect/domain/club/service/SheetImportServiceTest.java index 489c48d2e..383aaf27e 100644 --- a/src/test/java/gg/agit/konect/domain/club/service/SheetImportServiceTest.java +++ b/src/test/java/gg/agit/konect/domain/club/service/SheetImportServiceTest.java @@ -16,9 +16,11 @@ import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Spy; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.support.SimpleTransactionStatus; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.api.services.sheets.v4.Sheets; import com.google.api.services.sheets.v4.model.ValueRange; @@ -83,6 +85,9 @@ class SheetImportServiceTest extends ServiceTestSupport { @Mock private PlatformTransactionManager transactionManager; + @Spy + private ObjectMapper objectMapper = new ObjectMapper(); + @InjectMocks private SheetImportService sheetImportService; @@ -90,17 +95,16 @@ class SheetImportServiceTest extends ServiceTestSupport { void previewPreMembersFromSheetReturnsDirectAndPreMembers() throws IOException { Club club = ClubFixture.create(UniversityFixture.create()); club.updateGoogleSheetId(SPREADSHEET_ID); + club.updateSheetColumnMapping(objectMapper.writeValueAsString( + SheetColumnMapping.defaultMapping().toMap() + )); User directUser = UserFixture.createUser(club.getUniversity(), "Alex Kim", "2021232948"); given(clubRepository.getById(CLUB_ID)).willReturn(club); - given(sheetHeaderMapper.analyzeAllSheets(SPREADSHEET_ID)).willReturn( - new SheetHeaderMapper.SheetAnalysisResult(SheetColumnMapping.defaultMapping(), null, null) - ); given(clubMemberRepository.findStudentNumbersByClubId(CLUB_ID)).willReturn(Set.of()); given(clubPreMemberRepository.findStudentNumberAndNameByClubId(CLUB_ID)) .willReturn(List.of()); given(clubMemberRepository.findUserIdsByClubId(CLUB_ID)).willReturn(List.of()); - given(transactionManager.getTransaction(any())).willReturn(new SimpleTransactionStatus()); given(userRepository.findAllByUniversityIdAndStudentNumberIn( eq(club.getUniversity().getId()), anySet() @@ -148,6 +152,21 @@ void previewPreMembersFromSheetThrowsWhenSheetIsNotRegistered() { verifyNoInteractions(googleSheetsService, sheetHeaderMapper); } + @Test + void previewPreMembersFromSheetThrowsWhenSheetMappingIsMissing() { + Club club = ClubFixture.create(UniversityFixture.create()); + club.updateGoogleSheetId(SPREADSHEET_ID); + + given(clubRepository.getById(CLUB_ID)).willReturn(club); + + assertThatThrownBy(() -> sheetImportService.previewPreMembersFromSheet(CLUB_ID, REQUESTER_ID)) + .isInstanceOf(CustomException.class) + .extracting(exception -> ((CustomException)exception).getErrorCode()) + .isEqualTo(ApiResponseCode.CLUB_SHEET_ANALYSIS_REQUIRED); + + verifyNoInteractions(googleSheetsService, sheetHeaderMapper); + } + @Test void confirmImportPreMembersImportsOnlyEnabledMembers() { Club club = ClubFixture.create(UniversityFixture.create()); From fa0dd52ec49cff2a7b960705b2a7c731efa70e62 Mon Sep 17 00:00:00 2001 From: JanooGwan Date: Thu, 9 Apr 2026 21:14:01 +0900 Subject: [PATCH 5/5] =?UTF-8?q?fix:=20=EC=8B=9C=ED=8A=B8=20preview=20?= =?UTF-8?q?=EB=8C=80=ED=95=99=20=EC=A7=80=EC=97=B0=20=EB=A1=9C=EB=94=A9=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/club/repository/ClubRepository.java | 14 ++++++++++++++ .../domain/club/service/SheetImportService.java | 2 +- .../club/service/SheetImportServiceTest.java | 7 +++---- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/java/gg/agit/konect/domain/club/repository/ClubRepository.java b/src/main/java/gg/agit/konect/domain/club/repository/ClubRepository.java index 3cee58933..5dbab27af 100644 --- a/src/main/java/gg/agit/konect/domain/club/repository/ClubRepository.java +++ b/src/main/java/gg/agit/konect/domain/club/repository/ClubRepository.java @@ -21,11 +21,25 @@ public interface ClubRepository extends Repository { """) Optional findById(@Param(value = "id") Integer id); + @Query(value = """ + SELECT c + FROM Club c + LEFT JOIN FETCH c.university + LEFT JOIN FETCH c.clubRecruitment cr + WHERE c.id = :id + """) + Optional findByIdWithUniversity(@Param(value = "id") Integer id); + default Club getById(Integer id) { return findById(id).orElseThrow(() -> CustomException.of(ApiResponseCode.NOT_FOUND_CLUB)); } + default Club getByIdWithUniversity(Integer id) { + return findByIdWithUniversity(id).orElseThrow(() -> + CustomException.of(ApiResponseCode.NOT_FOUND_CLUB)); + } + List findAll(); Club save(Club club); diff --git a/src/main/java/gg/agit/konect/domain/club/service/SheetImportService.java b/src/main/java/gg/agit/konect/domain/club/service/SheetImportService.java index c337003d6..d49f1ddd7 100644 --- a/src/main/java/gg/agit/konect/domain/club/service/SheetImportService.java +++ b/src/main/java/gg/agit/konect/domain/club/service/SheetImportService.java @@ -76,7 +76,7 @@ public SheetImportPreviewResponse previewPreMembersFromSheet( } private Club resolveClubWithRegisteredSheet(Integer clubId) { - Club club = clubRepository.getById(clubId); + Club club = clubRepository.getByIdWithUniversity(clubId); String spreadsheetId = club.getGoogleSheetId(); if (spreadsheetId == null || spreadsheetId.isBlank()) { throw CustomException.of(ApiResponseCode.CLUB_SHEET_ANALYSIS_REQUIRED); diff --git a/src/test/java/gg/agit/konect/domain/club/service/SheetImportServiceTest.java b/src/test/java/gg/agit/konect/domain/club/service/SheetImportServiceTest.java index 383aaf27e..0adc652c7 100644 --- a/src/test/java/gg/agit/konect/domain/club/service/SheetImportServiceTest.java +++ b/src/test/java/gg/agit/konect/domain/club/service/SheetImportServiceTest.java @@ -18,7 +18,6 @@ import org.mockito.Mock; import org.mockito.Spy; import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.support.SimpleTransactionStatus; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.api.services.sheets.v4.Sheets; @@ -100,7 +99,7 @@ void previewPreMembersFromSheetReturnsDirectAndPreMembers() throws IOException { )); User directUser = UserFixture.createUser(club.getUniversity(), "Alex Kim", "2021232948"); - given(clubRepository.getById(CLUB_ID)).willReturn(club); + given(clubRepository.getByIdWithUniversity(CLUB_ID)).willReturn(club); given(clubMemberRepository.findStudentNumbersByClubId(CLUB_ID)).willReturn(Set.of()); given(clubPreMemberRepository.findStudentNumberAndNameByClubId(CLUB_ID)) .willReturn(List.of()); @@ -142,7 +141,7 @@ void previewPreMembersFromSheetReturnsDirectAndPreMembers() throws IOException { void previewPreMembersFromSheetThrowsWhenSheetIsNotRegistered() { Club club = ClubFixture.create(UniversityFixture.create()); - given(clubRepository.getById(CLUB_ID)).willReturn(club); + given(clubRepository.getByIdWithUniversity(CLUB_ID)).willReturn(club); assertThatThrownBy(() -> sheetImportService.previewPreMembersFromSheet(CLUB_ID, REQUESTER_ID)) .isInstanceOf(CustomException.class) @@ -157,7 +156,7 @@ void previewPreMembersFromSheetThrowsWhenSheetMappingIsMissing() { Club club = ClubFixture.create(UniversityFixture.create()); club.updateGoogleSheetId(SPREADSHEET_ID); - given(clubRepository.getById(CLUB_ID)).willReturn(club); + given(clubRepository.getByIdWithUniversity(CLUB_ID)).willReturn(club); assertThatThrownBy(() -> sheetImportService.previewPreMembersFromSheet(CLUB_ID, REQUESTER_ID)) .isInstanceOf(CustomException.class)