diff --git a/src/main/java/com/assu/server/domain/certification/component/CertificationSessionManager.java b/src/main/java/com/assu/server/domain/certification/component/CertificationSessionManager.java index 91108335..fcdc07e6 100644 --- a/src/main/java/com/assu/server/domain/certification/component/CertificationSessionManager.java +++ b/src/main/java/com/assu/server/domain/certification/component/CertificationSessionManager.java @@ -1,67 +1,15 @@ package com.assu.server.domain.certification.component; import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; - -import org.springframework.stereotype.Component; import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; + import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -// -// @Slf4j // ⭐️ SLF4j 로그 사용을 위해 추가 -// @Component -// public class CertificationSessionManager { -// private final Map> sessionUserMap = new ConcurrentHashMap<>(); -// -// public void openSession(Long sessionId) { -// sessionUserMap.put(sessionId, ConcurrentHashMap.newKeySet()); -// // ⭐️ 로그 추가 -// log.info("✅ New certification session opened. SessionID: {}", sessionId); -// } -// -// public void addUserToSession(Long sessionId, Long userId) { -// Set users = sessionUserMap.computeIfAbsent(sessionId, k -> { -// log.warn("Attempted to add user to a non-existent session. Creating new set for SessionID: {}", k); -// return ConcurrentHashMap.newKeySet(); -// }); -// -// boolean isAdded = users.add(userId); -// -// // ⭐️ 요청하신 멤버 추가 확인 로그 -// if (isAdded) { -// log.info("👤 User added to session. SessionID: {}, UserID: {}. Current participants: {}", -// sessionId, userId, users.size()); -// } else { -// log.info("👤 User already in session. SessionID: {}, UserID: {}. Current participants: {}", -// sessionId, userId, users.size()); -// } -// } -// -// public int getCurrentUserCount(Long sessionId) { -// return sessionUserMap.getOrDefault(sessionId, Set.of()).size(); -// } -// -// public boolean hasUser(Long sessionId, Long userId) { -// return sessionUserMap.getOrDefault(sessionId, Set.of()).contains(userId); -// } -// -// public List snapshotUserIds(Long sessionId) { -// return List.copyOf(sessionUserMap.getOrDefault(sessionId, Set.of())); -// } -// -// -// -// public void removeSession(Long sessionId) { -// sessionUserMap.remove(sessionId); -// // ⭐️ 로그 추가 -// log.info("❌ Certification session removed. SessionID: {}", sessionId); -// } -// } + @Component @RequiredArgsConstructor public class CertificationSessionManager { diff --git a/src/main/java/com/assu/server/domain/certification/config/CertifyWebSocketConfig.java b/src/main/java/com/assu/server/domain/certification/config/CertifyWebSocketConfig.java deleted file mode 100644 index 56da642a..00000000 --- a/src/main/java/com/assu/server/domain/certification/config/CertifyWebSocketConfig.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.assu.server.domain.certification.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.simp.config.ChannelRegistration; -import org.springframework.messaging.simp.config.MessageBrokerRegistry; -import org.springframework.web.socket.WebSocketHandler; -import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; -import org.springframework.web.socket.config.annotation.StompEndpointRegistry; -import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; - -import lombok.RequiredArgsConstructor; - -// @EnableWebSocketMessageBroker -// @Configuration -// @RequiredArgsConstructor -// public class CertifyWebSocketConfig implements WebSocketMessageBrokerConfigurer { -// -// private final StompAuthChannelInterceptor stompAuthChannelInterceptor; -// @Override -// public void configureMessageBroker(MessageBrokerRegistry config) { -// config.enableSimpleBroker("/certification"); // 인증현황을 받아보기 위한 구독 주소 -// config.setApplicationDestinationPrefixes("/app"); // 클라이언트가 인증 요청을 보내는 주소 -// } -// -// @Override -// public void registerStompEndpoints(StompEndpointRegistry registry) { -// registry.addEndpoint("/ws-certify").setAllowedOriginPatterns("*"); // 클라이언트 WebSocket 연결 주소 -// // .setAllowedOriginPatterns("http://10.0.2.2:8080", "ws://10.0.2.2:8080");// CORS 허용 -// } -// -// @Override -// public void configureClientInboundChannel(ChannelRegistration registration) { -// registration.interceptors(stompAuthChannelInterceptor); -// } -// -// } diff --git a/src/main/java/com/assu/server/domain/certification/config/StompAuthChannelInterceptor.java b/src/main/java/com/assu/server/domain/certification/config/StompAuthChannelInterceptor.java index 20e33674..8534c3ab 100644 --- a/src/main/java/com/assu/server/domain/certification/config/StompAuthChannelInterceptor.java +++ b/src/main/java/com/assu/server/domain/certification/config/StompAuthChannelInterceptor.java @@ -35,41 +35,6 @@ public Message preSend(Message message, MessageChannel channel) { log.info("Authentication set: {}", authentication); } } - return message; } - - // @Override - // public Message preSend(Message message, MessageChannel channel) { - // StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message); - // log.info("StompCommand: {}", accessor.getCommand()); // StompCommand 로그 추가 - // - // if (StompCommand.CONNECT.equals(accessor.getCommand())) { - // log.info("CONNECT command received."); - // // 프론트에서 connect 시 Authorization 헤더 넣어야 함 - // String authHeader = accessor.getFirstNativeHeader("Authorization"); - // log.info("Authorization Header: {}", authHeader); // Authorization 헤더 로그 추가 - // - // if (authHeader != null && authHeader.startsWith("Bearer ")) { - // String token = jwtUtil.getTokenFromHeader(authHeader); - // log.info("Extracted Token: {}", token); // 추출된 토큰 로그 추가 - // - // // JwtUtil 이용해서 Authentication 복원 - // Authentication authentication = jwtUtil.getAuthentication(token); - // log.info("Authentication restored: {}", authentication); // 복원된 인증 정보 로그 추가 - // - // // WebSocket 세션에 Authentication(UserPrincipal) 저장 - // accessor.setUser(authentication); - // log.info("User principal set on accessor."); - // } else { - // log.warn("Authorization header is missing or not in Bearer format."); - // } - // } else if (StompCommand.SEND.equals(accessor.getCommand())) { - // // SEND 명령어에 대한 로그 추가 (메시지 전송 시) - // Object payload = message.getPayload(); - // log.info("SEND command received. Destination: {}, Payload: {}", accessor.getDestination(), payload); - // } - // - // return message; - // } } \ No newline at end of file diff --git a/src/main/java/com/assu/server/domain/certification/controller/CertificationController.java b/src/main/java/com/assu/server/domain/certification/controller/CertificationController.java index d86c9280..196ea8f8 100644 --- a/src/main/java/com/assu/server/domain/certification/controller/CertificationController.java +++ b/src/main/java/com/assu/server/domain/certification/controller/CertificationController.java @@ -1,26 +1,19 @@ package com.assu.server.domain.certification.controller; -import java.time.LocalDateTime; - import org.springframework.http.ResponseEntity; -import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import com.assu.server.domain.certification.dto.CertificationRequestDTO; +import com.assu.server.domain.certification.dto.CertificationGroupRequestDTO; +import com.assu.server.domain.certification.dto.CertificationPersonalRequestDTO; import com.assu.server.domain.certification.dto.CertificationResponseDTO; import com.assu.server.domain.certification.service.CertificationService; -import com.assu.server.domain.member.entity.Member; -import com.assu.server.domain.member.repository.MemberRepository; 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; -import com.assu.server.global.exception.GeneralException; import com.assu.server.global.util.PrincipalDetails; - import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; @@ -31,7 +24,6 @@ public class CertificationController { private final CertificationService certificationService; - private final MemberRepository memberRepository; // 지금은 그냥 임시 데이터 하드 코딩이라 여기에 둔거여 @PostMapping("/certification/session") @Operation( @@ -50,33 +42,40 @@ public class CertificationController { "\n**Response:**\n" + " - 성공 시 201(Created)와 sessionId, adminId 반환" ) - public ResponseEntity> getSessionId( + public ResponseEntity> getSessionId( @AuthenticationPrincipal PrincipalDetails pd, - @RequestBody CertificationRequestDTO.groupRequest dto + @RequestBody CertificationGroupRequestDTO dto ) { - CertificationResponseDTO.getSessionIdResponse result = certificationService.getSessionId(dto, pd.getMember()); + CertificationResponseDTO result = certificationService.getSessionId(dto, pd.getMember()); return ResponseEntity.ok(BaseResponse.onSuccess(SuccessStatus.GROUP_SESSION_CREATE, result)); } - // @MessageMapping("/certify") - // @Operation(summary = "그룹 세션 인증 api", description = "그룹에 대한 세션 인증 요청을 보냅니다.") - // public ResponseEntity> certifyGroup( - // CertificationRequestDTO.groupSessionRequest dto , PrincipalDetails pd - // - // ) { - // certificationService.handleCertification(dto, pd.getMember()); - // - // return ResponseEntity.ok(BaseResponse.onSuccess(SuccessStatus.GROUP_CERTIFICATION_SUCCESS, null)); - // } - @PostMapping("/certification/personal") - @Operation(summary = "개인 인증 api", description = "사실 크게 필요없는데, 제휴 내역 통계를 위해 데이터를 post하는 api 입니다. " - + "가게 별 제휴를 조회하고 people값이 null 인 제휴를 선택한 경우 그룹 인증 대신 요청하는 api 입니다.") + @Operation( + summary = "개인 인증 요청 API", + description = + "# [v1.0 (2025-09-09)](https://clumsy-seeder-416.notion.site/2471197c19ed80fd9a8dcc43fb938a5d?source=copy_link)\n" + + "- 개인 단위 인증을 위한 API입니다.\n" + + "- 그룹 인증이 아닌 경우, 통계 및 제휴 이력 적재를 목적으로 사용됩니다.\n" + + "- 가게별 제휴 조회 시 `people` 값이 null 인 경우 호출됩니다.\n" + + "\n**Request Body:**\n" + + " - `storeId` (Long, required): 인증이 발생한 스토어 ID\n" + + " - `adminId` (Long, required): 인증을 요청한 관리자 ID\n" + + " - `tableNumber` (Integer, required): 인증이 발생한 테이블 번호\n" + + "\n**Authentication:**\n" + + " - 로그인된 사용자 인증 정보 필요 (`@AuthenticationPrincipal`)\n" + + "\n**Processing:**\n" + + " - 전달받은 정보 기반으로 개인 인증 이력 저장\n" + + " - 세션 생성은 하지 않음\n" + + "\n**Response:**\n" + + " - 성공 시 200(OK)\n" + + " - 성공 메시지 반환" + ) public ResponseEntity> personalCertification( @AuthenticationPrincipal PrincipalDetails pd, - @RequestBody CertificationRequestDTO.personalRequest dto + @RequestBody CertificationPersonalRequestDTO dto ) { certificationService.certificatePersonal(dto, pd.getMember()); diff --git a/src/main/java/com/assu/server/domain/certification/controller/GroupCertificationController.java b/src/main/java/com/assu/server/domain/certification/controller/GroupCertificationController.java index 3114836c..e7772f53 100644 --- a/src/main/java/com/assu/server/domain/certification/controller/GroupCertificationController.java +++ b/src/main/java/com/assu/server/domain/certification/controller/GroupCertificationController.java @@ -4,19 +4,16 @@ import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.Payload; -import org.springframework.messaging.simp.SimpMessageHeaderAccessor; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.stereotype.Component; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import com.assu.server.domain.certification.dto.GroupSessionRequest; import com.assu.server.domain.certification.service.CertificationService; -import com.assu.server.domain.member.entity.Member; import com.assu.server.global.util.PrincipalDetails; +import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -30,6 +27,16 @@ public class GroupCertificationController { private final CertificationService certificationService; @MessageMapping("/certify") + @Operation( + summary = "그룹 인증 요청 WebSocket API", + description = "# [v1.0 (2025-12-23)](https://clumsy-seeder-416.notion.site/x-22b1197c19ed801d99b1ddb7c5d7ee26?source=copy_link)\n" + + "- WebSocket을 통해 그룹 인증 요청을 보냅니다.\n" + + "- 로그인 필요\n" + + "- Payload는 JSON 형식입니다.\n\n" + + "**Request Payload:**\n" + + " - `adminId` (Long, required): 인증하고자 하는 제휴의 관리자 ID\n" + + " - `sessionId` (Long, required): 인증하고자 하는 그룹 세션 ID\n\n" + ) public void certifyGroup(@Payload GroupSessionRequest dto, Principal principal) { if (principal instanceof UsernamePasswordAuthenticationToken) { @@ -38,15 +45,12 @@ public void certifyGroup(@Payload GroupSessionRequest dto, try { log.info("### SUCCESS ### 인증 요청 메시지 수신 - user: {}, adminId: {}, sessionId: {}", - principalDetails.getUsername(), dto.getAdminId(), dto.getSessionId()); + principalDetails.getUsername(), dto.adminId(), dto.sessionId()); - // 헤더를 직접 다룰 필요 없이, 바로 principalDetails 객체를 사용 if (principalDetails != null) { certificationService.handleCertification(dto, principalDetails.getMember()); - log.info("### SUCCESS ### 그룹 인증 처리 완료"); } } catch (Exception e) { - log.error("### ERROR ### 인증 처리 실패", e); } } } diff --git a/src/main/java/com/assu/server/domain/certification/controller/WebSocketTestController.java b/src/main/java/com/assu/server/domain/certification/controller/WebSocketTestController.java deleted file mode 100644 index 7460e5cc..00000000 --- a/src/main/java/com/assu/server/domain/certification/controller/WebSocketTestController.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.assu.server.domain.certification.controller; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.messaging.handler.annotation.MessageMapping; -import org.springframework.messaging.handler.annotation.Payload; -import org.springframework.stereotype.Controller; - -@Slf4j -@Controller -public class WebSocketTestController { - - @MessageMapping("/test") - public void test(@Payload String payload) { - log.info("### 테스트용 메시지 수신 성공! 페이로드: {}", payload); - } -} diff --git a/src/main/java/com/assu/server/domain/certification/converter/CertificationConverter.java b/src/main/java/com/assu/server/domain/certification/converter/CertificationConverter.java deleted file mode 100644 index e4422cbc..00000000 --- a/src/main/java/com/assu/server/domain/certification/converter/CertificationConverter.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.assu.server.domain.certification.converter; - - -import com.assu.server.domain.certification.dto.CertificationRequestDTO; -import com.assu.server.domain.certification.dto.CertificationResponseDTO; -import com.assu.server.domain.certification.entity.AssociateCertification; -import com.assu.server.domain.certification.entity.enums.SessionStatus; -import com.assu.server.domain.member.entity.Member; -import com.assu.server.domain.store.entity.Store; - -public class CertificationConverter { - public static AssociateCertification toAssociateCertification(CertificationRequestDTO.groupRequest dto, Store store, Member member) { - return AssociateCertification.builder() - .store(store) - .partner(store.getPartner()) - .status(SessionStatus.OPENED) - .isCertified(false) - .peopleNumber(dto.getPeople()) - .tableNumber(dto.getTableNumber()) - .student(member.getStudentProfile()) - .build(); - } - - public static CertificationResponseDTO.getSessionIdResponse toSessionIdResponse(Long sessionId){ - return CertificationResponseDTO.getSessionIdResponse.builder() - .sessionId(sessionId) - .build(); - } - - public static AssociateCertification toPersonalCertification(CertificationRequestDTO.personalRequest dto, Store store, Member member) { - return AssociateCertification.builder() - .store(store) - .partner(store.getPartner()) - .isCertified(true) - .tableNumber(dto.getTableNumber()) - .peopleNumber(1) - .student(member.getStudentProfile()) - .build(); - } -} diff --git a/src/main/java/com/assu/server/domain/certification/dto/CertificationGroupRequestDTO.java b/src/main/java/com/assu/server/domain/certification/dto/CertificationGroupRequestDTO.java new file mode 100644 index 00000000..1d765934 --- /dev/null +++ b/src/main/java/com/assu/server/domain/certification/dto/CertificationGroupRequestDTO.java @@ -0,0 +1,36 @@ +package com.assu.server.domain.certification.dto; + +import com.assu.server.domain.certification.entity.AssociateCertification; +import com.assu.server.domain.certification.entity.enums.SessionStatus; +import com.assu.server.domain.member.entity.Member; +import com.assu.server.domain.store.entity.Store; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; + +public record CertificationGroupRequestDTO( + @Schema(description = "1명 이상의 사람 수를 입력해주세요") + @NotNull(message= "1명 이상의 사람 수를 입력해주세요") + Integer people, + @Schema(description = "storeId를 입력해주세요") + @NotNull(message = "storeId를 입력해주세요") + Long storeId, + @Schema(description = "adminId를 입력해주세요") + @NotNull(message="adminId를 입력해주세요") + Long adminId, + @Schema(description = "00~99 사이의 tableNumber를 입력해주세요") + @NotNull(message = "00~99 사이의 tableNumber를 입력해주세요") + Integer tableNumber +) { + public AssociateCertification toAssociateCertification(Store store, Member member) { + return AssociateCertification.builder() + .store(store) + .partner(store.getPartner()) + .status(SessionStatus.OPENED) + .isCertified(false) + .peopleNumber(this.people()) + .tableNumber(this.tableNumber()) + .student(member.getStudentProfile()) + .build(); + } +} diff --git a/src/main/java/com/assu/server/domain/certification/dto/CertificationPersonalRequestDTO.java b/src/main/java/com/assu/server/domain/certification/dto/CertificationPersonalRequestDTO.java new file mode 100644 index 00000000..93ebfc2e --- /dev/null +++ b/src/main/java/com/assu/server/domain/certification/dto/CertificationPersonalRequestDTO.java @@ -0,0 +1,31 @@ +package com.assu.server.domain.certification.dto; + +import com.assu.server.domain.certification.entity.AssociateCertification; +import com.assu.server.domain.member.entity.Member; +import com.assu.server.domain.store.entity.Store; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; + +public record CertificationPersonalRequestDTO( + @Schema(description = "storeId를 입력해주세요") + @NotNull(message="storeId를 입력해주세요") + Long storeId, + @Schema(description = "adminId를 입력해주세요") + @NotNull(message="adminId를 입력해주세요") + Long adminId, + @Schema(description = "00~99 사이의 tableNumber를 입력해주세요") + @NotNull(message = "00~99 사이의 tableNumber를 입력해주세요") + Integer tableNumber +) { + public AssociateCertification toPersonalCertification(Store store, Member member) { + return AssociateCertification.builder() + .store(store) + .partner(store.getPartner()) + .isCertified(true) + .tableNumber(this.tableNumber()) + .peopleNumber(1) + .student(member.getStudentProfile()) + .build(); + } +} diff --git a/src/main/java/com/assu/server/domain/certification/dto/CertificationProgressResponseDTO.java b/src/main/java/com/assu/server/domain/certification/dto/CertificationProgressResponseDTO.java index 510a1b8c..a9823913 100644 --- a/src/main/java/com/assu/server/domain/certification/dto/CertificationProgressResponseDTO.java +++ b/src/main/java/com/assu/server/domain/certification/dto/CertificationProgressResponseDTO.java @@ -2,39 +2,21 @@ import java.util.List; -import lombok.AllArgsConstructor; -import lombok.Getter; +import com.assu.server.domain.certification.entity.AssociateCertification; +import com.assu.server.domain.certification.entity.enums.SessionStatus; +import com.assu.server.domain.member.entity.Member; +import com.assu.server.domain.store.entity.Store; -// public class CurrentProgress { -// private int count; -// -// -// @Getter -// public static class CertificationNumber{ -// public CertificationNumber(int count){ -// this.count= count; -// } -// -// int count; -// } -// -// @Getter -// public static class CompletedNotification{ -// public CompletedNotification(String message, List userIds){ -// -// this.message= message; -// this.userIds= userIds; -// } -// String message; -// List userIds; -// } +import io.swagger.v3.oas.annotations.media.Schema; -// } -@Getter -@AllArgsConstructor -public class CertificationProgressResponseDTO { - private String type; - private Integer count; - private String message; - private List userIds; +public record CertificationProgressResponseDTO ( + @Schema(description = "progress/completed 중 1개 선택하여 string으로 입력") + String type, + @Schema(description = "현재 인증 완료된 인원 수") + Integer count, + @Schema(description = "인증이 모두 완료되었을 시 완료 메세지 첨부하여 전송됨") + String message, + @Schema(description = "인증에 포함된 userId를 반환") + List userIds +){ } \ No newline at end of file diff --git a/src/main/java/com/assu/server/domain/certification/dto/CertificationRequestDTO.java b/src/main/java/com/assu/server/domain/certification/dto/CertificationRequestDTO.java deleted file mode 100644 index 0ba578c4..00000000 --- a/src/main/java/com/assu/server/domain/certification/dto/CertificationRequestDTO.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.assu.server.domain.certification.dto; - -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -public class CertificationRequestDTO { - - @Getter - public static class groupRequest{ - Integer people; - Long storeId; - Long adminId; - Integer tableNumber; - } - - @Getter - public static class personalRequest{ - Long storeId; - Long adminId; - Integer tableNumber; - } - - // @Getter - // @NoArgsConstructor(access = AccessLevel.PROTECTED) - // @AllArgsConstructor - // public static class groupSessionRequest { - // private Long adminId; - // private Long sessionId; - // } -} diff --git a/src/main/java/com/assu/server/domain/certification/dto/CertificationResponseDTO.java b/src/main/java/com/assu/server/domain/certification/dto/CertificationResponseDTO.java index f1f0c504..329ead15 100644 --- a/src/main/java/com/assu/server/domain/certification/dto/CertificationResponseDTO.java +++ b/src/main/java/com/assu/server/domain/certification/dto/CertificationResponseDTO.java @@ -1,16 +1,13 @@ package com.assu.server.domain.certification.dto; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -public class CertificationResponseDTO { - @Builder - @Getter - @NoArgsConstructor - @AllArgsConstructor - public static class getSessionIdResponse { - Long sessionId; - } +public record CertificationResponseDTO( + @Schema(description = "qr에 넣을 세션 아이디를 반환합니다.") + Long sessionId) { + } diff --git a/src/main/java/com/assu/server/domain/certification/dto/GroupSessionRequest.java b/src/main/java/com/assu/server/domain/certification/dto/GroupSessionRequest.java index 4099e376..d6479e56 100644 --- a/src/main/java/com/assu/server/domain/certification/dto/GroupSessionRequest.java +++ b/src/main/java/com/assu/server/domain/certification/dto/GroupSessionRequest.java @@ -1,15 +1,15 @@ package com.assu.server.domain.certification.dto; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; -@Getter -@NoArgsConstructor -@Setter -public class GroupSessionRequest { - Long adminId; - Long sessionId; +public record GroupSessionRequest( + @Schema(description = "adminId를 입력해주세요") + @NotNull(message = "adminId를 입력해주세요") + Long adminId, + @Schema(description = "qr에서 읽은 세션아이디를 입력해주세요") + @NotNull(message = "인증하고자 하는 session의 sessionId를 입력해주세요") + Long sessionId) { @Override public String toString() { diff --git a/src/main/java/com/assu/server/domain/certification/handler/CertificationWebsocketHandler.java b/src/main/java/com/assu/server/domain/certification/handler/CertificationWebsocketHandler.java deleted file mode 100644 index 7009e674..00000000 --- a/src/main/java/com/assu/server/domain/certification/handler/CertificationWebsocketHandler.java +++ /dev/null @@ -1,41 +0,0 @@ -// package com.assu.server.domain.certification.handler; -// -// import org.springframework.stereotype.Component; -// import org.springframework.web.socket.CloseStatus; -// import org.springframework.web.socket.TextMessage; -// import org.springframework.web.socket.WebSocketSession; -// import org.springframework.web.socket.handler.TextWebSocketHandler; -// -// @Component -// public class CertificationWebsocketHandler extends TextWebSocketHandler { -// -// @Override -// public void afterConnectionEstablished(WebSocketSession session) throws Exception { -// // 클라이언트 연결이 성공적으로 수립되었을 때 호출됩니다. -// System.out.println("Client connected: " + session.getId()); -// } -// -// @Override -// protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { -// // 클라이언트로부터 텍스트 메시지를 받았을 때 호출됩니다. -// String payload = message.getPayload(); -// System.out.println("Message received from " + session.getId() + ": " + payload); -// -// // 받은 메시지를 다시 클라이언트에게 보내거나 다른 로직을 처리합니다. -// session.sendMessage(new TextMessage("Echo: " + payload)); -// } -// -// @Override -// public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { -// // 클라이언트 연결이 종료되었을 때 호출됩니다. -// System.out.println("Client disconnected: " + session.getId() + " with status " + status.getCode()); -// } -// -// @Override -// public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { -// // 전송 오류가 발생했을 때 호출됩니다. -// System.err.println("Transport error for session " + session.getId() + ": " + exception.getMessage()); -// // 필요한 경우 연결을 종료하거나 오류를 처리합니다. -// session.close(CloseStatus.SERVER_ERROR); -// } -// } \ No newline at end of file diff --git a/src/main/java/com/assu/server/domain/certification/repository/CertificationRepository.java b/src/main/java/com/assu/server/domain/certification/repository/CertificationRepository.java deleted file mode 100644 index 7f55e899..00000000 --- a/src/main/java/com/assu/server/domain/certification/repository/CertificationRepository.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.assu.server.domain.certification.repository; - -public class CertificationRepository { -} diff --git a/src/main/java/com/assu/server/domain/certification/repository/QRCertificationRepository.java b/src/main/java/com/assu/server/domain/certification/repository/QRCertificationRepository.java deleted file mode 100644 index ff7029fb..00000000 --- a/src/main/java/com/assu/server/domain/certification/repository/QRCertificationRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.assu.server.domain.certification.repository; - -import org.springframework.data.jpa.repository.JpaRepository; - -import com.assu.server.domain.certification.entity.QRCertification; - -public interface QRCertificationRepository extends JpaRepository { -} diff --git a/src/main/java/com/assu/server/domain/certification/service/CertificationService.java b/src/main/java/com/assu/server/domain/certification/service/CertificationService.java index cde9e47c..f66177f5 100644 --- a/src/main/java/com/assu/server/domain/certification/service/CertificationService.java +++ b/src/main/java/com/assu/server/domain/certification/service/CertificationService.java @@ -1,15 +1,16 @@ package com.assu.server.domain.certification.service; -import com.assu.server.domain.certification.dto.CertificationRequestDTO; +import com.assu.server.domain.certification.dto.CertificationGroupRequestDTO; +import com.assu.server.domain.certification.dto.CertificationPersonalRequestDTO; import com.assu.server.domain.certification.dto.CertificationResponseDTO; import com.assu.server.domain.certification.dto.GroupSessionRequest; import com.assu.server.domain.member.entity.Member; public interface CertificationService { - CertificationResponseDTO.getSessionIdResponse getSessionId(CertificationRequestDTO.groupRequest dto, Member member); + CertificationResponseDTO getSessionId(CertificationGroupRequestDTO dto, Member member); void handleCertification(GroupSessionRequest dto, Member member); - void certificatePersonal(CertificationRequestDTO.personalRequest dto, Member member); + void certificatePersonal(CertificationPersonalRequestDTO dto, Member member); } diff --git a/src/main/java/com/assu/server/domain/certification/service/CertificationServiceImpl.java b/src/main/java/com/assu/server/domain/certification/service/CertificationServiceImpl.java index 65f9244c..7898f963 100644 --- a/src/main/java/com/assu/server/domain/certification/service/CertificationServiceImpl.java +++ b/src/main/java/com/assu/server/domain/certification/service/CertificationServiceImpl.java @@ -3,7 +3,7 @@ import java.time.Duration; import java.util.List; - +import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Service; import com.assu.server.domain.admin.entity.Admin; @@ -11,9 +11,9 @@ import com.assu.server.domain.admin.service.AdminService; import com.assu.server.domain.certification.SessionTimeoutManager; import com.assu.server.domain.certification.component.CertificationSessionManager; -import com.assu.server.domain.certification.converter.CertificationConverter; +import com.assu.server.domain.certification.dto.CertificationGroupRequestDTO; +import com.assu.server.domain.certification.dto.CertificationPersonalRequestDTO; import com.assu.server.domain.certification.dto.CertificationProgressResponseDTO; -import com.assu.server.domain.certification.dto.CertificationRequestDTO; import com.assu.server.domain.certification.dto.CertificationResponseDTO; import com.assu.server.domain.certification.dto.GroupSessionRequest; import com.assu.server.domain.certification.entity.AssociateCertification; @@ -25,9 +25,9 @@ import com.assu.server.domain.user.entity.Student; import com.assu.server.global.apiPayload.code.status.ErrorStatus; import com.assu.server.global.exception.GeneralException; + import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; -import org.springframework.messaging.simp.SimpMessagingTemplate; // AdminService 참조, 순환 참조 문제 주의 @Transactional @@ -49,34 +49,34 @@ public class CertificationServiceImpl implements CertificationService { @Override - public CertificationResponseDTO.getSessionIdResponse getSessionId( - CertificationRequestDTO.groupRequest dto, Member member){ + public CertificationResponseDTO getSessionId( + CertificationGroupRequestDTO dto, Member member){ Long userId = member.getId(); // admin id 추출 - Admin admin = adminRepository.findById(dto.getAdminId()).orElseThrow( + Admin admin = adminRepository.findById(dto.adminId()).orElseThrow( () -> new GeneralException(ErrorStatus.NO_SUCH_ADMIN) ); // store id 추출 - Store store = storeRepository.findById(dto.getStoreId()).orElseThrow( + Store store = storeRepository.findById(dto.storeId()).orElseThrow( () -> new GeneralException(ErrorStatus.NO_SUCH_STORE) ); // 세션 생성 및 구독 로직 AssociateCertification ownerCertification = associateCertificationRepository.save( - CertificationConverter.toAssociateCertification(dto, store, member)); + dto.toAssociateCertification(store, member)); Long sessionId = ownerCertification.getId(); sessionManager.openSession(sessionId); // 세션 생성 직후 만료 시간을 5분으로 설정 - timeoutManager.scheduleTimeout(sessionId, Duration.ofMinutes(10));// TODO: 나중에 5분으로 변경 + timeoutManager.scheduleTimeout(sessionId, Duration.ofMinutes(5));// TODO: 나중에 5분으로 변경 - // 세션 여는 대표자는 제일 먼저 인증 + // redis 세션에 그룹 인증 요청자 부터 추가 sessionManager.addUserToSession(sessionId, userId); - return CertificationConverter.toSessionIdResponse(sessionId); + return new CertificationResponseDTO(sessionId); } @@ -85,7 +85,7 @@ public void handleCertification(GroupSessionRequest dto, Member member) { Long userId = member.getId(); // 제휴 대상인지 확인하기 - Long adminId = dto.getAdminId(); + Long adminId = dto.adminId(); Student student = member.getStudentProfile(); List admins = adminService.findMatchingAdmins(student.getUniversity(), student.getDepartment(), student.getMajor()); @@ -98,7 +98,7 @@ public void handleCertification(GroupSessionRequest dto, Member member) { // session 존재 여부 확인 - Long sessionId = dto.getSessionId(); + Long sessionId = dto.sessionId(); AssociateCertification session = associateCertificationRepository.findById(sessionId).orElseThrow( () -> new GeneralException(ErrorStatus.NO_SUCH_SESSION) ); @@ -133,17 +133,13 @@ public void handleCertification(GroupSessionRequest dto, Member member) { } @Override - public void certificatePersonal(CertificationRequestDTO.personalRequest dto, Member member){ + public void certificatePersonal(CertificationPersonalRequestDTO dto, Member member){ // store id 추출 - Store store = storeRepository.findById(dto.getStoreId()).orElseThrow( + Store store = storeRepository.findById(dto.storeId()).orElseThrow( () -> new GeneralException(ErrorStatus.NO_SUCH_STORE) ); - AssociateCertification personalCertificationData = CertificationConverter.toPersonalCertification(dto, store, member); + AssociateCertification personalCertificationData = dto.toPersonalCertification(store, member); associateCertificationRepository.save(personalCertificationData); - } - - - } diff --git a/src/main/java/com/assu/server/domain/partnership/controller/PaperController.java b/src/main/java/com/assu/server/domain/partnership/controller/PaperController.java deleted file mode 100644 index 6ae860c1..00000000 --- a/src/main/java/com/assu/server/domain/partnership/controller/PaperController.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.assu.server.domain.partnership.controller; - -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RestController; - -import com.assu.server.domain.member.entity.Member; -import com.assu.server.domain.member.repository.MemberRepository; -import com.assu.server.domain.partnership.dto.PaperResponseDTO; -import com.assu.server.domain.partnership.service.PaperQueryService; -import com.assu.server.global.apiPayload.BaseResponse; -import com.assu.server.global.apiPayload.code.status.SuccessStatus; -import com.assu.server.global.util.PrincipalDetails; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Parameters; -import io.swagger.v3.oas.annotations.tags.Tag; - -import lombok.RequiredArgsConstructor; - -@RestController -@Tag(name = "제휴 관련 내용 '조회' api", description = "상세 설명") -@RequiredArgsConstructor -public class PaperController { - - private final PaperQueryService paperQueryService; - - @GetMapping("/store/{storeId}/papers") - @Operation(summary = "유저에게 적용 가능한 제휴 컨텐츠 조회", description = "유저가 속한 단과대, 학부 admin_id과 store_id 를 가진 제휴 컨텐츠 제공") - @Parameters({ - @Parameter(name = "storeId", description = "QR에서 추출한 storeId를 입력해주세요") - }) - public ResponseEntity> getStorePaperContent(@PathVariable Long storeId, - @AuthenticationPrincipal PrincipalDetails pd - ) { - PaperResponseDTO.partnershipContent result = paperQueryService.getStorePaperContent(storeId, pd.getMember()); - - return ResponseEntity.ok(BaseResponse.onSuccess(SuccessStatus.PAPER_STORE_HISTORY_SUCCESS, result)); - } - -} diff --git a/src/main/java/com/assu/server/domain/partnership/controller/PartnershipController.java b/src/main/java/com/assu/server/domain/partnership/controller/PartnershipController.java index c8dbd95b..bbcf0b04 100644 --- a/src/main/java/com/assu/server/domain/partnership/controller/PartnershipController.java +++ b/src/main/java/com/assu/server/domain/partnership/controller/PartnershipController.java @@ -1,49 +1,68 @@ package com.assu.server.domain.partnership.controller; +import java.util.List; + +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; -import com.assu.server.domain.member.entity.Member; -import com.assu.server.domain.notification.service.NotificationCommandService; +import com.assu.server.domain.partnership.dto.PartnershipFinalRequestDTO; import com.assu.server.domain.partnership.dto.PartnershipRequestDTO; import com.assu.server.domain.partnership.dto.PartnershipResponseDTO; import com.assu.server.domain.partnership.service.PartnershipService; -import com.assu.server.domain.store.repository.StoreRepository; import com.assu.server.global.apiPayload.BaseResponse; import com.assu.server.global.apiPayload.code.status.SuccessStatus; import com.assu.server.global.util.PrincipalDetails; + import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.RequiredArgsConstructor; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; - -import java.util.List; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; @RestController -@Tag(name = "제휴 요청 api", description = "최종적으로 @@ 제휴를 요청할때 사용하는 api ") +@Tag(name = "제휴 요청 api", description = "최종적으로 제휴를 요청할때 사용하는 api ") @RequiredArgsConstructor @RequestMapping("/partnership") public class PartnershipController { private final PartnershipService partnershipService; - private final NotificationCommandService notificationCommandService; - - private final StoreRepository storeRepository; @PostMapping("/usage") - @Operation(summary= "유저의 인증 후 최종적으로 호출", description = "인증완료 화면 전에 바로 호출되어 유저의 제휴 내역에 데이터가 들어가게 됩니다. (개인 인증인 경우도 포함됩니다.)") + @Operation( + summary = "유저의 인증 후 최종 제휴 데이터 기록 API", + description = "# [v1.0 (2025-12-23)](https://clumsy-seeder-416.notion.site/2681197c19ed8052804eddd5a1f3ce96?source=copy_link)\n" + + "- 인증 완료 화면 전에 호출되어 유저의 제휴 내역에 데이터를 기록합니다.\n" + + "- 개인 인증 케이스도 포함됩니다.\n\n" + + "**Request Body:**\n" + + " - `storeId` (Long, required): 제휴 매장 ID\n" + + " - `tableNumber` (String, required): 테이블 번호\n" + + " - `adminName` (String, required): 관리자 이름\n" + + " - `placeName` (String, required): 제휴 장소 이름\n" + + " - `partnershipContent` (String, required): 제휴 내용\n" + + " - `contentId` (Long, required): 제휴 컨텐츠 ID\n" + + " - `discount` (Long, optional): 할인 금액\n" + + " - `userIds` (List, optional): 인증 대상 유저 ID 목록\n\n" + + "**Response:**\n" + + " - 성공: 200 OK, `isSuccess=true`, `result=null`\n" + + " - 실패: 적절한 에러 코드 및 메시지" + ) public ResponseEntity> finalPartnershipRequest( - @AuthenticationPrincipal PrincipalDetails userDetails,@RequestBody PartnershipRequestDTO.finalRequest dto + @AuthenticationPrincipal PrincipalDetails pd,@RequestBody PartnershipFinalRequestDTO dto ) { - Member member = userDetails.getMember(); - partnershipService.recordPartnershipUsage(dto, member); + + partnershipService.recordPartnershipUsage(dto,pd.getMember()); return ResponseEntity.ok(BaseResponse.onSuccess(SuccessStatus.USER_PAPER_REQUEST_SUCCESS, null)); } diff --git a/src/main/java/com/assu/server/domain/partnership/converter/PartnershipConverter.java b/src/main/java/com/assu/server/domain/partnership/converter/PartnershipConverter.java index e62f2637..b6b600fb 100644 --- a/src/main/java/com/assu/server/domain/partnership/converter/PartnershipConverter.java +++ b/src/main/java/com/assu/server/domain/partnership/converter/PartnershipConverter.java @@ -2,11 +2,14 @@ import java.time.LocalDate; import java.time.LocalDateTime; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; -import com.assu.server.domain.common.entity.BaseEntity; -import com.assu.server.domain.partnership.dto.PaperContentResponseDTO; import com.assu.server.domain.admin.entity.Admin; +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.partnership.dto.PartnershipRequestDTO; @@ -14,27 +17,10 @@ import com.assu.server.domain.partnership.entity.Goods; import com.assu.server.domain.partnership.entity.Paper; import com.assu.server.domain.partnership.entity.PaperContent; -import com.assu.server.domain.partnership.entity.enums.CriterionType; -import com.assu.server.domain.partnership.entity.enums.OptionType; -import com.assu.server.domain.user.entity.PartnershipUsage; -import com.assu.server.domain.user.entity.Student; import com.assu.server.domain.store.entity.Store; public class PartnershipConverter { - public static PartnershipUsage toPartnershipUsage(PartnershipRequestDTO.finalRequest dto, Student student, Long paperId) { - return PartnershipUsage.builder() - .adminName(dto.getAdminName()) - .date(LocalDate.now()) - .place(dto.getPlaceName()) - .student(student) - .paperId(paperId) - .isReviewed(false) - .contentId(dto.getContentId()) - .partnershipContent(dto.getPartnershipContent()) - .build(); - } - public static Paper toDraftPaperEntity(Admin admin, Partner partner, Store store) { return Paper.builder() .admin(admin) @@ -92,99 +78,6 @@ public static List> toGoodsBatches( } - - public static List toContentResponseList(List contents) { - return contents.stream() - .map(PartnershipConverter::toContentResponse) - .toList(); - } - - - public static PaperContentResponseDTO.storePaperContentResponse toContentResponse(PaperContent content) { - List goodsList = extractGoods(content); - Integer peopleValue = extractPeople(content); - - String paperContentText; - if(content.getNote()!= null){ - paperContentText = content.getNote(); - }else{ - paperContentText = buildPaperContentText(content, goodsList, peopleValue); - } - - - return PaperContentResponseDTO.storePaperContentResponse.builder() - .adminId(content.getPaper().getAdmin().getId()) - .adminName(content.getPaper().getAdmin().getName()) - .cost(content.getCost()) - .paperContent(paperContentText) - .contentId(content.getId()) - .goods(goodsList) - .people(peopleValue) - .build(); - } - - - private static List extractGoods(PaperContent content) { - if (content.getOptionType() == OptionType.SERVICE ) { - return content.getGoods().stream() - .map(Goods::getBelonging) - .toList(); - } - return null; - } - - private static Integer extractPeople(PaperContent content) { - if (content.getCriterionType() == CriterionType.HEADCOUNT) { - return content.getPeople(); - } - return null; - } - - private static String buildPaperContentText(PaperContent content, List goodsList, Integer peopleValue) { - String result = ""; - - boolean isGoodsSingle = goodsList != null && goodsList.size() == 1; - boolean isGoodsMultiple = goodsList != null && goodsList.size() > 1; - - // 1. HEADCOUNT + SERVICE + 여러 개 goods - if (content.getCriterionType() == CriterionType.HEADCOUNT && - content.getOptionType() == OptionType.SERVICE && - isGoodsMultiple) { - result = peopleValue + "명 이상 식사 시 " + content.getCategory() + " 제공"; - } - // 2. HEADCOUNT + SERVICE + 단일 goods - else if (content.getCriterionType() == CriterionType.HEADCOUNT && - content.getOptionType() == OptionType.SERVICE && - isGoodsSingle) { - result = peopleValue + "명 이상 식사 시 " + goodsList.get(0) + " 제공"; - } - // 3. HEADCOUNT + DISCOUNT - else if (content.getCriterionType() == CriterionType.HEADCOUNT && - content.getOptionType() == OptionType.DISCOUNT) { - result = peopleValue + "명 이상 식사 시 " + content.getDiscount() + "% 할인"; - } - // 4. PRICE + SERVICE + 여러 개 goods - else if (content.getCriterionType() == CriterionType.PRICE && - content.getOptionType() == OptionType.SERVICE && - isGoodsMultiple) { - result = content.getCost() + "원 이상 주문 시 " + content.getCategory() + " 제공"; - } - // 5. PRICE + SERVICE + 단일 goods - else if (content.getCriterionType() == CriterionType.PRICE && - content.getOptionType() == OptionType.SERVICE && - isGoodsSingle) { - result = content.getCost() + "원 이상 주문 시 " + goodsList.get(0) + " 제공"; - } - // 6. PRICE + DISCOUNT - else if (content.getCriterionType() == CriterionType.PRICE && - content.getOptionType() == OptionType.DISCOUNT) { - result = content.getCost() + "원 이상 주문 시 " + content.getDiscount() + "% 할인"; - } - - return result; - } - - public static Paper toPaperForManual( Admin admin, Store store, LocalDate start, LocalDate end, diff --git a/src/main/java/com/assu/server/domain/partnership/dto/PaperContentRequestDTO.java b/src/main/java/com/assu/server/domain/partnership/dto/PaperContentRequestDTO.java deleted file mode 100644 index c7596636..00000000 --- a/src/main/java/com/assu/server/domain/partnership/dto/PaperContentRequestDTO.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.assu.server.domain.partnership.dto; - -public class PaperContentRequestDTO { -} diff --git a/src/main/java/com/assu/server/domain/partnership/dto/PaperContentResponseDTO.java b/src/main/java/com/assu/server/domain/partnership/dto/PaperContentResponseDTO.java deleted file mode 100644 index 46dd3255..00000000 --- a/src/main/java/com/assu/server/domain/partnership/dto/PaperContentResponseDTO.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.assu.server.domain.partnership.dto; - -import java.util.List; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -public class PaperContentResponseDTO { - @Builder - @Getter - @NoArgsConstructor - @AllArgsConstructor - public static class storePaperContentResponse{ - Long adminId; - String adminName; - String paperContent; - Long contentId; - List goods; - Integer people; - Long cost; - } -} diff --git a/src/main/java/com/assu/server/domain/partnership/dto/PaperResponseDTO.java b/src/main/java/com/assu/server/domain/partnership/dto/PaperResponseDTO.java deleted file mode 100644 index 85427c94..00000000 --- a/src/main/java/com/assu/server/domain/partnership/dto/PaperResponseDTO.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.assu.server.domain.partnership.dto; - -import java.util.List; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -public class PaperResponseDTO { - @Builder - @Getter - @NoArgsConstructor - @AllArgsConstructor - public static class partnershipContent{ - String storeName; - Long storeId; - List contents; - } -} diff --git a/src/main/java/com/assu/server/domain/partnership/dto/PartnershipFinalRequestDTO.java b/src/main/java/com/assu/server/domain/partnership/dto/PartnershipFinalRequestDTO.java new file mode 100644 index 00000000..cc4af6dc --- /dev/null +++ b/src/main/java/com/assu/server/domain/partnership/dto/PartnershipFinalRequestDTO.java @@ -0,0 +1,55 @@ +package com.assu.server.domain.partnership.dto; + +import java.time.LocalDate; +import java.util.List; + +import com.assu.server.domain.user.entity.PartnershipUsage; +import com.assu.server.domain.user.entity.Student; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public record PartnershipFinalRequestDTO( + + @NotNull(message="storeId를 입력해주세요") + @Schema(description="storeId 입력") + Long storeId, + + @Schema(description = "adminId 입력") + @NotNull(message="adminId를 입력해주세요") + Long adminId, + + @Schema(description = "00~99 사이의 tableNumber를 입력해주세요") + @NotNull(message = "00~99 사이의 tableNumber를 입력해주세요") + String tableNumber, + @Schema(description = "@@학생회 이름 그대로 입력해주세요") + @NotBlank(message = "학생회 이름을 '~~학생회' 와 같이 입력해주세요") + String adminName, + @Schema(description = "가게 명을 입력해주세요") + @NotBlank(message="가게 명을 입력해주세요") + String placeName, + @Schema(description = "제휴 항목을 그대로 입력해주세요") + @NotBlank(message = "받은 제휴 항목을 그대로 입력해주세요") + String partnershipContent, + @Schema(description = "제휴 콘텐츠 아이디를 입력해주세요") + @NotNull(message="제휴 콘텐츠 아이디를 입력해주세요") + Long contentId, + + Long discount, // 이거 사실 필요 없는건데 나중에 db 옮길때 한꺼번에 처리하려구요 + @Schema(description = "함께 인증한 유저들의 아이디를 입력해주세요(없을 시 공란)") + ListuserIds +) { + public PartnershipUsage toPartnershipUsage(Student student, Long paperId) { + return PartnershipUsage.builder() + .adminName(this.adminName()) + .date(LocalDate.now()) + .place(this.placeName()) + .student(student) + .paperId(paperId) + .isReviewed(false) + .contentId(this.contentId()) + .partnershipContent(this.partnershipContent()) + .build(); + } +} diff --git a/src/main/java/com/assu/server/domain/partnership/dto/PartnershipRequestDTO.java b/src/main/java/com/assu/server/domain/partnership/dto/PartnershipRequestDTO.java index 65dea839..dfbc7edf 100644 --- a/src/main/java/com/assu/server/domain/partnership/dto/PartnershipRequestDTO.java +++ b/src/main/java/com/assu/server/domain/partnership/dto/PartnershipRequestDTO.java @@ -12,17 +12,6 @@ import java.time.LocalDate; public class PartnershipRequestDTO { - @Getter - public static class finalRequest{ - Long storeId; - String tableNumber; - String adminName; - String placeName; - String partnershipContent; - Long contentId; - Long discount; - List userIds; - } @Getter public static class WritePartnershipRequestDTO { diff --git a/src/main/java/com/assu/server/domain/partnership/repository/GoodsRepository.java b/src/main/java/com/assu/server/domain/partnership/repository/GoodsRepository.java index 389f700b..66f970b5 100644 --- a/src/main/java/com/assu/server/domain/partnership/repository/GoodsRepository.java +++ b/src/main/java/com/assu/server/domain/partnership/repository/GoodsRepository.java @@ -1,22 +1,18 @@ package com.assu.server.domain.partnership.repository; import java.util.List; -import java.util.Optional; -import com.assu.server.domain.partnership.entity.Goods; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -import java.util.List; +import com.assu.server.domain.partnership.entity.Goods; public interface GoodsRepository extends JpaRepository { List findByContentId(Long contentId); - List findByContentIdIn(List contentIds); - @Modifying @Query("delete from Goods g where g.content.id in :contentIds") void deleteAllByContentIds(@Param("contentIds") List contentIds); diff --git a/src/main/java/com/assu/server/domain/partnership/repository/PaperContentRepository.java b/src/main/java/com/assu/server/domain/partnership/repository/PaperContentRepository.java index 945d3b81..18ddf5f9 100644 --- a/src/main/java/com/assu/server/domain/partnership/repository/PaperContentRepository.java +++ b/src/main/java/com/assu/server/domain/partnership/repository/PaperContentRepository.java @@ -1,15 +1,13 @@ package com.assu.server.domain.partnership.repository; -import com.assu.server.domain.common.enums.ActivationStatus; -import com.assu.server.domain.partnership.entity.PaperContent; -import com.assu.server.domain.partnership.entity.enums.CriterionType; -import com.assu.server.domain.partnership.entity.enums.OptionType; +import java.util.List; +import java.util.Optional; + import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -import java.util.List; -import java.util.Optional; +import com.assu.server.domain.partnership.entity.PaperContent; public interface PaperContentRepository extends JpaRepository { diff --git a/src/main/java/com/assu/server/domain/partnership/repository/PaperRepository.java b/src/main/java/com/assu/server/domain/partnership/repository/PaperRepository.java index 8c67b899..a368fe45 100644 --- a/src/main/java/com/assu/server/domain/partnership/repository/PaperRepository.java +++ b/src/main/java/com/assu/server/domain/partnership/repository/PaperRepository.java @@ -1,9 +1,9 @@ package com.assu.server.domain.partnership.repository; -import com.assu.server.domain.admin.entity.Admin; -import com.assu.server.domain.common.enums.ActivationStatus; -import com.assu.server.domain.partner.entity.Partner; -import com.assu.server.domain.partnership.entity.Paper; +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @@ -11,9 +11,8 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -import java.time.LocalDate; -import java.util.List; -import java.util.Optional; +import com.assu.server.domain.common.enums.ActivationStatus; +import com.assu.server.domain.partnership.entity.Paper; public interface PaperRepository extends JpaRepository { diff --git a/src/main/java/com/assu/server/domain/partnership/service/PaperContentService.java b/src/main/java/com/assu/server/domain/partnership/service/PaperContentService.java deleted file mode 100644 index 2c2051a8..00000000 --- a/src/main/java/com/assu/server/domain/partnership/service/PaperContentService.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.assu.server.domain.partnership.service; - -import org.springframework.data.jpa.repository.JpaRepository; - -import com.assu.server.domain.partnership.entity.PaperContent; - -public interface PaperContentService { - -} diff --git a/src/main/java/com/assu/server/domain/partnership/service/PaperContentServiceImpl.java b/src/main/java/com/assu/server/domain/partnership/service/PaperContentServiceImpl.java deleted file mode 100644 index 38720e52..00000000 --- a/src/main/java/com/assu/server/domain/partnership/service/PaperContentServiceImpl.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.assu.server.domain.partnership.service; - -import org.springframework.stereotype.Service; - -import jakarta.transaction.Transactional; -import lombok.RequiredArgsConstructor; - - -@Service -@RequiredArgsConstructor -public class PaperContentServiceImpl implements PaperContentService { -} diff --git a/src/main/java/com/assu/server/domain/partnership/service/PaperQueryService.java b/src/main/java/com/assu/server/domain/partnership/service/PaperQueryService.java deleted file mode 100644 index bdc9ebe7..00000000 --- a/src/main/java/com/assu/server/domain/partnership/service/PaperQueryService.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.assu.server.domain.partnership.service; - -import com.assu.server.domain.member.entity.Member; -import com.assu.server.domain.partnership.dto.PaperResponseDTO; - -public interface PaperQueryService { - PaperResponseDTO.partnershipContent getStorePaperContent(Long storeId, Member member); -} diff --git a/src/main/java/com/assu/server/domain/partnership/service/PaperQueryServiceImpl.java b/src/main/java/com/assu/server/domain/partnership/service/PaperQueryServiceImpl.java deleted file mode 100644 index 8d32e652..00000000 --- a/src/main/java/com/assu/server/domain/partnership/service/PaperQueryServiceImpl.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.assu.server.domain.partnership.service; - -import java.util.List; - -import org.springframework.stereotype.Service; - -import com.assu.server.domain.admin.entity.Admin; -import com.assu.server.domain.admin.service.AdminService; -import com.assu.server.domain.member.entity.Member; -import com.assu.server.domain.common.enums.ActivationStatus; -import com.assu.server.domain.common.enums.UserRole; -import com.assu.server.domain.partnership.converter.PartnershipConverter; -import com.assu.server.domain.partnership.dto.PaperContentResponseDTO; -import com.assu.server.domain.partnership.dto.PaperResponseDTO; -import com.assu.server.domain.partnership.entity.Paper; -import com.assu.server.domain.partnership.entity.PaperContent; -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.repository.StoreRepository; -import com.assu.server.domain.user.entity.Student; -import com.assu.server.global.apiPayload.code.status.ErrorStatus; -import com.assu.server.global.exception.GeneralException; - -import jakarta.transaction.Transactional; -import lombok.RequiredArgsConstructor; - -@Service -@RequiredArgsConstructor -@Transactional -public class PaperQueryServiceImpl implements PaperQueryService { - - private final AdminService adminService; - private final PaperRepository paperRepository; - private final PaperContentRepository contentRepository; - private final StoreRepository storeRepository; - - @Override - public PaperResponseDTO.partnershipContent getStorePaperContent(Long storeId, Member member){ - - // 역할이 학생이 아닌 경우 : 이미 type별로 ui를 분기 시켜놔서 그럴일 없을 것 같긴 하지만 혹시 몰라서 처리함 - if(member.getRole() != UserRole.STUDENT) - throw new GeneralException(ErrorStatus.NO_STUDENT_TYPE); - - Student student = member.getStudentProfile(); - - // 유저의 학교, 단과대, 학부 정보를 조회하여 일치하는 admin을 찾습니다. - List adminList = adminService.findMatchingAdmins( - student.getUniversity(), - student.getDepartment(), - student.getMajor()); - - // 추출한 admin, store와 일치하는 paperId 를 추출합니다. - List paperList = adminList.stream() - .flatMap(admin -> - paperRepository.findByStoreIdAndAdminIdAndStatus(storeId, admin.getId(), ActivationStatus.ACTIVE) - .stream()).toList(); - - //paperId로 paperContent 를 조회 - List contentList = paperList.stream() - .flatMap(paper-> - contentRepository.findByPaperId(paper.getId()).stream() - ).toList(); - - - - Store store = storeRepository.findById(storeId).orElseThrow( - () -> new GeneralException(ErrorStatus.NO_SUCH_STORE) - ); - - // dto 변환 - List contents = - PartnershipConverter.toContentResponseList(contentList); - - // partnershipContent DTO 생성 - return PaperResponseDTO.partnershipContent.builder() - .storeName(store.getName()) - .storeId(storeId) - .contents(contents) - .build(); - - - } - -} diff --git a/src/main/java/com/assu/server/domain/partnership/service/PartnershipService.java b/src/main/java/com/assu/server/domain/partnership/service/PartnershipService.java index 783a57a6..1f8f4455 100644 --- a/src/main/java/com/assu/server/domain/partnership/service/PartnershipService.java +++ b/src/main/java/com/assu/server/domain/partnership/service/PartnershipService.java @@ -1,6 +1,7 @@ package com.assu.server.domain.partnership.service; import com.assu.server.domain.member.entity.Member; +import com.assu.server.domain.partnership.dto.PartnershipFinalRequestDTO; import com.assu.server.domain.partnership.dto.PartnershipRequestDTO; import com.assu.server.domain.partnership.dto.PartnershipResponseDTO; import org.springframework.web.multipart.MultipartFile; @@ -15,7 +16,7 @@ PartnershipResponseDTO.WritePartnershipResponseDTO updatePartnership( Long memberId ); - void recordPartnershipUsage(PartnershipRequestDTO.finalRequest dto, Member member); + void recordPartnershipUsage(PartnershipFinalRequestDTO dto, Member member); // 제휴업체/관리자 맺은 제휴 리스트 List listPartnershipsForAdmin(boolean all, Long partnerId); diff --git a/src/main/java/com/assu/server/domain/partnership/service/PartnershipServiceImpl.java b/src/main/java/com/assu/server/domain/partnership/service/PartnershipServiceImpl.java index 76679a2b..c4899c3a 100644 --- a/src/main/java/com/assu/server/domain/partnership/service/PartnershipServiceImpl.java +++ b/src/main/java/com/assu/server/domain/partnership/service/PartnershipServiceImpl.java @@ -13,6 +13,7 @@ import com.assu.server.domain.member.entity.Member; import com.assu.server.domain.notification.service.NotificationCommandService; import com.assu.server.domain.partnership.converter.PartnershipConverter; +import com.assu.server.domain.partnership.dto.PartnershipFinalRequestDTO; import com.assu.server.domain.partnership.dto.PartnershipRequestDTO; import com.assu.server.domain.user.entity.PartnershipUsage; import com.assu.server.domain.user.entity.Student; @@ -63,9 +64,9 @@ public class PartnershipServiceImpl implements PartnershipService { @Override @Transactional - public void recordPartnershipUsage(PartnershipRequestDTO.finalRequest dto, Member member) { + public void recordPartnershipUsage(PartnershipFinalRequestDTO dto, Member member) { // 1. 제휴 내용(PaperContent) 조회 - PaperContent content = contentRepository.findById(dto.getContentId()).orElseThrow( + PaperContent content = contentRepository.findById(dto.contentId()).orElseThrow( () -> new GeneralException(ErrorStatus.NO_SUCH_CONTENT) ); Long paperId = content.getPaper().getId(); @@ -75,8 +76,8 @@ public void recordPartnershipUsage(PartnershipRequestDTO.finalRequest dto, Membe // 요청자 본인 ID 추가 uniqueUserIds.add(member.getId()); // DTO에 포함된 사용자 ID들 추가 (null일 경우 무시) - if (dto.getUserIds() != null) { - uniqueUserIds.addAll(dto.getUserIds()); + if (dto.userIds() != null) { + uniqueUserIds.addAll(dto.userIds()); } // 3. 모든 학생 정보를 DB에서 한 번의 쿼리로 조회 (N+1 문제 해결) @@ -86,18 +87,18 @@ public void recordPartnershipUsage(PartnershipRequestDTO.finalRequest dto, Membe List usages = studentsToUpdate.stream() .map(student -> { student.setStamp(); - return PartnershipConverter.toPartnershipUsage(dto, student, paperId); + return dto.toPartnershipUsage(student, paperId); }) .collect(Collectors.toList()); // 5. 생성된 모든 Usage 기록을 한 번에 저장 partnershipUsageRepository.saveAll(usages); - Store store = storeRepository.findById(dto.getStoreId()).orElseThrow( + Store store = storeRepository.findById(dto.storeId()).orElseThrow( () -> new GeneralException(ErrorStatus.NO_SUCH_STORE) ); Partner partner = store.getPartner(); Long partnerId = partner.getId(); - notificationService.sendOrder(partnerId, 0L, dto.getTableNumber(), dto.getPartnershipContent()); + notificationService.sendOrder(partnerId, 0L, dto.tableNumber(), dto.partnershipContent()); // @Transactional 환경에서는 studentsToUpdate의 변경 사항(스탬프)이 자동으로 DB에 반영됩니다. } diff --git a/src/main/java/com/assu/server/domain/store/controller/StoreController.java b/src/main/java/com/assu/server/domain/store/controller/StoreController.java index 51fd8668..156a667b 100644 --- a/src/main/java/com/assu/server/domain/store/controller/StoreController.java +++ b/src/main/java/com/assu/server/domain/store/controller/StoreController.java @@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.assu.server.domain.store.dto.StoreResponseDTO; +import com.assu.server.domain.store.dto.TodayBestResponseDTO; import com.assu.server.domain.store.service.StoreService; import com.assu.server.global.apiPayload.BaseResponse; import com.assu.server.global.apiPayload.code.status.SuccessStatus; @@ -26,17 +27,30 @@ public class StoreController { private final StoreService storeService; @GetMapping("/best") - @Operation(summary = "홈화면의 현재 인기 매장 조회 api", description = "관리자, 사용자, 제휴업체 모두 사용하는 api") - public ResponseEntity> getTodayBestStore() { - StoreResponseDTO.todayBest result = storeService.getTodayBestStore(); + @Operation( + summary = "오늘의 인기 매장 조회 API", + description = + "# [v1.0 (2025-12-23)](https://clumsy-seeder-416.notion.site/Today-22b1197c19ed80aebfc3e6b337d02ece?source=copy_link)\n" + + "- 오늘 기준 인기 매장 목록을 조회합니다.\n" + + "- 로그인 필요 없음\n" + + "\n**Response:**\n" + + " - `bestStores` (List): 오늘 가장 인기 있는 매장 이름 목록" + ) + public ResponseEntity> getTodayBestStore() { + TodayBestResponseDTO result = storeService.getTodayBestStore(); return ResponseEntity.ok(BaseResponse.onSuccess(SuccessStatus.BEST_STORE_SUCCESS, result)); } - @Operation( - summary = "내 가게 순위 조회 API", - description = "partnerId로 접근해주세요." - ) + @Operation( + summary = "내 가게 순위 조회 API", + description = "# [v1.0 (2025-12-23)](https://www.notion.so/내-가게-순위-API_문서)\n" + + "- partnerId로 접근 가능합니다.\n" + + "- 로그인된 파트너만 조회 가능\n\n" + + "**Response:**\n" + + " - `rank` (Long): 그 주 순위 (1부터)\n" + + " - `usageCount` (Long): 그 주 사용 건수" + ) @GetMapping("/ranking") public ResponseEntity> getWeeklyRank( @AuthenticationPrincipal PrincipalDetails pd) { diff --git a/src/main/java/com/assu/server/domain/store/dto/TodayBestResponseDTO.java b/src/main/java/com/assu/server/domain/store/dto/TodayBestResponseDTO.java new file mode 100644 index 00000000..58ae9c6c --- /dev/null +++ b/src/main/java/com/assu/server/domain/store/dto/TodayBestResponseDTO.java @@ -0,0 +1,8 @@ +package com.assu.server.domain.store.dto; + +import java.util.List; + +public record TodayBestResponseDTO( + List bestStores +) { +} diff --git a/src/main/java/com/assu/server/domain/store/service/StoreService.java b/src/main/java/com/assu/server/domain/store/service/StoreService.java index d21b3ee5..072ea843 100644 --- a/src/main/java/com/assu/server/domain/store/service/StoreService.java +++ b/src/main/java/com/assu/server/domain/store/service/StoreService.java @@ -1,10 +1,11 @@ package com.assu.server.domain.store.service; import com.assu.server.domain.partnership.dto.PartnershipResponseDTO; import com.assu.server.domain.store.dto.StoreResponseDTO; +import com.assu.server.domain.store.dto.TodayBestResponseDTO; import com.assu.server.domain.user.dto.StudentResponseDTO; public interface StoreService { - StoreResponseDTO.todayBest getTodayBestStore(); + TodayBestResponseDTO getTodayBestStore(); StoreResponseDTO.WeeklyRankResponseDTO getWeeklyRank(Long memberId); StoreResponseDTO.ListWeeklyRankResponseDTO getListWeeklyRank(Long memberId); } diff --git a/src/main/java/com/assu/server/domain/store/service/StoreServiceImpl.java b/src/main/java/com/assu/server/domain/store/service/StoreServiceImpl.java index 232c21ba..5430d6f0 100644 --- a/src/main/java/com/assu/server/domain/store/service/StoreServiceImpl.java +++ b/src/main/java/com/assu/server/domain/store/service/StoreServiceImpl.java @@ -4,6 +4,7 @@ import java.util.List; import org.springframework.stereotype.Service; import com.assu.server.domain.store.dto.StoreResponseDTO; +import com.assu.server.domain.store.dto.TodayBestResponseDTO; import com.assu.server.domain.store.repository.StoreRepository; import com.assu.server.domain.user.repository.PartnershipUsageRepository; import jakarta.transaction.Transactional; @@ -25,13 +26,11 @@ public class StoreServiceImpl implements StoreService { @Override @Transactional - public StoreResponseDTO.todayBest getTodayBestStore() { + public TodayBestResponseDTO getTodayBestStore() { List bestStores = storeRepository.findTodayBestStoreNames(); - - return StoreResponseDTO.todayBest.builder() - .bestStores(bestStores) - .build(); + return new TodayBestResponseDTO(bestStores); } + @Override @Transactional public StoreResponseDTO.WeeklyRankResponseDTO getWeeklyRank(Long memberId) { diff --git a/src/main/java/com/assu/server/domain/user/controller/StudentController.java b/src/main/java/com/assu/server/domain/user/controller/StudentController.java index ff98eaa8..11f068d3 100644 --- a/src/main/java/com/assu/server/domain/user/controller/StudentController.java +++ b/src/main/java/com/assu/server/domain/user/controller/StudentController.java @@ -1,28 +1,27 @@ package com.assu.server.domain.user.controller; +import java.util.List; + import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import com.assu.server.domain.member.entity.Member; import com.assu.server.domain.user.dto.StudentResponseDTO; +import com.assu.server.domain.user.service.StudentService; import com.assu.server.global.apiPayload.BaseResponse; +import com.assu.server.global.apiPayload.code.status.SuccessStatus; import com.assu.server.global.util.PrincipalDetails; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; -import com.assu.server.domain.user.dto.StudentResponseDTO; -import com.assu.server.domain.user.service.StudentService; -import com.assu.server.global.apiPayload.BaseResponse; -import com.assu.server.global.apiPayload.code.status.SuccessStatus; -import org.springframework.web.bind.annotation.*; - -import java.util.List; @RestController @Tag(name = "유저 관련 api", description = "유저와 관련된 로직을 처리하는 api") @@ -45,10 +44,10 @@ public class StudentController { " - 해당 storeId, storeName 반환"+ " - 해당 월에 사용한 제휴 수 반환" ) - public ResponseEntity> getMyPartnership( + public ResponseEntity> getMyPartnership( @PathVariable int year, @PathVariable int month, @AuthenticationPrincipal PrincipalDetails pd ){ - StudentResponseDTO.myPartnership result = studentService.getMyPartnership(pd.getId(), year, month); + StudentResponseDTO.MyPartnership result = studentService.getMyPartnership(pd.getId(), year, month); return ResponseEntity.ok(BaseResponse.onSuccess(SuccessStatus.PARTNERSHIP_HISTORY_SUCCESS, result)); } @@ -67,7 +66,7 @@ public ResponseEntity> getMyPartn " - StudentResponseTO.UsageDetailDTO 객체 반환 \n" ) - public ResponseEntity>> getUnreviewedUsage( + public ResponseEntity>> getUnreviewedUsage( @AuthenticationPrincipal PrincipalDetails pd, Pageable pageable ){ diff --git a/src/main/java/com/assu/server/domain/user/dto/StudentResponseDTO.java b/src/main/java/com/assu/server/domain/user/dto/StudentResponseDTO.java index d415f75a..3c62db03 100644 --- a/src/main/java/com/assu/server/domain/user/dto/StudentResponseDTO.java +++ b/src/main/java/com/assu/server/domain/user/dto/StudentResponseDTO.java @@ -1,62 +1,33 @@ package com.assu.server.domain.user.dto; -import java.time.LocalDate; -import java.time.LocalDateTime; import java.util.List; import com.assu.server.domain.partnership.entity.enums.CriterionType; import com.assu.server.domain.partnership.entity.enums.OptionType; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.RequiredArgsConstructor; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import java.time.LocalDate; -import java.time.LocalDateTime; - public class StudentResponseDTO { - @Getter - @Builder - @AllArgsConstructor - @RequiredArgsConstructor - public static class myPartnership { - private long serviceCount; - private List details; - } - - @Getter - @AllArgsConstructor - @Builder - public static class UsageDetailDTO { - private String adminName; - private Long partnershipUsageId; - private String storeName; - private Long partnerId; - private Long storeId; - private String usedAt; - private String benefitDescription; - private boolean isReviewed; - } - /* @Getter - @NoArgsConstructor - @AllArgsConstructor - @Builder - public static class CheckPartnershipUsageResponseDTO { - private Long id; - private String place; - private LocalDate date; - private String partnershipContent; - private Boolean isReviewed; //리뷰 작성하기 버튼 활성화 ? - private Integer discount; //가격? 비율 - private LocalDateTime createdAt; - } - */ + public record MyPartnership ( + long serviceCount, + List details + ){} + + + public record UsageDetail ( + String adminName, + Long partnershipUsageId, + String storeName, + Long partnerId, + Long storeId, + String usedAt, + String benefitDescription, + boolean isReviewed + ){} @Getter @NoArgsConstructor diff --git a/src/main/java/com/assu/server/domain/user/repository/StudentRepository.java b/src/main/java/com/assu/server/domain/user/repository/StudentRepository.java index 625d4fbb..3762339e 100644 --- a/src/main/java/com/assu/server/domain/user/repository/StudentRepository.java +++ b/src/main/java/com/assu/server/domain/user/repository/StudentRepository.java @@ -1,15 +1,8 @@ package com.assu.server.domain.user.repository; - -import java.util.Optional; - import org.springframework.data.jpa.repository.JpaRepository; import com.assu.server.domain.user.entity.Student; public interface StudentRepository extends JpaRepository { - - Optional findStudentById(Long id); - - } diff --git a/src/main/java/com/assu/server/domain/user/service/StudentService.java b/src/main/java/com/assu/server/domain/user/service/StudentService.java index 5076c46e..dd671fc6 100644 --- a/src/main/java/com/assu/server/domain/user/service/StudentService.java +++ b/src/main/java/com/assu/server/domain/user/service/StudentService.java @@ -1,16 +1,16 @@ package com.assu.server.domain.user.service; +import java.util.List; + import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import com.assu.server.domain.user.dto.StudentResponseDTO; -import java.util.List; - public interface StudentService { - StudentResponseDTO.myPartnership getMyPartnership(Long studentId, int year, int month); + StudentResponseDTO.MyPartnership getMyPartnership(Long studentId, int year, int month); StudentResponseDTO.CheckStampResponseDTO getStamp(Long memberId);//조회 - Page getUnreviewedUsage(Long memberId, Pageable pageable); + Page getUnreviewedUsage(Long memberId, Pageable pageable); List getUsablePartnership(Long memberId, Boolean all); void syncUserPapersForAllStudents(); } diff --git a/src/main/java/com/assu/server/domain/user/service/StudentServiceImpl.java b/src/main/java/com/assu/server/domain/user/service/StudentServiceImpl.java index a13eb1fd..5b68e1e8 100644 --- a/src/main/java/com/assu/server/domain/user/service/StudentServiceImpl.java +++ b/src/main/java/com/assu/server/domain/user/service/StudentServiceImpl.java @@ -5,10 +5,14 @@ import java.time.format.DateTimeFormatter; import java.util.List; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + import com.assu.server.domain.admin.entity.Admin; import com.assu.server.domain.admin.repository.AdminRepository; import com.assu.server.domain.common.enums.ActivationStatus; -import com.assu.server.domain.partner.entity.Partner; import com.assu.server.domain.partnership.entity.Goods; import com.assu.server.domain.partnership.entity.Paper; import com.assu.server.domain.partnership.entity.PaperContent; @@ -27,13 +31,9 @@ import com.assu.server.domain.user.repository.UserPaperRepository; import com.assu.server.global.apiPayload.code.status.ErrorStatus; import com.assu.server.global.exception.DatabaseException; -import jakarta.transaction.Transactional; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import lombok.RequiredArgsConstructor; @Service @RequiredArgsConstructor @@ -55,44 +55,51 @@ public StudentResponseDTO.CheckStampResponseDTO getStamp(Long memberId) { return StudentConverter.checkStampResponseDTO(student, "스탬프 조회 성공"); } + @Override - @Transactional - public StudentResponseDTO.myPartnership getMyPartnership(Long studentId, int year, int month) { - List usages = partnershipUsageRepository.findByYearAndMonth(studentId, year, month); + @Transactional(readOnly=true) + public StudentResponseDTO.MyPartnership getMyPartnership(Long studentId, int year, int month) { + + List usages = + partnershipUsageRepository.findByYearAndMonth(studentId, year, month); - return StudentResponseDTO.myPartnership.builder() - .serviceCount(usages.size()) - .details(usages.stream() + List details = + usages.stream() .map(u -> { - // 1. partnershipUsage의 paperContentId로 paperContent를 조회합니다. - // findById는 Optional을 반환하므로, orElse(null)로 처리합니다. - PaperContent paperContent = paperContentRepository.findById(u.getContentId()) + PaperContent paperContent = paperContentRepository + .findById(u.getContentId()) .orElse(null); - // 2. PaperContent에서 storeId를 가져옵니다. - Store store = (paperContent != null) ? paperContent.getPaper().getStore() : null; - LocalDateTime ld= u.getCreatedAt(); - String formatDate =ld.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); - - return StudentResponseDTO.UsageDetailDTO.builder() - .partnershipUsageId(u.getId()) - .adminName(u.getAdminName()) - .storeName(u.getPlace()) - .usedAt(formatDate) - .benefitDescription(u.getPartnershipContent()) - .isReviewed(u.getIsReviewed()) - .storeId(store.getId()) // 3. storeId를 DTO에 매핑합니다. - .partnerId(store.getPartner().getId()) - .build(); - }).toList() - ) - .build(); + Store store = paperContent != null + ? paperContent.getPaper().getStore() + : null; + + String formatDate = u.getCreatedAt() + .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); + + return new StudentResponseDTO.UsageDetail( + u.getAdminName(), + u.getId(), + u.getPlace(), + store != null ? store.getPartner().getId() : null, + store != null ? store.getId() : null, + formatDate, + u.getPartnershipContent(), + u.getIsReviewed() + ); + }) + .toList(); + + return new StudentResponseDTO.MyPartnership( + usages.size(), + details + ); } @Override @Transactional - public Page getUnreviewedUsage(Long memberId, Pageable pageable) { + public Page getUnreviewedUsage(Long memberId, Pageable pageable) { // 프론트에서 1-based 페이지를 보낸 경우 0-based 로 보정 pageable = PageRequest.of( Math.max(pageable.getPageNumber() - 1, 0), @@ -115,16 +122,15 @@ public Page getUnreviewedUsage(Long memberId, LocalDateTime ld = u.getCreatedAt(); String formatDate = ld.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); - return StudentResponseDTO.UsageDetailDTO.builder() - .partnershipUsageId(u.getId()) - .adminName(u.getAdminName()) - .storeName(u.getPlace()) - .usedAt(formatDate) - .benefitDescription(u.getPartnershipContent()) - .isReviewed(u.getIsReviewed()) - .storeId((store != null) ? store.getId() : null) // store null 체크 - .partnerId((store != null && store.getPartner() != null) ? store.getPartner().getId() : null) - .build(); + return new StudentResponseDTO.UsageDetail( + u.getAdminName(), + u.getId(), + u.getPlace(), + (store != null && store.getPartner() != null) ? store.getPartner().getId() : null, + (store!= null)? store.getId(): null, formatDate, + u.getPartnershipContent(), + u.getIsReviewed()); + }); } diff --git a/src/main/java/com/assu/server/global/util/PrincipalDetails.java b/src/main/java/com/assu/server/global/util/PrincipalDetails.java index dc7582be..121342e0 100644 --- a/src/main/java/com/assu/server/global/util/PrincipalDetails.java +++ b/src/main/java/com/assu/server/global/util/PrincipalDetails.java @@ -1,23 +1,20 @@ package com.assu.server.global.util; -import com.assu.server.domain.member.entity.Member; +import java.util.Collection; +import java.util.List; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + import com.assu.server.domain.auth.entity.AuthRealm; import com.assu.server.domain.common.enums.ActivationStatus; import com.assu.server.domain.common.enums.UserRole; import com.assu.server.domain.member.entity.Member; + import lombok.Builder; import lombok.Getter; - import lombok.RequiredArgsConstructor; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; - -import java.util.ArrayList; - -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; @Getter @Builder