diff --git a/src/main/java/com/assu/server/domain/map/controller/MapController.java b/src/main/java/com/assu/server/domain/map/controller/MapController.java index 289502b7..c0fae377 100644 --- a/src/main/java/com/assu/server/domain/map/controller/MapController.java +++ b/src/main/java/com/assu/server/domain/map/controller/MapController.java @@ -4,6 +4,7 @@ import com.assu.server.domain.map.dto.*; import com.assu.server.domain.map.service.MapService; import com.assu.server.domain.map.service.PlaceSearchService; +import com.assu.server.domain.store.entity.enums.StoreCategory; import com.assu.server.global.apiPayload.BaseResponse; import com.assu.server.global.apiPayload.code.status.ErrorStatus; import com.assu.server.global.apiPayload.code.status.SuccessStatus; @@ -29,10 +30,13 @@ public class MapController { @Operation( summary = "현재 위치 기반 주변 장소 조회 API", - description = "# [v1.3 (2025-01-04)](https://clumsy-seeder-416.notion.site/2441197c19ed80bcb55fcad675dd9837?source=copy_link)\n" + + description = "# [v1.4 (2025-01-04)](https://clumsy-seeder-416.notion.site/2441197c19ed80bcb55fcad675dd9837?source=copy_link)\n" + "- 로그인한 유저의 역할에 따라 Map 객체를 반환합니다.\n" + "- 경도, 위도 순서로 입력한 Viewport 객체 입력.\n" + "- 성공 시 200(OK)과 Map 객체 반환.\n"+ + "\n**Request Params (STUDENT 전용):**\n" + + " - `storeCategory` (StoreCategory, optional): 카테고리 필터\n" + + " - `adminId` (Long, optional): 특정 학생회 필터\n" + "\n**Request Body:**\n" + " - `viewport` 객체 (JSON, required): 공간인덱싱을 위한 경도, 위도 객체\n" + " - `lng1` (double): 좌 상단 경도\n" + @@ -86,13 +90,15 @@ public class MapController { @GetMapping("/nearby") public BaseResponse getLocations( @ModelAttribute MapRequestDTO viewport, + @RequestParam(required = false) StoreCategory storeCategory, + @RequestParam(required = false) Long adminId, @AuthenticationPrincipal PrincipalDetails pd ) { Long memberId = pd.getMember().getId(); UserRole role = pd.getMember().getRole(); return switch (role) { - case STUDENT -> BaseResponse.onSuccess(SuccessStatus._OK, mapService.getStores(viewport, memberId)); + case STUDENT -> BaseResponse.onSuccess(SuccessStatus._OK, mapService.getStores(viewport, memberId, storeCategory, adminId)); case ADMIN -> BaseResponse.onSuccess(SuccessStatus._OK, mapService.getPartners(viewport, memberId)); case PARTNER -> BaseResponse.onSuccess(SuccessStatus._OK, mapService.getAdmins(viewport, memberId)); default -> BaseResponse.onFailure(ErrorStatus._BAD_REQUEST, null); diff --git a/src/main/java/com/assu/server/domain/map/service/MapService.java b/src/main/java/com/assu/server/domain/map/service/MapService.java index d48a90e2..17120017 100644 --- a/src/main/java/com/assu/server/domain/map/service/MapService.java +++ b/src/main/java/com/assu/server/domain/map/service/MapService.java @@ -4,13 +4,14 @@ import com.assu.server.domain.map.dto.MapRequestDTO; import com.assu.server.domain.map.dto.PartnerMapResponseDTO; import com.assu.server.domain.map.dto.StoreMapResponseDTO; +import com.assu.server.domain.store.entity.enums.StoreCategory; import java.util.List; public interface MapService { List getAdmins(MapRequestDTO viewport, Long memberId); List getPartners(MapRequestDTO viewport, Long memberId); - List getStores(MapRequestDTO viewport, Long memberId); + List getStores(MapRequestDTO viewport, Long memberId, StoreCategory storeCategory, Long adminId); List searchStores(String keyword, Long memberId); List searchPartner(String keyword, Long memberId); diff --git a/src/main/java/com/assu/server/domain/map/service/MapServiceImpl.java b/src/main/java/com/assu/server/domain/map/service/MapServiceImpl.java index d7d74c8c..991efcd1 100644 --- a/src/main/java/com/assu/server/domain/map/service/MapServiceImpl.java +++ b/src/main/java/com/assu/server/domain/map/service/MapServiceImpl.java @@ -16,6 +16,7 @@ import com.assu.server.domain.partnership.repository.PaperContentRepository; import com.assu.server.domain.partnership.repository.PaperRepository; import com.assu.server.domain.store.entity.Store; +import com.assu.server.domain.store.entity.enums.StoreCategory; import com.assu.server.domain.store.repository.StoreRepository; import com.assu.server.domain.student.entity.UserPaper; import com.assu.server.domain.student.repository.UserPaperRepository; @@ -90,7 +91,7 @@ public List getAdmins(MapRequestDTO viewport, Long memberId * papercontent의 note가 있으면 benefit 대신 note를 사용. */ @Override - public List getStores(MapRequestDTO viewport, Long memberId) { + public List getStores(MapRequestDTO viewport, Long memberId, StoreCategory storeCategory, Long adminId) { final String wkt = toWKT(viewport); // 1) 뷰포트 내 매장 조회 (Partner, Member fetch join) @@ -100,7 +101,7 @@ public List getStores(MapRequestDTO viewport, Long memberId } // 2) 해당 학생의 활성 UserPaper 조회 (paper, store, admin fetch join 포함) - final List userPapers = userPaperRepository.findActivePartnershipsByStudentId(memberId); + final List userPapers = userPaperRepository.findActivePartnershipsByStudentId(memberId, storeCategory, adminId); if (userPapers.isEmpty()) { return List.of(); // active 제휴가 없으면 빈 리스트 반환 } @@ -170,14 +171,14 @@ public List getStores(MapRequestDTO viewport, Long memberId List partnerships = benefitsByAdmin.entrySet().stream() .map(entry -> { - Long adminId = entry.getKey(); + Long paperAdminId = entry.getKey(); List benefits = entry.getValue(); String adminName = sPapers.stream() - .filter(p -> p.getAdmin().getId().equals(adminId)) + .filter(p -> p.getAdmin().getId().equals(paperAdminId)) .findFirst() .map(p -> p.getAdmin().getName()) .orElse(null); - return new StoreMapResponseDTO.PartnershipInfo(adminId, adminName, benefits); + return new StoreMapResponseDTO.PartnershipInfo(paperAdminId, adminName, benefits); }) .filter(p -> p.adminId() != null && !p.benefits().isEmpty()) .toList(); @@ -231,7 +232,7 @@ private String generateBenefitText(PaperContent content) { public List searchStores(String keyword, Long memberId) { String normalizedKeyword = (keyword == null) ? "" : keyword.replace(" ", "").toLowerCase(); - List userPapers = userPaperRepository.findActivePartnershipsByStudentId(memberId); + List userPapers = userPaperRepository.findActivePartnershipsByStudentId(memberId, null, null); if (userPapers.isEmpty()) { return List.of(); diff --git a/src/main/java/com/assu/server/domain/store/entity/Store.java b/src/main/java/com/assu/server/domain/store/entity/Store.java index f348d99f..9adec009 100644 --- a/src/main/java/com/assu/server/domain/store/entity/Store.java +++ b/src/main/java/com/assu/server/domain/store/entity/Store.java @@ -2,6 +2,7 @@ import com.assu.server.domain.common.entity.BaseEntity; import com.assu.server.domain.common.enums.ActivationStatus; import com.assu.server.domain.partner.entity.Partner; +import com.assu.server.domain.store.entity.enums.StoreCategory; import jakarta.persistence.*; import lombok.*; @@ -44,6 +45,9 @@ public class Store extends BaseEntity { private double latitude; private double longitude; + @Enumerated(EnumType.STRING) + private StoreCategory storeCategory; + public void linkPartner(Partner partner) { this.partner = partner; } diff --git a/src/main/java/com/assu/server/domain/store/entity/enums/StoreCategory.java b/src/main/java/com/assu/server/domain/store/entity/enums/StoreCategory.java new file mode 100644 index 00000000..c19cbc10 --- /dev/null +++ b/src/main/java/com/assu/server/domain/store/entity/enums/StoreCategory.java @@ -0,0 +1,5 @@ +package com.assu.server.domain.store.entity.enums; + +public enum StoreCategory { + RESTAURANT, CAFE, BAR, BEAUTY, ENTERTAINMENT, SPORTS, LIVING, HOSPITAL, EDUCATION, OTHERS +} \ No newline at end of file diff --git a/src/main/java/com/assu/server/domain/store/repository/StoreRepository.java b/src/main/java/com/assu/server/domain/store/repository/StoreRepository.java index 28ea7caa..5c8c2b9f 100644 --- a/src/main/java/com/assu/server/domain/store/repository/StoreRepository.java +++ b/src/main/java/com/assu/server/domain/store/repository/StoreRepository.java @@ -1,5 +1,7 @@ package com.assu.server.domain.store.repository; import java.util.Optional; + +import com.assu.server.domain.store.entity.enums.StoreCategory; import org.springframework.data.jpa.repository.JpaRepository; import com.assu.server.domain.store.entity.Store; import com.assu.server.domain.partner.entity.Partner; @@ -113,7 +115,9 @@ Optional findBySameAddress( WHERE s.point IS NOT NULL AND function('ST_Contains', function('ST_GeomFromText', :wkt, 4326), s.point) = true """) - List findAllWithinViewportWithPartner(@Param("wkt") String wkt); + List findAllWithinViewportWithPartner( + @Param("wkt") String wkt + ); @Query(""" SELECT DISTINCT s diff --git a/src/main/java/com/assu/server/domain/student/controller/StudentController.java b/src/main/java/com/assu/server/domain/student/controller/StudentController.java index 59fe399f..b3fc21cd 100644 --- a/src/main/java/com/assu/server/domain/student/controller/StudentController.java +++ b/src/main/java/com/assu/server/domain/student/controller/StudentController.java @@ -2,6 +2,7 @@ import java.util.List; +import com.assu.server.domain.store.entity.enums.StoreCategory; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; @@ -114,7 +115,8 @@ public BaseResponse earnStamp( "**Request Parameters:**\n" + "- `all` (Boolean, optional): 전체 조회 여부 - 기본값: false\n" + " - true: 모든 이용 가능한 제휴 조회\n" + - " - false: 최대 2개만 조회\n\n" + + " - false: 최대 2개만 조회\n" + + "- `storeCategory` (StoreCategory, optional): 카테고리 필터 - 미입력 시 전체 조회\n\n" + "**Response:**\n" + "- 성공 시 200(OK)와 이용 가능한 제휴 목록 반환\n" + "- 401(UNAUTHORIZED): 인증되지 않은 사용자\n" + @@ -123,9 +125,11 @@ public BaseResponse earnStamp( @GetMapping("/usable") public BaseResponse> getUsablePartnership( @AuthenticationPrincipal PrincipalDetails pd, - @RequestParam(name = "all", defaultValue = "false") boolean all - ) { - return BaseResponse.onSuccess(SuccessStatus._OK, studentService.getUsablePartnership(pd.getId(), all)); + @RequestParam(name = "all", defaultValue = "false") boolean all, + @RequestParam(required = false) StoreCategory storeCategory, + @RequestParam(required = false) Long adminId + ) { + return BaseResponse.onSuccess(SuccessStatus._OK, studentService.getUsablePartnership(pd.getId(), all, storeCategory, adminId)); } @Operation( diff --git a/src/main/java/com/assu/server/domain/student/repository/UserPaperRepository.java b/src/main/java/com/assu/server/domain/student/repository/UserPaperRepository.java index ef94173f..d6338f85 100644 --- a/src/main/java/com/assu/server/domain/student/repository/UserPaperRepository.java +++ b/src/main/java/com/assu/server/domain/student/repository/UserPaperRepository.java @@ -1,11 +1,10 @@ package com.assu.server.domain.student.repository; +import com.assu.server.domain.store.entity.enums.StoreCategory; import com.assu.server.domain.student.entity.UserPaper; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; - -import java.time.LocalDate; import java.util.List; public interface UserPaperRepository extends JpaRepository { @@ -17,13 +16,15 @@ public interface UserPaperRepository extends JpaRepository { LEFT JOIN FETCH p.admin a WHERE up.student.id = :studentId AND p.isActivated = com.assu.server.domain.common.enums.ActivationStatus.ACTIVE + AND (:storeCategory IS NULL OR s.storeCategory = :storeCategory) + AND (:adminId IS NULL OR a.id = :adminId) ORDER BY p.id DESC """) - List findActivePartnershipsByStudentId(@Param("studentId") Long studentId); - - boolean existsByStudentIdAndPaperId(Long studentId, Long paperId); - - boolean existsByStudentIdAndPaperIdAndPaperContentId(Long studentId, Long paperId, Long paperContentId); + List findActivePartnershipsByStudentId( + @Param("studentId") Long studentId, + @Param("storeCategory") StoreCategory storeCategory, + @Param("adminId") Long adminId + ); @Query(""" SELECT up FROM UserPaper up diff --git a/src/main/java/com/assu/server/domain/student/service/StudentService.java b/src/main/java/com/assu/server/domain/student/service/StudentService.java index cebcbce5..d17df18e 100644 --- a/src/main/java/com/assu/server/domain/student/service/StudentService.java +++ b/src/main/java/com/assu/server/domain/student/service/StudentService.java @@ -2,6 +2,7 @@ import java.util.List; +import com.assu.server.domain.store.entity.enums.StoreCategory; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -12,7 +13,7 @@ public interface StudentService { StudentResponseDTO.MyPartnership getMyPartnership(Long studentId, int year, int month); StudentResponseDTO.CheckStampResponseDTO getStamp(Long memberId);//조회 Page getUnreviewedUsage(Long memberId, Pageable pageable); - List getUsablePartnership(Long memberId, Boolean all); + List getUsablePartnership(Long memberId, Boolean all, StoreCategory storeCategory, Long adminId); void syncUserPapersForAllStudents(); StudentResponseDTO.CheckStampResponseDTO addStamp(Long id); StudentProfileResponseDTO getStudentProfile(Long memberId); diff --git a/src/main/java/com/assu/server/domain/student/service/StudentServiceImpl.java b/src/main/java/com/assu/server/domain/student/service/StudentServiceImpl.java index 5780e772..103502b5 100644 --- a/src/main/java/com/assu/server/domain/student/service/StudentServiceImpl.java +++ b/src/main/java/com/assu/server/domain/student/service/StudentServiceImpl.java @@ -7,6 +7,7 @@ import java.util.stream.Collectors; import com.assu.server.domain.notification.service.NotificationCommandService; +import com.assu.server.domain.store.entity.enums.StoreCategory; import com.assu.server.domain.student.entity.StampEventApplicant; import com.assu.server.domain.student.repository.StampEventApplicantRepository; import org.springframework.data.domain.Page; @@ -141,8 +142,8 @@ public Page getUnreviewedUsage(Long memberId, Pa } @Override - public List getUsablePartnership(Long memberId, Boolean all) { - List userPapers = userPaperRepository.findActivePartnershipsByStudentId(memberId); + public List getUsablePartnership(Long memberId, Boolean all, StoreCategory storeCategory, Long adminId) { + List userPapers = userPaperRepository.findActivePartnershipsByStudentId(memberId, storeCategory, adminId); // Goods 일괄 조회 (N+1 방지) List contentIds = userPapers.stream() diff --git a/src/test/java/com/assu/server/domain/map/service/MapServiceImplTest.java b/src/test/java/com/assu/server/domain/map/service/MapServiceImplTest.java index 2df13db1..dd10e05e 100644 --- a/src/test/java/com/assu/server/domain/map/service/MapServiceImplTest.java +++ b/src/test/java/com/assu/server/domain/map/service/MapServiceImplTest.java @@ -79,6 +79,93 @@ private UserPaper userPaper(Paper paper) { return UserPaper.builder().paper(paper).build(); } + // ===== getStores ===== + + @Test + @DisplayName("뷰포트 내 가게가 없으면 빈 리스트를 반환하고 UserPaper 조회를 하지 않는다") + void getStores_EmptyViewport_ReturnsEmptyList() { + // 1. Given + when(storeRepository.findAllWithinViewportWithPartner(anyString())).thenReturn(List.of()); + + // 2. When + List result = mapService.getStores(viewport(), STUDENT_ID, null, null); + + // 3. Then + assertTrue(result.isEmpty()); + verify(userPaperRepository, never()).findActivePartnershipsByStudentId(any(), any(), any()); + } + + @Test + @DisplayName("활성 제휴가 없는 학생이 주변 가게를 조회하면 빈 리스트를 반환한다") + void getStores_NoActivePartnership_ReturnsEmptyList() { + // 1. Given + when(storeRepository.findAllWithinViewportWithPartner(anyString())).thenReturn(List.of(store(500L, "숭실카페"))); + when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID, null, null)).thenReturn(List.of()); + + // 2. When + List result = mapService.getStores(viewport(), STUDENT_ID, null, null); + + // 3. Then + assertTrue(result.isEmpty()); + } + + @Test + @DisplayName("adminId 필터링 시 해당 학생회 제휴가 있는 가게만 반환한다") + void getStores_WithAdminIdFilter_ReturnsOnlyMatchingAdminStores() { + // 1. Given - A학생회(가게1), B학생회(가게2) 각각 제휴 + Store store1 = store(1L, "가게1"); + Store store2 = store(2L, "가게2"); + Admin adminA = admin(10L, "A학생회"); + Admin adminB = admin(20L, "B학생회"); + Paper paperA = Paper.builder().id(100L).store(store1).admin(adminA).build(); + Paper paperB = Paper.builder().id(200L).store(store2).admin(adminB).build(); + + when(storeRepository.findAllWithinViewportWithPartner(anyString())).thenReturn(List.of(store1, store2)); + // DB가 adminId=10으로 필터링해서 paperA만 반환 + when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID, null, 10L)) + .thenReturn(List.of(userPaper(paperA))); + + PaperContent contentA = PaperContent.builder().id(1000L).paper(paperA).note("A학생회 혜택").build(); + when(paperContentRepository.findByPaperIdIn(anyList())).thenReturn(List.of(contentA)); + + // 2. When - adminId=10(A학생회)으로 필터링 + List result = mapService.getStores(viewport(), STUDENT_ID, null, 10L); + + // 3. Then - 가게1만 반환 + assertEquals(1, result.size()); + assertEquals(1L, result.get(0).storeId()); + assertEquals("A학생회", result.get(0).partnerships().get(0).adminName()); + verify(userPaperRepository).findActivePartnershipsByStudentId(STUDENT_ID, null, 10L); + } + + @Test + @DisplayName("여러 학생회가 같은 가게와 제휴하면 학생회별로 혜택이 그룹화된다") + void getStores_MultipleAdmins_GroupsBenefitsByAdmin() { + // 1. Given - A학생회, B학생회 모두 가게1과 제휴 + Store store1 = store(1L, "가게1"); + Admin adminA = admin(10L, "A학생회"); + Admin adminB = admin(20L, "B학생회"); + Paper paperA = Paper.builder().id(100L).store(store1).admin(adminA).build(); + Paper paperB = Paper.builder().id(200L).store(store1).admin(adminB).build(); + + when(storeRepository.findAllWithinViewportWithPartner(anyString())).thenReturn(List.of(store1)); + when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID, null, null)) + .thenReturn(List.of(userPaper(paperA), userPaper(paperB))); + + PaperContent contentA = PaperContent.builder().id(1000L).paper(paperA).note("A혜택").build(); + PaperContent contentB = PaperContent.builder().id(2000L).paper(paperB).note("B혜택").build(); + when(paperContentRepository.findByPaperIdIn(anyList())).thenReturn(List.of(contentA, contentB)); + + // 2. When + List result = mapService.getStores(viewport(), STUDENT_ID, null, null); + + // 3. Then - 가게1에 2개의 학생회 혜택 + assertEquals(1, result.size()); + assertEquals(2, result.get(0).partnerships().size()); + } + + // ===== getPartners ===== + @Test @DisplayName("뷰포트 내 제휴업체가 없으면 빈 리스트를 반환한다") void getPartners_EmptyViewport_ReturnsEmptyList() { @@ -97,7 +184,7 @@ void getPartners_EmptyViewport_ReturnsEmptyList() { @DisplayName("활성 제휴가 없는 학생이 매장을 검색하면 빈 리스트를 반환한다") void searchStores_NoActivePartnership_ReturnsEmptyList() { // 1. Given - when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID)).thenReturn(List.of()); + when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID, null, null)).thenReturn(List.of()); // 2. When List result = mapService.searchStores("맥주", STUDENT_ID); @@ -112,7 +199,7 @@ void searchStores_NoMatchingKeyword_ReturnsEmptyList() { // 1. Given Store store = store(500L, "숭실마트"); Paper paper = Paper.builder().id(100L).store(store).admin(admin(10L, "총학생회")).build(); - when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID)) + when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID, null, null)) .thenReturn(List.of(userPaper(paper))); // 2. When @@ -128,7 +215,7 @@ void searchStores_MatchingKeyword_UsesNoteAsBenefit() { // 1. Given ("역전 할머니 맥주" 매장을 "할머니맥주"로 검색) Store store = store(500L, "역전 할머니 맥주"); Paper paper = Paper.builder().id(100L).store(store).admin(admin(10L, "총학생회")).build(); - when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID)) + when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID, null, null)) .thenReturn(List.of(userPaper(paper))); PaperContent content = PaperContent.builder() @@ -155,7 +242,7 @@ void searchStores_NoNote_GeneratesDiscountBenefitText() { // 1. Given Store store = store(500L, "숭실분식"); Paper paper = Paper.builder().id(100L).store(store).admin(admin(10L, "총학생회")).build(); - when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID)) + when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID, null, null)) .thenReturn(List.of(userPaper(paper))); PaperContent content = PaperContent.builder() @@ -180,7 +267,7 @@ void searchStores_NoNote_GeneratesServiceBenefitText() { // 1. Given Store store = store(500L, "숭실분식"); Paper paper = Paper.builder().id(100L).store(store).admin(admin(10L, "총학생회")).build(); - when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID)) + when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID, null, null)) .thenReturn(List.of(userPaper(paper))); PaperContent content = PaperContent.builder() @@ -205,7 +292,7 @@ void searchStores_NoPaperContent_ExcludesStore() { // 1. Given Store store = store(500L, "숭실분식"); Paper paper = Paper.builder().id(100L).store(store).admin(admin(10L, "총학생회")).build(); - when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID)) + when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID, null, null)) .thenReturn(List.of(userPaper(paper))); when(paperContentRepository.findByPaperIdIn(List.of(100L))).thenReturn(List.of()); diff --git a/src/test/java/com/assu/server/domain/student/service/StudentServiceImplTest.java b/src/test/java/com/assu/server/domain/student/service/StudentServiceImplTest.java index 9f657450..6d1b1873 100644 --- a/src/test/java/com/assu/server/domain/student/service/StudentServiceImplTest.java +++ b/src/test/java/com/assu/server/domain/student/service/StudentServiceImplTest.java @@ -28,6 +28,7 @@ import com.assu.server.domain.partnership.repository.PaperContentRepository; import com.assu.server.domain.partnership.repository.PaperRepository; import com.assu.server.domain.store.entity.Store; +import com.assu.server.domain.store.entity.enums.StoreCategory; import com.assu.server.domain.student.dto.StudentResponseDTO; import com.assu.server.domain.student.entity.Student; import com.assu.server.domain.student.entity.UserPaper; @@ -141,11 +142,11 @@ void getUsablePartnership_NotAll_LimitsToTwo() { usablePartnership(100L, 1000L, "안주", OptionType.SERVICE), usablePartnership(101L, 1001L, "음료", OptionType.SERVICE), usablePartnership(102L, 1002L, "주류", OptionType.SERVICE)); - when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID)).thenReturn(userPapers); + when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID, null, null)).thenReturn(userPapers); // 2. When List result = - studentService.getUsablePartnership(STUDENT_ID, false); + studentService.getUsablePartnership(STUDENT_ID, false, null, null); // 3. Then assertEquals(2, result.size()); @@ -159,11 +160,11 @@ void getUsablePartnership_All_ReturnsEverything() { usablePartnership(100L, 1000L, "안주", OptionType.SERVICE), usablePartnership(101L, 1001L, "음료", OptionType.SERVICE), usablePartnership(102L, 1002L, "주류", OptionType.SERVICE)); - when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID)).thenReturn(userPapers); + when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID, null, null)).thenReturn(userPapers); // 2. When List result = - studentService.getUsablePartnership(STUDENT_ID, true); + studentService.getUsablePartnership(STUDENT_ID, true, null, null); // 3. Then assertEquals(3, result.size()); @@ -176,7 +177,7 @@ void getUsablePartnership_All_ReturnsEverything() { void getUsablePartnership_NoCategory_FallsBackToGoodsBelonging() { // 1. Given (category가 null인 SERVICE 제휴) UserPaper userPaper = usablePartnership(100L, 1000L, null, OptionType.SERVICE); - when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID)).thenReturn(List.of(userPaper)); + when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID, null, null)).thenReturn(List.of(userPaper)); Goods goods = Goods.builder() .content(userPaper.getPaperContent()).belonging("생맥주 500cc").build(); @@ -184,12 +185,68 @@ void getUsablePartnership_NoCategory_FallsBackToGoodsBelonging() { // 2. When List result = - studentService.getUsablePartnership(STUDENT_ID, true); + studentService.getUsablePartnership(STUDENT_ID, true, null, null); // 3. Then assertEquals("생맥주 500cc", result.get(0).getCategory()); } + @Test + @DisplayName("storeCategory=BAR이면 레포지토리에 BAR가 전달되고 반환된 BAR 결과만 노출된다") + void getUsablePartnership_WithStoreCategory_PassesCategoryToRepository() { + // 1. Given - DB가 BAR만 필터링해서 2건 반환한다고 가정 + List barUserPapers = List.of( + usablePartnership(100L, 1000L, "안주", OptionType.SERVICE), + usablePartnership(101L, 1001L, "주류", OptionType.SERVICE)); + when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID, StoreCategory.BAR, null)) + .thenReturn(barUserPapers); + + // null이면 전체(BAR 2개 + RESTAURANT 1개) 반환 + List allUserPapers = List.of( + usablePartnership(100L, 1000L, "안주", OptionType.SERVICE), + usablePartnership(101L, 1001L, "주류", OptionType.SERVICE), + usablePartnership(102L, 1002L, "파스타", OptionType.SERVICE)); + when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID, null, null)) + .thenReturn(allUserPapers); + + // 2. When + List barResult = + studentService.getUsablePartnership(STUDENT_ID, true, StoreCategory.BAR, null); + List allResult = + studentService.getUsablePartnership(STUDENT_ID, true, null, null); + + // 3. Then + assertEquals(2, barResult.size()); + assertEquals(3, allResult.size()); + verify(userPaperRepository).findActivePartnershipsByStudentId(STUDENT_ID, StoreCategory.BAR, null); + verify(userPaperRepository).findActivePartnershipsByStudentId(STUDENT_ID, null, null); + } + + @Test + @DisplayName("adminId를 지정하면 레포지토리에 adminId가 전달되고 반환된 결과만 노출된다") + void getUsablePartnership_WithAdminId_PassesAdminIdToRepository() { + // 1. Given - DB가 adminId=10으로 필터링해서 1건 반환한다고 가정 + List filtered = List.of(usablePartnership(100L, 1000L, "안주", OptionType.SERVICE)); + when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID, null, 10L)).thenReturn(filtered); + + List all = List.of( + usablePartnership(100L, 1000L, "안주", OptionType.SERVICE), + usablePartnership(101L, 1001L, "음료", OptionType.SERVICE)); + when(userPaperRepository.findActivePartnershipsByStudentId(STUDENT_ID, null, null)).thenReturn(all); + + // 2. When + List filteredResult = + studentService.getUsablePartnership(STUDENT_ID, true, null, 10L); + List allResult = + studentService.getUsablePartnership(STUDENT_ID, true, null, null); + + // 3. Then + assertEquals(1, filteredResult.size()); + assertEquals(2, allResult.size()); + verify(userPaperRepository).findActivePartnershipsByStudentId(STUDENT_ID, null, 10L); + verify(userPaperRepository).findActivePartnershipsByStudentId(STUDENT_ID, null, null); + } + // ===== syncUserPapersForStudent ===== @Test