From 633ecc3d9e0bfdd222973abd8f92d993a8def6a5 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 11 Nov 2024 20:17:13 +0900 Subject: [PATCH 001/563] =?UTF-8?q?=ED=8F=AC=ED=8A=B8=20=EB=B2=88=ED=98=B8?= =?UTF-8?q?=20default=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 3a6d4fd8..94f0cd80 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,6 +1,5 @@ server: forward-headers-strategy: native - port: 7777 spring: application: From e1083da93b779962c78b20521592cb1689038c0c Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 11 Nov 2024 20:19:08 +0900 Subject: [PATCH 002/563] =?UTF-8?q?=ED=8F=AC=ED=8A=B8=20=EB=B2=88=ED=98=B8?= =?UTF-8?q?=20default=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 94f0cd80..f4ce885d 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,6 +1,5 @@ server: forward-headers-strategy: native - spring: application: name: ${SPRING_APP_NAME} From 5e4021c77520aa315e690ef965b1b953219c4057 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 11 Nov 2024 20:19:16 +0900 Subject: [PATCH 003/563] =?UTF-8?q?=ED=8F=AC=ED=8A=B8=20=EB=B2=88=ED=98=B8?= =?UTF-8?q?=20default=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f4ce885d..94f0cd80 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,5 +1,6 @@ server: forward-headers-strategy: native + spring: application: name: ${SPRING_APP_NAME} From 6f66f32cd9bfb63040e6a9503d4591f54fbc35bf Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Mon, 11 Nov 2024 20:22:16 +0900 Subject: [PATCH 004/563] =?UTF-8?q?feat:=20=EC=9D=BC=EC=A0=95=ED=91=9C=20C?= =?UTF-8?q?UD=20=EC=88=98=EC=A0=95=20(#3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ScheduleController.java | 53 +++++++++++++++---- .../response/ScheduleRegistResponseDTO.java | 21 -------- .../service/ScheduleCommandService.java | 6 +-- .../service/ScheduleCommandServiceImpl.java | 49 ++++++++++------- .../schedule/common/exception/ErrorCode.java | 5 +- .../global/exception/CommonException.java | 6 +-- 6 files changed, 80 insertions(+), 60 deletions(-) delete mode 100644 src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/response/ScheduleRegistResponseDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java index efd9838e..b62cae21 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java @@ -1,12 +1,15 @@ package stanl_2.final_backend.domain.schedule.command.application.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.schedule.command.application.dto.request.ScheduleModifyRequestDTO; import stanl_2.final_backend.domain.schedule.command.application.dto.request.ScheduleRegistRequestDTO; -import stanl_2.final_backend.domain.schedule.command.application.dto.response.ScheduleModifyResponseDTO; -import stanl_2.final_backend.domain.schedule.command.application.dto.response.ScheduleRegistResponseDTO; import stanl_2.final_backend.domain.schedule.command.application.service.ScheduleCommandService; import stanl_2.final_backend.domain.schedule.common.response.ResponseMessage; @@ -21,27 +24,57 @@ public ScheduleController(ScheduleCommandService scheduleCommandService) { this.scheduleCommandService = scheduleCommandService; } + @Operation(summary = "일정 등록 api") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "일정 등록 성공", + content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + }) @PostMapping("") public ResponseEntity registSchedule(@RequestBody ScheduleRegistRequestDTO scheduleRegistRequestDTO){ - ScheduleRegistResponseDTO scheduleRegistResponseDTO = scheduleCommandService.registSchedule(scheduleRegistRequestDTO); + Boolean answer = scheduleCommandService.registSchedule(scheduleRegistRequestDTO); - return ResponseEntity.ok(new ResponseMessage(200,"성공",scheduleRegistResponseDTO)); + return ResponseEntity.ok(ResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(answer) + .build()); } - @PutMapping("") - public ResponseEntity modifySchedule(@RequestBody ScheduleModifyRequestDTO scheduleModifyRequestDTO){ - ScheduleModifyResponseDTO scheduleModifyResponseDTO = scheduleCommandService.modifySchedule(scheduleModifyRequestDTO); + @Operation(summary = "일정 수정 api") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "일정 수정 성공", + content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + }) + @PutMapping("{id}") + public ResponseEntity modifySchedule(@PathVariable String id, + @RequestBody ScheduleModifyRequestDTO scheduleModifyRequestDTO){ - return ResponseEntity.ok(new ResponseMessage(200,"성공",scheduleModifyResponseDTO)); + scheduleModifyRequestDTO.setId(id); + Boolean answer = scheduleCommandService.modifySchedule(scheduleModifyRequestDTO); + + return ResponseEntity.ok(ResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(answer) + .build()); } + @Operation(summary = "일정 삭제 api") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "일정 삭제 성공", + content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + }) @DeleteMapping("{id}") public ResponseEntity deleteSchedule(@PathVariable String id){ - Boolean active = scheduleCommandService.deleteSchedule(id); + Boolean answer = scheduleCommandService.deleteSchedule(id); - return ResponseEntity.ok(new ResponseMessage(200,"성공",active)); + return ResponseEntity.ok(ResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(answer) + .build()); } } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/response/ScheduleRegistResponseDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/response/ScheduleRegistResponseDTO.java deleted file mode 100644 index 80ec5a98..00000000 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/response/ScheduleRegistResponseDTO.java +++ /dev/null @@ -1,21 +0,0 @@ -package stanl_2.final_backend.domain.schedule.command.application.dto.response; - -import lombok.*; - -@AllArgsConstructor -@NoArgsConstructor -@Getter -@Setter -@ToString -public class ScheduleRegistResponseDTO { - - private String id; - private String name; - private String content; - private String reservationTime; - private String createdAt; - private String updatedAt; - private String deletedAt; - private Boolean active; - private String memberId; -} diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/service/ScheduleCommandService.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/service/ScheduleCommandService.java index a0cca27c..b211cd05 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/service/ScheduleCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/service/ScheduleCommandService.java @@ -2,14 +2,12 @@ import stanl_2.final_backend.domain.schedule.command.application.dto.request.ScheduleModifyRequestDTO; import stanl_2.final_backend.domain.schedule.command.application.dto.request.ScheduleRegistRequestDTO; -import stanl_2.final_backend.domain.schedule.command.application.dto.response.ScheduleModifyResponseDTO; -import stanl_2.final_backend.domain.schedule.command.application.dto.response.ScheduleRegistResponseDTO; public interface ScheduleCommandService { - ScheduleRegistResponseDTO registSchedule(ScheduleRegistRequestDTO scheduleRegistRequestDTO); + Boolean registSchedule(ScheduleRegistRequestDTO scheduleRegistRequestDTO); - ScheduleModifyResponseDTO modifySchedule(ScheduleModifyRequestDTO scheduleModifyRequestDTO); + Boolean modifySchedule(ScheduleModifyRequestDTO scheduleModifyRequestDTO); Boolean deleteSchedule(String scheduleId); } \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java index 58c381ce..9fd41416 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java @@ -3,12 +3,12 @@ import lombok.extern.slf4j.Slf4j; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.data.mapping.MappingException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.schedule.command.application.dto.request.ScheduleModifyRequestDTO; import stanl_2.final_backend.domain.schedule.command.application.dto.request.ScheduleRegistRequestDTO; -import stanl_2.final_backend.domain.schedule.command.application.dto.response.ScheduleModifyResponseDTO; -import stanl_2.final_backend.domain.schedule.command.application.dto.response.ScheduleRegistResponseDTO; import stanl_2.final_backend.domain.schedule.command.application.service.ScheduleCommandService; import stanl_2.final_backend.domain.schedule.command.domain.aggregate.entity.Schedule; import stanl_2.final_backend.domain.schedule.command.domain.repository.ScheduleRepository; @@ -39,33 +39,44 @@ private Timestamp getCurrentTimestamp() { @Override @Transactional - public ScheduleRegistResponseDTO registSchedule(ScheduleRegistRequestDTO scheduleRegistRequestDTO) { - - Schedule schedule = modelMapper.map(scheduleRegistRequestDTO,Schedule.class); - - scheduleRepository.save(schedule); - - ScheduleRegistResponseDTO scheduleRegistResponseDTO = modelMapper.map(schedule, ScheduleRegistResponseDTO.class); - - return scheduleRegistResponseDTO; + public Boolean registSchedule(ScheduleRegistRequestDTO scheduleRegistRequestDTO) { + + try { + Schedule schedule = modelMapper.map(scheduleRegistRequestDTO, Schedule.class); + + scheduleRepository.save(schedule); + + return true; + } catch (DataIntegrityViolationException e){ + // DB 무결정 제약 조건 (NOT NULL, UNIQUE) 위반 + throw new CommonException(ErrorCode.DATA_INTEGRITY_VIOLATION); + } catch (MappingException e){ + // ModelMapper 매핑 오류 + throw new CommonException(ErrorCode.MAPPING_ERROR); + } catch (Exception e) { + // 서버 오류 + throw new CommonException(ErrorCode.INTERNAL_SERVER_ERROR); + } } @Override @Transactional - public ScheduleModifyResponseDTO modifySchedule(ScheduleModifyRequestDTO scheduleModifyRequestDTO) { + public Boolean modifySchedule(ScheduleModifyRequestDTO scheduleModifyRequestDTO) { Schedule schedule = scheduleRepository.findById(scheduleModifyRequestDTO.getId()) .orElseThrow(() -> new CommonException(ErrorCode.SCHEDULE_NOT_FOUND)); - Schedule updateSchedule = modelMapper.map(scheduleModifyRequestDTO, Schedule.class); - updateSchedule.setCreatedAt(schedule.getCreatedAt()); - updateSchedule.setActive(schedule.getActive()); - - scheduleRepository.save(updateSchedule); + try { + Schedule updateSchedule = modelMapper.map(scheduleModifyRequestDTO, Schedule.class); + updateSchedule.setCreatedAt(schedule.getCreatedAt()); + updateSchedule.setActive(schedule.getActive()); - ScheduleModifyResponseDTO scheduleModifyResponseDTO = modelMapper.map(updateSchedule,ScheduleModifyResponseDTO.class); + scheduleRepository.save(updateSchedule); - return scheduleModifyResponseDTO; + return true; + } catch (MappingException e){ + throw new CommonException(ErrorCode.MAPPING_ERROR); + } } @Override diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/common/exception/ErrorCode.java b/src/main/java/stanl_2/final_backend/domain/schedule/common/exception/ErrorCode.java index 295cb79f..739aada9 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/common/exception/ErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/common/exception/ErrorCode.java @@ -12,6 +12,8 @@ public enum ErrorCode { * 400(Bad Request) * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. */ + DATA_INTEGRITY_VIOLATION(40001, HttpStatus.BAD_REQUEST, "데이터 무결 위반하였습니다."), + CONSTRAINT_VIOLATION(40002, HttpStatus.BAD_REQUEST, "제약 조건 위반하였습니다."), @@ -45,7 +47,8 @@ public enum ErrorCode { * 500(Internal Server Error) * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. */ - INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); + INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."), + MAPPING_ERROR(50001, HttpStatus.INTERNAL_SERVER_ERROR, "ModleMapper 매핑 오류입니다."); private final Integer code; diff --git a/src/main/java/stanl_2/final_backend/global/exception/CommonException.java b/src/main/java/stanl_2/final_backend/global/exception/CommonException.java index 2ab808fc..01666dae 100644 --- a/src/main/java/stanl_2/final_backend/global/exception/CommonException.java +++ b/src/main/java/stanl_2/final_backend/global/exception/CommonException.java @@ -5,14 +5,10 @@ import org.springframework.beans.factory.annotation.Autowired; @Getter +@RequiredArgsConstructor public class CommonException extends RuntimeException { private final ErrorCode errorCode; - @Autowired - public CommonException(ErrorCode errorCode) { - this.errorCode = errorCode; - } - // 에러 발생시 ErroCode 별 메시지 @Override public String getMessage() { From 603cc9a79619f590915c83709644412454c2c639 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Mon, 11 Nov 2024 21:17:46 +0900 Subject: [PATCH 005/563] =?UTF-8?q?feat:=20=EC=9D=BC=EC=A0=95=ED=91=9C=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=84=B8=ED=8C=85=20=EC=99=84=EB=A3=8C=20?= =?UTF-8?q?(#20)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/ScheduleController.java | 36 ++++++++++++++++++- .../query/repository/ScheduleMapper.java | 3 ++ .../query/service/ScheduleService.java | 3 ++ .../query/service/ScheduleServiceImpl.java | 33 +++++++++++++++++ .../query/service/ScheduleServiceImple.java | 9 ----- src/main/resources/application.yml | 2 +- .../query/repository/ScheduleMapper.xml | 6 ++-- 7 files changed, 77 insertions(+), 15 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleServiceImpl.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleServiceImple.java diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java index 971d6f27..2e2678f4 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java @@ -1,11 +1,45 @@ package stanl_2.final_backend.domain.schedule.query.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +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.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.schedule.common.response.ResponseMessage; +import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; +import stanl_2.final_backend.domain.schedule.query.service.ScheduleService; @RestController("queryScheduleController") -@RequestMapping("/api/v1/center") +@RequestMapping("/api/v1/schedule") public class ScheduleController { + private final ScheduleService scheduleService; + @Autowired + public ScheduleController(ScheduleService scheduleService) { + this.scheduleService = scheduleService; + } + + @Operation(summary = "일정 전체 조회 api") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "일정 조회 성공", + content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + }) + @GetMapping("") + public ResponseEntity selectAllSchedule(@PathVariable String id){ + + ScheduleDTO scheduleDTO = scheduleService.selectAllSchedule(id); + + return ResponseEntity.ok(ResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(scheduleDTO) + .build()); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java index 16749291..9cee4d68 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java @@ -1,4 +1,7 @@ package stanl_2.final_backend.domain.schedule.query.repository; +import org.apache.ibatis.annotations.Mapper; + +@Mapper public interface ScheduleMapper { } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleService.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleService.java index a0b2dd2d..31f46177 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleService.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleService.java @@ -1,4 +1,7 @@ package stanl_2.final_backend.domain.schedule.query.service; +import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; + public interface ScheduleService { + ScheduleDTO selectAllSchedule(String id); } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleServiceImpl.java new file mode 100644 index 00000000..ded7e282 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleServiceImpl.java @@ -0,0 +1,33 @@ +package stanl_2.final_backend.domain.schedule.query.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; +import stanl_2.final_backend.domain.schedule.query.repository.ScheduleMapper; + +import java.sql.Timestamp; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +@Slf4j +@Service("querScheduleServiceImpl") +public class ScheduleServiceImpl implements ScheduleService{ + + private final ScheduleMapper scheduleMapper; + private Timestamp getCurrentTimestamp() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return Timestamp.from(nowKst.toInstant()); + } + + @Autowired + public ScheduleServiceImpl(ScheduleMapper scheduleMapper) { + this.scheduleMapper = scheduleMapper; + } + + @Override + public ScheduleDTO selectAllSchedule(String id) { + + return null; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleServiceImple.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleServiceImple.java deleted file mode 100644 index 8be9faba..00000000 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleServiceImple.java +++ /dev/null @@ -1,9 +0,0 @@ -package stanl_2.final_backend.domain.schedule.query.service; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -@Slf4j -@Service("querScheduleServiceImpl") -public class ScheduleServiceImple implements ScheduleService{ -} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8c8d8586..94f0cd80 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -23,7 +23,7 @@ spring: jpa: show-sql: true hibernate: - ddl-auto: create + ddl-auto: update properties: hibernate.format_sql: true diff --git a/src/main/resources/final_backend/domain/schedule/query/repository/ScheduleMapper.xml b/src/main/resources/final_backend/domain/schedule/query/repository/ScheduleMapper.xml index 2c094da2..506c3e3d 100644 --- a/src/main/resources/final_backend/domain/schedule/query/repository/ScheduleMapper.xml +++ b/src/main/resources/final_backend/domain/schedule/query/repository/ScheduleMapper.xml @@ -3,7 +3,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> - @@ -15,7 +14,7 @@ - SELECT A.SCH_ID, A.SCH_NAME, @@ -27,7 +26,6 @@ A.ACTIVE, A.MEM_ID FROM SCHEDULE A - WHERE + WHERE SCH_ID = #{ id } - \ No newline at end of file From 1b9550a9235491c140e2b13c7c99b8a6c3405313 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Mon, 11 Nov 2024 21:31:06 +0900 Subject: [PATCH 006/563] =?UTF-8?q?feat:=20=EA=B3=84=EC=95=BD=EC=84=9C=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EB=B0=8F=20ddl=20=EC=88=98=EC=A0=95(#21)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ContractController.java | 51 +++++++ .../dto/request/ContractRegistRequestDTO.java | 34 +++++ .../application/service/ContractService.java | 7 + .../domain/aggregate/entity/Contract.java | 142 ++++++++++++++++++ .../domain/repository/ContractRepository.java | 7 + .../domain/service/ContractServiceImpl.java | 34 +++++ src/main/resources/application_dev.yml | 2 +- src/main/resources/sql/ddl.sql | 69 +++++++-- 8 files changed, 335 insertions(+), 11 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractRegistRequestDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/command/domain/repository/ContractRepository.java create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java new file mode 100644 index 00000000..cf37dc2e --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java @@ -0,0 +1,51 @@ +package stanl_2.final_backend.domain.contract.command.application.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage; +import stanl_2.final_backend.domain.contract.command.application.dto.request.ContractRegistRequestDTO; +import stanl_2.final_backend.domain.contract.command.application.service.ContractService; + +@RestController("contractController") +@RequestMapping("/api/v1/contract") +public class ContractController { + + private final ContractService contractService; + + @Autowired + public ContractController(ContractService contractService) { + this.contractService = contractService; + } + + /** + * [POST] http://localhost:7777/api/v1/contract + * Request + * { + * "name": "tes1", + * "num": 123 + * } + * */ + @Operation(summary = "계약서 등록 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + }) + @PostMapping("{id}") + public ResponseEntity postTest(@PathVariable String id, + @RequestBody ContractRegistRequestDTO contractRegistRequestDTO) { + contractRegistRequestDTO.setMemId(id); + contractService.registerContract(contractRegistRequestDTO); + + return ResponseEntity.ok(ResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractRegistRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractRegistRequestDTO.java new file mode 100644 index 00000000..d7fbb4db --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractRegistRequestDTO.java @@ -0,0 +1,34 @@ +package stanl_2.final_backend.domain.contract.command.application.dto.request; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class ContractRegistRequestDTO { + + private String name; + private String custName; + private String custIdenNo; + private String custAddrress; + private String custEmail; + private String custPhone; + private String compName; + private String custCla; + private String custPurCond; + private String seriNum; + private String seleOpti; + private Integer downPay; + private Integer intePay; + private Integer remPay; + private Integer consPay; + private String delvDate; + private String delvLoc; + private String state; + private String noOfVeh; + private String createdUrl; + private String deletedUrl; + private String memId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractService.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractService.java new file mode 100644 index 00000000..4e50a130 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractService.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.contract.command.application.service; + +import stanl_2.final_backend.domain.contract.command.application.dto.request.ContractRegistRequestDTO; + +public interface ContractService { + void registerContract(ContractRegistRequestDTO contractRegistRequestDTO); +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java new file mode 100644 index 00000000..471ce257 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java @@ -0,0 +1,142 @@ +package stanl_2.final_backend.domain.contract.command.domain.aggregate.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.ColumnDefault; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +import java.sql.Timestamp; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "TB_CONTRACT") +public class Contract { + + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "CON") + ) + @Column(name = "CONR_ID") + private String id; + + @Column(name = "CONR_NAME", nullable = false) + private String name; + + @Column(name = "CONR_CUST_NAME", nullable = false) + private String custName; + + @Column(name = "CONR_CUST_IDEN_NO", nullable = false) + private String custIdenNo; + + @Column(name = "CONR_CUST_ADR", nullable = false) + private String custAddrress; + + @Column(name = "CONR_CUST_EMA", nullable = false) + private String custEmail; + + @Column(name = "CONR_CUST_PHO", nullable = false) + private String custPhone; + + @Column(name = "CONR_COMP_NAME") + private String compName; + + @Column(name = "CONR_CUST_CLA", nullable = false) + @ColumnDefault("'PERSONAL'") + private String custCla; + + @Column(name = "CONR_CUST_PUR_COND", nullable = false) + @ColumnDefault("'CASH'") + private String custPurCond; + + @Column(name = "CONR_SERI_NUM", nullable = false) + private String seriNum; + + @Column(name = "CONR_SELE_OPTI", nullable = false) + private String seleOpti; + + @Column(name = "CONR_DOWN_PAY", nullable = false) + private Integer downPay; + + @Column(name = "CONR_INTE_PAY", nullable = false) + private Integer intePay; + + @Column(name = "CONR_REM_PAY", nullable = false) + private Integer remPay; + + @Column(name = "CONR_CONS_PAY", nullable = false) + private Integer consPay; + + @Column(name = "CONR_DELV_DATE") + private String delvDate; + + @Column(name = "CONR_DELV_LOC") + private String delvLoc; + + @Column(name = "CONR_STAT", nullable = false) + @ColumnDefault("'WAIT'") + private String state; + + @Column(name = "CONR_NO_OF_VEH", nullable = false) + @ColumnDefault("1") + private String noOfVeh; + + @Column(name = "CREATED_URL", nullable = false) + private String createdUrl; + + @Column(name = "DELETED_URL") + private String deletedUrl; + + @Column(name = "ACTIVE", nullable = false) + private boolean active = true; + + @Column(name = "CREATED_AT", nullable = false, updatable = false) + private Timestamp createdAt; + + @Column(name = "UPDATED_AT", nullable = false) + private Timestamp updatedAt; + + @Column(name = "DELETED_AT") + private Timestamp deletedAt; + + @Column(name = "MEM_ID", nullable = false) + private String memId; + + @Column(name = "CENT_ID", nullable = false) + private String centId; + + @Column(name = "CUST_ID") + private String custId; + + @Column(name = "PROD_ID", nullable = false) + private String prodId; + + /* 설명. updatedAt 자동화 */ + // Insert 되기 전에 실행 + @PrePersist + private void prePersist() { + this.createdAt = getCurrentTimestamp(); + this.updatedAt = this.createdAt; + } + + // Update 되기 전에 실행 + @PreUpdate + private void preUpdate() { + this.updatedAt = getCurrentTimestamp(); + } + + private Timestamp getCurrentTimestamp() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return Timestamp.from(nowKst.toInstant()); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/repository/ContractRepository.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/repository/ContractRepository.java new file mode 100644 index 00000000..51c8d8a4 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/repository/ContractRepository.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.contract.command.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import stanl_2.final_backend.domain.contract.command.domain.aggregate.entity.Contract; + +public interface ContractRepository extends JpaRepository { +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java new file mode 100644 index 00000000..ac3df64a --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java @@ -0,0 +1,34 @@ +package stanl_2.final_backend.domain.contract.command.domain.service; + +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.contract.command.application.dto.request.ContractRegistRequestDTO; +import stanl_2.final_backend.domain.contract.command.application.service.ContractService; +import stanl_2.final_backend.domain.contract.command.domain.aggregate.entity.Contract; +import stanl_2.final_backend.domain.contract.command.domain.repository.ContractRepository; + +@Service("contractServiceImpl") +public class ContractServiceImpl implements ContractService { + + private final ContractRepository contractRepository; + private final ModelMapper modelMapper; + + public ContractServiceImpl(ContractRepository contractRepository, ModelMapper modelMapper) { + this.contractRepository = contractRepository; + this.modelMapper = modelMapper; + } + + @Override + @Transactional + public void registerContract(ContractRegistRequestDTO contractRegistRequestDTO) { + // 회원인지 확인여부 + + Contract contract = modelMapper.map(contractRegistRequestDTO, Contract.class); + + contract.setCentId("CEN_000000001"); + contract.setProdId("PRO_000000001"); + + contractRepository.save(contract); + } +} diff --git a/src/main/resources/application_dev.yml b/src/main/resources/application_dev.yml index e9390662..aabe59aa 100644 --- a/src/main/resources/application_dev.yml +++ b/src/main/resources/application_dev.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: create + ddl-auto: update logging: level: diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index f931c37e..80500b1b 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -127,17 +127,28 @@ CREATE TABLE tb_contract CONR_CUST_CLA VARCHAR(255) NOT NULL DEFAULT 'PERSONAL', CONR_CUST_PUR_COND VARCHAR(255) NOT NULL DEFAULT 'CASH', CONR_SERI_NUM VARCHAR(255) NOT NULL, + CONR_SELE_OPTI VARCHAR(255) NOT NULL, + CONR_DOWN_PAY INT NOT NULL, + CONR_INTE_PAY INT NOT NULL, + CONR_REM_PAY INT NOT NULL, + CONR_CONS_PAY INT NOT NULL, + CONR_DELV_DATE VARCHAR(255) NULL, + CONR_DELV_LOC VARCHAR(255) NULL, + CONR_STAT VARCHAR(255) NULL DEFAULT 'WAIT', CONR_NO_OF_VEH INT NOT NULL DEFAULT 1, + CREATED_URL VARCHAR(255) NOT NULL, + DELETED_URL VARCHAR(255) NULL, ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, CREATED_AT CHAR(19) NOT NULL, UPDATED_AT CHAR(19) NOT NULL, + DELETED_AT CHAR(19) NULL, MEM_ID VARCHAR(255) NOT NULL, CENT_ID VARCHAR(255) NOT NULL, - CUST_ID2 VARCHAR(255) NOT NULL, + CUST_ID VARCHAR(255) NULL, PROD_ID VARCHAR(255) NOT NULL, PRIMARY KEY (CONR_ID), FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE, - FOREIGN KEY (CUST_ID2) REFERENCES tb_customer_info (CUST_ID) ON DELETE CASCADE, + FOREIGN KEY (CUST_ID) REFERENCES tb_customer_info (CUST_ID) ON DELETE CASCADE, FOREIGN KEY (PROD_ID) REFERENCES tb_product (PROD_ID) ON DELETE CASCADE, FOREIGN KEY (CENT_ID) REFERENCES tb_center (CENT_ID) ON DELETE CASCADE ); @@ -208,7 +219,7 @@ CREATE TABLE tb_purchase_order MEM_ID VARCHAR(255) NOT NULL, PRIMARY KEY (PUR_ORD_ID), FOREIGN KEY (ORD_ID) REFERENCES tb_order (ORD_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) + FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), ); CREATE TABLE tb_EVALUATION @@ -222,9 +233,11 @@ CREATE TABLE tb_EVALUATION ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, CENT_ID VARCHAR(255) NOT NULL, MEM_ID VARCHAR(255) NOT NULL, + WRI_ID VARCHAR(255) NOT NULL, PRIMARY KEY (EVAL_ID), FOREIGN KEY (CENT_ID) REFERENCES tb_center (CENT_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) + FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), + FOREIGN KEY (WRI_ID) REFERENCES tb_member (MEM_ID) ); CREATE TABLE tb_promotion @@ -250,9 +263,23 @@ CREATE TABLE tb_file ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, CREATED_AT CHAR(19) NOT NULL, DELETED_AT CHAR(19) NULL, - MEM_ID VARCHAR(255) NOT NULL, + MEM_ID VARCHAR(255) NULL, + NOT_ID VARCHAR(255) NULL, + CONR_ID VARCHAR(255) NULL, + PROB_ID VARCHAR(255) NULL, + CENT_ID VARCHAR(255) NULL, + PROM_ID VARCHAR(255) NULL, + EVAL_ID VARCHAR(255) NULL, + CONR_ID2 VARCHAR(255) NULL, PRIMARY KEY (FILE_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) + FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), + FOREIGN KEY (NOT_ID) REFERENCES tb_notice (NOT_ID), + FOREIGN KEY (CONR_ID) REFERENCES tb_contract (CONR_ID), + FOREIGN KEY (PROB_ID) REFERENCES tb_problem (PROB_ID), + FOREIGN KEY (CENT_ID) REFERENCES tb_center (CENT_ID), + FOREIGN KEY (PROM_ID) REFERENCES tb_promotion (PROM_ID), + FOREIGN KEY (EVAL_ID) REFERENCES tb_evaluation (EVAL_ID), + FOREIGN KEY (CONR_ID2) REFERENCES tb_contract (CONR_ID) ); CREATE TABLE tb_schedule @@ -284,10 +311,29 @@ CREATE TABLE tb_alarm CREATE TABLE tb_member_detail ( - MEM_DET_ID VARCHAR(255) NOT NULL, - MEM_DET_REL VARCHAR(255) NOT NULL, + MEM_DET_ID VARCHAR(255) NOT NULL, + MEM_DET_REL VARCHAR(255) NOT NULL, MEM_DET_NAME VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, + MEM_DET_BIR VARCHAR(255) NOT NULL, + MEM_DET_IDEN_NO VARCHAR(255) NOT NULL, + MEM_DET_PHO VARCHAR(255) NOT NULL, + MEM_DET_SEX VARCHAR(255) NOT NULL DEFAULT 'FEMALE' COMMENT 'FEMALE/MALE', + MEM_DET_DIS BOOLEAN NOT NULL, + MEM_DET_DIE BOOLEAN NOT NULL, + MEM_DET_NOTE VARCHAR(255) NULL, + MEM_DET_ENTD VARCHAR(255) NOT NULL, + MEM_DET_GRAD VARCHAR(255) NOT NULL, + MEM_DET_FNL_EDC VARCHAR(255) NOT NULL, + MEM_DET_EDU VARCHAR(255) NOT NULL, + MEM_DET_MJR VARCHAR(255) NOT NULL, + MEM_DET_EMP_DATE VARCHAR(255) NOT NULL, + MEM_DET_RTR_DATE VARCHAR(255) NOT NULL, + CAR_INFO VARCHAR(255) NOT NULL, + CERT_DATE VARCHAR(255) NOT NULL, + CERT_INST VARCHAR(255) NOT NULL, + CERT_NAME VARCHAR(255) NULL, + CERT_SCO VARCHAR(255) NOT NULL, + MEM_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', PRIMARY KEY (MEM_DET_ID), FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) ); @@ -297,9 +343,12 @@ CREATE TABLE tb_UPDATE_HISTORY UPD_ID VARCHAR(255) NOT NULL, UPD_IP VARCHAR(255) NOT NULL, UPDATED_AT CHAR(19) NOT NULL, + UPDATED_URL VARCHAR(255) NOT NULL, MEM_ID VARCHAR(255) NOT NULL, + CONR_ID VARCHAR(255) NOT NULL, PRIMARY KEY (UPD_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) + FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), + FOREIGN KEY (CONR_ID) REFERENCES tb_contract (CONR_ID) ); CREATE TABLE tb_PRODUCT_OPTION From 93d47d3eea0b0393fc2005b58818ce1dfa887d15 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 11 Nov 2024 21:34:43 +0900 Subject: [PATCH 007/563] =?UTF-8?q?fix:=20Sample=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/domain/aggregate/entity/Sample.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/aggregate/entity/Sample.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/aggregate/entity/Sample.java index 33b0696b..442dcdee 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/aggregate/entity/Sample.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/aggregate/entity/Sample.java @@ -11,6 +11,7 @@ import java.sql.Timestamp; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; @NoArgsConstructor @AllArgsConstructor @@ -36,13 +37,13 @@ public class Sample { private Integer num; @Column(name = "CREATED_AT", nullable = false, updatable = false) - private Timestamp createdAt; + private String createdAt; @Column(name = "UPDATED_AT", nullable = false) - private Timestamp updatedAt; + private String updatedAt; @Column(name = "DELETED_AT") - private Timestamp deletedAt; + private String deletedAt; @Column(name = "ACTIVE") private Boolean active = true; @@ -62,9 +63,9 @@ private void preUpdate() { this.updatedAt = getCurrentTimestamp(); } - private Timestamp getCurrentTimestamp() { + private String getCurrentTimestamp() { ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); - return Timestamp.from(nowKst.toInstant()); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } } From 1d1a37f43bdea1d22b1ba7500fde6c0fdc71b904 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 11 Nov 2024 21:41:15 +0900 Subject: [PATCH 008/563] =?UTF-8?q?=EC=83=98=ED=94=8C=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95(#4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/aggragate/entity/Notice.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java new file mode 100644 index 00000000..4b3029a0 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java @@ -0,0 +1,43 @@ +package stanl_2.final_backend.domain.notices.command.domain.aggragate.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +import java.sql.Timestamp; + +@Entity +@Table(name="NOTICE") +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class Notice { + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name="prefix", value = "NOT") + ) + @Column(name = "NOT_ID") + private String id; + + @Column(name = "NOT_TTL") + private String title; + + @Column(name = "NOT_TAG") + private String tag; + + @Column(name = "NOT_CLA") + private String classification; + + @Column(columnDefinition = "TEXT", name = "content") + private String content; + + @Column(name = "CREATED_AT", nullable = false, updatable = false) + private Timestamp createdAt; +} From 3097d3c17e8ac5f56ba07bd77b9a3cf5b5a44e7f Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 11 Nov 2024 21:46:07 +0900 Subject: [PATCH 009/563] =?UTF-8?q?fix:=20getCurrentTimeStamp=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../A_sample/command/domain/aggregate/entity/Sample.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/aggregate/entity/Sample.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/aggregate/entity/Sample.java index 442dcdee..4d709591 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/aggregate/entity/Sample.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/aggregate/entity/Sample.java @@ -53,17 +53,17 @@ public class Sample { // Insert 되기 전에 실행 @PrePersist private void prePersist() { - this.createdAt = getCurrentTimestamp(); + this.createdAt = getCurrentTime(); this.updatedAt = this.createdAt; } // Update 되기 전에 실행 @PreUpdate private void preUpdate() { - this.updatedAt = getCurrentTimestamp(); + this.updatedAt = getCurrentTime(); } - private String getCurrentTimestamp() { + private String getCurrentTime() { ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } From 0a35983b8f1ac5661ff6d29257838aadfd712c56 Mon Sep 17 00:00:00 2001 From: giuseog Date: Mon, 11 Nov 2024 21:46:39 +0900 Subject: [PATCH 010/563] =?UTF-8?q?feat:=20=EB=A7=A4=EC=9E=A5=20crud=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=EC=A4=91(#8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../A_sample/common/exception/ErrorCode.java | 2 +- .../controller/CenterController.java | 51 +++++++++--- .../dto/request/CenterDeleteRequestDTO.java | 9 +++ .../dto/request/CenterModifyRequestDTO.java | 9 +++ .../dto/request/CenterRegistRequestDTO.java | 1 - .../dto/response/CenterDeleteResponseDTO.java | 16 ++++ .../dto/response/CenterModifyResponseDTO.java | 16 ++++ .../dto/response/CenterRegistResponseDTO.java | 1 - .../service/CenterCommandService.java | 9 ++- .../service/CenterCommandServiceImpl.java | 47 ----------- .../domain/aggregate/entity/Center.java | 18 +++-- .../service/CenterCommandServiceImpl.java | 78 +++++++++++++++++++ 12 files changed, 185 insertions(+), 72 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterDeleteRequestDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyRequestDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterDeleteResponseDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterModifyResponseDTO.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandServiceImpl.java create mode 100644 src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/ErrorCode.java b/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/ErrorCode.java index c03f67bc..73bf4827 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/ErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/ErrorCode.java @@ -39,7 +39,7 @@ public enum ErrorCode { * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. */ SAMPLE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "sample 데이터를 찾지 못했습니다"), - + CENTER_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "center 데이터를 찾지 못했습니다."), /** * 500(Internal Server Error) diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java index 568317e6..523afa22 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java @@ -1,8 +1,14 @@ package stanl_2.final_backend.domain.center.command.application.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.A_sample.command.application.dto.response.SampleModifyResponseDTO; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistRequestDTO; import stanl_2.final_backend.domain.center.command.application.dto.response.CenterRegistResponseDTO; import stanl_2.final_backend.domain.center.command.application.service.CenterCommandService; @@ -19,29 +25,50 @@ public CenterController(CenterCommandService centerCommandService) { this.centerCommandService = centerCommandService; } - // 나중에 적용 예정 - swagger 설정 - // @Operation(summary = "Get center Test") - // @ApiResponses(value = { - // @ApiResponse(responseCode = "200", description = "성공", - // content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) - // }) - + @Operation(summary = "매장 등록") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage.class))}) + }) @PostMapping("") public ResponseEntity postTest(@RequestBody CenterRegistRequestDTO centerRegistRequestDTO){ + /* 설명. memberId 토큰으로 받는 것 고려 */ CenterRegistResponseDTO centerRegistResponseDTO = centerCommandService.registCenter(centerRegistRequestDTO); - return ResponseEntity.ok(new ResponseMessage(200, "post 성공", centerRegistResponseDTO)); + centerRegistRequestDTO.setId(id); + centerCommandService.registerCenter(centerRegistRequestDTO); + + return ResponseEntity.ok(stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); } - @PutMapping("") + @Operation(summary = "매장 수정") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage.class))}) + }) + @PutMapping("{id}") public ResponseEntity putTest(){ + sampleModifyRequestDTO.setId(id); + SampleModifyResponseDTO sampleModifyResponseDTO = sampleCommandService.modifySample(id, sampleModifyRequestDTO); - return ResponseEntity.ok(new ResponseMessage(200, "put 성공", " ")); - } + return ResponseEntity.ok(stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(sampleModifyResponseDTO) + .build()); } - @DeleteMapping("") + @Operation(summary = "샘플 삭제 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage.class))}) + }) + @DeleteMapping("{id}") public ResponseEntity deleteTest(){ diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterDeleteRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterDeleteRequestDTO.java new file mode 100644 index 00000000..3d84f947 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterDeleteRequestDTO.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.center.command.application.dto.request; + +public class CenterDeleteRequestDTO { + private String name; + private String address; + private String phone; + private Integer memberCount; + private String operatingAt; +} diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyRequestDTO.java new file mode 100644 index 00000000..5190590e --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyRequestDTO.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.center.command.application.dto.request; + +public class CenterModifyRequestDTO { + private String name; + private String address; + private String phone; + private Integer memberCount; + private String operatingAt; +} diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterRegistRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterRegistRequestDTO.java index d256f1a1..9f61f560 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterRegistRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterRegistRequestDTO.java @@ -9,7 +9,6 @@ @ToString public class CenterRegistRequestDTO { -// private Long id; private String name; private String address; private String phone; diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterDeleteResponseDTO.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterDeleteResponseDTO.java new file mode 100644 index 00000000..fc5d31f4 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterDeleteResponseDTO.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.center.command.application.dto.response; + +import java.sql.Timestamp; + +public class CenterDeleteResponseDTO { + + private String name; + private String address; + private String phone; + private Integer memberCount; + private String operatingAt; + private Timestamp createdAt; + private Timestamp updatedAt; + private Timestamp deletedAt; + private Boolean active; +} diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterModifyResponseDTO.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterModifyResponseDTO.java new file mode 100644 index 00000000..03b6d5be --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterModifyResponseDTO.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.center.command.application.dto.response; + +import java.sql.Timestamp; + +public class CenterModifyResponseDTO { + + private String name; + private String address; + private String phone; + private Integer memberCount; + private String operatingAt; + private Timestamp createdAt; + private Timestamp updatedAt; + private Timestamp deletedAt; + private Boolean active; +} diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterRegistResponseDTO.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterRegistResponseDTO.java index 2178977c..2b0736f9 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterRegistResponseDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterRegistResponseDTO.java @@ -12,7 +12,6 @@ @ToString public class CenterRegistResponseDTO { - private Long id; private String name; private String address; private String phone; diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandService.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandService.java index 47a9ad6d..c7bda1eb 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandService.java @@ -1,10 +1,13 @@ package stanl_2.final_backend.domain.center.command.application.service; +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.center.command.application.dto.request.CenterModifyRequestDTO; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistRequestDTO; -import stanl_2.final_backend.domain.center.command.application.dto.response.CenterRegistResponseDTO; +@Service public interface CenterCommandService { - CenterRegistResponseDTO registCenter(CenterRegistRequestDTO centerRegistRequestDTO); - + void registCenter(CenterRegistRequestDTO centerRegistRequestDTO); + void modifyCenter(String id, CenterModifyRequestDTO centerModifyRequestDTO) + void deleteCenter(String id); } diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandServiceImpl.java deleted file mode 100644 index a9ec0226..00000000 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandServiceImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -package stanl_2.final_backend.domain.center.command.application.service; - -import lombok.extern.slf4j.Slf4j; -import org.modelmapper.ModelMapper; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistRequestDTO; -import stanl_2.final_backend.domain.center.command.application.dto.response.CenterRegistResponseDTO; -import stanl_2.final_backend.domain.center.command.domain.aggregate.entity.Center; -import stanl_2.final_backend.domain.center.command.domain.repository.CenterRepository; - -@Slf4j -@Service("commandCenterServiceImpl") -public class CenterCommandServiceImpl implements CenterCommandService { - - private final CenterRepository centerRepository; - private final ModelMapper modelMapper; - - - public CenterCommandServiceImpl(CenterRepository centerRepository, ModelMapper modelMapper) { - this.centerRepository = centerRepository; - this.modelMapper = modelMapper; - } - - @Override - @Transactional - public CenterRegistResponseDTO registCenter(CenterRegistRequestDTO centerRegistRequestDTO) { - -// Center center = new Center(); -// center.setName(centerRegistRequestDTO.getName()); -// center.setAddress(centerRegistRequestDTO.getAddress()); -// center.setPhone(centerRegistRequestDTO.getPhone()); -// center.setMemberCount(centerRegistRequestDTO.getMemberCount()); -// center.setOperatingAt(centerRegistRequestDTO.getOperatingAt()); - - - Center center = modelMapper.map(centerRegistRequestDTO, Center.class); - center.setCreatedAt(center.getCreatedAt()); - center.setUpdatedAt(center.getUpdatedAt()); - - centerRepository.save(center); - - CenterRegistResponseDTO centerRegistResponseDTO = modelMapper.map(center, CenterRegistResponseDTO.class); - - return centerRegistResponseDTO; - } -} diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java b/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java index 99e43113..b6394a5b 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java @@ -2,6 +2,8 @@ import jakarta.persistence.*; import lombok.*; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; import java.sql.Timestamp; import java.time.ZoneId; @@ -18,11 +20,13 @@ @Getter public class Center { - /* 설명. 테스트를 위한 Long 선언 => mem_0000001 구현을 위해서는 고도화 진행 필요*/ @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "CENT_ID", nullable = false) - private Long id; + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "CEN") + ) + private String id; @Column(name = "CENT_NAME", nullable = false) private String name; @@ -55,12 +59,12 @@ public class Center { /* 설명. updatedAt 자동화 */ // Insert 되기 전에 실행 @PrePersist - public void prePersist() { - Timestamp currentTimestamp = getCurrentTimestamp(); - this.createdAt = currentTimestamp; + private void prePersist() { + this.createdAt = getCurrentTimestamp(); this.updatedAt = this.createdAt; } + // Update 되기 전에 실행 @PreUpdate public void preUpdate() { diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java new file mode 100644 index 00000000..c49ee4a1 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java @@ -0,0 +1,78 @@ +package stanl_2.final_backend.domain.center.command.domain.service; + +import lombok.extern.slf4j.Slf4j; +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.A_sample.common.exception.CommonException; +import stanl_2.final_backend.domain.A_sample.common.exception.ErrorCode; +import stanl_2.final_backend.domain.center.command.application.dto.request.CenterModifyRequestDTO; +import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistRequestDTO; +import stanl_2.final_backend.domain.center.command.application.dto.response.CenterModifyResponseDTO; +import stanl_2.final_backend.domain.center.command.application.service.CenterCommandService; +import stanl_2.final_backend.domain.center.command.domain.aggregate.entity.Center; +import stanl_2.final_backend.domain.center.command.domain.repository.CenterRepository; + +import java.sql.Timestamp; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@Slf4j +@Service("commandCenterServiceImpl") +public class CenterCommandServiceImpl implements CenterCommandService { + + private final CenterRepository centerRepository; + private final ModelMapper modelMapper; + + @Autowired + public CenterCommandServiceImpl(CenterRepository centerRepository, ModelMapper modelMapper) { + this.centerRepository = centerRepository; + this.modelMapper = modelMapper; + } + + private Timestamp getCurrentTimestamp() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return Timestamp.from(nowKst.toInstant()); + } + + @Override + @Transactional + public void registCenter(CenterRegistRequestDTO centerRegistRequestDTO) { + + Center newCenter = modelMapper.map(centerRegistRequestDTO, Center.class); + + centerRepository.save(newCenter); + } + + @Override + @Transactional + public void modifyCenter(String id, CenterModifyRequestDTO centerModifyRequestDTO) { + Center center = centerRepository.findById(id) + .orElseThrow(() -> new CommonException(ErrorCode.CENTER_NOT_FOUND)); + +// centerModifyRequestDTO.setId(id); + Center updateCenter = modelMapper.map(centerModifyRequestDTO, Center.class); + updateCenter.setCreatedAt(center.getCreatedAt()); + updateCenter.setActive(center.getActive()); + + centerRepository.save(updateCenter); + + CenterModifyResponseDTO centerModifyResponseDTO= modelMapper.map(updateCenter, CenterModifyResponseDTO.class); + + } + + @Override + @Transactional + public void deleteCenter(String id) { + + Center center = centerRepository.findById(id) + .orElseThrow(() -> new CommonException(ErrorCode.CENTER_NOT_FOUND)); + + center.setActive(false); + center.setDeletedAt(getCurrentTimestamp()); + + centerRepository.save(center); + } +} From ba65382eec0b1a13f49875332ace4cfe0198d2c4 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Tue, 12 Nov 2024 11:14:44 +0900 Subject: [PATCH 011/563] =?UTF-8?q?refactor:=20ddl=20=EC=88=98=EC=A0=95(#1?= =?UTF-8?q?0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/sql/ddl.sql | 578 ++++++++++++++++++++++++++++++--- 1 file changed, 535 insertions(+), 43 deletions(-) diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index 80500b1b..9c05600f 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -128,13 +128,13 @@ CREATE TABLE tb_contract CONR_CUST_PUR_COND VARCHAR(255) NOT NULL DEFAULT 'CASH', CONR_SERI_NUM VARCHAR(255) NOT NULL, CONR_SELE_OPTI VARCHAR(255) NOT NULL, - CONR_DOWN_PAY INT NOT NULL, - CONR_INTE_PAY INT NOT NULL, - CONR_REM_PAY INT NOT NULL, - CONR_CONS_PAY INT NOT NULL, + CONR_DOWN_PAY INT NOT NULL, + CONR_INTE_PAY INT NOT NULL, + CONR_REM_PAY INT NOT NULL, + CONR_CONS_PAY INT NOT NULL, CONR_DELV_DATE VARCHAR(255) NULL, CONR_DELV_LOC VARCHAR(255) NULL, - CONR_STAT VARCHAR(255) NULL DEFAULT 'WAIT', + CONR_STAT VARCHAR(255) NULL DEFAULT 'WAIT', CONR_NO_OF_VEH INT NOT NULL DEFAULT 1, CREATED_URL VARCHAR(255) NOT NULL, DELETED_URL VARCHAR(255) NULL, @@ -144,7 +144,7 @@ CREATE TABLE tb_contract DELETED_AT CHAR(19) NULL, MEM_ID VARCHAR(255) NOT NULL, CENT_ID VARCHAR(255) NOT NULL, - CUST_ID VARCHAR(255) NULL, + CUST_ID VARCHAR(255) NULL, PROD_ID VARCHAR(255) NOT NULL, PRIMARY KEY (CONR_ID), FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE, @@ -219,7 +219,7 @@ CREATE TABLE tb_purchase_order MEM_ID VARCHAR(255) NOT NULL, PRIMARY KEY (PUR_ORD_ID), FOREIGN KEY (ORD_ID) REFERENCES tb_order (ORD_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), + FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) ); CREATE TABLE tb_EVALUATION @@ -265,12 +265,12 @@ CREATE TABLE tb_file DELETED_AT CHAR(19) NULL, MEM_ID VARCHAR(255) NULL, NOT_ID VARCHAR(255) NULL, - CONR_ID VARCHAR(255) NULL, - PROB_ID VARCHAR(255) NULL, - CENT_ID VARCHAR(255) NULL, - PROM_ID VARCHAR(255) NULL, - EVAL_ID VARCHAR(255) NULL, - CONR_ID2 VARCHAR(255) NULL, + CONR_ID VARCHAR(255) NULL, + PROB_ID VARCHAR(255) NULL, + CENT_ID VARCHAR(255) NULL, + PROM_ID VARCHAR(255) NULL, + EVAL_ID VARCHAR(255) NULL, + CONR_ID2 VARCHAR(255) NULL, PRIMARY KEY (FILE_ID), FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), FOREIGN KEY (NOT_ID) REFERENCES tb_notice (NOT_ID), @@ -284,15 +284,16 @@ CREATE TABLE tb_file CREATE TABLE tb_schedule ( - SCH_ID VARCHAR(255) NOT NULL, + SCH_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', SCH_NAME VARCHAR(255) NOT NULL, SCH_CONT VARCHAR(255) NOT NULL, - SCH_RES CHAR(19) NOT NULL, + SCH_SRT_AT CHAR(19) NOT NULL, + SCH_END_AT CHAR(19) NOT NULL, CREATED_AT CHAR(19) NOT NULL, UPDATED_AT CHAR(19) NOT NULL, DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - MEM_ID VARCHAR(255) NOT NULL, + ACTIVE BOOLEAN NOT NULL COMMENT 'TRUE/FALSE', + MEM_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', PRIMARY KEY (SCH_ID), FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) ); @@ -311,40 +312,40 @@ CREATE TABLE tb_alarm CREATE TABLE tb_member_detail ( - MEM_DET_ID VARCHAR(255) NOT NULL, - MEM_DET_REL VARCHAR(255) NOT NULL, - MEM_DET_NAME VARCHAR(255) NOT NULL, - MEM_DET_BIR VARCHAR(255) NOT NULL, - MEM_DET_IDEN_NO VARCHAR(255) NOT NULL, - MEM_DET_PHO VARCHAR(255) NOT NULL, - MEM_DET_SEX VARCHAR(255) NOT NULL DEFAULT 'FEMALE' COMMENT 'FEMALE/MALE', - MEM_DET_DIS BOOLEAN NOT NULL, - MEM_DET_DIE BOOLEAN NOT NULL, - MEM_DET_NOTE VARCHAR(255) NULL, - MEM_DET_ENTD VARCHAR(255) NOT NULL, - MEM_DET_GRAD VARCHAR(255) NOT NULL, - MEM_DET_FNL_EDC VARCHAR(255) NOT NULL, - MEM_DET_EDU VARCHAR(255) NOT NULL, - MEM_DET_MJR VARCHAR(255) NOT NULL, + MEM_DET_ID VARCHAR(255) NOT NULL, + MEM_DET_REL VARCHAR(255) NOT NULL, + MEM_DET_NAME VARCHAR(255) NOT NULL, + MEM_DET_BIR VARCHAR(255) NOT NULL, + MEM_DET_IDEN_NO VARCHAR(255) NOT NULL, + MEM_DET_PHO VARCHAR(255) NOT NULL, + MEM_DET_SEX VARCHAR(255) NOT NULL DEFAULT 'FEMALE' COMMENT 'FEMALE/MALE', + MEM_DET_DIS BOOLEAN NOT NULL, + MEM_DET_DIE BOOLEAN NOT NULL, + MEM_DET_NOTE VARCHAR(255) NULL, + MEM_DET_ENTD VARCHAR(255) NOT NULL, + MEM_DET_GRAD VARCHAR(255) NOT NULL, + MEM_DET_FNL_EDC VARCHAR(255) NOT NULL, + MEM_DET_EDU VARCHAR(255) NOT NULL, + MEM_DET_MJR VARCHAR(255) NOT NULL, MEM_DET_EMP_DATE VARCHAR(255) NOT NULL, MEM_DET_RTR_DATE VARCHAR(255) NOT NULL, - CAR_INFO VARCHAR(255) NOT NULL, - CERT_DATE VARCHAR(255) NOT NULL, - CERT_INST VARCHAR(255) NOT NULL, - CERT_NAME VARCHAR(255) NULL, - CERT_SCO VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', + CAR_INFO VARCHAR(255) NOT NULL, + CERT_DATE VARCHAR(255) NOT NULL, + CERT_INST VARCHAR(255) NOT NULL, + CERT_NAME VARCHAR(255) NULL, + CERT_SCO VARCHAR(255) NOT NULL, + MEM_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', PRIMARY KEY (MEM_DET_ID), FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) ); CREATE TABLE tb_UPDATE_HISTORY ( - UPD_ID VARCHAR(255) NOT NULL, - UPD_IP VARCHAR(255) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - UPDATED_URL VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, + UPD_ID VARCHAR(255) NOT NULL, + UPD_IP VARCHAR(255) NOT NULL, + UPDATED_AT CHAR(19) NOT NULL, + UPDATED_URL VARCHAR(255) NOT NULL, + MEM_ID VARCHAR(255) NOT NULL, CONR_ID VARCHAR(255) NOT NULL, PRIMARY KEY (UPD_ID), FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), @@ -391,3 +392,494 @@ CREATE TABLE tb_sales_history CONR_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고' ); +INSERT INTO tb_organization_chart (ORG_CHA_ID, ORG_CHA_NAME, ORG_CHA_DEPTH) +VALUES ('ORG_000000001', '본사', 1), + ('ORG_000000002', '서울지사', 2), + ('ORG_000000003', '부산지사', 2), + ('ORG_000000004', '인천지사', 2), + ('ORG_000000005', '대전지사', 2), + ('ORG_000000006', '영업부', 3), + ('ORG_000000007', '기술부', 3), + ('ORG_000000008', '고객지원부', 3), + ('ORG_000000009', '마케팅부', 3), + ('ORG_000000010', '재무부', 3); + +INSERT INTO tb_center (CENT_ID, CENT_NAME, CENT_ADR, CENT_PHO, CENT_MEM_CNT, CREATED_AT, UPDATED_AT, ACTIVE, + CENT_OPR_AT) +VALUES ('CEN_000000001', '서울센터', '서울특별시 중구', '010-1234-5678', 50, '2024-01-01 12:00:00', '2024-01-01 12:00:00', TRUE, + '09:00-18:00'), + ('CEN_000000002', '부산센터', '부산광역시 해운대구', '010-2345-6789', 40, '2024-01-02 12:00:00', '2024-01-02 12:00:00', TRUE, + '09:00-18:00'), + ('CEN_000000003', '대구센터', '대구광역시', '010-3456-7890', 30, '2024-01-03 12:00:00', '2024-01-03 12:00:00', TRUE, + '09:00-18:00'), + ('CEN_000000004', '인천센터', '인천광역시', '010-4567-8901', 20, '2024-01-04 12:00:00', '2024-01-04 12:00:00', TRUE, + '09:00-18:00'), + ('CEN_000000005', '울산센터', '울산광역시', '010-5678-9012', 25, '2024-01-05 12:00:00', '2024-01-05 12:00:00', TRUE, + '09:00-18:00'), + ('CEN_000000006', '대전센터', '대전광역시', '010-6789-0123', 35, '2024-01-06 12:00:00', '2024-01-06 12:00:00', TRUE, + '09:00-18:00'), + ('CEN_000000007', '광주센터', '광주광역시', '010-7890-1234', 45, '2024-01-07 12:00:00', '2024-01-07 12:00:00', TRUE, + '09:00-18:00'), + ('CEN_000000008', '경기센터', '경기도 수원시', '010-8901-2345', 20, '2024-01-08 12:00:00', '2024-01-08 12:00:00', TRUE, + '09:00-18:00'), + ('CEN_000000009', '청주센터', '충청북도 청주시', '010-9012-3456', 10, '2024-01-09 12:00:00', '2024-01-09 12:00:00', TRUE, + '09:00-18:00'), + ('CEN_000000010', '전주센터', '전라북도 전주시', '010-0123-4567', 15, '2024-01-10 12:00:00', '2024-01-10 12:00:00', TRUE, + '09:00-18:00'); + +INSERT INTO tb_member (MEM_ID, MEM_LOGN_ID, MEM_PWD, MEM_NAME, MEM_EMA, MEM_AGE, MEM_SEX, MEM_IDEN_NO, MEM_PHO, MEM_ADR, + MEM_POS, MEM_GRD, MEM_JOB_TYPE, CENTER_ID, ORG_CHA_ID, CREATED_AT, UPDATED_AT, ACTIVE) +VALUES ('MEM_000000001', 101, 'pwd1234', '김철수', 'chulsoo@example.com', 35, 'MALE', '123456-7890123', '010-9876-5432', + '서울특별시', 'Manager', 'Bachelor', 'Full-time', 'CEN_000000001', 'ORG_000000006', '2024-01-01 12:00:00', + '2024-01-01 12:00:00', TRUE), + ('MEM_000000002', 102, 'pwd2345', '이영희', 'younghee@example.com', 28, 'FEMALE', '234567-8901234', '010-8765-4321', + '부산광역시', 'Staff', 'Master', 'Full-time', 'CEN_000000002', 'ORG_000000006', '2024-01-02 12:00:00', + '2024-01-02 12:00:00', TRUE), + ('MEM_000000003', 103, 'pwd3456', '박지훈', 'jihun@example.com', 42, 'MALE', '345678-9012345', '010-7654-3210', + '대구광역시', 'Engineer', 'Doctorate', 'Contract', 'CEN_000000003', 'ORG_000000007', '2024-01-03 12:00:00', + '2024-01-03 12:00:00', TRUE), + ('MEM_000000004', 104, 'pwd4567', '최민수', 'minsoo@example.com', 38, 'MALE', '456789-0123456', '010-6543-2109', + '인천광역시', 'Manager', 'Bachelor', 'Part-time', 'CEN_000000004', 'ORG_000000007', '2024-01-04 12:00:00', + '2024-01-04 12:00:00', TRUE), + ('MEM_000000005', 105, 'pwd5678', '박미영', 'miyoung@example.com', 29, 'FEMALE', '567890-1234567', '010-5432-1098', + '울산광역시', 'Intern', 'High School', 'Full-time', 'CEN_000000005', 'ORG_000000008', '2024-01-05 12:00:00', + '2024-01-05 12:00:00', TRUE), + ('MEM_000000006', 106, 'pwd6789', '정수현', 'soohyun@example.com', 31, 'FEMALE', '678901-2345678', '010-4321-0987', + '대전광역시', 'Senior Engineer', 'Master', 'Full-time', 'CEN_000000006', 'ORG_000000007', '2024-01-06 12:00:00', + '2024-01-06 12:00:00', TRUE), + ('MEM_000000007', 107, 'pwd7890', '한지민', 'jimin@example.com', 45, 'FEMALE', '789012-3456789', '010-3210-9876', + '광주광역시', 'Sales Rep', 'Bachelor', 'Part-time', 'CEN_000000007', 'ORG_000000006', '2024-01-07 12:00:00', + '2024-01-07 12:00:00', TRUE), + ('MEM_000000008', 108, 'pwd8901', '이준호', 'junho@example.com', 36, 'MALE', '890123-4567890', '010-2109-8765', + '경기도 수원시', 'Technician', 'Bachelor', 'Full-time', 'CEN_000000008', 'ORG_000000008', '2024-01-08 12:00:00', + '2024-01-08 12:00:00', TRUE), + ('MEM_000000009', 109, 'pwd9012', '윤아름', 'areum@example.com', 27, 'FEMALE', '901234-5678901', '010-1098-7654', + '충청북도 청주시', 'Assistant', 'High School', 'Intern', 'CEN_000000009', 'ORG_000000006', '2024-01-09 12:00:00', + '2024-01-09 12:00:00', TRUE), + ('MEM_000000010', 110, 'pwd0123', '김정훈', 'junghoon@example.com', 33, 'MALE', '012345-6789012', '010-0987-6543', + '전라북도 전주시', 'Consultant', 'Master', 'Contract', 'CEN_000000010', 'ORG_000000009', '2024-01-10 12:00:00', + '2024-01-10 12:00:00', TRUE); + +INSERT INTO tb_member_role (MEM_ROL_ID, MEM_ROL_NAME, MEM_ID) +VALUES ('ROL_000000001', '팀장', 'MEM_000000001'), + ('ROL_000000002', '영업사원', 'MEM_000000002'), + ('ROL_000000003', '기술지원', 'MEM_000000003'), + ('ROL_000000004', '관리자', 'MEM_000000004'), + ('ROL_000000005', '고객관리', 'MEM_000000005'), + ('ROL_000000006', '인턴', 'MEM_000000009'), + ('ROL_000000007', '파트타임', 'MEM_000000007'), + ('ROL_000000008', '정규직', 'MEM_000000008'), + ('ROL_000000009', '계약직', 'MEM_000000010'), + ('ROL_000000010', '프로젝트 매니저', 'MEM_000000006'); + +INSERT INTO tb_customer_info (CUST_ID, CUST_NAME, CUST_AGE, CUST_SEX, CUST_PHO, CUST_EMER_PHO, CUST_EMA, ACTIVE, MEM_ID) +VALUES ('CUS_000000001', '홍길동', 45, 'MALE', '010-1111-2222', '010-2222-3333', 'gildong@example.com', TRUE, + 'MEM_000000001'), + ('CUS_000000002', '김민수', 38, 'MALE', '010-3333-4444', '010-4444-5555', 'minsoo@example.com', TRUE, + 'MEM_000000002'), + ('CUS_000000003', '이영희', 32, 'FEMALE', '010-5555-6666', '010-6666-7777', 'younghee@example.com', TRUE, + 'MEM_000000003'), + ('CUS_000000004', '박지훈', 27, 'MALE', '010-7777-8888', '010-8888-9999', 'jihun@example.com', TRUE, + 'MEM_000000004'), + ('CUS_000000005', '최정민', 22, 'FEMALE', '010-9999-0000', '010-0000-1111', 'jungmin@example.com', TRUE, + 'MEM_000000005'), + ('CUS_000000006', '정수민', 31, 'MALE', '010-1212-3434', '010-2323-4545', 'suming@example.com', TRUE, + 'MEM_000000006'), + ('CUS_000000007', '한민정', 29, 'FEMALE', '010-4545-5656', '010-5656-6767', 'hanmj@example.com', TRUE, + 'MEM_000000007'), + ('CUS_000000008', '이동수', 41, 'MALE', '010-6767-7878', '010-7878-8989', 'dongsu@example.com', TRUE, + 'MEM_000000008'), + ('CUS_000000009', '윤소라', 33, 'FEMALE', '010-8989-9090', '010-9090-1010', 'sora@example.com', TRUE, + 'MEM_000000009'), + ('CUS_000000010', '박성훈', 36, 'MALE', '010-1010-1112', '010-1212-1313', 'seonghun@example.com', TRUE, + 'MEM_000000010'); + +INSERT INTO tb_product (PROD_ID, PROD_SER_NO, PROD_COST, PROD_NAME, PROD_STCK, CREATED_AT, UPDATED_AT, ACTIVE) +VALUES ('PRO_000000001', 'KNAHAA4AALU1A00001', 25000000, '쏘렌토', 10, '2024-01-10 10:00:00', '2024-01-10 11:00:00', TRUE), + ('PRO_000000002', 'KNAHBA4BALR2Z00002', 22000000, '스포티지', 15, '2024-01-11 11:00:00', '2024-01-11 12:00:00', + TRUE), + ('PRO_000000003', 'KMBHC64CAMJ5A00003', 28000000, 'K7', 8, '2024-01-12 12:00:00', '2024-01-12 13:00:00', TRUE), + ('PRO_000000004', 'KNJFA42DALU3C00004', 19000000, '셀토스', 12, '2024-01-13 13:00:00', '2024-01-13 14:00:00', TRUE), + ('PRO_000000005', 'KFBGBM5EARP1M00005', 18000000, 'K3', 20, '2024-01-14 14:00:00', '2024-01-14 15:00:00', TRUE), + ('PRO_000000006', 'KNHGA6BALUP7A00006', 34000000, '모하비', 7, '2024-01-15 15:00:00', '2024-01-15 16:00:00', TRUE), + ('PRO_000000007', 'KNJFA34AALU4Z00007', 32000000, 'K8', 5, '2024-01-16 16:00:00', '2024-01-16 17:00:00', TRUE), + ('PRO_000000008', 'KMAHDA2AAMJ3T00008', 27000000, '스팅어', 9, '2024-01-17 17:00:00', '2024-01-17 18:00:00', TRUE), + ('PRO_000000009', 'KNAHCA5BALU5C00009', 23000000, '니로', 14, '2024-01-18 18:00:00', '2024-01-18 19:00:00', TRUE), + ('PRO_000000010', 'KNFHC54CAMR1A00010', 28000000, 'K5', 6, '2024-01-19 19:00:00', '2024-01-19 20:00:00', TRUE); + + +INSERT INTO tb_contract (CONR_ID, CONR_NAME, CONR_CUST_NAME, CONR_CUST_IDEN_NO, CONR_CUST_ADR, + CONR_CUST_EMA, CONR_CUST_PHO, CONR_COMP_NAME, CONR_CUST_CLA, CONR_CUST_PUR_COND, + CONR_SERI_NUM, CONR_SELE_OPTI, CONR_DOWN_PAY, CONR_INTE_PAY, CONR_REM_PAY, + CONR_CONS_PAY, CONR_DELV_DATE, CONR_DELV_LOC, CONR_STAT, CONR_NO_OF_VEH, + CREATED_URL, DELETED_URL, ACTIVE, CREATED_AT, UPDATED_AT, DELETED_AT, + MEM_ID, CENT_ID, CUST_ID, PROD_ID) +VALUES +-- 계약 1 +('CON_000000001', '쏘렌토 계약', '박지훈', '880512-1234567', '서울특별시 강남구', + 'jihun@example.com', '010-1234-5678', '기아자동차', 'PERSONAL', 'CASH', + 'SER_001', '풀옵션', 5000000, 300000, 25000000, 30000000, + '2024-02-20', '서울특별시 강남구 배송센터', 'WAIT', 1, + '/contracts/1', NULL, TRUE, '2024-01-10 12:00:00', '2024-01-11 13:00:00', NULL, + 'MEM_000000001', 'CEN_000000001', 'CUS_000000001', 'PRO_000000001'), + +-- 계약 2 +('CON_000000002', '스포티지 계약', '김영희', '900123-2345678', '부산광역시 해운대구', + 'younghee@example.com', '010-2345-6789', '기아자동차', 'PERSONAL', 'CREDIT', + 'SER_002', '기본옵션', 3000000, 200000, 17000000, 25000000, + '2024-03-05', '부산광역시 해운대구 배송센터', 'CONFIRMED', 1, + '/contracts/2', NULL, TRUE, '2024-01-12 14:00:00', '2024-01-13 15:00:00', NULL, + 'MEM_000000002', 'CEN_000000002', 'CUS_000000002', 'PRO_000000002'), + +-- 계약 3 +('CON_000000003', 'K7 계약', '이철수', '950405-3456789', '대구광역시 수성구', + 'chulsoo@example.com', '010-3456-7890', '기아자동차', 'BUSINESS', 'LEASE', + 'SER_003', '풀옵션', 8000000, 400000, 28000000, 35000000, + '2024-03-10', '대구광역시 배송센터', 'WAIT', 2, + '/contracts/3', NULL, TRUE, '2024-01-14 09:00:00', '2024-01-15 10:00:00', NULL, + 'MEM_000000003', 'CEN_000000003', 'CUS_000000003', 'PRO_000000003'), + +-- 계약 4 +('CON_000000004', '셀토스 계약', '박민준', '970512-4567890', '인천광역시 미추홀구', + 'minjoon@example.com', '010-4567-8901', '기아자동차', 'PERSONAL', 'CASH', + 'SER_004', '기본옵션', 4000000, 250000, 12000000, 20000000, + '2024-04-01', '인천광역시 배송센터', 'DELIVERED', 1, + '/contracts/4', NULL, TRUE, '2024-01-16 11:00:00', '2024-01-17 12:00:00', NULL, + 'MEM_000000004', 'CEN_000000004', 'CUS_000000004', 'PRO_000000004'), + +-- 계약 5 +('CON_000000005', 'K3 계약', '최민수', '980618-5678901', '울산광역시 중구', + 'minsoo@example.com', '010-5678-9012', '기아자동차', 'BUSINESS', 'INSTALLMENT', + 'SER_005', '기본옵션', 2000000, 100000, 13000000, 18000000, + '2024-04-15', '울산광역시 배송센터', 'CANCELLED', 1, + '/contracts/5', NULL, TRUE, '2024-01-18 13:00:00', '2024-01-19 14:00:00', NULL, + 'MEM_000000005', 'CEN_000000005', 'CUS_000000005', 'PRO_000000005'), + +-- 계약 6 +('CON_000000006', '모하비 계약', '김민주', '850824-6789012', '대전광역시 서구', + 'minju@example.com', '010-6789-0123', '기아자동차', 'PERSONAL', 'CASH', + 'SER_006', '풀옵션', 6000000, 500000, 34000000, 40000000, + '2024-05-01', '대전광역시 배송센터', 'CONFIRMED', 1, + '/contracts/6', NULL, TRUE, '2024-01-20 15:00:00', '2024-01-21 16:00:00', NULL, + 'MEM_000000006', 'CEN_000000006', 'CUS_000000006', 'PRO_000000006'), + +-- 계약 7 +('CON_000000007', 'K8 계약', '정수영', '940705-7890123', '광주광역시 북구', + 'suyoung@example.com', '010-7890-1234', '기아자동차', 'LEASE', 'LEASE', + 'SER_007', '럭셔리 패키지', 7000000, 350000, 25000000, 32000000, + '2024-05-20', '광주광역시 배송센터', 'WAIT', 1, + '/contracts/7', NULL, TRUE, '2024-01-22 17:00:00', '2024-01-23 18:00:00', NULL, + 'MEM_000000007', 'CEN_000000007', 'CUS_000000007', 'PRO_000000007'), + +-- 계약 8 +('CON_000000008', '스팅어 계약', '이준호', '930910-8901234', '경기도 수원시', + 'junho@example.com', '010-8901-2345', '기아자동차', 'PERSONAL', 'CREDIT', + 'SER_008', '풀옵션', 5000000, 300000, 22000000, 27000000, + '2024-06-01', '수원시 배송센터', 'DELIVERED', 1, + '/contracts/8', NULL, TRUE, '2024-01-24 19:00:00', '2024-01-25 20:00:00', NULL, + 'MEM_000000008', 'CEN_000000008', 'CUS_000000008', 'PRO_000000008'), + +-- 계약 9 +('CON_000000009', '니로 계약', '박소영', '890321-9012345', '충청북도 청주시', + 'soyoung@example.com', '010-9012-3456', '기아자동차', 'PERSONAL', 'CASH', + 'SER_009', '기본옵션', 3500000, 150000, 17000000, 23000000, + '2024-06-15', '청주시 배송센터', 'CONFIRMED', 1, + '/contracts/9', NULL, TRUE, '2024-01-26 21:00:00', '2024-01-27 22:00:00', NULL, + 'MEM_000000009', 'CEN_000000009', 'CUS_000000009', 'PRO_000000009'), + +-- 계약 10 +('CON_000000010', 'K5 계약', '김정훈', '900815-0123456', '전라북도 전주시', + 'junghoon@example.com', '010-0123-4567', '기아자동차', 'BUSINESS', 'LEASE', + 'SER_010', '풀옵션', 6000000, 250000, 20000000, 28000000, + '2024-07-01', '전주시 배송센터', 'WAIT', 1, + '/contracts/10', NULL, TRUE, '2024-01-28 23:00:00', '2024-01-29 23:59:00', NULL, + 'MEM_000000010', 'CEN_000000010', 'CUS_000000010', 'PRO_000000010'); + +INSERT INTO tb_order (ORD_ID, ORD_TTL, ACTIVE, CREATED_AT, UPDATED_AT, ORD_STAT, CONR_ID, MEM_ID, MEM_ID2) +VALUES ('ORD_000000001', '쏘렌토 주문', TRUE, '2024-01-12 09:00:00', '2024-01-12 10:00:00', 'WAIT', 'CON_000000001', + 'MEM_000000001', 'MEM_000000002'), + ('ORD_000000002', '스포티지 주문', TRUE, '2024-01-13 10:00:00', '2024-01-13 11:00:00', 'CONFIRMED', 'CON_000000002', + 'MEM_000000002', 'MEM_000000003'), + ('ORD_000000003', 'K7 주문', TRUE, '2024-01-14 11:00:00', '2024-01-14 12:00:00', 'DELIVERED', 'CON_000000003', + 'MEM_000000003', 'MEM_000000004'), + ('ORD_000000004', '셀토스 주문', TRUE, '2024-01-15 12:00:00', '2024-01-15 13:00:00', 'CANCELLED', 'CON_000000004', + 'MEM_000000004', 'MEM_000000005'), + ('ORD_000000005', 'K3 주문', TRUE, '2024-01-16 14:00:00', '2024-01-16 15:00:00', 'WAIT', 'CON_000000005', + 'MEM_000000005', 'MEM_000000006'), + ('ORD_000000006', '모하비 주문', TRUE, '2024-01-17 09:00:00', '2024-01-17 10:00:00', 'DELIVERED', 'CON_000000006', + 'MEM_000000006', 'MEM_000000007'), + ('ORD_000000007', 'K8 주문', TRUE, '2024-01-18 10:00:00', '2024-01-18 11:00:00', 'WAIT', 'CON_000000007', + 'MEM_000000007', 'MEM_000000008'), + ('ORD_000000008', '스팅어 주문', TRUE, '2024-01-19 12:00:00', '2024-01-19 13:00:00', 'CONFIRMED', 'CON_000000008', + 'MEM_000000008', 'MEM_000000009'), + ('ORD_000000009', '니로 주문', TRUE, '2024-01-20 14:00:00', '2024-01-20 15:00:00', 'CANCELLED', 'CON_000000009', + 'MEM_000000009', 'MEM_000000010'), + ('ORD_000000010', 'K5 주문', TRUE, '2024-01-21 15:00:00', '2024-01-21 16:00:00', 'WAIT', 'CON_000000010', + 'MEM_000000010', 'MEM_000000001'); + +INSERT INTO tb_problem (PROB_ID, PROB_TTL, PROB_CONT, CREATED_AT, UPDATED_AT, ACTIVE, CUST_ID, MEM_ID, PROD_ID) +VALUES ('PROB_000000001', '쏘렌토 엔진 문제', '엔진에서 소음 발생', '2024-01-12 14:00:00', '2024-01-12 15:00:00', TRUE, + 'CUS_000000001', 'MEM_000000001', 'PRO_000000001'), + ('PROB_000000002', '스포티지 브레이크 문제', '브레이크 페달 작동 불량', '2024-01-13 10:00:00', '2024-01-13 11:00:00', TRUE, + 'CUS_000000002', 'MEM_000000002', 'PRO_000000002'), + ('PROB_000000003', 'K7 전자 장비 문제', '내비게이션 오류', '2024-01-14 09:00:00', '2024-01-14 10:00:00', TRUE, + 'CUS_000000003', 'MEM_000000003', 'PRO_000000003'), + ('PROB_000000004', '셀토스 에어컨 문제', '에어컨 작동 안됨', '2024-01-15 11:00:00', '2024-01-15 12:00:00', TRUE, + 'CUS_000000004', 'MEM_000000004', 'PRO_000000004'), + ('PROB_000000005', 'K3 연비 문제', '연비가 예상보다 낮음', '2024-01-16 12:00:00', '2024-01-16 13:00:00', TRUE, + 'CUS_000000005', 'MEM_000000005', 'PRO_000000005'), + ('PROB_000000006', '모하비 변속기 문제', '변속기 오류 발생', '2024-01-17 13:00:00', '2024-01-17 14:00:00', TRUE, + 'CUS_000000006', 'MEM_000000006', 'PRO_000000006'), + ('PROB_000000007', 'K8 배터리 문제', '배터리 수명 짧음', '2024-01-18 14:00:00', '2024-01-18 15:00:00', TRUE, 'CUS_000000007', + 'MEM_000000007', 'PRO_000000007'), + ('PROB_000000008', '스팅어 오디오 문제', '오디오 스피커 잡음', '2024-01-19 15:00:00', '2024-01-19 16:00:00', TRUE, + 'CUS_000000008', 'MEM_000000008', 'PRO_000000008'), + ('PROB_000000009', '니로 전기 문제', '충전 불량', '2024-01-20 16:00:00', '2024-01-20 17:00:00', TRUE, 'CUS_000000009', + 'MEM_000000009', 'PRO_000000009'), + ('PROB_000000010', 'K5 시동 문제', '시동이 걸리지 않음', '2024-01-21 17:00:00', '2024-01-21 18:00:00', TRUE, 'CUS_000000010', + 'MEM_000000010', 'PRO_000000010'); + +INSERT INTO tb_purchase_order (PUR_ORD_ID, PUR_ORD_TTL, PUR_CONT, CREATED_AT, UPDATED_AT, ACTIVE, PUR_ORD_STAT, ORD_ID, + MEM_ID) +VALUES ('PUR_000000001', '쏘렌토 부품 주문', '엔진 부품 요청', '2024-01-12 10:00:00', '2024-01-12 11:00:00', TRUE, 'WAIT', + 'ORD_000000001', 'MEM_000000001'), + ('PUR_000000002', '스포티지 타이어 주문', '타이어 4개 요청', '2024-01-13 12:00:00', '2024-01-13 13:00:00', TRUE, 'CONFIRMED', + 'ORD_000000002', 'MEM_000000002'), + ('PUR_000000003', 'K7 배터리 주문', '배터리 교체 요청', '2024-01-14 13:00:00', '2024-01-14 14:00:00', TRUE, 'DELIVERED', + 'ORD_000000003', 'MEM_000000003'), + ('PUR_000000004', '셀토스 브레이크 주문', '브레이크 패드 요청', '2024-01-15 14:00:00', '2024-01-15 15:00:00', TRUE, 'WAIT', + 'ORD_000000004', 'MEM_000000004'), + ('PUR_000000005', 'K3 내비게이션 주문', '내비게이션 교체', '2024-01-16 15:00:00', '2024-01-16 16:00:00', TRUE, 'CANCELLED', + 'ORD_000000005', 'MEM_000000005'), + ('PUR_000000006', '모하비 오일 필터 주문', '오일 필터 10개 요청', '2024-01-17 09:00:00', '2024-01-17 10:00:00', TRUE, + 'CONFIRMED', 'ORD_000000006', 'MEM_000000006'), + ('PUR_000000007', 'K8 헤드라이트 주문', 'LED 헤드라이트 2개 요청', '2024-01-18 10:00:00', '2024-01-18 11:00:00', TRUE, 'WAIT', + 'ORD_000000007', 'MEM_000000007'), + ('PUR_000000008', '스팅어 도어 핸들 주문', '도어 핸들 4개 요청', '2024-01-19 12:00:00', '2024-01-19 13:00:00', TRUE, 'CONFIRMED', + 'ORD_000000008', 'MEM_000000008'), + ('PUR_000000009', '니로 충전 케이블 주문', '전기차 충전 케이블 요청', '2024-01-20 14:00:00', '2024-01-20 15:00:00', TRUE, + 'DELIVERED', 'ORD_000000009', 'MEM_000000009'), + ('PUR_000000010', 'K5 엔진오일 주문', '엔진오일 20L 요청', '2024-01-21 15:00:00', '2024-01-21 16:00:00', TRUE, 'WAIT', + 'ORD_000000010', 'MEM_000000010'); + +INSERT INTO tb_notice (NOT_ID, NOT_TTL, NOT_TAG, NOT_CLA, NOT_CONT, CREATED_AT, UPDATED_AT, ACTIVE, MEM_ID) +VALUES ('NOT_000000001', '신차 출시 공지', 'ALL', 'IMPORTANT', '신형 쏘렌토가 출시되었습니다.', '2024-01-10 09:00:00', + '2024-01-10 09:30:00', TRUE, 'MEM_000000001'), + ('NOT_000000002', '정기 점검 공지', 'SERVICE', 'NORMAL', '1월 정기 점검 안내입니다.', '2024-01-11 10:00:00', + '2024-01-11 10:30:00', TRUE, 'MEM_000000002'), + ('NOT_000000003', '할인 프로모션', 'SALES', 'IMPORTANT', 'K5 한정 할인 프로모션 안내', '2024-01-12 11:00:00', + '2024-01-12 11:30:00', TRUE, 'MEM_000000003'), + ('NOT_000000004', '서비스 센터 이전 안내', 'SERVICE', 'NORMAL', '서울센터가 이전되었습니다.', '2024-01-13 12:00:00', + '2024-01-13 12:30:00', TRUE, 'MEM_000000004'), + ('NOT_000000005', '부품 재고 부족', 'ALL', 'WARNING', '모하비 부품 재고가 부족합니다.', '2024-01-14 13:00:00', + '2024-01-14 13:30:00', TRUE, 'MEM_000000005'), + ('NOT_000000006', '설 연휴 휴무 안내', 'ALL', 'NORMAL', '설 연휴 기간 동안 휴무합니다.', '2024-01-15 14:00:00', + '2024-01-15 14:30:00', TRUE, 'MEM_000000006'), + ('NOT_000000007', 'K8 시승 이벤트', 'EVENT', 'NORMAL', 'K8 시승 이벤트에 참여하세요.', '2024-01-16 15:00:00', + '2024-01-16 15:30:00', TRUE, 'MEM_000000007'), + ('NOT_000000008', '스포티지 리콜 안내', 'SERVICE', 'CRITICAL', '스포티지 일부 모델 리콜 안내', '2024-01-17 16:00:00', + '2024-01-17 16:30:00', TRUE, 'MEM_000000008'), + ('NOT_000000009', '신입사원 모집', 'HR', 'NORMAL', '기아자동차 신입사원 모집 공고', '2024-01-18 17:00:00', '2024-01-18 17:30:00', + TRUE, 'MEM_000000009'), + ('NOT_000000010', '고객 감사 이벤트', 'EVENT', 'NORMAL', '고객 감사 사은품 증정 이벤트', '2024-01-19 18:00:00', + '2024-01-19 18:30:00', TRUE, 'MEM_000000010'); + +INSERT INTO tb_file (FILE_ID, FILE_NAME, FILE_URL, FILE_TYPE, ACTIVE, CREATED_AT, MEM_ID, NOT_ID) +VALUES ('FIL_000000001', '쏘렌토_계약서.pdf', '/files/contract1.pdf', 'pdf', TRUE, '2024-01-10 10:00:00', 'MEM_000000001', + 'NOT_000000001'), + ('FIL_000000002', '스포티지_메뉴얼.pdf', '/files/manual2.pdf', 'pdf', TRUE, '2024-01-11 10:30:00', 'MEM_000000002', + 'NOT_000000002'), + ('FIL_000000003', 'K7_견적서.pdf', '/files/estimate3.pdf', 'pdf', TRUE, '2024-01-12 11:00:00', 'MEM_000000003', + 'NOT_000000003'), + ('FIL_000000004', '셀토스_리콜_공지.pdf', '/files/recall4.pdf', 'pdf', TRUE, '2024-01-13 12:00:00', 'MEM_000000004', + 'NOT_000000004'), + ('FIL_000000005', 'K3_정비보고서.pdf', '/files/report5.pdf', 'pdf', TRUE, '2024-01-14 13:00:00', 'MEM_000000005', + 'NOT_000000005'), + ('FIL_000000006', '모하비_오일교환.pdf', '/files/oil6.pdf', 'pdf', TRUE, '2024-01-15 14:00:00', 'MEM_000000006', + 'NOT_000000006'), + ('FIL_000000007', 'K8_이벤트_포스터.png', '/files/event7.png', 'image', TRUE, '2024-01-16 15:00:00', 'MEM_000000007', + 'NOT_000000007'), + ('FIL_000000008', '스팅어_사용설명서.pdf', '/files/manual8.pdf', 'pdf', TRUE, '2024-01-17 16:00:00', 'MEM_000000008', + 'NOT_000000008'), + ('FIL_000000009', '니로_충전가이드.pdf', '/files/guide9.pdf', 'pdf', TRUE, '2024-01-18 17:00:00', 'MEM_000000009', + 'NOT_000000009'), + ('FIL_000000010', 'K5_고객이벤트_배너.jpg', '/files/banner10.jpg', 'image', TRUE, '2024-01-19 18:00:00', + 'MEM_000000010', 'NOT_000000010'); + +INSERT INTO tb_schedule (SCH_ID, SCH_NAME, SCH_CONT, SCH_SRT_AT, SCH_END_AT, CREATED_AT, UPDATED_AT, DELETED_AT, ACTIVE, + MEM_ID) +VALUES ('SCH_000000001', '쏘렌토 신차 발표회', '신차 발표회 준비 및 진행', '2024-02-01 09:00:00', '2024-02-01 12:00:00', + '2024-01-20 10:00:00', '2024-01-20 10:30:00', NULL, TRUE, 'MEM_000000001'), + ('SCH_000000002', '스포티지 고객 상담', '신차 구매 고객 상담 일정', '2024-02-05 10:00:00', '2024-02-05 11:30:00', + '2024-01-21 11:00:00', '2024-01-21 11:30:00', NULL, TRUE, 'MEM_000000002'), + ('SCH_000000003', 'K7 프로모션 회의', '프로모션 기획 및 준비 회의', '2024-02-10 13:00:00', '2024-02-10 15:00:00', + '2024-01-22 12:00:00', '2024-01-22 12:30:00', NULL, TRUE, 'MEM_000000003'), + ('SCH_000000004', '셀토스 시승 이벤트', '고객 대상 시승 이벤트', '2024-02-12 14:00:00', '2024-02-12 17:00:00', + '2024-01-23 13:00:00', '2024-01-23 13:30:00', NULL, TRUE, 'MEM_000000004'), + ('SCH_000000005', 'K3 서비스 교육', '서비스 센터 직원 교육', '2024-02-15 09:00:00', '2024-02-15 12:00:00', + '2024-01-24 14:00:00', '2024-01-24 14:30:00', NULL, TRUE, 'MEM_000000005'), + ('SCH_000000006', '모하비 유지보수 회의', '유지보수 전략 논의', '2024-02-17 10:00:00', '2024-02-17 11:00:00', + '2024-01-25 15:00:00', '2024-01-25 15:30:00', NULL, TRUE, 'MEM_000000006'), + ('SCH_000000007', 'K8 내부 검토', '신차 내부 디자인 검토', '2024-02-20 11:00:00', '2024-02-20 13:00:00', + '2024-01-26 16:00:00', '2024-01-26 16:30:00', NULL, TRUE, 'MEM_000000007'), + ('SCH_000000008', '스팅어 기술 세미나', '스팅어 기술 세미나 및 발표', '2024-02-22 14:00:00', '2024-02-22 16:00:00', + '2024-01-27 17:00:00', '2024-01-27 17:30:00', NULL, TRUE, 'MEM_000000008'), + ('SCH_000000009', '니로 전기차 테스트', '니로 전기차 충전 테스트', '2024-02-25 09:00:00', '2024-02-25 11:00:00', + '2024-01-28 18:00:00', '2024-01-28 18:30:00', NULL, TRUE, 'MEM_000000009'), + ('SCH_000000010', 'K5 재고 점검', 'K5 재고 관리 및 점검', '2024-02-28 15:00:00', '2024-02-28 17:00:00', + '2024-01-29 19:00:00', '2024-01-29 19:30:00', NULL, TRUE, 'MEM_000000010'); + +INSERT INTO tb_alarm (ALR_ID, ALR_MSG, ALR_URL, ALR_READ_STAT, CREATED_AT, MEM_ID) +VALUES ('ALR_000000001', '신차 출시 공지 알림', '/notices/1', FALSE, '2024-01-20 12:00:00', 'MEM_000000001'), + ('ALR_000000002', '스포티지 리콜 안내', '/notices/2', FALSE, '2024-01-21 14:00:00', 'MEM_000000002'), + ('ALR_000000003', 'K7 고객 이벤트 초대', '/events/3', TRUE, '2024-01-22 09:00:00', 'MEM_000000003'), + ('ALR_000000004', '셀토스 정비 완료', '/service/4', FALSE, '2024-01-23 10:00:00', 'MEM_000000004'), + ('ALR_000000005', 'K3 테스트 드라이브 일정 변경', '/schedules/5', TRUE, '2024-01-24 11:00:00', 'MEM_000000005'), + ('ALR_000000006', '모하비 부품 입고 알림', '/inventory/6', FALSE, '2024-01-25 12:00:00', 'MEM_000000006'), + ('ALR_000000007', 'K8 디자인 변경 확정', '/design/7', TRUE, '2024-01-26 13:00:00', 'MEM_000000007'), + ('ALR_000000008', '스팅어 성능 테스트 보고서', '/reports/8', FALSE, '2024-01-27 14:00:00', 'MEM_000000008'), + ('ALR_000000009', '니로 전기차 충전소 위치', '/maps/9', TRUE, '2024-01-28 15:00:00', 'MEM_000000009'), + ('ALR_000000010', 'K5 재고 부족 경고', '/inventory/10', FALSE, '2024-01-29 16:00:00', 'MEM_000000010'); + +INSERT INTO tb_member_detail (MEM_DET_ID, MEM_DET_REL, MEM_DET_NAME, MEM_DET_BIR, MEM_DET_IDEN_NO, MEM_DET_PHO, + MEM_DET_SEX, MEM_DET_DIS, MEM_DET_DIE, MEM_DET_NOTE, MEM_DET_ENTD, MEM_DET_GRAD, + MEM_DET_FNL_EDC, MEM_DET_EDU, MEM_DET_MJR, MEM_DET_EMP_DATE, MEM_DET_RTR_DATE, CAR_INFO, + CERT_DATE, CERT_INST, CERT_NAME, CERT_SCO, MEM_ID) +VALUES ('DET_000000001', '배우자', '박미숙', '1988-05-12', '880512-1234567', '010-1111-2222', 'FEMALE', FALSE, FALSE, NULL, + '2010', 'A', '대졸', '경영학', '기아', '2020-01-01', '2025-01-01', 'K5', '2023-06-01', '기아자동차', '세일즈', '95', + 'MEM_000000001'), + ('DET_000000002', '자녀', '김철수', '2012-08-05', '120805-2345678', '010-2222-3333', 'MALE', FALSE, FALSE, NULL, + '2022', 'B+', '고졸', '정보통신', '기아', '2022-01-15', '2027-01-15', 'K3', '2023-07-15', '기아자동차', '서비스', '88', + 'MEM_000000002'), + ('DET_000000003', '배우자', '이영희', '1985-03-09', '850309-3456789', '010-3333-4444', 'FEMALE', FALSE, FALSE, + '부부 동반 여행', '2009', 'A-', '대졸', '경제학', '기아', '2019-05-12', '2024-05-12', '스포티지', '2023-04-20', '기아자동차', '기술지원', + '92', 'MEM_000000003'), + ('DET_000000004', '배우자', '정수민', '1990-11-11', '901111-4567890', '010-4444-5555', 'FEMALE', FALSE, FALSE, NULL, + '2015', 'A', '대졸', '기계공학', '기아', '2021-09-01', '2026-09-01', 'K7', '2023-02-10', '기아자동차', '품질관리', '87', + 'MEM_000000004'), + ('DET_000000005', '자녀', '한지수', '2016-01-20', '160120-5678901', '010-5555-6666', 'MALE', TRUE, FALSE, '특별 교육 필요', + '2021', 'B', '초등학교', '과학', '기아', '2023-03-05', '2028-03-05', '니로', '2023-03-01', '기아자동차', '연구개발', '85', + 'MEM_000000005'), + ('DET_000000006', '자녀', '최지우', '2005-06-15', '050615-6789012', '010-6666-7777', 'FEMALE', FALSE, FALSE, NULL, + '2023', 'A+', '대졸', '디자인', '기아', '2020-07-01', '2025-07-01', '스팅어', '2023-08-20', '기아자동차', '디자인', '90', + 'MEM_000000006'), + ('DET_000000007', '배우자', '윤미라', '1987-12-30', '871230-7890123', '010-7777-8888', 'FEMALE', FALSE, FALSE, + '해외 출장 동반', '2011', 'B+', '대졸', '마케팅', '기아', '2020-12-01', '2025-12-01', '쏘렌토', '2023-01-25', '기아자동차', '마케팅', + '93', 'MEM_000000007'), + ('DET_000000008', '자녀', '이동희', '2010-09-10', '100910-8901234', '010-8888-9999', 'MALE', FALSE, FALSE, NULL, + '2025', 'B-', '중졸', '정보기술', '기아', '2023-05-05', '2028-05-05', '모하비', '2023-12-30', '기아자동차', '엔지니어링', '89', + 'MEM_000000008'), + ('DET_000000009', '배우자', '정수영', '1991-04-17', '910417-9012345', '010-9999-0000', 'FEMALE', FALSE, FALSE, + '가족 동반 캠핑', '2018', 'A-', '대졸', '화학', '기아', '2022-06-15', '2027-06-15', '셀토스', '2023-11-22', '기아자동차', '생산', + '91', 'MEM_000000009'), + ('DET_000000010', '자녀', '김준호', '2014-07-22', '140722-0123456', '010-0000-1111', 'MALE', FALSE, FALSE, NULL, + '2027', 'B+', '고졸', '경영', '기아', '2023-08-12', '2028-08-12', 'K8', '2023-09-10', '기아자동차', '세일즈', '87', + 'MEM_000000010'); + +INSERT INTO tb_update_history (UPD_ID, UPD_IP, UPDATED_AT, UPDATED_URL, MEM_ID, CONR_ID) +VALUES ('UPD_000000001', '192.168.1.10', '2024-01-10 12:00:00', '/contracts/1', 'MEM_000000001', 'CON_000000001'), + ('UPD_000000002', '192.168.1.20', '2024-01-11 14:00:00', '/contracts/2', 'MEM_000000002', 'CON_000000002'), + ('UPD_000000003', '192.168.1.30', '2024-01-12 16:00:00', '/contracts/3', 'MEM_000000003', 'CON_000000003'), + ('UPD_000000004', '192.168.1.40', '2024-01-13 09:00:00', '/contracts/4', 'MEM_000000004', 'CON_000000004'), + ('UPD_000000005', '192.168.1.50', '2024-01-14 10:00:00', '/contracts/5', 'MEM_000000005', 'CON_000000005'), + ('UPD_000000006', '192.168.1.60', '2024-01-15 11:00:00', '/contracts/6', 'MEM_000000006', 'CON_000000006'), + ('UPD_000000007', '192.168.1.70', '2024-01-16 12:00:00', '/contracts/7', 'MEM_000000007', 'CON_000000007'), + ('UPD_000000008', '192.168.1.80', '2024-01-17 13:00:00', '/contracts/8', 'MEM_000000008', 'CON_000000008'), + ('UPD_000000009', '192.168.1.90', '2024-01-18 14:00:00', '/contracts/9', 'MEM_000000009', 'CON_000000009'), + ('UPD_000000010', '192.168.1.100', '2024-01-19 15:00:00', '/contracts/10', 'MEM_000000010', 'CON_000000010'); + +INSERT INTO tb_evaluation (EVAL_ID, EVAL_TTL, EVAL_CONT, CREATED_AT, UPDATED_AT, ACTIVE, CENT_ID, MEM_ID, WRI_ID) +VALUES ('EVAL_000000001', '쏘렌토 성능 평가', '쏘렌토의 성능에 대한 평가입니다.', '2024-01-20 10:00:00', '2024-01-21 10:30:00', TRUE, + 'CEN_000000001', 'MEM_000000001', 'MEM_000000002'), + ('EVAL_000000002', '스포티지 안전 평가', '스포티지의 안전성 평가 보고서.', '2024-01-21 11:00:00', '2024-01-22 11:30:00', TRUE, + 'CEN_000000002', 'MEM_000000002', 'MEM_000000003'), + ('EVAL_000000003', 'K7 고객 만족도 평가', '고객 만족도 조사 결과입니다.', '2024-01-23 12:00:00', '2024-01-24 12:30:00', TRUE, + 'CEN_000000003', 'MEM_000000003', 'MEM_000000004'), + ('EVAL_000000004', '셀토스 디자인 평가', '셀토스 디자인 피드백.', '2024-01-25 13:00:00', '2024-01-26 13:30:00', TRUE, + 'CEN_000000004', 'MEM_000000004', 'MEM_000000005'), + ('EVAL_000000005', 'K3 내부 공간 평가', '내부 공간의 효율성 평가.', '2024-01-27 14:00:00', '2024-01-28 14:30:00', TRUE, + 'CEN_000000005', 'MEM_000000005', 'MEM_000000006'), + ('EVAL_000000006', '모하비 유지비 평가', '유지 비용과 효율성 평가.', '2024-01-29 15:00:00', '2024-01-30 15:30:00', TRUE, + 'CEN_000000006', 'MEM_000000006', 'MEM_000000007'), + ('EVAL_000000007', 'K8 디자인 리뉴얼', '새로운 디자인에 대한 평가.', '2024-02-01 16:00:00', '2024-02-02 16:30:00', TRUE, + 'CEN_000000007', 'MEM_000000007', 'MEM_000000008'), + ('EVAL_000000008', '스팅어 출력 평가', '스팅어의 출력 성능 보고서.', '2024-02-03 17:00:00', '2024-02-04 17:30:00', TRUE, + 'CEN_000000008', 'MEM_000000008', 'MEM_000000009'), + ('EVAL_000000009', '니로 전기차 평가', '전기차 효율성 평가 결과.', '2024-02-05 18:00:00', '2024-02-06 18:30:00', TRUE, + 'CEN_000000009', 'MEM_000000009', 'MEM_000000010'), + ('EVAL_000000010', 'K5 연비 평가', 'K5의 연비 효율성 보고서.', '2024-02-07 19:00:00', '2024-02-08 19:30:00', TRUE, + 'CEN_000000010', 'MEM_000000010', 'MEM_000000001'); + +INSERT INTO tb_promotion (PROM_ID, PROM_TTL, PROM_CONT, CREATED_AT, UPDATED_AT, ACTIVE, MEM_ID) +VALUES ('PROM_000000001', '쏘렌토 겨울 특별 할인', '쏘렌토 구매 시 15% 할인 제공', '2024-01-05 09:00:00', '2024-01-06 10:00:00', TRUE, + 'MEM_000000001'), + ('PROM_000000002', '스포티지 리스 프로모션', '리스 계약 시 추가 혜택 제공', '2024-01-10 11:00:00', '2024-01-10 12:00:00', TRUE, + 'MEM_000000002'), + ('PROM_000000003', 'K7 고객 감사 이벤트', 'K7 구매 고객 대상 사은품 증정', '2024-01-15 14:00:00', '2024-01-15 15:00:00', TRUE, + 'MEM_000000003'), + ('PROM_000000004', '셀토스 한정 프로모션', '셀토스 구매 시 무료 보증 연장', '2024-01-20 09:00:00', '2024-01-20 10:00:00', TRUE, + 'MEM_000000004'), + ('PROM_000000005', 'K3 연비 보장 이벤트', 'K3 연비 테스트 이벤트 참여 시 혜택 제공', '2024-01-25 13:00:00', '2024-01-25 14:00:00', + TRUE, 'MEM_000000005'), + ('PROM_000000006', '모하비 프리미엄 서비스', '모하비 구매 고객 대상 프리미엄 서비스 제공', '2024-01-30 11:00:00', '2024-01-31 12:00:00', + TRUE, 'MEM_000000006'), + ('PROM_000000007', 'K8 럭셔리 패키지 할인', '럭셔리 패키지 선택 시 10% 할인 제공', '2024-02-01 12:00:00', '2024-02-02 13:00:00', TRUE, + 'MEM_000000007'), + ('PROM_000000008', '스팅어 시승 이벤트', '스팅어 시승 후 계약 시 추가 혜택', '2024-02-05 10:00:00', '2024-02-05 11:00:00', TRUE, + 'MEM_000000008'), + ('PROM_000000009', '니로 전기차 구매 지원', '전기차 구매 시 충전기 설치 지원', '2024-02-10 09:00:00', '2024-02-10 10:00:00', TRUE, + 'MEM_000000009'), + ('PROM_000000010', 'K5 재고 한정 특별 할인', '재고 한정 K5 모델에 대한 추가 할인 제공', '2024-02-15 15:00:00', '2024-02-16 16:00:00', + TRUE, 'MEM_000000010'); + +INSERT INTO tb_product_option (PROD_ID, PROD_SER_NO, OPT_CNTY, OPT_MNFR, OPT_VHC_TYPE, OPT_CHSS, OPT_DTIL_TYPE, + OPT_BODY_TYPE, + OPT_SFTY_DVCE, OPT_ENGN_CPCT, OPT_SCRT_CODE, OPT_PRDC_YEAR, OPT_PRDC_PLNT, OPT_ENGN, + OPT_MSSN, + OPT_TRIM, OPT_XTNL_COLR, OPT_ITNL_COLR, OPT_HUD, OPT_NAVI, OPT_DRVE_WISE, OPT_SMRT_CNCT, + OPT_STYL, OPT_MY_CFRT_PCKG, OPT_OTDR_PCKG, OPT_SUN_ROOF, OPT_SOND, ACTIVE) +VALUES +-- 데이터 1 +('PRO_000000001', 'KNAHAA4AALU1A00001', 'K', 'N', 'A', 'A', 'L', '4', '2', 'A', 'P', 'A', 'U', '1', '0', '1', 'B', 'W', + '1', '1', '1', '1', '1', '0', '1', '1', '1', TRUE), +-- 데이터 2 +('PRO_000000002', 'KNAHBA4BALR2Z00002', 'K', 'N', 'H', 'B', 'L', '4', '4', 'B', 'R', 'B', 'Z', '1', '1', '0', 'G', 'B', + '0', '1', '0', '1', '0', '1', '0', '0', '1', TRUE), +-- 데이터 3 +('PRO_000000003', 'KMBHC64CAMJ5A00003', 'K', 'M', 'H', 'C', 'M', '6', '3', 'C', 'P', 'C', 'A', '0', '1', '1', 'R', 'G', + '1', '1', '1', '0', '1', '1', '0', '1', '0', TRUE), +-- 데이터 4 +('PRO_000000004', 'KNJFA42DALU3C00004', 'K', 'N', 'J', 'D', 'L', '2', '4', 'A', 'R', 'D', 'C', '1', '0', '0', 'B', 'R', + '0', '0', '0', '1', '0', '0', '1', '0', '1', TRUE), +-- 데이터 5 +('PRO_000000005', 'KFBGBM5EARP1M00005', 'K', 'B', 'F', 'B', 'M', '5', '1', 'B', 'P', 'E', 'M', '1', '1', '1', 'W', 'B', + '1', '1', '0', '0', '1', '1', '0', '1', '1', TRUE), +-- 데이터 6 +('PRO_000000006', 'KNHGA6BALUP7A00006', 'K', 'N', 'H', 'G', 'L', '6', '3', 'A', 'R', 'F', 'A', '0', '0', '1', 'B', 'G', + '1', '1', '1', '1', '0', '1', '1', '0', '0', TRUE), +-- 데이터 7 +('PRO_000000007', 'KNJFA34AALU4Z00007', 'K', 'N', 'J', 'A', 'M', '4', '4', 'C', 'P', 'G', 'Z', '1', '1', '0', 'R', 'W', + '0', '0', '1', '0', '1', '0', '1', '1', '1', TRUE), +-- 데이터 8 +('PRO_000000008', 'KMAHDA2AAMJ3T00008', 'K', 'M', 'H', 'D', 'L', '2', '2', 'A', 'R', 'H', 'T', '0', '0', '1', 'B', 'G', + '1', '1', '0', '1', '0', '1', '1', '0', '0', TRUE), +-- 데이터 9 +('PRO_000000009', 'KNAHCA5BALU5C00009', 'K', 'N', 'A', 'C', 'M', '5', '1', 'B', 'P', 'J', 'C', '1', '1', '0', 'R', 'R', + '0', '1', '1', '1', '1', '0', '1', '1', '0', TRUE), +-- 데이터 10 +('PRO_000000010', 'KNFHC54CAMR1A00010', 'K', 'N', 'F', 'F', 'N', '4', '3', 'C', 'R', 'K', 'A', '0', '1', '1', 'W', 'B', + '1', '1', '0', '0', '0', '1', '0', '1', '1', TRUE); + +INSERT INTO tb_sales_history (SAL_HIST_ID, CONR_ID) +VALUES ('SAL_000000001', 'CON_000000001'), + ('SAL_000000002', 'CON_000000002'), + ('SAL_000000003', 'CON_000000003'), + ('SAL_000000004', 'CON_000000004'), + ('SAL_000000005', 'CON_000000005'), + ('SAL_000000006', 'CON_000000006'), + ('SAL_000000007', 'CON_000000007'), + ('SAL_000000008', 'CON_000000008'), + ('SAL_000000009', 'CON_000000009'), + ('SAL_000000010', 'CON_000000010'); + + From 296e84f5cdba229b17b6f9a1fdd163adb178f27e Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Tue, 12 Nov 2024 11:22:35 +0900 Subject: [PATCH 012/563] =?UTF-8?q?feat:=20ddl=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EB=B3=B8=20merge=20=EC=A0=84=20commit=20(#20)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/SampleCommandServiceImpl.java | 7 +++-- .../domain/aggregate/entity/Schedule.java | 15 ++++----- .../service/ScheduleCommandServiceImpl.java | 8 ++--- .../query/controller/ScheduleController.java | 8 +++-- .../schedule/query/dto/ScheduleDTO.java | 6 ++-- .../query/service/ScheduleService.java | 4 ++- .../query/service/ScheduleServiceImpl.java | 13 ++++++-- src/main/resources/application.yml | 2 +- .../query/repository/ScheduleMapper.xml | 31 ------------------- src/main/resources/sql/ddl.sql | 3 +- .../center/query/repository/CenterMapper.xml | 0 .../query/repository/ScheduleMapper.xml | 29 +++++++++++++++++ 12 files changed, 69 insertions(+), 57 deletions(-) delete mode 100644 src/main/resources/final_backend/domain/schedule/query/repository/ScheduleMapper.xml rename src/main/resources/{ => stanl_2}/final_backend/domain/center/query/repository/CenterMapper.xml (100%) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java index ac1a2886..2c0670c0 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java @@ -17,6 +17,7 @@ import java.sql.Timestamp; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; @Service("commandSampleService") public class SampleCommandServiceImpl implements SampleCommandService { @@ -30,9 +31,9 @@ public SampleCommandServiceImpl(SampleRepository sampleRepository, ModelMapper m this.modelMapper = modelMapper; } - private Timestamp getCurrentTimestamp() { + private String getCurrentTime() { ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); - return Timestamp.from(nowKst.toInstant()); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } @Override @@ -71,7 +72,7 @@ public void deleteSample(String id) { .orElseThrow(() -> new CommonException(ErrorCode.SAMPLE_NOT_FOUND)); sample.setActive(false); - sample.setDeletedAt(getCurrentTimestamp()); + sample.setDeletedAt(getCurrentTime()); sampleRepository.save(sample); } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java index 2afc42c2..eebb939c 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java @@ -10,6 +10,7 @@ import java.sql.Timestamp; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; @Entity @Table(name="SCHEDULE") @@ -39,13 +40,13 @@ public class Schedule { private String reservationTime; @Column(name = "CREATED_AT", nullable = false) - private Timestamp createdAt; + private String createdAt; @Column(name = "UPDATED_AT", nullable = false) - private Timestamp updatedAt; + private String updatedAt; @Column(name = "DELETED_AT") - private Timestamp deletedAt; + private String deletedAt; @Column(name = "ACTIVE", nullable = false) private Boolean active = true; @@ -57,18 +58,18 @@ public class Schedule { // Insert 되기 전에 실행 @PrePersist public void prePersist() { - this.createdAt = getCurrentTimestamp(); + this.createdAt = getCurrentTime(); this.updatedAt = this.createdAt; } // Update 되기 전에 실행 @PreUpdate public void preUpdate() { - this.updatedAt = getCurrentTimestamp(); + this.updatedAt = getCurrentTime(); } - private Timestamp getCurrentTimestamp() { + private String getCurrentTime() { ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); - return Timestamp.from(nowKst.toInstant()); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java index 9fd41416..ed23b4d2 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java @@ -18,6 +18,7 @@ import java.sql.Timestamp; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; @Slf4j @Service("commandScheduleServiceImpl") @@ -32,11 +33,10 @@ public ScheduleCommandServiceImpl(ScheduleRepository scheduleRepository, ModelMa this.modelMapper = modelMapper; } - private Timestamp getCurrentTimestamp() { + private String getCurrentTime() { ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); - return Timestamp.from(nowKst.toInstant()); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } - @Override @Transactional public Boolean registSchedule(ScheduleRegistRequestDTO scheduleRegistRequestDTO) { @@ -86,7 +86,7 @@ public Boolean deleteSchedule(String scheduleId) { .orElseThrow(() -> new CommonException(ErrorCode.SCHEDULE_NOT_FOUND)); schedule.setActive(false); - schedule.setDeletedAt(getCurrentTimestamp()); + schedule.setDeletedAt(getCurrentTime()); scheduleRepository.save(schedule); diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java index 2e2678f4..6ba62fea 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java @@ -15,6 +15,8 @@ import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; import stanl_2.final_backend.domain.schedule.query.service.ScheduleService; +import java.util.List; + @RestController("queryScheduleController") @RequestMapping("/api/v1/schedule") public class ScheduleController { @@ -31,15 +33,15 @@ public ScheduleController(ScheduleService scheduleService) { @ApiResponse(responseCode = "200", description = "일정 조회 성공", content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) }) - @GetMapping("") + @GetMapping("{id}") public ResponseEntity selectAllSchedule(@PathVariable String id){ - ScheduleDTO scheduleDTO = scheduleService.selectAllSchedule(id); + List schedules = scheduleService.selectAllSchedule(id); return ResponseEntity.ok(ResponseMessage.builder() .httpStatus(200) .msg("성공") - .result(scheduleDTO) + .result(schedules) .build()); } } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java index 9b24f7eb..a41f606d 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java @@ -15,9 +15,9 @@ public class ScheduleDTO { private String name; private String content; private String reservationTime; - private Timestamp createdAt; - private Timestamp updatedAt; - private Timestamp deletedAt; + private String createdAt; + private String updatedAt; + private String deletedAt; private Boolean active; private String memberId; } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleService.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleService.java index 31f46177..a9c4455f 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleService.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleService.java @@ -2,6 +2,8 @@ import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; +import java.util.List; + public interface ScheduleService { - ScheduleDTO selectAllSchedule(String id); + List selectAllSchedule(String id); } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleServiceImpl.java index ded7e282..ee9241b9 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleServiceImpl.java @@ -3,21 +3,24 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.schedule.command.domain.aggregate.entity.Schedule; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; import stanl_2.final_backend.domain.schedule.query.repository.ScheduleMapper; import java.sql.Timestamp; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; @Slf4j @Service("querScheduleServiceImpl") public class ScheduleServiceImpl implements ScheduleService{ private final ScheduleMapper scheduleMapper; - private Timestamp getCurrentTimestamp() { + private String getCurrentTime() { ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); - return Timestamp.from(nowKst.toInstant()); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } @Autowired @@ -26,7 +29,11 @@ public ScheduleServiceImpl(ScheduleMapper scheduleMapper) { } @Override - public ScheduleDTO selectAllSchedule(String id) { + public List selectAllSchedule(String id) { + + String currentMonth = getCurrentTime().substring(5,7); + +// List scheduleList = scheduleMapper.find return null; } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 94f0cd80..8c8d8586 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -23,7 +23,7 @@ spring: jpa: show-sql: true hibernate: - ddl-auto: update + ddl-auto: create properties: hibernate.format_sql: true diff --git a/src/main/resources/final_backend/domain/schedule/query/repository/ScheduleMapper.xml b/src/main/resources/final_backend/domain/schedule/query/repository/ScheduleMapper.xml deleted file mode 100644 index 506c3e3d..00000000 --- a/src/main/resources/final_backend/domain/schedule/query/repository/ScheduleMapper.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index f931c37e..c029f803 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -260,7 +260,8 @@ CREATE TABLE tb_schedule SCH_ID VARCHAR(255) NOT NULL, SCH_NAME VARCHAR(255) NOT NULL, SCH_CONT VARCHAR(255) NOT NULL, - SCH_RES CHAR(19) NOT NULL, + SCH_SRT_AT CHAR(19) NOT NULL, + SCH_END_AT CHAR(19) NOT NULL, CREATED_AT CHAR(19) NOT NULL, UPDATED_AT CHAR(19) NOT NULL, DELETED_AT CHAR(19) NULL, diff --git a/src/main/resources/final_backend/domain/center/query/repository/CenterMapper.xml b/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml similarity index 100% rename from src/main/resources/final_backend/domain/center/query/repository/CenterMapper.xml rename to src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml diff --git a/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml b/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml index c0a90c39..7ada392a 100644 --- a/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml @@ -2,3 +2,32 @@ + + + + + + + + + + + + + + + \ No newline at end of file From d7126cc2e140a222bf65187d2cd4b0865d326fd7 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Tue, 12 Nov 2024 11:34:40 +0900 Subject: [PATCH 013/563] =?UTF-8?q?refactor:=20=EA=B3=84=EC=95=BD=EC=84=9C?= =?UTF-8?q?=20=EB=93=B1=EB=A1=9D=20=EC=88=98=EC=A0=95(#21)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/domain/service/ContractServiceImpl.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java index ac3df64a..8b4319e7 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java @@ -22,12 +22,16 @@ public ContractServiceImpl(ContractRepository contractRepository, ModelMapper mo @Override @Transactional public void registerContract(ContractRegistRequestDTO contractRegistRequestDTO) { - // 회원인지 확인여부 + // 회원인지 확인여부 및 값 가져오기 + + // 일련번호로 제품테이블의 총식별번호 찾아서 제품 가져오기 + + // 고객전화번호로 고객테이블 찾아서 고객이 있으면 넘어가고, 고객이 없으면 고객테이블에 고객 정보 넣기 Contract contract = modelMapper.map(contractRegistRequestDTO, Contract.class); - contract.setCentId("CEN_000000001"); - contract.setProdId("PRO_000000001"); + contract.setCentId("CEN_000000001"); // 회원의 매장번호 넣기 + contract.setProdId("PRO_000000001"); // 제품 번호 넣기 contractRepository.save(contract); } From 6115ac3d1ab35abeca34af21d1cd9a72ec1802d8 Mon Sep 17 00:00:00 2001 From: giuseog Date: Tue, 12 Nov 2024 12:18:30 +0900 Subject: [PATCH 014/563] =?UTF-8?q?feat:=20=EC=98=81=EC=97=85=20=EB=A7=A4?= =?UTF-8?q?=EC=9E=A5=20cud=20=EA=B5=AC=ED=98=84(#8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/SampleCommandServiceImpl.java | 2 +- .../controller/CenterController.java | 35 ++++++++++--------- .../dto/request/CenterDeleteRequestDTO.java | 7 ++++ .../dto/request/CenterModifyRequestDTO.java | 8 +++++ .../dto/request/CenterRegistRequestDTO.java | 1 - .../dto/response/CenterDeleteResponseDTO.java | 16 --------- .../dto/response/CenterModifyResponseDTO.java | 16 --------- .../dto/response/CenterRegistResponseDTO.java | 24 ------------- .../service/CenterCommandService.java | 2 +- .../domain/aggregate/entity/Center.java | 24 +++++++------ .../service/CenterCommandServiceImpl.java | 20 +++++------ src/main/resources/application.yml | 2 +- 12 files changed, 60 insertions(+), 97 deletions(-) delete mode 100644 src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterDeleteResponseDTO.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterModifyResponseDTO.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterRegistResponseDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java index ac1a2886..d195dd1c 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java @@ -71,7 +71,7 @@ public void deleteSample(String id) { .orElseThrow(() -> new CommonException(ErrorCode.SAMPLE_NOT_FOUND)); sample.setActive(false); - sample.setDeletedAt(getCurrentTimestamp()); +// sample.setDeletedAt(getCurrentTimestamp()); sampleRepository.save(sample); } diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java index 523afa22..ee46de80 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java @@ -8,9 +8,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.A_sample.command.application.dto.response.SampleModifyResponseDTO; +import stanl_2.final_backend.domain.center.command.application.dto.request.CenterModifyRequestDTO; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistRequestDTO; -import stanl_2.final_backend.domain.center.command.application.dto.response.CenterRegistResponseDTO; import stanl_2.final_backend.domain.center.command.application.service.CenterCommandService; import stanl_2.final_backend.domain.center.common.response.ResponseMessage; @@ -34,14 +33,12 @@ public CenterController(CenterCommandService centerCommandService) { public ResponseEntity postTest(@RequestBody CenterRegistRequestDTO centerRegistRequestDTO){ /* 설명. memberId 토큰으로 받는 것 고려 */ - CenterRegistResponseDTO centerRegistResponseDTO = centerCommandService.registCenter(centerRegistRequestDTO); + centerCommandService.registCenter(centerRegistRequestDTO); - centerRegistRequestDTO.setId(id); - centerCommandService.registerCenter(centerRegistRequestDTO); - return ResponseEntity.ok(stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage.builder() + return ResponseEntity.ok(ResponseMessage.builder() .httpStatus(200) - .msg("성공") + .msg("등록 성공") .result(null) .build()); } @@ -52,16 +49,17 @@ public ResponseEntity postTest(@RequestBody CenterRegistRequestDTO centerRegi content = {@Content(schema = @Schema(implementation = stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage.class))}) }) @PutMapping("{id}") - public ResponseEntity putTest(){ + public ResponseEntity putTest(@PathVariable("id") String id, + @RequestBody CenterModifyRequestDTO centerModifyRequestDTO){ - sampleModifyRequestDTO.setId(id); - SampleModifyResponseDTO sampleModifyResponseDTO = sampleCommandService.modifySample(id, sampleModifyRequestDTO); + centerCommandService.modifyCenter(id, centerModifyRequestDTO); - return ResponseEntity.ok(stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage.builder() + return ResponseEntity.ok(ResponseMessage.builder() .httpStatus(200) - .msg("성공") - .result(sampleModifyResponseDTO) - .build()); } + .msg("수정 성공") + .result(null) + .build()); + } @Operation(summary = "샘플 삭제 테스트") @ApiResponses(value = { @@ -69,10 +67,15 @@ public ResponseEntity putTest(){ content = {@Content(schema = @Schema(implementation = stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage.class))}) }) @DeleteMapping("{id}") - public ResponseEntity deleteTest(){ + public ResponseEntity deleteTest(@PathVariable("id") String id){ + centerCommandService.deleteCenter(id); - return ResponseEntity.ok(new ResponseMessage(200, "delete 성공", " ")); + return ResponseEntity.ok(ResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); } } diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterDeleteRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterDeleteRequestDTO.java index 3d84f947..4b7ec007 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterDeleteRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterDeleteRequestDTO.java @@ -1,5 +1,12 @@ package stanl_2.final_backend.domain.center.command.application.dto.request; +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString public class CenterDeleteRequestDTO { private String name; private String address; diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyRequestDTO.java index 5190590e..494587e7 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyRequestDTO.java @@ -1,6 +1,14 @@ package stanl_2.final_backend.domain.center.command.application.dto.request; +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString public class CenterModifyRequestDTO { + private String id; private String name; private String address; private String phone; diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterRegistRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterRegistRequestDTO.java index 9f61f560..c3a967fd 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterRegistRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterRegistRequestDTO.java @@ -8,7 +8,6 @@ @Getter @ToString public class CenterRegistRequestDTO { - private String name; private String address; private String phone; diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterDeleteResponseDTO.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterDeleteResponseDTO.java deleted file mode 100644 index fc5d31f4..00000000 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterDeleteResponseDTO.java +++ /dev/null @@ -1,16 +0,0 @@ -package stanl_2.final_backend.domain.center.command.application.dto.response; - -import java.sql.Timestamp; - -public class CenterDeleteResponseDTO { - - private String name; - private String address; - private String phone; - private Integer memberCount; - private String operatingAt; - private Timestamp createdAt; - private Timestamp updatedAt; - private Timestamp deletedAt; - private Boolean active; -} diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterModifyResponseDTO.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterModifyResponseDTO.java deleted file mode 100644 index 03b6d5be..00000000 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterModifyResponseDTO.java +++ /dev/null @@ -1,16 +0,0 @@ -package stanl_2.final_backend.domain.center.command.application.dto.response; - -import java.sql.Timestamp; - -public class CenterModifyResponseDTO { - - private String name; - private String address; - private String phone; - private Integer memberCount; - private String operatingAt; - private Timestamp createdAt; - private Timestamp updatedAt; - private Timestamp deletedAt; - private Boolean active; -} diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterRegistResponseDTO.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterRegistResponseDTO.java deleted file mode 100644 index 2b0736f9..00000000 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterRegistResponseDTO.java +++ /dev/null @@ -1,24 +0,0 @@ -package stanl_2.final_backend.domain.center.command.application.dto.response; - -import lombok.*; - -import java.sql.Timestamp; -import java.time.LocalDateTime; - -@AllArgsConstructor -@NoArgsConstructor -@Getter -@Setter -@ToString -public class CenterRegistResponseDTO { - - private String name; - private String address; - private String phone; - private Integer memberCount; - private String operatingAt; - private Timestamp createdAt; - private Timestamp updatedAt; - private Timestamp deletedAt; - private Boolean active; -} diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandService.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandService.java index c7bda1eb..d5d137a5 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandService.java @@ -8,6 +8,6 @@ public interface CenterCommandService { void registCenter(CenterRegistRequestDTO centerRegistRequestDTO); - void modifyCenter(String id, CenterModifyRequestDTO centerModifyRequestDTO) + void modifyCenter(String id, CenterModifyRequestDTO centerModifyRequestDTO); void deleteCenter(String id); } diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java b/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java index b6394a5b..0f82cbbd 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java @@ -8,12 +8,13 @@ import java.sql.Timestamp; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; /* 설명. 테스트를 위한 어노테이션(나중에 삭제 예정)*/ @ToString @Entity -@Table(name="CENTER") +@Table(name="TB_CENTER") @AllArgsConstructor @NoArgsConstructor @Setter @@ -24,8 +25,9 @@ public class Center { @GeneratedValue(generator = "PrefixGeneratorConfig") @GenericGenerator(name = "PrefixGeneratorConfig", type = PrefixGeneratorConfig.class, - parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "CEN") + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "CENT") ) + @Column(name ="CENT_ID") private String id; @Column(name = "CENT_NAME", nullable = false) @@ -44,13 +46,13 @@ public class Center { private String operatingAt; @Column(name = "CREATED_AT", nullable = false) - private Timestamp createdAt; + private String createdAt; @Column(name = "UPDATED_AT", nullable = false) - private Timestamp updatedAt; + private String updatedAt; @Column(name = "DELETED_AT") - private Timestamp deletedAt; + private String deletedAt; @Column(name = "ACTIVE", nullable = false) private Boolean active = true; @@ -60,20 +62,20 @@ public class Center { // Insert 되기 전에 실행 @PrePersist private void prePersist() { - this.createdAt = getCurrentTimestamp(); + this.createdAt = getCurrentTime(); this.updatedAt = this.createdAt; } - // Update 되기 전에 실행 @PreUpdate - public void preUpdate() { - this.updatedAt = getCurrentTimestamp(); + private void preUpdate() { + this.updatedAt = getCurrentTime(); } - private Timestamp getCurrentTimestamp() { + private String getCurrentTime() { ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); - return Timestamp.from(nowKst.toInstant()); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } + } diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java index c49ee4a1..36281ab8 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java @@ -9,7 +9,6 @@ import stanl_2.final_backend.domain.A_sample.common.exception.ErrorCode; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterModifyRequestDTO; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistRequestDTO; -import stanl_2.final_backend.domain.center.command.application.dto.response.CenterModifyResponseDTO; import stanl_2.final_backend.domain.center.command.application.service.CenterCommandService; import stanl_2.final_backend.domain.center.command.domain.aggregate.entity.Center; import stanl_2.final_backend.domain.center.command.domain.repository.CenterRepository; @@ -32,10 +31,6 @@ public CenterCommandServiceImpl(CenterRepository centerRepository, ModelMapper m this.modelMapper = modelMapper; } - private Timestamp getCurrentTimestamp() { - ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); - return Timestamp.from(nowKst.toInstant()); - } @Override @Transactional @@ -52,15 +47,14 @@ public void modifyCenter(String id, CenterModifyRequestDTO centerModifyRequestDT Center center = centerRepository.findById(id) .orElseThrow(() -> new CommonException(ErrorCode.CENTER_NOT_FOUND)); -// centerModifyRequestDTO.setId(id); Center updateCenter = modelMapper.map(centerModifyRequestDTO, Center.class); + + updateCenter.setId(center.getId()); updateCenter.setCreatedAt(center.getCreatedAt()); + updateCenter.setUpdatedAt(getCurrentTime()); updateCenter.setActive(center.getActive()); centerRepository.save(updateCenter); - - CenterModifyResponseDTO centerModifyResponseDTO= modelMapper.map(updateCenter, CenterModifyResponseDTO.class); - } @Override @@ -71,8 +65,14 @@ public void deleteCenter(String id) { .orElseThrow(() -> new CommonException(ErrorCode.CENTER_NOT_FOUND)); center.setActive(false); - center.setDeletedAt(getCurrentTimestamp()); + center.setDeletedAt(getCurrentTime()); centerRepository.save(center); } + + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } + } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8c8d8586..94f0cd80 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -23,7 +23,7 @@ spring: jpa: show-sql: true hibernate: - ddl-auto: create + ddl-auto: update properties: hibernate.format_sql: true From a822eb2ada687825112aaa8e271a05908247453d Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Tue, 12 Nov 2024 15:21:29 +0900 Subject: [PATCH 015/563] =?UTF-8?q?feat:=20=EA=B3=84=EC=95=BD=EC=84=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EA=B5=AC=ED=98=84(#21)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/SampleCommandServiceImpl.java | 2 +- .../controller/ContractController.java | 37 ++++++++++++++--- .../dto/request/ContractModifyRequestDTO.java | 33 +++++++++++++++ .../dto/request/ContractRegistRequestDTO.java | 1 - .../response/ContractModifyResponseDTO.java | 31 ++++++++++++++ .../application/service/ContractService.java | 4 ++ .../domain/aggregate/entity/Contract.java | 21 +++++----- .../domain/service/ContractServiceImpl.java | 40 +++++++++++++++++++ .../global/exception/ErrorCode.java | 2 +- src/main/resources/application.yml | 2 +- src/main/resources/application_prod.yml | 2 +- src/main/resources/sql/ddl.sql | 4 +- 12 files changed, 158 insertions(+), 21 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractModifyRequestDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/response/ContractModifyResponseDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java index ac1a2886..d195dd1c 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java @@ -71,7 +71,7 @@ public void deleteSample(String id) { .orElseThrow(() -> new CommonException(ErrorCode.SAMPLE_NOT_FOUND)); sample.setActive(false); - sample.setDeletedAt(getCurrentTimestamp()); +// sample.setDeletedAt(getCurrentTimestamp()); sampleRepository.save(sample); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java index cf37dc2e..003988e5 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java @@ -9,7 +9,9 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage; +import stanl_2.final_backend.domain.contract.command.application.dto.request.ContractModifyRequestDTO; import stanl_2.final_backend.domain.contract.command.application.dto.request.ContractRegistRequestDTO; +import stanl_2.final_backend.domain.contract.command.application.dto.response.ContractModifyResponseDTO; import stanl_2.final_backend.domain.contract.command.application.service.ContractService; @RestController("contractController") @@ -36,16 +38,41 @@ public ContractController(ContractService contractService) { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) }) - @PostMapping("{id}") - public ResponseEntity postTest(@PathVariable String id, - @RequestBody ContractRegistRequestDTO contractRegistRequestDTO) { - contractRegistRequestDTO.setMemId(id); + @PostMapping("") + public ResponseEntity postContract(@RequestBody ContractRegistRequestDTO contractRegistRequestDTO) { + contractService.registerContract(contractRegistRequestDTO); return ResponseEntity.ok(ResponseMessage.builder() .httpStatus(200) - .msg("성공") + .msg("계약서가 등록되었습니다.") .result(null) .build()); } + + /** + * [PUT] http://localhost:7777/api/v1/sample/MEM_000000001 + * Request + * { + * "name": "abcc" + * } + * */ + @Operation(summary = "계약서 수정 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + }) + @PutMapping("{id}") + public ResponseEntity putContract(@PathVariable String id, + @RequestBody ContractModifyRequestDTO contractModifyRequestDTO) { + + contractModifyRequestDTO.setId(id); + ContractModifyResponseDTO contractModifyResponseDTO = contractService.modifyContract(contractModifyRequestDTO); + + return ResponseEntity.ok(ResponseMessage.builder() + .httpStatus(200) + .msg("계약서가 수정되었습니다.") + .result(contractModifyResponseDTO) + .build()); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractModifyRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractModifyRequestDTO.java new file mode 100644 index 00000000..c90314ec --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractModifyRequestDTO.java @@ -0,0 +1,33 @@ +package stanl_2.final_backend.domain.contract.command.application.dto.request; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class ContractModifyRequestDTO { + + private String id; + private String name; + private String custName; + private String custIdenNo; + private String custAddrress; + private String custEmail; + private String custPhone; + private String compName; + private String custCla; + private String custPurCond; + private String seriNum; + private String seleOpti; + private Integer downPay; + private Integer intePay; + private Integer remPay; + private Integer consPay; + private String delvDate; + private String delvLoc; + private String state; + private String noOfVeh; + private String memId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractRegistRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractRegistRequestDTO.java index d7fbb4db..f24d7255 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractRegistRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractRegistRequestDTO.java @@ -29,6 +29,5 @@ public class ContractRegistRequestDTO { private String state; private String noOfVeh; private String createdUrl; - private String deletedUrl; private String memId; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/response/ContractModifyResponseDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/response/ContractModifyResponseDTO.java new file mode 100644 index 00000000..f4f19a6b --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/response/ContractModifyResponseDTO.java @@ -0,0 +1,31 @@ +package stanl_2.final_backend.domain.contract.command.application.dto.response; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class ContractModifyResponseDTO { + private String id; + private String name; + private String custName; + private String custIdenNo; + private String custAddrress; + private String custEmail; + private String custPhone; + private String compName; + private String custCla; + private String custPurCond; + private String seriNum; + private String seleOpti; + private Integer downPay; + private Integer intePay; + private Integer remPay; + private Integer consPay; + private String delvDate; + private String delvLoc; + private String state; + private String noOfVeh; +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractService.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractService.java index 4e50a130..5f38afd3 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractService.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractService.java @@ -1,7 +1,11 @@ package stanl_2.final_backend.domain.contract.command.application.service; +import stanl_2.final_backend.domain.contract.command.application.dto.request.ContractModifyRequestDTO; import stanl_2.final_backend.domain.contract.command.application.dto.request.ContractRegistRequestDTO; +import stanl_2.final_backend.domain.contract.command.application.dto.response.ContractModifyResponseDTO; public interface ContractService { void registerContract(ContractRegistRequestDTO contractRegistRequestDTO); + + ContractModifyResponseDTO modifyContract(ContractModifyRequestDTO contractModifyRequestDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java index 471ce257..18904700 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java @@ -12,6 +12,7 @@ import java.sql.Timestamp; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; @NoArgsConstructor @AllArgsConstructor @@ -91,23 +92,25 @@ public class Contract { @ColumnDefault("1") private String noOfVeh; - @Column(name = "CREATED_URL", nullable = false) + @Lob + @Column(name = "CREATED_URL", nullable = false, columnDefinition = "TEXT") private String createdUrl; - @Column(name = "DELETED_URL") + @Lob + @Column(name = "DELETED_URL", columnDefinition = "TEXT") private String deletedUrl; @Column(name = "ACTIVE", nullable = false) private boolean active = true; @Column(name = "CREATED_AT", nullable = false, updatable = false) - private Timestamp createdAt; + private String createdAt; @Column(name = "UPDATED_AT", nullable = false) - private Timestamp updatedAt; + private String updatedAt; @Column(name = "DELETED_AT") - private Timestamp deletedAt; + private String deletedAt; @Column(name = "MEM_ID", nullable = false) private String memId; @@ -125,18 +128,18 @@ public class Contract { // Insert 되기 전에 실행 @PrePersist private void prePersist() { - this.createdAt = getCurrentTimestamp(); + this.createdAt = getCurrentTime(); this.updatedAt = this.createdAt; } // Update 되기 전에 실행 @PreUpdate private void preUpdate() { - this.updatedAt = getCurrentTimestamp(); + this.updatedAt = getCurrentTime(); } - private Timestamp getCurrentTimestamp() { + private String getCurrentTime() { ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); - return Timestamp.from(nowKst.toInstant()); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java index 8b4319e7..85b2ce40 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java @@ -3,10 +3,18 @@ import org.modelmapper.ModelMapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.contract.command.application.dto.request.ContractModifyRequestDTO; import stanl_2.final_backend.domain.contract.command.application.dto.request.ContractRegistRequestDTO; +import stanl_2.final_backend.domain.contract.command.application.dto.response.ContractModifyResponseDTO; import stanl_2.final_backend.domain.contract.command.application.service.ContractService; import stanl_2.final_backend.domain.contract.command.domain.aggregate.entity.Contract; import stanl_2.final_backend.domain.contract.command.domain.repository.ContractRepository; +import stanl_2.final_backend.global.exception.CommonException; +import stanl_2.final_backend.global.exception.ErrorCode; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; @Service("contractServiceImpl") public class ContractServiceImpl implements ContractService { @@ -35,4 +43,36 @@ public void registerContract(ContractRegistRequestDTO contractRegistRequestDTO) contractRepository.save(contract); } + + @Override + @Transactional + public ContractModifyResponseDTO modifyContract(ContractModifyRequestDTO contractModifyRequestDTO) { + + // 회원인지 확인여부 및 값 가져오기 + + // 일련번호로 제품테이블의 총식별번호 찾아서 제품 가져오기 + + // 가져온 제품 정보에 수정된 값 넣기 + + // 고객전화번호로 고객테이블 찾아서 가져오기 + + // 가져온 고객 정보에 수정된 값 넣기 + + Contract contract = contractRepository.findById(contractModifyRequestDTO.getId()) + .orElseThrow(() -> new CommonException(ErrorCode.CONTRACT_NOT_FOUND)); + + Contract updateContract = modelMapper.map(contractModifyRequestDTO, Contract.class); + updateContract.setCreatedAt(contract.getCreatedAt()); + updateContract.setUpdatedAt(contract.getUpdatedAt()); + updateContract.setActive(contract.isActive()); + updateContract.setCentId(contract.getCentId()); + updateContract.setProdId(contract.getProdId()); + updateContract.setCreatedUrl(contract.getCreatedUrl()); + + contractRepository.save(updateContract); + + ContractModifyResponseDTO contractModifyResponseDTO = modelMapper.map(updateContract, ContractModifyResponseDTO.class); + + return contractModifyResponseDTO; + } } diff --git a/src/main/java/stanl_2/final_backend/global/exception/ErrorCode.java b/src/main/java/stanl_2/final_backend/global/exception/ErrorCode.java index 42bfc05c..59bd09d9 100644 --- a/src/main/java/stanl_2/final_backend/global/exception/ErrorCode.java +++ b/src/main/java/stanl_2/final_backend/global/exception/ErrorCode.java @@ -49,7 +49,7 @@ public enum ErrorCode { * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. */ USERDETAILS_NOT_FOUND(40400, HttpStatus.NOT_FOUND, "User Details를 찾을 수 없습니다."), - + CONTRACT_NOT_FOUND(40403, HttpStatus.NOT_FOUND, "계약서를 찾을 수 없습니다."), /** * 500(Internal Server Error) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8c8d8586..94f0cd80 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -23,7 +23,7 @@ spring: jpa: show-sql: true hibernate: - ddl-auto: create + ddl-auto: update properties: hibernate.format_sql: true diff --git a/src/main/resources/application_prod.yml b/src/main/resources/application_prod.yml index bdb395a7..df3dc479 100644 --- a/src/main/resources/application_prod.yml +++ b/src/main/resources/application_prod.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: create + ddl-auto: update logging: level: diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index 9c05600f..48cab224 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -136,8 +136,8 @@ CREATE TABLE tb_contract CONR_DELV_LOC VARCHAR(255) NULL, CONR_STAT VARCHAR(255) NULL DEFAULT 'WAIT', CONR_NO_OF_VEH INT NOT NULL DEFAULT 1, - CREATED_URL VARCHAR(255) NOT NULL, - DELETED_URL VARCHAR(255) NULL, + CREATED_URL TEXT NOT NULL, + DELETED_URL TEXT NULL, ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, CREATED_AT CHAR(19) NOT NULL, UPDATED_AT CHAR(19) NOT NULL, From 1866efba3d7a8d12955fdd05c692554135ca1138 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Tue, 12 Nov 2024 15:32:01 +0900 Subject: [PATCH 016/563] =?UTF-8?q?feat:=20=EA=B3=84=EC=95=BD=EC=84=9C=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EA=B5=AC=ED=98=84(#21)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ContractController.java | 54 ++++++++++++++++--- .../application/service/ContractService.java | 2 + .../domain/service/ContractServiceImpl.java | 21 ++++++++ 3 files changed, 70 insertions(+), 7 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java index 003988e5..4a87c17b 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java @@ -51,11 +51,30 @@ public ResponseEntity postContract(@RequestBody ContractRegistR } /** - * [PUT] http://localhost:7777/api/v1/sample/MEM_000000001 + * [PUT] http://localhost:8080/api/v1/contract/CON_000000011 * Request * { - * "name": "abcc" - * } + * "name": "계약서 수정", + * "custName": "John Doe", + * "custIdenNo": "123456-7890123", + * "custAddrress": "123 Main Street, City, Country", + * "custEmail": "johndoe@example.com", + * "custPhone": "+1-234-567-8901", + * "compName": "Doe Industries", + * "custCla": "Premium", + * "custPurCond": "Full Payment", + * "seriNum": "A1B2C3D4", + * "seleOpti": "Extended Warranty", + * "downPay": 10000, + * "intePay": 500, + * "remPay": 15000, + * "consPay": 5000, + * "delvDate": "2024-12-15", + * "delvLoc": "Warehouse No. 3, Industrial Park", + * "state": "WAIT", + * "noOfVeh": "2", + * "memId": "MEM_000000001" + * } * */ @Operation(summary = "계약서 수정 테스트") @ApiResponses(value = { @@ -70,9 +89,30 @@ public ResponseEntity putContract(@PathVariable String id, ContractModifyResponseDTO contractModifyResponseDTO = contractService.modifyContract(contractModifyRequestDTO); return ResponseEntity.ok(ResponseMessage.builder() - .httpStatus(200) - .msg("계약서가 수정되었습니다.") - .result(contractModifyResponseDTO) - .build()); + .httpStatus(200) + .msg("계약서가 수정되었습니다.") + .result(contractModifyResponseDTO) + .build()); + } + + /** + * [DELETE] http://localhost:7777/api/v1/sample?mem_id=SAM_000001 + * */ + @Operation(summary = "샘플 삭제 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + }) + @DeleteMapping("{id}") + public ResponseEntity deleteContract(@PathVariable String id) { + + // 회원 아이디 받아와야 함 + contractService.deleteContract(id); + + return ResponseEntity.ok(ResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); } } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractService.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractService.java index 5f38afd3..e93320cb 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractService.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractService.java @@ -8,4 +8,6 @@ public interface ContractService { void registerContract(ContractRegistRequestDTO contractRegistRequestDTO); ContractModifyResponseDTO modifyContract(ContractModifyRequestDTO contractModifyRequestDTO); + + void deleteContract(String id); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java index 85b2ce40..a691d989 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java @@ -12,6 +12,7 @@ import stanl_2.final_backend.global.exception.CommonException; import stanl_2.final_backend.global.exception.ErrorCode; +import java.sql.Timestamp; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -27,6 +28,11 @@ public ContractServiceImpl(ContractRepository contractRepository, ModelMapper mo this.modelMapper = modelMapper; } + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } + @Override @Transactional public void registerContract(ContractRegistRequestDTO contractRegistRequestDTO) { @@ -75,4 +81,19 @@ public ContractModifyResponseDTO modifyContract(ContractModifyRequestDTO contrac return contractModifyResponseDTO; } + + @Override + @Transactional + public void deleteContract(String id) { + + // 회원 확인 + + Contract contract = contractRepository.findById(id) + .orElseThrow(() -> new CommonException(ErrorCode.CONTRACT_NOT_FOUND)); + + contract.setActive(false); + contract.setDeletedAt(getCurrentTime()); + + contractRepository.save(contract); + } } From c37e766e6c97a10ab9af684b2f0a05640546f250 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Tue, 12 Nov 2024 15:48:08 +0900 Subject: [PATCH 017/563] =?UTF-8?q?feat:=20=EC=9D=BC=EC=A0=95=ED=91=9C=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C(=EC=A0=84=EC=B2=B4=20=EC=A1=B0=ED=9A=8C,=20?= =?UTF-8?q?=EB=85=84&=EC=9B=94=EB=B3=84=20=EC=A1=B0=ED=9A=8C,=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=EC=A1=B0=ED=9A=8C)=20=EA=B5=AC=ED=98=84=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20(#20)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/ScheduleModifyRequestDTO.java | 3 +- .../dto/request/ScheduleRegistRequestDTO.java | 3 +- .../response/ScheduleModifyResponseDTO.java | 3 +- .../domain/aggregate/entity/Schedule.java | 9 ++- .../query/controller/ScheduleController.java | 56 ++++++++++++++++++- .../schedule/query/dto/ScheduleDTO.java | 3 +- .../query/dto/ScheduleYearMonthDTO.java | 24 ++++++++ .../query/repository/ScheduleMapper.java | 11 ++++ .../query/service/ScheduleService.java | 7 ++- .../query/service/ScheduleServiceImpl.java | 50 +++++++++++++++-- src/main/resources/application.yml | 2 +- src/main/resources/sql/ddl.sql | 5 +- .../query/repository/ScheduleMapper.xml | 49 ++++++++++++++-- 13 files changed, 201 insertions(+), 24 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleYearMonthDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/request/ScheduleModifyRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/request/ScheduleModifyRequestDTO.java index 5c2d98fb..878de409 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/request/ScheduleModifyRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/request/ScheduleModifyRequestDTO.java @@ -14,6 +14,7 @@ public class ScheduleModifyRequestDTO { private String id; private String name; private String content; - private String reservationTime; + private String startAt; + private String endAt; private String memberId; } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/request/ScheduleRegistRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/request/ScheduleRegistRequestDTO.java index 8abc5e95..d1d6d660 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/request/ScheduleRegistRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/request/ScheduleRegistRequestDTO.java @@ -13,6 +13,7 @@ public class ScheduleRegistRequestDTO { private String name; private String content; - private String reservationTime; + private String startAt; + private String endAt; private String memberId; } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/response/ScheduleModifyResponseDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/response/ScheduleModifyResponseDTO.java index a59acea5..6aa22e5d 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/response/ScheduleModifyResponseDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/response/ScheduleModifyResponseDTO.java @@ -12,7 +12,8 @@ public class ScheduleModifyResponseDTO { private String id; private String name; private String content; - private String reservationTime; + private String startAt; + private String endAt; private String createdAt; private String updatedAt; private String deletedAt; diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java index eebb939c..30240ef4 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java @@ -13,7 +13,7 @@ import java.time.format.DateTimeFormatter; @Entity -@Table(name="SCHEDULE") +@Table(name="TB_SCHEDULE") @AllArgsConstructor @NoArgsConstructor @Getter @@ -36,8 +36,11 @@ public class Schedule { @Column(name = "SCH_CONT", nullable = false) private String content; - @Column(name = "SCH_RES", nullable = false) - private String reservationTime; + @Column(name = "SCH_SRT_AT", nullable = false) + private String startAt; + + @Column(name = "SCH_END_AT", nullable = false) + private String endAt; @Column(name = "CREATED_AT", nullable = false) private String createdAt; diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java index 6ba62fea..25425913 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java @@ -7,12 +7,14 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.parameters.P; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import stanl_2.final_backend.domain.schedule.common.response.ResponseMessage; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; +import stanl_2.final_backend.domain.schedule.query.dto.ScheduleYearMonthDTO; import stanl_2.final_backend.domain.schedule.query.service.ScheduleService; import java.util.List; @@ -33,10 +35,10 @@ public ScheduleController(ScheduleService scheduleService) { @ApiResponse(responseCode = "200", description = "일정 조회 성공", content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) }) - @GetMapping("{id}") - public ResponseEntity selectAllSchedule(@PathVariable String id){ + @GetMapping("{memberId}") + public ResponseEntity selectAllSchedule(@PathVariable("memberId") String memberId){ - List schedules = scheduleService.selectAllSchedule(id); + List schedules = scheduleService.selectAllSchedule(memberId); return ResponseEntity.ok(ResponseMessage.builder() .httpStatus(200) @@ -44,4 +46,52 @@ public ResponseEntity selectAllSchedule(@PathVariable String id .result(schedules) .build()); } + + + @Operation(summary = "일정 조건별(년&일) 전체 조회 api") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "일정 조건별(년&일) 전체 조회 성공", + content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + }) + @GetMapping("{memberId}/{year}/{month}") + public ResponseEntity selectMonthSchedule(@PathVariable("memberId") String memberId, + @PathVariable("year") String year, + @PathVariable("month") String month){ + + ScheduleYearMonthDTO scheduleYearMonthDTO = new ScheduleYearMonthDTO(); + scheduleYearMonthDTO.setMemberId(memberId); + scheduleYearMonthDTO.setYear(year); + scheduleYearMonthDTO.setMonth(month); + + List yearMonthSchedule = scheduleService.selectYearMonthSchedule(scheduleYearMonthDTO); + + return ResponseEntity.ok(ResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(yearMonthSchedule) + .build()); + } + + + @Operation(summary = "일정 상세 조회 api") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "일정 상세 조회 성공", + content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + }) + @GetMapping("{memberId}/{scheduleId}") + public ResponseEntity selectDetailSchedule(@PathVariable("memberId") String memberId, + @PathVariable("scheduleId") String scheduleId){ + + ScheduleDTO scheduleDTO = new ScheduleDTO(); + scheduleDTO.setMemberId(memberId); + scheduleDTO.setId(scheduleId); + + ScheduleDTO responseSchedule = scheduleService.selectDetailSchedule(scheduleDTO); + + return ResponseEntity.ok(ResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(responseSchedule) + .build()); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java index a41f606d..f8503699 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java @@ -14,7 +14,8 @@ public class ScheduleDTO { private String id; private String name; private String content; - private String reservationTime; + private String startAt; + private String endAt; private String createdAt; private String updatedAt; private String deletedAt; diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleYearMonthDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleYearMonthDTO.java new file mode 100644 index 00000000..0dc0fa27 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleYearMonthDTO.java @@ -0,0 +1,24 @@ +package stanl_2.final_backend.domain.schedule.query.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString +public class ScheduleYearMonthDTO { + + private String id; + private String content; + private String startAt; + private String endAt; + private String createdAt; + private String updatedAt; + private String deletedAt; + private Boolean active; + private String memberId; + + private String year; + private String month; +} diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java index 9cee4d68..65200e0d 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java @@ -1,7 +1,18 @@ package stanl_2.final_backend.domain.schedule.query.repository; import org.apache.ibatis.annotations.Mapper; +import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; +import stanl_2.final_backend.domain.schedule.query.dto.ScheduleYearMonthDTO; + +import java.util.List; +import java.util.Map; @Mapper public interface ScheduleMapper { + + List findSchedulesByMemberIdAndSrtAt(Map arg); + + List findSchedulesByMemberIdAndYearMonth(Map arg); + + ScheduleDTO findScheduleByMemberIdAndScheduleId(ScheduleDTO scheduleDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleService.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleService.java index a9c4455f..88e93ab6 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleService.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleService.java @@ -1,9 +1,14 @@ package stanl_2.final_backend.domain.schedule.query.service; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; +import stanl_2.final_backend.domain.schedule.query.dto.ScheduleYearMonthDTO; import java.util.List; public interface ScheduleService { - List selectAllSchedule(String id); + List selectAllSchedule(String memberId); + + List selectYearMonthSchedule(ScheduleYearMonthDTO scheduleYearMonthDTO); + + ScheduleDTO selectDetailSchedule(ScheduleDTO scheduleDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleServiceImpl.java index ee9241b9..62623e27 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleServiceImpl.java @@ -3,15 +3,17 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import stanl_2.final_backend.domain.schedule.command.domain.aggregate.entity.Schedule; +import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; +import stanl_2.final_backend.domain.schedule.query.dto.ScheduleYearMonthDTO; import stanl_2.final_backend.domain.schedule.query.repository.ScheduleMapper; -import java.sql.Timestamp; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.util.HashMap; import java.util.List; +import java.util.Map; @Slf4j @Service("querScheduleServiceImpl") @@ -29,12 +31,48 @@ public ScheduleServiceImpl(ScheduleMapper scheduleMapper) { } @Override - public List selectAllSchedule(String id) { + @Transactional(readOnly = true) + public List selectAllSchedule(String memberId) { - String currentMonth = getCurrentTime().substring(5,7); + String currentMonth = getCurrentTime().substring(0,7); -// List scheduleList = scheduleMapper.find + Map arg = new HashMap<>(); - return null; + arg.put("memberId",memberId); + arg.put("month",currentMonth); + + List scheduleList = scheduleMapper.findSchedulesByMemberIdAndSrtAt(arg); + + return scheduleList; } + + @Override + @Transactional(readOnly = true) + public List selectYearMonthSchedule(ScheduleYearMonthDTO scheduleYearMonthDTO) { + + String memberId = scheduleYearMonthDTO.getMemberId(); + String year = scheduleYearMonthDTO.getYear(); + String month = scheduleYearMonthDTO.getMonth(); + + String checkDate = year + "-" + month; + + Map arg = new HashMap<>(); + + arg.put("memberId", memberId); + arg.put("yearMonth", checkDate); + + List scheduleList = scheduleMapper.findSchedulesByMemberIdAndYearMonth(arg); + + return scheduleList; + } + + @Override + @Transactional(readOnly = true) + public ScheduleDTO selectDetailSchedule(ScheduleDTO scheduleDTO) { + + ScheduleDTO responseSchedule = scheduleMapper.findScheduleByMemberIdAndScheduleId(scheduleDTO); + + return responseSchedule; + } + } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8c8d8586..94f0cd80 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -23,7 +23,7 @@ spring: jpa: show-sql: true hibernate: - ddl-auto: create + ddl-auto: update properties: hibernate.format_sql: true diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index 9c05600f..fa3912d7 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -1,3 +1,6 @@ +-- 외래 키 제약 조건 비활성화 +SET FOREIGN_KEY_CHECKS = 0; + -- 자식 테이블부터 삭제 DROP TABLE IF EXISTS tb_product_option; DROP TABLE IF EXISTS tb_update_history; @@ -710,7 +713,7 @@ INSERT INTO tb_schedule (SCH_ID, SCH_NAME, SCH_CONT, SCH_SRT_AT, SCH_END_AT, CRE MEM_ID) VALUES ('SCH_000000001', '쏘렌토 신차 발표회', '신차 발표회 준비 및 진행', '2024-02-01 09:00:00', '2024-02-01 12:00:00', '2024-01-20 10:00:00', '2024-01-20 10:30:00', NULL, TRUE, 'MEM_000000001'), - ('SCH_000000002', '스포티지 고객 상담', '신차 구매 고객 상담 일정', '2024-02-05 10:00:00', '2024-02-05 11:30:00', + ('SCH_000000002', '스포티지 고객 상담', '신차 구매 고객 상담 일정', '2024-11-05 10:00:00', '2024-02-05 11:30:00', '2024-01-21 11:00:00', '2024-01-21 11:30:00', NULL, TRUE, 'MEM_000000002'), ('SCH_000000003', 'K7 프로모션 회의', '프로모션 기획 및 준비 회의', '2024-02-10 13:00:00', '2024-02-10 15:00:00', '2024-01-22 12:00:00', '2024-01-22 12:30:00', NULL, TRUE, 'MEM_000000003'), diff --git a/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml b/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml index 7ada392a..f2896c3b 100644 --- a/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml @@ -15,19 +15,58 @@ - SELECT A.SCH_ID, A.SCH_NAME, A.SCH_CONT, - A.START_AT, - A.END_AT, + A.SCH_SRT_AT, + A.SCH_END_AT, A.CREATED_AT, A.UPDATED_AT, A.DELETED_AT, A.ACTIVE, A.MEM_ID - FROM SCHEDULE A - WHERE SCH_ID = #{ id } + FROM TB_SCHEDULE A + WHERE MEM_ID = #{ memberId } + AND SCH_SRT_AT LIKE CONCAT(#{ month },'%') + AND ACTIVE = TRUE; + + + + + \ No newline at end of file From 249a3474fe1b35629b403d272ab6f06a9211b94a Mon Sep 17 00:00:00 2001 From: giuseog Date: Tue, 12 Nov 2024 16:25:01 +0900 Subject: [PATCH 018/563] =?UTF-8?q?feat:=20=EB=A7=A4=EC=9E=A5=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C,=20=EC=83=81=EC=84=B8=EC=A1=B0=ED=9A=8C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(pagination=20=EC=B6=94=EA=B0=80)=20(#8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../center/common/util/RequestList.java | 14 ++++ .../query/controller/CenterController.java | 35 ++++++++- .../center/query/dto/CenterSelectAllDTO.java | 21 ++++++ .../center/query/dto/CenterSelectIdDTO.java | 11 +-- .../center/query/repository/CenterMapper.java | 11 ++- .../center/query/service/CenterService.java | 12 ++- .../query/service/CenterServiceImpl.java | 32 +++++++- .../center/query/repository/CenterMapper.xml | 35 --------- .../query/repository/ScheduleMapper.xml | 33 -------- .../center/query/repository/CenterMapper.xml | 75 +++++++++++++++++++ 10 files changed, 195 insertions(+), 84 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/center/common/util/RequestList.java create mode 100644 src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectAllDTO.java delete mode 100644 src/main/resources/final_backend/domain/center/query/repository/CenterMapper.xml delete mode 100644 src/main/resources/final_backend/domain/schedule/query/repository/ScheduleMapper.xml create mode 100644 src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml diff --git a/src/main/java/stanl_2/final_backend/domain/center/common/util/RequestList.java b/src/main/java/stanl_2/final_backend/domain/center/common/util/RequestList.java new file mode 100644 index 00000000..c625aa6b --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/center/common/util/RequestList.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.center.common.util; + +import lombok.*; +import org.springframework.data.domain.Pageable; + +@Builder +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class RequestList { + private T data; + private Pageable pageable; +} diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java b/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java index 2571517c..1e733732 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java @@ -1,15 +1,22 @@ package stanl_2.final_backend.domain.center.query.controller; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; 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.RequestMapping; import org.springframework.web.bind.annotation.RestController; import stanl_2.final_backend.domain.center.common.response.ResponseMessage; +import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectIdDTO; import stanl_2.final_backend.domain.center.query.service.CenterService; +import java.util.List; +import java.util.Map; + @RestController("queryCenterController") @RequestMapping("/api/v1/center") public class CenterController { @@ -21,11 +28,31 @@ public CenterController(CenterService centerService) { this.centerService = centerService; } - @GetMapping("{centerId}") - public ResponseEntity getTest(@PathVariable Long centerId){ + /* 설명. 조회, 상세조회, 검색 */ + @GetMapping("") + public ResponseEntity getCenterAll(@PageableDefault(size = 20) Pageable pageable){ +// List centerSelectAllDTOList = centerService.selectAll(); - CenterSelectIdDTO centerSelectIdDTO = centerService.selectByCenterId(centerId); + Page> responseCenters = centerService.selectAll(pageable); - return ResponseEntity.ok(new ResponseMessage(200, "get 성공", centerSelectIdDTO)); + return ResponseEntity.ok(ResponseMessage.builder() + .httpStatus(200) + .msg("조회 성공") + .result(responseCenters) + .build()); } + + + @GetMapping("{id}") + public ResponseEntity getCenterById(@PathVariable("id") String id){ + + CenterSelectIdDTO centerSelectIdDTO = centerService.selectByCenterId(id); + + return ResponseEntity.ok(ResponseMessage.builder() + .httpStatus(200) + .msg("상세 조회 성공") + .result(centerSelectIdDTO) + .build()); + } + } diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectAllDTO.java new file mode 100644 index 00000000..bb127578 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectAllDTO.java @@ -0,0 +1,21 @@ +package stanl_2.final_backend.domain.center.query.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString +public class CenterSelectAllDTO { + private String id; + private String name; + private String address; + private String phone; + private Integer memberCount; + private String operatingAt; + private String createdAt; + private String updatedAt; + private String deletedAt; + private Boolean active; +} diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectIdDTO.java b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectIdDTO.java index f2a00838..46767464 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectIdDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectIdDTO.java @@ -2,9 +2,6 @@ import lombok.*; -import java.sql.Timestamp; -import java.time.LocalDateTime; - @AllArgsConstructor @NoArgsConstructor @Getter @@ -12,14 +9,14 @@ @ToString public class CenterSelectIdDTO { - private Long id; + private String id; private String name; private String address; private String phone; private Integer memberCount; private String operatingAt; - private Timestamp createdAt; - private Timestamp updatedAt; - private Timestamp deletedAt; + private String createdAt; + private String updatedAt; + private String deletedAt; private Boolean active; } diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java b/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java index 44b2e042..e7111ca8 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java @@ -1,9 +1,18 @@ package stanl_2.final_backend.domain.center.query.repository; import org.apache.ibatis.annotations.Mapper; +import stanl_2.final_backend.domain.center.common.util.RequestList; +import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectIdDTO; +import java.util.List; +import java.util.Map; + @Mapper public interface CenterMapper { - CenterSelectIdDTO findCenterById(Long centerId); + CenterSelectIdDTO findCenterById(String id); + + List> findCenterAll(RequestList requestList); + + int findCenterCount(); } diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterService.java b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterService.java index 23e8aacc..5e32ebf3 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterService.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterService.java @@ -1,7 +1,17 @@ package stanl_2.final_backend.domain.center.query.service; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectIdDTO; +import java.util.List; +import java.util.Map; + +@Service public interface CenterService { - CenterSelectIdDTO selectByCenterId(Long centerId); + CenterSelectIdDTO selectByCenterId(String id); + + Page> selectAll(Pageable pageable); } diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterServiceImpl.java index 7acff264..07791035 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterServiceImpl.java @@ -2,11 +2,19 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.center.common.util.RequestList; +import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectIdDTO; import stanl_2.final_backend.domain.center.query.repository.CenterMapper; +import java.util.List; +import java.util.Map; + @Slf4j @Service("queryCenterServiceImpl") public class CenterServiceImpl implements CenterService{ @@ -19,11 +27,29 @@ public CenterServiceImpl(CenterMapper centerMapper) { } @Override - @Transactional(readOnly = true) - public CenterSelectIdDTO selectByCenterId(Long centerId) { + @Transactional + public CenterSelectIdDTO selectByCenterId(String id) { - CenterSelectIdDTO centerSelectIdDTO = centerMapper.findCenterById(centerId); + CenterSelectIdDTO centerSelectIdDTO = centerMapper.findCenterById(id); return centerSelectIdDTO; } + + @Override + @Transactional + public Page> selectAll(Pageable pageable) { + + RequestList requestList = RequestList.builder() + .pageable(pageable) + .build(); + + List> centerList = centerMapper.findCenterAll(requestList); + + int total = centerMapper.findCenterCount(); + + return new PageImpl<>(centerList, pageable, total); + } + + + } diff --git a/src/main/resources/final_backend/domain/center/query/repository/CenterMapper.xml b/src/main/resources/final_backend/domain/center/query/repository/CenterMapper.xml deleted file mode 100644 index ebfec867..00000000 --- a/src/main/resources/final_backend/domain/center/query/repository/CenterMapper.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/final_backend/domain/schedule/query/repository/ScheduleMapper.xml b/src/main/resources/final_backend/domain/schedule/query/repository/ScheduleMapper.xml deleted file mode 100644 index 2c094da2..00000000 --- a/src/main/resources/final_backend/domain/schedule/query/repository/ScheduleMapper.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml b/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml new file mode 100644 index 00000000..fa560e84 --- /dev/null +++ b/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From be9dd7b4b74a39235d90a46789b3d809846564b9 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Tue, 12 Nov 2024 17:38:18 +0900 Subject: [PATCH 019/563] =?UTF-8?q?feat:=20=EA=B3=84=EC=95=BD=EC=84=9C=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84=20(#2?= =?UTF-8?q?1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ContractController.java | 2 +- .../dto/request/ContractModifyRequestDTO.java | 7 ++ .../domain/service/ContractServiceImpl.java | 4 +- .../common/exception/CommonException.java | 16 ++++ .../contract/common/exception/ErrorCode.java | 54 +++++++++++++ .../common/exception/ExceptionResponse.java | 22 ++++++ .../common/response/ResponseMessage.java | 14 ++++ .../query/controller/ContractController.java | 48 ++++++++++++ .../contract/query/dto/ContractDTO.java | 42 ++++++++++ .../query/repository/ContractMapper.java | 9 +++ .../query/service/ContractService.java | 7 ++ .../query/service/ContractServiceImpl.java | 33 ++++++++ .../global/exception/ErrorCode.java | 1 - .../query/repository/ContractMapper.xml | 76 +++++++++++++++++++ 14 files changed, 331 insertions(+), 4 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/common/exception/CommonException.java create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/common/exception/ErrorCode.java create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/common/exception/ExceptionResponse.java create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/common/response/ResponseMessage.java create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractServiceImpl.java create mode 100644 src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java index 4a87c17b..b6ef25a6 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java @@ -8,11 +8,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage; import stanl_2.final_backend.domain.contract.command.application.dto.request.ContractModifyRequestDTO; import stanl_2.final_backend.domain.contract.command.application.dto.request.ContractRegistRequestDTO; import stanl_2.final_backend.domain.contract.command.application.dto.response.ContractModifyResponseDTO; import stanl_2.final_backend.domain.contract.command.application.service.ContractService; +import stanl_2.final_backend.domain.contract.common.response.ResponseMessage; @RestController("contractController") @RequestMapping("/api/v1/contract") diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractModifyRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractModifyRequestDTO.java index c90314ec..a61f7b72 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractModifyRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractModifyRequestDTO.java @@ -29,5 +29,12 @@ public class ContractModifyRequestDTO { private String delvLoc; private String state; private String noOfVeh; + private String createdUrl; + private Boolean active; + private String createdAt; + private String updatedAt; private String memId; + private String centId; + private String custId; + private String prodId; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java index a691d989..a38683cc 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java @@ -9,8 +9,8 @@ import stanl_2.final_backend.domain.contract.command.application.service.ContractService; import stanl_2.final_backend.domain.contract.command.domain.aggregate.entity.Contract; import stanl_2.final_backend.domain.contract.command.domain.repository.ContractRepository; -import stanl_2.final_backend.global.exception.CommonException; -import stanl_2.final_backend.global.exception.ErrorCode; +import stanl_2.final_backend.domain.contract.common.exception.CommonException; +import stanl_2.final_backend.domain.contract.common.exception.ErrorCode; import java.sql.Timestamp; import java.time.ZoneId; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/common/exception/CommonException.java b/src/main/java/stanl_2/final_backend/domain/contract/common/exception/CommonException.java new file mode 100644 index 00000000..55d24ddd --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/common/exception/CommonException.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.contract.common.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class CommonException extends RuntimeException { + private final ErrorCode errorCode; + + // 에러 발생시 ErroCode 별 메시지 + @Override + public String getMessage() { + return this.errorCode.getMsg(); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ErrorCode.java b/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ErrorCode.java new file mode 100644 index 00000000..d20039d7 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ErrorCode.java @@ -0,0 +1,54 @@ +package stanl_2.final_backend.domain.contract.common.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum ErrorCode { + + /** + * 400(Bad Request) + * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. + */ + + + + /** + * 401(Unauthorized) + * 비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만, + * 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다. + * 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다. + */ + + + /** + * 403(Forbidden) + * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. + * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다. + */ + + + + /** + * 404(Not Found) + * 서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다. + * 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있습니다. + * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. + * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. + */ + CONTRACT_NOT_FOUND(40401, HttpStatus.NOT_FOUND, "계약서를 찾을 수 없습니다."), + + + /** + * 500(Internal Server Error) + * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. + */ + INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); + + + private final Integer code; + private final HttpStatus httpStatus; + private final String msg; +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ExceptionResponse.java new file mode 100644 index 00000000..70ac42a9 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ExceptionResponse.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.contract.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class ExceptionResponse { + private final Integer code; + private final String msg; + private final HttpStatus httpStatus; + + public ExceptionResponse(ErrorCode errorCode) { + this.code = errorCode.getCode(); + this.msg = errorCode.getMsg(); + this.httpStatus = errorCode.getHttpStatus(); + } + + public static ExceptionResponse of(ErrorCode errorCode) { + return new ExceptionResponse(errorCode); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/common/response/ResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/contract/common/response/ResponseMessage.java new file mode 100644 index 00000000..f9d8d77e --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/common/response/ResponseMessage.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.contract.common.response; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class ResponseMessage { + private int httpStatus; + private String msg; + private Object result; +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java new file mode 100644 index 00000000..805cbb78 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java @@ -0,0 +1,48 @@ +package stanl_2.final_backend.domain.contract.query.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +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.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.contract.query.dto.ContractDTO; +import stanl_2.final_backend.domain.contract.query.service.ContractService; +import stanl_2.final_backend.domain.schedule.common.response.ResponseMessage; + +@RestController("queryContractController") +@RequestMapping("/api/v1/contract") +public class ContractController { + + private final ContractService contractService; + + public ContractController(ContractService contractService) { + this.contractService = contractService; + } + + @Operation(summary = "계약서 상세 조회 api") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "계약서 상세 조회 성공", + content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + }) + @GetMapping("{id}/{memId}") + public ResponseEntity selectDetailContract(@PathVariable("id") String id, + @PathVariable("memId") String memId) { + + ContractDTO contractDTO = new ContractDTO(); + contractDTO.setId(id); + contractDTO.setMemId(memId); + + ContractDTO responseContract = contractService.selectDetailContract(contractDTO); + + return ResponseEntity.ok(ResponseMessage.builder() + .httpStatus(200) + .msg("계약서 상세조회 성공") + .result(responseContract) + .build()); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractDTO.java new file mode 100644 index 00000000..863c7916 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractDTO.java @@ -0,0 +1,42 @@ +package stanl_2.final_backend.domain.contract.query.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString +public class ContractDTO { + + private String id; + private String name; + private String custName; + private String custIdenNo; + private String custAddrress; + private String custEmail; + private String custPhone; + private String compName; + private String custCla; + private String custPurCond; + private String seriNum; + private String seleOpti; + private Integer downPay; + private Integer intePay; + private Integer remPay; + private Integer consPay; + private String delvDate; + private String delvLoc; + private String state; + private String noOfVeh; + private String createdUrl; + private String updatedUrl; + private boolean active; + private String createdAt; + private String updatedAt; + private String deletedAt; + private String memId; + private String centId; + private String custId; + private String prodId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java new file mode 100644 index 00000000..bc8eda64 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.contract.query.repository; + +import org.apache.ibatis.annotations.Mapper; +import stanl_2.final_backend.domain.contract.query.dto.ContractDTO; + +@Mapper +public interface ContractMapper { + ContractDTO findContractByIdAndMemId(ContractDTO contractDTO); +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractService.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractService.java new file mode 100644 index 00000000..8fe8e1aa --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractService.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.contract.query.service; + +import stanl_2.final_backend.domain.contract.query.dto.ContractDTO; + +public interface ContractService { + ContractDTO selectDetailContract(ContractDTO contractDTO); +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractServiceImpl.java new file mode 100644 index 00000000..000dbed5 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractServiceImpl.java @@ -0,0 +1,33 @@ +package stanl_2.final_backend.domain.contract.query.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.contract.common.exception.CommonException; +import stanl_2.final_backend.domain.contract.common.exception.ErrorCode; +import stanl_2.final_backend.domain.contract.query.dto.ContractDTO; +import stanl_2.final_backend.domain.contract.query.repository.ContractMapper; + +@Service("queryContractService") +public class ContractServiceImpl implements ContractService { + + private final ContractMapper contractMapper; + + @Autowired + public ContractServiceImpl(ContractMapper contractMapper) { + this.contractMapper = contractMapper; + } + + + // 계약서 상세조회 + @Override + public ContractDTO selectDetailContract(ContractDTO contractDTO) { + + ContractDTO responseContract = contractMapper.findContractByIdAndMemId(contractDTO); + + if (responseContract == null) { + throw new CommonException(ErrorCode.CONTRACT_NOT_FOUND); + } + + return responseContract; + } +} diff --git a/src/main/java/stanl_2/final_backend/global/exception/ErrorCode.java b/src/main/java/stanl_2/final_backend/global/exception/ErrorCode.java index 59bd09d9..80353610 100644 --- a/src/main/java/stanl_2/final_backend/global/exception/ErrorCode.java +++ b/src/main/java/stanl_2/final_backend/global/exception/ErrorCode.java @@ -49,7 +49,6 @@ public enum ErrorCode { * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. */ USERDETAILS_NOT_FOUND(40400, HttpStatus.NOT_FOUND, "User Details를 찾을 수 없습니다."), - CONTRACT_NOT_FOUND(40403, HttpStatus.NOT_FOUND, "계약서를 찾을 수 없습니다."), /** * 500(Internal Server Error) diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml new file mode 100644 index 00000000..205ba817 --- /dev/null +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 7dca05f0f11db15318095d8399dec9b114f66692 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Tue, 12 Nov 2024 17:51:13 +0900 Subject: [PATCH 020/563] =?UTF-8?q?feat:=20=EA=B3=84=EC=95=BD=EC=84=9C=20?= =?UTF-8?q?=EC=A0=84=EC=B2=B4=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=EC=A4=91(#21)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../contract/query/repository/ContractMapper.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index 205ba817..bf5593be 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -73,4 +73,17 @@ AND A.ACTIVE = TRUE; + + \ No newline at end of file From 1a04ba057824867a3713c3aff1296c1a445ea9d5 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 12 Nov 2024 18:00:19 +0900 Subject: [PATCH 021/563] =?UTF-8?q?=EC=83=98=ED=94=8C=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95(#4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/domain/aggregate/entity/Sample.java | 3 +-- .../domain/service/SampleCommandServiceImpl.java | 6 +++--- .../command/domain/aggragate/entity/Notice.java | 14 +++++++++++++- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/aggregate/entity/Sample.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/aggregate/entity/Sample.java index 4d709591..3aadf8a5 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/aggregate/entity/Sample.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/aggregate/entity/Sample.java @@ -63,9 +63,8 @@ private void preUpdate() { this.updatedAt = getCurrentTime(); } - private String getCurrentTime() { + private String getCurrentTime() { ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } - } diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java index ac1a2886..f9bd78ae 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java @@ -12,11 +12,11 @@ import stanl_2.final_backend.domain.A_sample.command.domain.repository.SampleRepository; import stanl_2.final_backend.domain.A_sample.common.exception.CommonException; import stanl_2.final_backend.domain.A_sample.common.exception.ErrorCode; -import stanl_2.final_backend.domain.A_sample.query.dto.SampleDTO; import java.sql.Timestamp; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; @Service("commandSampleService") public class SampleCommandServiceImpl implements SampleCommandService { @@ -30,9 +30,9 @@ public SampleCommandServiceImpl(SampleRepository sampleRepository, ModelMapper m this.modelMapper = modelMapper; } - private Timestamp getCurrentTimestamp() { + private String getCurrentTimestamp() { ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); - return Timestamp.from(nowKst.toInstant()); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } @Override diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java index 4b3029a0..fb9d1308 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java @@ -39,5 +39,17 @@ public class Notice { private String content; @Column(name = "CREATED_AT", nullable = false, updatable = false) - private Timestamp createdAt; + private String createdAt; + + @Column(name = "UPDATED_AT", nullable = false) + private String updatedAt; + + @Column(name = "DELETED_AT") + private String deletedAt; + + @Column(name = "ACTIVE", nullable = false) + private Boolean active = true; + + @Column(name = "MEM_ID", nullable = false) + private Boolean memberId; } From f7a3b70438b7f74ee2f30caf0db004f797c95522 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 12 Nov 2024 18:01:09 +0900 Subject: [PATCH 022/563] =?UTF-8?q?=EC=83=98=ED=94=8C=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95(#4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/aggragate/entity/Notice.java | 55 ------------------- 1 file changed, 55 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java index fb9d1308..e69de29b 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java @@ -1,55 +0,0 @@ -package stanl_2.final_backend.domain.notices.command.domain.aggragate.entity; - -import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import org.hibernate.annotations.GenericGenerator; -import stanl_2.final_backend.global.config.PrefixGeneratorConfig; - -import java.sql.Timestamp; - -@Entity -@Table(name="NOTICE") -@AllArgsConstructor -@NoArgsConstructor -@Setter -@Getter -public class Notice { - @Id - @GeneratedValue(generator = "PrefixGeneratorConfig") - @GenericGenerator(name = "PrefixGeneratorConfig", - type = PrefixGeneratorConfig.class, - parameters = @org.hibernate.annotations.Parameter(name="prefix", value = "NOT") - ) - @Column(name = "NOT_ID") - private String id; - - @Column(name = "NOT_TTL") - private String title; - - @Column(name = "NOT_TAG") - private String tag; - - @Column(name = "NOT_CLA") - private String classification; - - @Column(columnDefinition = "TEXT", name = "content") - private String content; - - @Column(name = "CREATED_AT", nullable = false, updatable = false) - private String createdAt; - - @Column(name = "UPDATED_AT", nullable = false) - private String updatedAt; - - @Column(name = "DELETED_AT") - private String deletedAt; - - @Column(name = "ACTIVE", nullable = false) - private Boolean active = true; - - @Column(name = "MEM_ID", nullable = false) - private Boolean memberId; -} From 83a895b1558a12e3601001cf198d88aced6a23c5 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 12 Nov 2024 18:13:52 +0900 Subject: [PATCH 023/563] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?(#26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/aggragate/entity/Notice.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java index e69de29b..fb9d1308 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java @@ -0,0 +1,55 @@ +package stanl_2.final_backend.domain.notices.command.domain.aggragate.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +import java.sql.Timestamp; + +@Entity +@Table(name="NOTICE") +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class Notice { + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name="prefix", value = "NOT") + ) + @Column(name = "NOT_ID") + private String id; + + @Column(name = "NOT_TTL") + private String title; + + @Column(name = "NOT_TAG") + private String tag; + + @Column(name = "NOT_CLA") + private String classification; + + @Column(columnDefinition = "TEXT", name = "content") + private String content; + + @Column(name = "CREATED_AT", nullable = false, updatable = false) + private String createdAt; + + @Column(name = "UPDATED_AT", nullable = false) + private String updatedAt; + + @Column(name = "DELETED_AT") + private String deletedAt; + + @Column(name = "ACTIVE", nullable = false) + private Boolean active = true; + + @Column(name = "MEM_ID", nullable = false) + private Boolean memberId; +} From 5271c6966595d3bd3b7a504cf8bfc48077d3abfa Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 12 Nov 2024 18:35:01 +0900 Subject: [PATCH 024/563] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20dto=20=EC=83=9D=EC=84=B1=20=EC=A4=91=20(#26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notices/command/application/dto/NoticeModifyDTO.java | 4 ++++ .../notices/command/application/dto/NoticeRegistDTO.java | 4 ++++ .../command/domain/repository/NoticeRepository.java | 7 +++++++ 3 files changed, 15 insertions(+) create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/command/domain/repository/NoticeRepository.java diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java new file mode 100644 index 00000000..1bee9503 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java @@ -0,0 +1,4 @@ +package stanl_2.final_backend.domain.notices.command.application.dto; + +public class NoticeModifyDTO { +} diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java new file mode 100644 index 00000000..f3e5531d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java @@ -0,0 +1,4 @@ +package stanl_2.final_backend.domain.notices.command.application.dto; + +public class NoticeRegistDTO { +} diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/repository/NoticeRepository.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/repository/NoticeRepository.java new file mode 100644 index 00000000..105920ab --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/repository/NoticeRepository.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.notices.command.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import stanl_2.final_backend.domain.notices.command.domain.aggragate.entity.Notice; + +public interface NoticeRepository extends JpaRepository { +} From bfdfc4fd9a9524f4b049075ebd5b775254ed282b Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 12 Nov 2024 18:41:17 +0900 Subject: [PATCH 025/563] =?UTF-8?q?=EC=83=98=ED=94=8C=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95(#4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/controller/SampleController.java | 13 ++++++------- ...leModifyRequestDTO.java => SampleModifyDTO.java} | 4 ++-- ...leRegistRequestDTO.java => SampleRegistDTO.java} | 4 ++-- .../dto/response/SampleModifyResponseDTO.java | 13 ------------- .../application/service/SampleCommandService.java | 9 ++++----- .../domain/service/SampleCommandServiceImpl.java | 12 +++++------- 6 files changed, 19 insertions(+), 36 deletions(-) rename src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/{request/SampleModifyRequestDTO.java => SampleModifyDTO.java} (80%) rename src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/{request/SampleRegistRequestDTO.java => SampleRegistDTO.java} (80%) delete mode 100644 src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/response/SampleModifyResponseDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java index b5eb399b..2d5921e4 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java @@ -8,9 +8,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.A_sample.command.application.dto.request.SampleRegistRequestDTO; -import stanl_2.final_backend.domain.A_sample.command.application.dto.request.SampleModifyRequestDTO; -import stanl_2.final_backend.domain.A_sample.command.application.dto.response.SampleModifyResponseDTO; +import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleRegistDTO; +import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleModifyDTO; import stanl_2.final_backend.domain.A_sample.command.application.service.SampleCommandService; import stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage; @@ -39,7 +38,7 @@ public SampleController(SampleCommandService sampleCommandService) { content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postTest(@RequestBody SampleRegistRequestDTO sampleRegistRequestDTO) { + public ResponseEntity postTest(@RequestBody SampleRegistDTO sampleRegistRequestDTO) { sampleCommandService.registerSample(sampleRegistRequestDTO); @@ -64,15 +63,15 @@ public ResponseEntity postTest(@RequestBody SampleRegistRequest }) @PutMapping("{id}") public ResponseEntity putTest(@PathVariable String id, - @RequestBody SampleModifyRequestDTO sampleModifyRequestDTO) { + @RequestBody SampleModifyDTO sampleModifyRequestDTO) { sampleModifyRequestDTO.setId(id); - SampleModifyResponseDTO sampleModifyResponseDTO = sampleCommandService.modifySample(id, sampleModifyRequestDTO); + SampleModifyDTO sampleModifyDTO = sampleCommandService.modifySample(id, sampleModifyRequestDTO); return ResponseEntity.ok(ResponseMessage.builder() .httpStatus(200) .msg("성공") - .result(sampleModifyResponseDTO) + .result(sampleModifyDTO) .build()); } diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/request/SampleModifyRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/SampleModifyDTO.java similarity index 80% rename from src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/request/SampleModifyRequestDTO.java rename to src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/SampleModifyDTO.java index ca49903d..f6d16e27 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/request/SampleModifyRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/SampleModifyDTO.java @@ -1,4 +1,4 @@ -package stanl_2.final_backend.domain.A_sample.command.application.dto.request; +package stanl_2.final_backend.domain.A_sample.command.application.dto; import lombok.*; @@ -7,7 +7,7 @@ @Setter @Getter @ToString -public class SampleModifyRequestDTO { +public class SampleModifyDTO { private String id; private String name; private Integer num; diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/request/SampleRegistRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/SampleRegistDTO.java similarity index 80% rename from src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/request/SampleRegistRequestDTO.java rename to src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/SampleRegistDTO.java index cefe0f79..ad5a9ea5 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/request/SampleRegistRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/SampleRegistDTO.java @@ -1,4 +1,4 @@ -package stanl_2.final_backend.domain.A_sample.command.application.dto.request; +package stanl_2.final_backend.domain.A_sample.command.application.dto; import lombok.*; @@ -7,7 +7,7 @@ @Setter @Getter @ToString -public class SampleRegistRequestDTO { +public class SampleRegistDTO { private String id; private String name; private Integer num; diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/response/SampleModifyResponseDTO.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/response/SampleModifyResponseDTO.java deleted file mode 100644 index f8ddc296..00000000 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/response/SampleModifyResponseDTO.java +++ /dev/null @@ -1,13 +0,0 @@ -package stanl_2.final_backend.domain.A_sample.command.application.dto.response; - -import lombok.*; - -@AllArgsConstructor -@NoArgsConstructor -@Setter -@Getter -@ToString -public class SampleModifyResponseDTO { - private String name; - private Integer num; -} diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/service/SampleCommandService.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/service/SampleCommandService.java index 2e08f5f1..1a702f58 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/service/SampleCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/service/SampleCommandService.java @@ -1,13 +1,12 @@ package stanl_2.final_backend.domain.A_sample.command.application.service; -import stanl_2.final_backend.domain.A_sample.command.application.dto.request.SampleRegistRequestDTO; -import stanl_2.final_backend.domain.A_sample.command.application.dto.request.SampleModifyRequestDTO; -import stanl_2.final_backend.domain.A_sample.command.application.dto.response.SampleModifyResponseDTO; +import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleRegistDTO; +import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleModifyDTO; public interface SampleCommandService { - void registerSample(SampleRegistRequestDTO sampleRegistRequestDTO); + void registerSample(SampleRegistDTO sampleRegistRequestDTO); - SampleModifyResponseDTO modifySample(String id, SampleModifyRequestDTO sampleModifyRequestDTO); + SampleModifyDTO modifySample(String id, SampleModifyDTO sampleModifyDTO); void deleteSample(String id); } diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java index f9bd78ae..4187bbfb 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java @@ -4,16 +4,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.A_sample.command.application.dto.request.SampleRegistRequestDTO; -import stanl_2.final_backend.domain.A_sample.command.application.dto.request.SampleModifyRequestDTO; -import stanl_2.final_backend.domain.A_sample.command.application.dto.response.SampleModifyResponseDTO; +import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleRegistDTO; +import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleModifyDTO; import stanl_2.final_backend.domain.A_sample.command.application.service.SampleCommandService; import stanl_2.final_backend.domain.A_sample.command.domain.aggregate.entity.Sample; import stanl_2.final_backend.domain.A_sample.command.domain.repository.SampleRepository; import stanl_2.final_backend.domain.A_sample.common.exception.CommonException; import stanl_2.final_backend.domain.A_sample.common.exception.ErrorCode; -import java.sql.Timestamp; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -37,7 +35,7 @@ private String getCurrentTimestamp() { @Override @Transactional - public void registerSample(SampleRegistRequestDTO sampleRegistRequestDTO) { + public void registerSample(SampleRegistDTO sampleRegistRequestDTO) { Sample newSample = modelMapper.map(sampleRegistRequestDTO, Sample.class); @@ -46,7 +44,7 @@ public void registerSample(SampleRegistRequestDTO sampleRegistRequestDTO) { @Override @Transactional - public SampleModifyResponseDTO modifySample(String id, SampleModifyRequestDTO sampleModifyRequestDTO) { + public SampleModifyDTO modifySample(String id, SampleModifyDTO sampleModifyRequestDTO) { Sample sample = sampleRepository.findById(id) .orElseThrow(() -> new CommonException(ErrorCode.SAMPLE_NOT_FOUND)); @@ -58,7 +56,7 @@ public SampleModifyResponseDTO modifySample(String id, SampleModifyRequestDTO sa sampleRepository.save(updateSample); - SampleModifyResponseDTO sampleModifyResponseDTO= modelMapper.map(updateSample, SampleModifyResponseDTO.class); + SampleModifyDTO sampleModifyResponseDTO= modelMapper.map(updateSample, SampleModifyDTO.class); return sampleModifyResponseDTO; } From e5d84650a4243e8378e59a9bdc9a830578e56070 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Tue, 12 Nov 2024 18:50:25 +0900 Subject: [PATCH 026/563] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EB=8F=84=EC=A4=91=20sample=20pull=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20commit(#17)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/aggregate/Role.java | 11 -- .../member/aggregate/dto/LoginMemberDTO.java | 13 -- .../member/aggregate/dto/SignUpMemberDTO.java | 22 ---- .../domain/member/aggregate/dto/jwtDTO.java | 12 -- .../member/aggregate/entity/Member.java | 60 ---------- .../aggregate/vo/request/LoginRequestVO.java | 18 --- .../aggregate/vo/request/SignUpRequestVO.java | 33 ----- .../controller/AuthController.java | 10 ++ .../service/AuthCommandService.java | 4 + .../domain/aggregate/entity/Member.java | 113 ++++++++++++++++++ .../domain/aggregate/entity/MemberRole.java | 30 +++++ .../domain/repository/AuthRepository.java | 7 ++ .../service/AuthCommandServiceImpl.java | 28 +++++ .../infrastructure/service/SampleService.java | 10 ++ .../common/exception/CommonException.java | 16 +++ .../member/common/exception/ErrorCode.java | 54 +++++++++ .../common/exception/ExceptionResponse.java | 22 ++++ .../common/response/ResponseMessage.java | 14 +++ .../member/controller/AuthController.java | 85 ------------- .../query/controller/SampleController.java | 73 +++++++++++ .../domain/member/query/dto/SampleDTO.java | 22 ++++ .../member/query/repository/SampleMapper.java | 12 ++ .../member/query/service/SampleService.java | 9 ++ .../query/service/SampleServiceImpl.java | 50 ++++++++ .../member/repository/MemberRepository.java | 10 -- .../domain/member/service/MemberService.java | 10 -- .../member/service/MemberServiceImpl.java | 87 -------------- .../security/config/ProdSecurityConfig.java | 29 ++--- ...ProdUsernamePwdAuthenticationProvider.java | 3 - .../security/config/SecurityConfig.java | 99 +++++++++++++++ .../UsernamePwdAuthenticationProvider.java | 37 ++++++ .../constants/ApplicationConstants.java | 21 ++-- .../security/events/AuthenticationEvents.java | 4 +- .../security/events/AuthorizationEvents.java | 6 +- .../security/filter/CsrfCookieFilter.java | 2 - .../filter/JWTTokenGeneratorFilter.java | 22 ++-- .../filter/JWTTokenValidatorFilter.java | 89 +++++--------- .../global/security/filter/TokenFilter.java | 88 ++++++++++++++ .../service/MemberDetailsServiceImpl.java | 2 - .../security/service/MemberPrincipal.java | 1 - ...pplication_dev.yml => application-dev.yml} | 0 ...lication_prod.yml => application-prod.yml} | 0 src/main/resources/sql/ddl.sql | 2 +- 43 files changed, 764 insertions(+), 476 deletions(-) delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/aggregate/Role.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/aggregate/dto/LoginMemberDTO.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/aggregate/dto/SignUpMemberDTO.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/aggregate/dto/jwtDTO.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/aggregate/entity/Member.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/aggregate/vo/request/LoginRequestVO.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/aggregate/vo/request/SignUpRequestVO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/MemberRole.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/domain/repository/AuthRepository.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/infrastructure/service/SampleService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/common/exception/CommonException.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/common/exception/ErrorCode.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/common/exception/ExceptionResponse.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/common/response/ResponseMessage.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/controller/AuthController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/controller/SampleController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/dto/SampleDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/repository/SampleMapper.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/service/SampleService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/service/SampleServiceImpl.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/repository/MemberRepository.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/service/MemberService.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/service/MemberServiceImpl.java create mode 100644 src/main/java/stanl_2/final_backend/global/security/config/SecurityConfig.java create mode 100644 src/main/java/stanl_2/final_backend/global/security/config/UsernamePwdAuthenticationProvider.java create mode 100644 src/main/java/stanl_2/final_backend/global/security/filter/TokenFilter.java rename src/main/resources/{application_dev.yml => application-dev.yml} (100%) rename src/main/resources/{application_prod.yml => application-prod.yml} (100%) diff --git a/src/main/java/stanl_2/final_backend/domain/member/aggregate/Role.java b/src/main/java/stanl_2/final_backend/domain/member/aggregate/Role.java deleted file mode 100644 index 9aaa5511..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/aggregate/Role.java +++ /dev/null @@ -1,11 +0,0 @@ -package stanl_2.final_backend.domain.member.aggregate; - -public enum Role { - ROLE_CUSTOMER, - ROLE_SALES_PERSON, - ROLE_SALES_MANAGER, - ROLE_INVENTORY_MANAGER, - ROLE_PRODUCT_MANAGER, - ROLE_SALES_ADMIN, - ROLE_SYSTEM_ADMIN; -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/aggregate/dto/LoginMemberDTO.java b/src/main/java/stanl_2/final_backend/domain/member/aggregate/dto/LoginMemberDTO.java deleted file mode 100644 index 41634840..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/aggregate/dto/LoginMemberDTO.java +++ /dev/null @@ -1,13 +0,0 @@ -package stanl_2.final_backend.domain.member.aggregate.dto; - -import lombok.*; - -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -@ToString -public class LoginMemberDTO { - private String loginId; - private String password; -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/aggregate/dto/SignUpMemberDTO.java b/src/main/java/stanl_2/final_backend/domain/member/aggregate/dto/SignUpMemberDTO.java deleted file mode 100644 index e9ef48ef..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/aggregate/dto/SignUpMemberDTO.java +++ /dev/null @@ -1,22 +0,0 @@ -package stanl_2.final_backend.domain.member.aggregate.dto; - -import lombok.*; -import stanl_2.final_backend.domain.member.aggregate.Role; - -import java.sql.Timestamp; - -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -@ToString -public class SignUpMemberDTO { - private String loginId; - private String password; - private String email; - private String name; - private String phone; - private Role role; - private Timestamp createdAt; - private Timestamp updatedAt; -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/aggregate/dto/jwtDTO.java b/src/main/java/stanl_2/final_backend/domain/member/aggregate/dto/jwtDTO.java deleted file mode 100644 index 0fcf6ffe..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/aggregate/dto/jwtDTO.java +++ /dev/null @@ -1,12 +0,0 @@ -package stanl_2.final_backend.domain.member.aggregate.dto; - -import lombok.*; - -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -@ToString -public class jwtDTO { - private String jwt; -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/aggregate/entity/Member.java b/src/main/java/stanl_2/final_backend/domain/member/aggregate/entity/Member.java deleted file mode 100644 index 80cad899..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/aggregate/entity/Member.java +++ /dev/null @@ -1,60 +0,0 @@ -package stanl_2.final_backend.domain.member.aggregate.entity; - -import jakarta.persistence.*; -import jakarta.validation.constraints.NotNull; -import lombok.*; -import stanl_2.final_backend.domain.member.aggregate.Role; - -import java.sql.Timestamp; - -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -@ToString -@Entity -@Table(name = "MEMBER") -public class Member { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "MEMBER_ID") - private Long id; - - @Column(name = "MEMBER_LOGIN_ID") - @NotNull - private String loginId; - - @Column(name = "MEMBER_PASSWORD") - @NotNull - private String password; - - @Column(name = "MEMBER_EMAIL") - @NotNull - private String email; - - @Column(name = "MEMBER_NAME") - @NotNull - private String name; - - @Column(name = "MEMBER_PHONE") - @NotNull - private String phone; - - @Column(name = "MEMBER_ROLE") - @Enumerated(EnumType.STRING) - @NotNull - private Role role; - - @Column(name = "MEMBER_CREATED_AT") - @NotNull - private Timestamp createdAt; - - @Column(name = "MEMBER_UPDATED_AT") - @NotNull - private Timestamp updatedAt; - - @Column(name = "MEMBER_ACTIVE") - @NotNull - private Boolean active = true; -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/aggregate/vo/request/LoginRequestVO.java b/src/main/java/stanl_2/final_backend/domain/member/aggregate/vo/request/LoginRequestVO.java deleted file mode 100644 index a48fd34f..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/aggregate/vo/request/LoginRequestVO.java +++ /dev/null @@ -1,18 +0,0 @@ -package stanl_2.final_backend.domain.member.aggregate.vo.request; - -import jakarta.validation.constraints.NotNull; -import lombok.*; - -@Getter -@Setter -@AllArgsConstructor -@NoArgsConstructor -@EqualsAndHashCode -public class LoginRequestVO { - - @NotNull(message = "아이디를 입력해주세요.") - private String loginId; - - @NotNull(message = "비밀번호를 입력해주세요.") - private String password; -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/aggregate/vo/request/SignUpRequestVO.java b/src/main/java/stanl_2/final_backend/domain/member/aggregate/vo/request/SignUpRequestVO.java deleted file mode 100644 index 144ca9c5..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/aggregate/vo/request/SignUpRequestVO.java +++ /dev/null @@ -1,33 +0,0 @@ -package stanl_2.final_backend.domain.member.aggregate.vo.request; - -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotNull; -import lombok.*; -import stanl_2.final_backend.domain.member.aggregate.Role; - -@Getter -@Setter -@AllArgsConstructor -@NoArgsConstructor -@EqualsAndHashCode -public class SignUpRequestVO { - - @NotNull(message = "아이디를 입력해주세요.") - private String loginId; - - @NotNull(message = "비밀번호를 입력해주세요.") - private String password; - - @NotNull(message = "이메일을 입력해 주세요.") - @Email - private String email; - - @NotNull(message = "성함을 입력해주세요.") - private String name; - - @NotNull(message = "연락처를 입력해주세요.") - private String phone; - - @NotNull(message = "역할을 입력해주세요.") - private Role role; -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java new file mode 100644 index 00000000..8ce2e887 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java @@ -0,0 +1,10 @@ +package stanl_2.final_backend.domain.member.command.application.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController("commandAuthController") +@RequestMapping("/api/v1/auth") +public class AuthController { + +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java new file mode 100644 index 00000000..b3137dd1 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java @@ -0,0 +1,4 @@ +package stanl_2.final_backend.domain.member.command.application.service; + +public interface AuthCommandService { +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java new file mode 100644 index 00000000..9cb8007f --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java @@ -0,0 +1,113 @@ +package stanl_2.final_backend.domain.member.command.domain.aggregate.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "TB_MEMBER") +public class Member { + + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "MEM") + ) + @Column(name = "MEM_ID", nullable = false) + private String id; + + @Column(name = "MEM_LOGIN_ID", nullable = false) + private String loginId; + + @Column(name = "MEM_PWD", nullable = false) + private String password; + + @Column(name = "MEM_NAME", nullable = false) + private String name; + + @Column(name = "MEM_EMA", nullable = false) + private String email; + + @Column(name = "MEM_AGE", nullable = false) + private Integer age; + + @Column(name = "MEM_SEX", nullable = false) + private String sex; + + @Column(name = "MEM_IDEN_NO", nullable = false) + private String idenNo; + + @Column(name = "MEM_MEM_PHO", nullable = false) + private String phone; + + @Column(name = "MEM_EMER_PHO") + private String emergPhone; + + @Column(name = "MEM_ADR", nullable = false) + private String address; + + @Column(name = "MEM_NOTE") + private String note; + + @Column(name = "MEM_POS", nullable = false) + private String position; + + @Column(name = "MEM_GRD", nullable = false) + private String grade; + + @Column(name = "MEM_JOB_TYPE", nullable = false) + private String jobType; + + @Column(name = "MEM_MIL", nullable = false) + private String military; + + @Column(name = "MEM_BANK_NAME") + private String bankName; + + @Column(name = "MEM_ACC") + private String account; + + @Column(name = "CREATED_AT", nullable = false, updatable = false) + private String createdAt; + + @Column(name = "UPDATED_AT", nullable = false) + private String updatedAt; + + @Column(name = "DELETED_AT") + private String deletedAt; + + @Column(name = "ACTIVE", nullable = false) + private Boolean active = true; + + /* 설명. updatedAt 자동화 */ + // Insert 되기 전에 실행 + @PrePersist + private void prePersist() { + this.createdAt = getCurrentTime(); + this.updatedAt = this.createdAt; + } + + // Update 되기 전에 실행 + @PreUpdate + private void preUpdate() { + this.updatedAt = getCurrentTime(); + } + + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/MemberRole.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/MemberRole.java new file mode 100644 index 00000000..62b7fbb9 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/MemberRole.java @@ -0,0 +1,30 @@ +package stanl_2.final_backend.domain.member.command.domain.aggregate.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "TB_MEMBER_ROLE") +public class MemberRole { + + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "MEM_ROL") + ) + @Column(name = "MEM_ROL_ID", nullable = false) + private String id; + + @Column(name = "MEM_ROL_NAME", nullable = false) + private String role; +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/repository/AuthRepository.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/repository/AuthRepository.java new file mode 100644 index 00000000..4850abd3 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/repository/AuthRepository.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.member.command.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.Member; + +public interface AuthRepository extends JpaRepository { +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java new file mode 100644 index 00000000..43513aba --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java @@ -0,0 +1,28 @@ +package stanl_2.final_backend.domain.member.command.domain.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; +import stanl_2.final_backend.domain.member.command.domain.repository.AuthRepository; + +import java.sql.Timestamp; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +@Service("commandAuthService") +public class AuthCommandServiceImpl implements AuthCommandService { + + private final AuthRepository authRepository; + + @Autowired + public AuthCommandServiceImpl(AuthRepository authRepository) { + this.authRepository = authRepository; + } + + private Timestamp getCurrentTimestamp() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return Timestamp.from(nowKst.toInstant()); + } + + +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/infrastructure/service/SampleService.java b/src/main/java/stanl_2/final_backend/domain/member/command/infrastructure/service/SampleService.java new file mode 100644 index 00000000..4982dcd5 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/command/infrastructure/service/SampleService.java @@ -0,0 +1,10 @@ +package stanl_2.final_backend.domain.member.command.infrastructure.service; + +import org.springframework.stereotype.Service; + +@Service("infrastructureSampleService") +public class SampleService { + + // 외부 시스템과의 통신이 필요하면 이 폴더에 파일 생성하여 통신 + +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/common/exception/CommonException.java b/src/main/java/stanl_2/final_backend/domain/member/common/exception/CommonException.java new file mode 100644 index 00000000..4fa115b7 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/common/exception/CommonException.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.member.common.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class CommonException extends RuntimeException { + private final ErrorCode errorCode; + + // 에러 발생시 ErroCode 별 메시지 + @Override + public String getMessage() { + return this.errorCode.getMsg(); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/common/exception/ErrorCode.java b/src/main/java/stanl_2/final_backend/domain/member/common/exception/ErrorCode.java new file mode 100644 index 00000000..f93d7d7e --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/common/exception/ErrorCode.java @@ -0,0 +1,54 @@ +package stanl_2.final_backend.domain.member.common.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum ErrorCode { + + /** + * 400(Bad Request) + * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. + */ + + + + /** + * 401(Unauthorized) + * 비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만, + * 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다. + * 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다. + */ + + + /** + * 403(Forbidden) + * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. + * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다. + */ + + + + /** + * 404(Not Found) + * 서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다. + * 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있습니다. + * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. + * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. + */ + SAMPLE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "sample 데이터를 찾지 못했습니다"), + + + /** + * 500(Internal Server Error) + * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. + */ + INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); + + + private final Integer code; + private final HttpStatus httpStatus; + private final String msg; +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/common/exception/ExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/member/common/exception/ExceptionResponse.java new file mode 100644 index 00000000..37d483ec --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/common/exception/ExceptionResponse.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.member.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class ExceptionResponse { + private final Integer code; + private final String msg; + private final HttpStatus httpStatus; + + public ExceptionResponse(ErrorCode errorCode) { + this.code = errorCode.getCode(); + this.msg = errorCode.getMsg(); + this.httpStatus = errorCode.getHttpStatus(); + } + + public static ExceptionResponse of(ErrorCode errorCode) { + return new ExceptionResponse(errorCode); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/common/response/ResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/member/common/response/ResponseMessage.java new file mode 100644 index 00000000..1742aabc --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/common/response/ResponseMessage.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.member.common.response; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class ResponseMessage { + private int httpStatus; + private String msg; + private Object result; +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/member/controller/AuthController.java b/src/main/java/stanl_2/final_backend/domain/member/controller/AuthController.java deleted file mode 100644 index 598c8c9e..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/controller/AuthController.java +++ /dev/null @@ -1,85 +0,0 @@ -package stanl_2.final_backend.domain.member.controller; - -import io.swagger.v3.oas.annotations.Operation; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.modelmapper.ModelMapper; -import org.springframework.http.ResponseEntity; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -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.RestController; -import stanl_2.final_backend.domain.member.aggregate.dto.SignUpMemberDTO; -import stanl_2.final_backend.domain.member.aggregate.dto.jwtDTO; -import stanl_2.final_backend.domain.member.aggregate.vo.request.LoginRequestVO; -import stanl_2.final_backend.domain.member.aggregate.vo.request.SignUpRequestVO; -import stanl_2.final_backend.domain.member.service.MemberService; -import stanl_2.final_backend.global.common.response.ResponseMessage; -import stanl_2.final_backend.global.exception.CommonException; -import stanl_2.final_backend.global.exception.ErrorCode; -import stanl_2.final_backend.global.utils.RequestUtils; - -@Slf4j -@RequiredArgsConstructor -@RestController("value = authController") -@RequestMapping("/api/v1/auth") -public class AuthController { - - private final MemberService memberService; - private final ModelMapper modelMapper; - private final AuthenticationManager authenticationManager; - - @PostMapping("/signup") - @Operation(summary = "회원가입 API") - public ResponseEntity signUp(@RequestBody SignUpRequestVO signUpRequestVO) { - - SignUpMemberDTO memberRequestDTO = modelMapper.map(signUpRequestVO, SignUpMemberDTO.class); - - if (memberService.signUp(memberRequestDTO)) { - new CommonException(ErrorCode.REGISTER_FAIL); - } - -// return ResponseEntity.ok("회원가입 성공!"); - return ResponseEntity.ok(new ResponseMessage(200, "회원가입 성공", null)); - } - - @PostMapping("/signin") - @Operation(summary = "로그인 API") - public ResponseEntity signin(@RequestBody LoginRequestVO loginRequestVO, - HttpServletRequest request, - HttpServletResponse response) { - - log.info("c1"); - // 인증 생성 - Authentication authentication = UsernamePasswordAuthenticationToken - .unauthenticated(loginRequestVO.getLoginId(), loginRequestVO.getPassword()); - - log.info("c2"); - // 인증 처리 - Authentication authenticationResponse = authenticationManager.authenticate(authentication); - log.info("c3"); - - // JWT 생성 - String jwt = memberService.signin(authenticationResponse); - log.info("c4"); - - if("".equals(jwt)){ - throw new CommonException(ErrorCode.LOGIN_FAILURE); - } - log.info("c5"); - - // 로그인 이력 저장(ip, local 컴퓨터) - log.info("{}", RequestUtils.getClientIp(request)); - log.info("{}", request.getHeader("User-Agent")); - jwtDTO jwt1 = new jwtDTO(); - jwt1.setJwt(jwt); -// return ResponseEntity.ok(jwt1); - return ResponseEntity.ok(new ResponseMessage(200, "로그인 성공", jwt)); - } - -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/controller/SampleController.java b/src/main/java/stanl_2/final_backend/domain/member/query/controller/SampleController.java new file mode 100644 index 00000000..83d62c19 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/query/controller/SampleController.java @@ -0,0 +1,73 @@ +package stanl_2.final_backend.domain.member.query.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +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.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.member.common.response.ResponseMessage; +import stanl_2.final_backend.domain.member.query.dto.SampleDTO; +import stanl_2.final_backend.domain.member.query.service.SampleService; + +@RestController(value = "querySampleController") +@RequestMapping("/api/v1/sample") +public class SampleController { + + private final SampleService sampleService; + + @Autowired + public SampleController(SampleService sampleService) { + this.sampleService = sampleService; + } + + /** + * [GET] http://localhost:7777/api/v1/sample/SAM_000000001 + * */ + @Operation(summary = "샘플 조회 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("{id}") + public ResponseEntity getTest(@PathVariable String id){ + + SampleDTO sampleDTO = sampleService.selectSampleInfo(id); + + return ResponseEntity.ok(ResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(sampleDTO) + .build()); + } + + /** + * [GET] http://localhost:7777/api/v1/sample/detail/SAM_000000001 + * */ + @Operation(summary = "샘플 상세 조회 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/detail/{id}") + public ResponseEntity getDetailTest(@PathVariable String id) { + + String name = sampleService.selectSampleName(id); + + return ResponseEntity.ok(ResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(name) + .build()); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/dto/SampleDTO.java b/src/main/java/stanl_2/final_backend/domain/member/query/dto/SampleDTO.java new file mode 100644 index 00000000..ffb84de5 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/query/dto/SampleDTO.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.member.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.sql.Timestamp; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@ToString +public class SampleDTO { + private String id; + private String name; + private Integer num; + private Timestamp createdAt; + private Timestamp updatedAt; + private Timestamp deletedAt; + private Boolean active; +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/repository/SampleMapper.java b/src/main/java/stanl_2/final_backend/domain/member/query/repository/SampleMapper.java new file mode 100644 index 00000000..a5a813fa --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/query/repository/SampleMapper.java @@ -0,0 +1,12 @@ +package stanl_2.final_backend.domain.member.query.repository; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import stanl_2.final_backend.domain.member.query.dto.SampleDTO; + +@Mapper +public interface SampleMapper { + String selectNameById(@Param("id") String id); + + SampleDTO selectById(@Param("id") String id); +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/SampleService.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/SampleService.java new file mode 100644 index 00000000..30b83554 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/SampleService.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.member.query.service; + +import stanl_2.final_backend.domain.member.query.dto.SampleDTO; + +public interface SampleService { + String selectSampleName(String id); + + SampleDTO selectSampleInfo(String id); +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/SampleServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/SampleServiceImpl.java new file mode 100644 index 00000000..c0f8ea77 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/SampleServiceImpl.java @@ -0,0 +1,50 @@ +package stanl_2.final_backend.domain.member.query.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.member.common.exception.CommonException; +import stanl_2.final_backend.domain.member.common.exception.ErrorCode; +import stanl_2.final_backend.domain.member.query.dto.SampleDTO; +import stanl_2.final_backend.domain.member.query.repository.SampleMapper; + +@Slf4j +@Service(value = "querySampleService") +public class SampleServiceImpl implements SampleService { + + private final SampleMapper sampleMapper; + + @Autowired + public SampleServiceImpl(SampleMapper sampleMapper) { + this.sampleMapper = sampleMapper; + } + + @Override + @Transactional(readOnly = true) + public String selectSampleName(String id) { + + String name = sampleMapper.selectNameById(id);; + + if(name == null){ + throw new CommonException(ErrorCode.SAMPLE_NOT_FOUND); + } + + return name; + } + + @Override + @Transactional(readOnly = true) + public SampleDTO selectSampleInfo(String id) { + + SampleDTO sampleDTO = sampleMapper.selectById(id); + + if(sampleDTO == null){ + throw new CommonException(ErrorCode.SAMPLE_NOT_FOUND); + } + + return sampleDTO; + } + + +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/repository/MemberRepository.java b/src/main/java/stanl_2/final_backend/domain/member/repository/MemberRepository.java deleted file mode 100644 index 8cc11387..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/repository/MemberRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package stanl_2.final_backend.domain.member.repository; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; -import stanl_2.final_backend.domain.member.aggregate.entity.Member; - -@Repository -public interface MemberRepository extends JpaRepository { - Member findByloginId(String loginId); -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/service/MemberService.java b/src/main/java/stanl_2/final_backend/domain/member/service/MemberService.java deleted file mode 100644 index 6f4d1d59..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/service/MemberService.java +++ /dev/null @@ -1,10 +0,0 @@ -package stanl_2.final_backend.domain.member.service; - -import org.springframework.security.core.Authentication; -import stanl_2.final_backend.domain.member.aggregate.dto.SignUpMemberDTO; - -public interface MemberService { - Boolean signUp(SignUpMemberDTO memberRequestDTO); - - String signin(Authentication authenticationResponse); -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/service/MemberServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/service/MemberServiceImpl.java deleted file mode 100644 index d04b9d49..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/service/MemberServiceImpl.java +++ /dev/null @@ -1,87 +0,0 @@ -package stanl_2.final_backend.domain.member.service; - -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.security.Keys; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.modelmapper.ModelMapper; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Service; -import stanl_2.final_backend.domain.member.aggregate.dto.SignUpMemberDTO; -import stanl_2.final_backend.domain.member.aggregate.entity.Member; -import stanl_2.final_backend.domain.member.repository.MemberRepository; -import stanl_2.final_backend.global.security.constants.ApplicationConstants; -import stanl_2.final_backend.global.security.service.MemberPrincipal; - -import javax.crypto.SecretKey; -import java.sql.Timestamp; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.stream.Collectors; - -@Slf4j -@Service("memberServiceImpl") -@RequiredArgsConstructor -public class MemberServiceImpl implements MemberService { - - private final MemberRepository memberRepository; - private final ModelMapper modelMapper; - private final PasswordEncoder passwordEncoder; - private final ApplicationConstants applicationConstants; - private static final long JWT_EXPIRATION_TIME = 30000000L; - - private Timestamp getCurrentTimestamp() { - ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); - return Timestamp.from(nowKst.toInstant()); - } - - @Override - public Boolean signUp(SignUpMemberDTO memberRequestDTO) { - Timestamp currentTimestamp = getCurrentTimestamp(); - - String hashPwd = passwordEncoder.encode(memberRequestDTO.getPassword()); - memberRequestDTO.setPassword(hashPwd); - - Member signUpMember = modelMapper.map(memberRequestDTO, Member.class); - signUpMember.setCreatedAt(currentTimestamp); - signUpMember.setUpdatedAt(currentTimestamp); - - Member newMember = memberRepository.save(signUpMember); - - if(newMember != null) { - return false; - } - - return true; - } - - @Override - public String signin(Authentication authenticationResponse) { - - String jwt = ""; - - if(authenticationResponse != null && authenticationResponse.isAuthenticated()){ - - // Base64로 디코딩된 SecretKey 사용 - byte[] decodedKey = applicationConstants.getJWT_SECRET_KEY(); - SecretKey secretKey = Keys.hmacShaKeyFor(decodedKey); - - MemberPrincipal memberDetails = (MemberPrincipal) authenticationResponse.getPrincipal(); - Member member = memberDetails.getMember(); // MemberPrincipal에서 Member를 얻어옴 - - jwt = Jwts.builder() - .setIssuer("STANL2") - .setSubject("JWT Token") - .claim("loginId", authenticationResponse.getName()) - .claim("authorities", authenticationResponse.getAuthorities() - .stream().map( - GrantedAuthority::getAuthority).collect(Collectors.joining(","))) - .setIssuedAt(new java.util.Date()) - .setExpiration(new java.util.Date((new java.util.Date()).getTime() + 30000000)) - .signWith(secretKey).compact(); - } - return jwt; - } -} diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index e55f400f..0882d701 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -23,6 +23,7 @@ import stanl_2.final_backend.global.security.filter.CsrfCookieFilter; import stanl_2.final_backend.global.security.filter.JWTTokenGeneratorFilter; import stanl_2.final_backend.global.security.filter.JWTTokenValidatorFilter; +import stanl_2.final_backend.global.security.filter.TokenFilter; import java.util.Arrays; import java.util.Collections; @@ -41,9 +42,8 @@ public ProdSecurityConfig(ApplicationConstants applicationConstants) { @Bean SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { - // CSRF 토큰 요청 속성을 사용하여 토큰 값을 헤더나 매개변수 값으로 해결하는 로직을 포함 + //CSRF 토큰 요청 속성을 사용하여 토큰 값을 헤더나 매개변수 값으로 해결하는 로직을 포함 CsrfTokenRequestAttributeHandler csrfTokenRequestAttributeHandler = new CsrfTokenRequestAttributeHandler(); - http.csrf(csrfConfig -> csrfConfig.disable()); http.sessionManagement(sessionConfig -> sessionConfig.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .cors(corsConfig -> corsConfig.configurationSource(new CorsConfigurationSource() { @Override @@ -58,33 +58,34 @@ public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { return config; } })) -// .csrf(csrfConfig -> csrfConfig.csrfTokenRequestHandler(csrfTokenRequestAttributeHandler) -// // 아래 API들에 대해서는 CSRF 보호를 무시하도록 지시(공개) -// .ignoringRequestMatchers("/api/v1/auth/signup", "/api/v1/auth/signin") -// // 로그인 작업 후 처음으로 CSRF 토큰을 생성하는데만 도움을 준다. -// .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())) + .csrf(csrfConfig -> csrfConfig.csrfTokenRequestHandler(csrfTokenRequestAttributeHandler) + // 아래 API들에 대해서는 CSRF 보호를 무시하도록 지시(공개) + .ignoringRequestMatchers("/api/v1/member/register", "/api/v1/member/login") + // 로그인 작업 후 처음으로 CSRF 토큰을 생성하는데만 도움을 준다. + .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())) // 로그인 시 사용(jwt 생성)2 -// .addFilterAfter(new JWTTokenGeneratorFilter(applicationConstants), BasicAuthenticationFilter.class) + .addFilterAfter(new JWTTokenGeneratorFilter(applicationConstants), BasicAuthenticationFilter.class) // 다른 api 접근시 사용(인증)1 -// .addFilterBefore(new JWTTokenValidatorFilter(applicationConstants), BasicAuthenticationFilter.class) + .addFilterBefore(new JWTTokenValidatorFilter(applicationConstants), BasicAuthenticationFilter.class) // csrf 보호 필터3 -// .addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class) + .addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class) // 데이터 파싱 필터(파싱해서 request로 )4 -// .addFilterAfter(new TokenFilter(applicationConstants), JWTTokenGeneratorFilter.class) + .addFilterAfter(new TokenFilter(applicationConstants), JWTTokenGeneratorFilter.class) .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()) .authorizeHttpRequests((requests -> requests // 모두 접근 가능 - .requestMatchers("/api/v1/auth/signup", "/api/v1/auth/signin").permitAll() - .anyRequest().permitAll())); -// .anyRequest().authenticated())); + .requestMatchers("/api/v1/member/register", "/api/v1/member/login", "/ws-stomp/**").permitAll() + .anyRequest().authenticated())); http.formLogin(withDefaults()); http.httpBasic(withDefaults()); return http.build(); } + + @Bean public PasswordEncoder passwordEncoder() { return PasswordEncoderFactories.createDelegatingPasswordEncoder(); diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java index 49790dcb..f37f8a17 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java @@ -1,7 +1,6 @@ package stanl_2.final_backend.global.security.config; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Profile; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -14,7 +13,6 @@ import stanl_2.final_backend.global.exception.CommonException; import stanl_2.final_backend.global.exception.ErrorCode; -@Slf4j @Component @Profile("prod") @RequiredArgsConstructor @@ -30,7 +28,6 @@ public Authentication authenticate(Authentication authentication) throws Authent String pwd = authentication.getCredentials().toString(); UserDetails userDetails = userDetailsService.loadUserByUsername(username); - log.info("1"); if(passwordEncoder.matches(pwd, userDetails.getPassword())) { return new UsernamePasswordAuthenticationToken(userDetails, pwd, userDetails.getAuthorities()); }else{ diff --git a/src/main/java/stanl_2/final_backend/global/security/config/SecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/SecurityConfig.java new file mode 100644 index 00000000..964b1185 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/security/config/SecurityConfig.java @@ -0,0 +1,99 @@ +package stanl_2.final_backend.global.security.config; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.authentication.password.CompromisedPasswordChecker; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.factory.PasswordEncoderFactories; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.password.HaveIBeenPwnedRestApiPasswordChecker; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; +import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; + +import java.util.Arrays; +import java.util.Collections; + +import static org.springframework.security.config.Customizer.withDefaults; + +@Configuration +@Profile("local") +public class SecurityConfig { + + private final ApplicationConstants applicationConstants; + + public SecurityConfig(ApplicationConstants applicationConstants) { + this.applicationConstants = applicationConstants; + } + + @Bean + SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { + //CSRF 토큰 요청 속성을 사용하여 토큰 값을 헤더나 매개변수 값으로 해결하는 로직을 포함 + CsrfTokenRequestAttributeHandler csrfTokenRequestAttributeHandler = new CsrfTokenRequestAttributeHandler(); + http.csrf(csrfConfig -> csrfConfig.disable()); + http.cors(corsConfig -> corsConfig.configurationSource(new CorsConfigurationSource() { + @Override + public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowedOrigins(Collections.singletonList("http://localhost:5173")); + config.setAllowedMethods(Collections.singletonList("*")); // 모든 유형의 http 메소드 트래픽 허용 + config.setAllowCredentials(true); // UI에서 백엔드로 user 자격 증명이나 기타 적용 가능한 쿠키 수락 + config.setAllowedHeaders(Collections.singletonList("*")); // 모든 종류의 헤더를 수락해도 괜찮다. + config.setExposedHeaders(Arrays.asList("Authorization")); // 헤터를 사용하여 JWT 토큰값 전송 + config.setMaxAge(3600L); // 1시간 + return config; + } + })) + // 로그인 시 사용(jwt 생성)2 + .addFilterAfter(new JWTTokenGeneratorFilter(applicationConstants), BasicAuthenticationFilter.class) + // 다른 api 접근시 사용(인증)1 + .addFilterBefore(new JWTTokenValidatorFilter(applicationConstants), BasicAuthenticationFilter.class) + // 데이터 파싱 필터(파싱해서 request로 )4 + .addFilterAfter(new TokenFilter(applicationConstants), JWTTokenGeneratorFilter.class) + + .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()) + .authorizeHttpRequests((requests -> requests + .anyRequest().permitAll())); + http.formLogin(withDefaults()); + http.httpBasic(withDefaults()); + return http.build(); + } + + + + + @Bean + public PasswordEncoder passwordEncoder() { + return PasswordEncoderFactories.createDelegatingPasswordEncoder(); + } + + /** + * 사용자 비밀 번호가 유출 되었는지 확인하는 메소드 + * From Spring Security 6.3부터 도입 + * */ + @Bean + public CompromisedPasswordChecker compromisedPasswordChecker() { + return new HaveIBeenPwnedRestApiPasswordChecker(); + } + + // 인증 메커니즘을 시작하는 역할 + @Bean + public AuthenticationManager authenticationManager(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder){ + // 인증 제공자 객체 + UsernamePwdAuthenticationProvider authenticationProvider = + new UsernamePwdAuthenticationProvider(userDetailsService, passwordEncoder); + + ProviderManager providerManager = new ProviderManager(authenticationProvider); + // provider manager는 Authentication 객체 내부의 비밀번호를 지우지 못하게 설정(유효성 검사) + providerManager.setEraseCredentialsAfterAuthentication(false); + return providerManager; + } + +} diff --git a/src/main/java/stanl_2/final_backend/global/security/config/UsernamePwdAuthenticationProvider.java b/src/main/java/stanl_2/final_backend/global/security/config/UsernamePwdAuthenticationProvider.java new file mode 100644 index 00000000..a0654fee --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/security/config/UsernamePwdAuthenticationProvider.java @@ -0,0 +1,37 @@ +package stanl_2.final_backend.global.security.config; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Profile; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; + +@Component +@Profile("local") +@RequiredArgsConstructor +public class UsernamePwdAuthenticationProvider implements AuthenticationProvider { + + private final UserDetailsService userDetailsService; + private final PasswordEncoder passwordEncoder; + + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + String username = authentication.getName(); + String pwd = authentication.getCredentials().toString(); + UserDetails userDetails = userDetailsService.loadUserByUsername(username); + // 그냥 id만 맞으면 비밀번호 틀려도 통과 + return new UsernamePasswordAuthenticationToken(userDetails, pwd, userDetails.getAuthorities()); + } + + // 추후에 여기에 OAuth2.0 추가 가능 할 듯 + @Override + public boolean supports(Class authentication) { + return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); + } +} diff --git a/src/main/java/stanl_2/final_backend/global/security/constants/ApplicationConstants.java b/src/main/java/stanl_2/final_backend/global/security/constants/ApplicationConstants.java index d4125f4b..2e122bc8 100644 --- a/src/main/java/stanl_2/final_backend/global/security/constants/ApplicationConstants.java +++ b/src/main/java/stanl_2/final_backend/global/security/constants/ApplicationConstants.java @@ -1,23 +1,18 @@ package stanl_2.final_backend.global.security.constants; import lombok.Getter; -import org.springframework.beans.factory.annotation.Value; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; -import java.util.Base64; - @Component @Getter +@RequiredArgsConstructor public class ApplicationConstants { - private final byte[] JWT_SECRET_KEY; - private final String JWT_HEADER; + // 비밀키는 자동으로 생성하여 상수로 설정 + public final String JWT_SECRET_KEY = "JWT_SECRET"; + + private final String JWT_SECRET_DEFAULT_VALUE="jxgEQeXHuPq8VdbyYFNkANdudQ53YUn4"; - public ApplicationConstants( - @Value("${jwt.secret-default-value}") String jwtSecretDefaultValue, - @Value("${jwt.header}") String jwtHeader - ) { - this.JWT_SECRET_KEY = Base64.getDecoder().decode(jwtSecretDefaultValue); - this.JWT_HEADER = jwtHeader; - } -} \ No newline at end of file + public final String JWT_HEADER = "Authorization"; +} diff --git a/src/main/java/stanl_2/final_backend/global/security/events/AuthenticationEvents.java b/src/main/java/stanl_2/final_backend/global/security/events/AuthenticationEvents.java index 0a4bff1f..91f0ca6e 100644 --- a/src/main/java/stanl_2/final_backend/global/security/events/AuthenticationEvents.java +++ b/src/main/java/stanl_2/final_backend/global/security/events/AuthenticationEvents.java @@ -11,13 +11,13 @@ public class AuthenticationEvents { // 로그인 성공시 발생하는 이벤트 @EventListener - public void onSuccess(AuthenticationSuccessEvent successEvent) { + public void onSuccess(AuthenticationSuccessEvent successEvent){ log.info("로그인 성공 유저: {}", successEvent.getAuthentication().getName()); } // 로그인 실패시 발생하는 이벤트 @EventListener - public void onFailure(AbstractAuthenticationFailureEvent failureEvent) { + public void onFailure(AbstractAuthenticationFailureEvent failureEvent){ log.error("로그인 실패 유저: {} due to: {}", failureEvent.getAuthentication().getName(), failureEvent.getException().getMessage()); } } diff --git a/src/main/java/stanl_2/final_backend/global/security/events/AuthorizationEvents.java b/src/main/java/stanl_2/final_backend/global/security/events/AuthorizationEvents.java index b2e22d6e..d9fa79a9 100644 --- a/src/main/java/stanl_2/final_backend/global/security/events/AuthorizationEvents.java +++ b/src/main/java/stanl_2/final_backend/global/security/events/AuthorizationEvents.java @@ -4,16 +4,14 @@ import org.springframework.context.event.EventListener; import org.springframework.security.authorization.event.AuthorizationDeniedEvent; import org.springframework.stereotype.Component; -import stanl_2.final_backend.global.exception.CommonException; -import stanl_2.final_backend.global.exception.ErrorCode; @Component @Slf4j public class AuthorizationEvents { @EventListener - public void onFailure(AuthorizationDeniedEvent deniedEvent) { + public void onFailure(AuthorizationDeniedEvent deniedEvent){ log.error("권한 없음 유저: {} due to: {}", deniedEvent.getAuthentication().get().getName() , deniedEvent.getAuthorizationDecision().toString()); - throw new CommonException(ErrorCode.FORBIDDEN_ROLE); + throw new CommonException(FORBIDDEN_ROLE); } } diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java index 8f075a21..0ab8a499 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java @@ -8,8 +8,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.web.filter.OncePerRequestFilter; -import stanl_2.final_backend.global.exception.CommonException; -import stanl_2.final_backend.global.exception.ErrorCode; import java.io.IOException; import java.util.Arrays; diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java index 8155046f..c3fdd977 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java @@ -12,11 +12,11 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.filter.OncePerRequestFilter; -import stanl_2.final_backend.domain.member.aggregate.entity.Member; import stanl_2.final_backend.global.security.constants.ApplicationConstants; import javax.crypto.SecretKey; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.stream.Collectors; @@ -30,22 +30,21 @@ public class JWTTokenGeneratorFilter extends OncePerRequestFilter { protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - log.info("g1"); Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - log.info("g2"); if (null != authentication) { + // pk, nickname 넣기 Member member = (Member) authentication.getPrincipal(); - log.info("g3"); - // Base64로 인코딩된 키를 디코딩하여 SecretKey 생성 - SecretKey secretKey = Keys.hmacShaKeyFor(applicationConstants.getJWT_SECRET_KEY()); - log.info("g4"); + // 비밀키 생성 + String secret = applicationConstants.getJWT_SECRET_DEFAULT_VALUE(); + SecretKey secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)); String jwt = Jwts.builder() .setIssuer("STANL2") .setSubject("JWT Token") -// .claim("username", authentication.getName()) -// .claim("id", member.getId()) + .claim("username", authentication.getName()) + .claim("id", member.getId()) + .claim("nickname", member.getNickname()) .claim("authorities", authentication.getAuthorities().stream() .map(GrantedAuthority::getAuthority).collect(Collectors.joining(","))) @@ -54,16 +53,15 @@ protected void doFilterInternal(HttpServletRequest request, .signWith(secretKey) .compact(); // Digital Signature 생성 - log.info("g5"); // JWT 토큰을 응답 헤더에 추가 response.setHeader(applicationConstants.getJWT_HEADER(), jwt); + log.error("{}", jwt); } - log.info("g6"); filterChain.doFilter(request, response); } @Override protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { - return !request.getServletPath().equals("/api/v1/auth/signin"); + return !request.getServletPath().equals("/api/v1/member/login"); } } diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java index a531eab9..3e4a68e2 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java @@ -5,7 +5,6 @@ import io.jsonwebtoken.security.Keys; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; -import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @@ -15,14 +14,10 @@ import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.filter.OncePerRequestFilter; -import stanl_2.final_backend.global.exception.CommonException; -import stanl_2.final_backend.global.exception.ErrorCode; -import stanl_2.final_backend.global.security.constants.ApplicationConstants; import javax.crypto.SecretKey; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.Base64; @Slf4j @RequiredArgsConstructor @@ -34,72 +29,44 @@ public class JWTTokenValidatorFilter extends OncePerRequestFilter { protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String jwt = request.getHeader(applicationConstants.getJWT_HEADER()); + if (null != jwt) { + try { + // 비밀키 가져오기 + String secret = applicationConstants.getJWT_SECRET_DEFAULT_VALUE(); + SecretKey secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)); + String jwtToken = jwt.substring(7); + // JWT 토큰 검증 + Claims claims = Jwts.parserBuilder() + .setSigningKey(secretKey) + .build() + .parseClaimsJws(jwtToken) + .getBody(); + String username = String.valueOf(claims.get("username")); + String authorities = String.valueOf(claims.get("authorities")); + Authentication authentication = new UsernamePasswordAuthenticationToken(username, null, + AuthorityUtils.commaSeparatedStringToAuthorityList(authorities)); - Cookie[] cookies = request.getCookies(); - if (cookies != null) { - for (Cookie cookie : cookies) { - if ("SESSIONID".equals(cookie.getName())) { - String jwt = cookie.getValue(); + // SecurityContextHolder에 저장 + SecurityContextHolder.getContext().setAuthentication(authentication); - try { - // 비밀 키를 Base64로 디코딩하여 생성 - byte[] decodedKey = Base64.getDecoder().decode(applicationConstants.getJWT_SECRET_KEY()); - SecretKey secretKey = Keys.hmacShaKeyFor(decodedKey); - - Claims claims = Jwts.parserBuilder() - .setSigningKey(secretKey) - .build() - .parseClaimsJws(jwt) - .getBody(); - - String authorities = (String) claims.get("authorities"); - Authentication authentication = new UsernamePasswordAuthenticationToken( - claims.getSubject(), null, - AuthorityUtils.commaSeparatedStringToAuthorityList(authorities)); - - SecurityContextHolder.getContext().setAuthentication(authentication); - } catch (Exception e) { - throw new CommonException(ErrorCode.INVALID_TOKEN_ERROR); - } - } + } catch (Exception exception) { + throw new CommonException(ErrorCode.INVALID_TOKEN_ERROR); } } filterChain.doFilter(request, response); } -// String jwt = request.getHeader(applicationConstants.getJWT_HEADER()); -// if (null != jwt) { -// try { -// // 비밀키 가져오기 -// SecretKey secretKey = Keys.hmacShaKeyFor(applicationConstants.getJWT_SECRET_KEY()); -// String jwtToken = jwt.substring(7); -// // JWT 토큰 검증 -// Claims claims = Jwts.parserBuilder() -// .setSigningKey(secretKey) -// .build() -// .parseClaimsJws(jwtToken) -// .getBody(); -// -// String username = String.valueOf(claims.get("username")); -// String authorities = String.valueOf(claims.get("authorities")); -// Authentication authentication = new UsernamePasswordAuthenticationToken(username, null, -// AuthorityUtils.commaSeparatedStringToAuthorityList(authorities)); -// -// // SecurityContextHolder에 저장 -// SecurityContextHolder.getContext().setAuthentication(authentication); -// -// } catch (Exception exception) { -// throw new CommonException(ErrorCode.INVALID_TOKEN_ERROR); -// } -// } -// filterChain.doFilter(request, response); -// } - @Override protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { String path = request.getServletPath(); - return path.equals("/api/v1/auth/signin") || - path.equals("/api/v1/auth/signup"); + return path.equals("/api/v1/member/login") || + path.equals("/api/v1/member/register") || + path.equals("/api/v1/member") || + path.startsWith("/api/v1/member/sms") || // 와일드카드 경로 포함 + path.startsWith("/api/v1/member/mail") || + path.equals("/api/v1/member/otherprofile") || + path.equals("/api/v1/member/password"); } } diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/TokenFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/TokenFilter.java new file mode 100644 index 00000000..43c62ce8 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/security/filter/TokenFilter.java @@ -0,0 +1,88 @@ +package stanl_2.final_backend.global.security.filter; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.crypto.SecretKey; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +@RequiredArgsConstructor +@Slf4j +public class TokenFilter extends OncePerRequestFilter { + private final ApplicationConstants applicationConstants; + + @Override + public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException { + String token = request.getHeader("Authorization"); + + if(token == null || token.isEmpty() || !token.startsWith("Bearer ")){ + // 보안 X + filterChain.doFilter(request, response); // 토큰이 없으면 필터를 그냥 통과시킴 + + // 보안 O +// throw new CommonException(ErrorCode.NOT_FOUND_JWT_TOKEN); + }else { + String jwtToken = token.substring(7); + + // 시크릿 키로 JWT를 파싱 + SecretKey secretKey = Keys.hmacShaKeyFor(applicationConstants.getJWT_SECRET_DEFAULT_VALUE().getBytes(StandardCharsets.UTF_8)); + + // JWT 토큰에서 클레임을 추출 + Claims claims = Jwts.parserBuilder() + .setSigningKey(secretKey) + .build() + .parseClaimsJws(jwtToken) + .getBody(); + + // jwt 토큰에서 추출 + Long id = claims.get("id", Long.class); + String loginId = claims.get("loginId", String.class); + String nationality = claims.get("nationality", String.class); + String sex = claims.get("sex", String.class); + Integer point = claims.get("point", Integer.class); + String nickname = claims.get("nickname", String.class); +// String profile = claims.get("profile", String.class); +// String introduction = claims.get("introduction", String.class); + String language = claims.get("language", String.class); + + // request에 넣기 + request.setAttribute("id", id); + request.setAttribute("loginId", loginId); + request.setAttribute("nationality", nationality); + request.setAttribute("sex", sex); + request.setAttribute("point", point); + request.setAttribute("nickname", nickname); +// request.setAttribute("profile", profile); +// request.setAttribute("introduction", introduction); + request.setAttribute("language", language); + request.setAttribute("Authorization", token); + } + + + + filterChain.doFilter(request, response); + } + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { + String path = request.getServletPath(); + return path.equals("/api/v1/member/login") || + path.equals("/api/v1/member/register") || + path.startsWith("/api/v1/member/sms") || // 와일드카드 경로 포함 + path.startsWith("/api/v1/member/mail") || + path.equals("/api/v1/member") || + path.equals("/api/v1/member/password") || + path.equals("/api/v1/member/otherprofile") || + path.startsWith("/ws"); + } + +} diff --git a/src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsServiceImpl.java b/src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsServiceImpl.java index c9425b01..a0f31cd8 100644 --- a/src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsServiceImpl.java @@ -5,8 +5,6 @@ import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; -import stanl_2.final_backend.domain.member.aggregate.entity.Member; -import stanl_2.final_backend.domain.member.repository.MemberRepository; // 사용자 정보 호출 @Slf4j diff --git a/src/main/java/stanl_2/final_backend/global/security/service/MemberPrincipal.java b/src/main/java/stanl_2/final_backend/global/security/service/MemberPrincipal.java index 4d7d2867..976d4531 100644 --- a/src/main/java/stanl_2/final_backend/global/security/service/MemberPrincipal.java +++ b/src/main/java/stanl_2/final_backend/global/security/service/MemberPrincipal.java @@ -4,7 +4,6 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; -import stanl_2.final_backend.domain.member.aggregate.entity.Member; import java.util.Collection; import java.util.Collections; diff --git a/src/main/resources/application_dev.yml b/src/main/resources/application-dev.yml similarity index 100% rename from src/main/resources/application_dev.yml rename to src/main/resources/application-dev.yml diff --git a/src/main/resources/application_prod.yml b/src/main/resources/application-prod.yml similarity index 100% rename from src/main/resources/application_prod.yml rename to src/main/resources/application-prod.yml diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index f931c37e..75598b23 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -47,7 +47,7 @@ CREATE TABLE tb_center CREATE TABLE tb_member ( MEM_ID VARCHAR(255) NOT NULL, - MEM_LOGN_ID INT NOT NULL, + MEM_LOGIN_ID VARCHAR(255) NOT NULL, MEM_PWD VARCHAR(255) NOT NULL, MEM_NAME VARCHAR(255) NOT NULL, MEM_EMA VARCHAR(255) NOT NULL, From f82849bfc45e4bce4cf1781caf2a837ab6968e5a Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 12 Nov 2024 18:54:42 +0900 Subject: [PATCH 027/563] =?UTF-8?q?fix:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EC=8B=9C=20=EC=83=9D=EC=84=B1=EC=8B=9C=EA=B0=81=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=20=EC=B6=94=EA=B0=80=20(#26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/SampleCommandServiceImpl.java | 1 - .../domain/aggragate/entity/Notice.java | 20 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java index f9bd78ae..a9a5d429 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java @@ -13,7 +13,6 @@ import stanl_2.final_backend.domain.A_sample.common.exception.CommonException; import stanl_2.final_backend.domain.A_sample.common.exception.ErrorCode; -import java.sql.Timestamp; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java index fb9d1308..c477a1bc 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java @@ -9,6 +9,9 @@ import stanl_2.final_backend.global.config.PrefixGeneratorConfig; import java.sql.Timestamp; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; @Entity @Table(name="NOTICE") @@ -52,4 +55,21 @@ public class Notice { @Column(name = "MEM_ID", nullable = false) private Boolean memberId; + + @PrePersist + private void prePersist() { + this.createdAt = getCurrentTime(); + this.updatedAt = this.createdAt; + } + + // Update 되기 전에 실행 + @PreUpdate + private void preUpdate() { + this.updatedAt = getCurrentTime(); + } + + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } } From 041a2122b4c04a2fef3b74a6dd8fe00baa4a8d76 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 12 Nov 2024 18:57:51 +0900 Subject: [PATCH 028/563] =?UTF-8?q?fix:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=97=94=ED=8B=B0=ED=8B=B0=20memberId=20=EC=9E=90?= =?UTF-8?q?=EB=A3=8C=ED=98=95=20=EC=88=98=EC=A0=95=20(#26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/SampleCommandServiceImpl.java | 1 - .../application/dto/NoticeRegistDTO.java | 4 ---- .../domain/aggragate/entity/Notice.java | 22 ++++++++++++++++++- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java index f9bd78ae..a9a5d429 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java @@ -13,7 +13,6 @@ import stanl_2.final_backend.domain.A_sample.common.exception.CommonException; import stanl_2.final_backend.domain.A_sample.common.exception.ErrorCode; -import java.sql.Timestamp; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java index f3e5531d..e69de29b 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java @@ -1,4 +0,0 @@ -package stanl_2.final_backend.domain.notices.command.application.dto; - -public class NoticeRegistDTO { -} diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java index fb9d1308..c8ea7b67 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java @@ -9,6 +9,9 @@ import stanl_2.final_backend.global.config.PrefixGeneratorConfig; import java.sql.Timestamp; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; @Entity @Table(name="NOTICE") @@ -51,5 +54,22 @@ public class Notice { private Boolean active = true; @Column(name = "MEM_ID", nullable = false) - private Boolean memberId; + private String memberId; + + @PrePersist + private void prePersist() { + this.createdAt = getCurrentTime(); + this.updatedAt = this.createdAt; + } + + // Update 되기 전에 실행 + @PreUpdate + private void preUpdate() { + this.updatedAt = getCurrentTime(); + } + + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } } From 4249fe96d62e9eb7f08c8c1dd361ad98256e7d51 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 12 Nov 2024 19:06:28 +0900 Subject: [PATCH 029/563] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20DTO=EC=84=B8=ED=8C=85=20(#26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/application/dto/NoticeModifyDTO.java | 10 ++++++++++ .../command/application/dto/NoticeRegistDTO.java | 13 +++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java index 1bee9503..d28c32b9 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java @@ -1,4 +1,14 @@ package stanl_2.final_backend.domain.notices.command.application.dto; public class NoticeModifyDTO { + private String title; + + private String tag; + + private String classification; + + private String content; + + private String updatedAt; + } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java index e69de29b..0aa49af6 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java @@ -0,0 +1,13 @@ +package stanl_2.final_backend.domain.notices.command.application.dto; + +public class NoticeRegistDTO { + private String title; + + private String tag; + + private String classification; + + private String content; + + private String memberId; +} From 420b54e74e32ba7d82dfd41a9e21b02059be0409 Mon Sep 17 00:00:00 2001 From: giuseog Date: Tue, 12 Nov 2024 19:16:19 +0900 Subject: [PATCH 030/563] =?UTF-8?q?feat:=20=EB=8F=99=EC=A0=81=20=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=20=EC=82=AC=EC=9A=A9=20=EA=B2=80=EC=83=89=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EA=B5=AC=ED=98=84=20(#8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/CenterController.java | 33 ++++++++++---- .../query/dto/CenterSearchRequestDTO.java | 14 ++++++ .../center/query/repository/CenterMapper.java | 6 +++ .../center/query/service/CenterService.java | 3 ++ .../query/service/CenterServiceImpl.java | 13 ++++++ .../center/query/repository/CenterMapper.xml | 45 ++++++++++++++++++- 6 files changed, 105 insertions(+), 9 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSearchRequestDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java b/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java index 1e733732..c4de4913 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java @@ -5,16 +5,13 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; 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.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.center.common.response.ResponseMessage; -import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; +import stanl_2.final_backend.domain.center.query.dto.CenterSearchRequestDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectIdDTO; import stanl_2.final_backend.domain.center.query.service.CenterService; -import java.util.List; +import java.util.HashMap; import java.util.Map; @RestController("queryCenterController") @@ -28,10 +25,8 @@ public CenterController(CenterService centerService) { this.centerService = centerService; } - /* 설명. 조회, 상세조회, 검색 */ @GetMapping("") public ResponseEntity getCenterAll(@PageableDefault(size = 20) Pageable pageable){ -// List centerSelectAllDTOList = centerService.selectAll(); Page> responseCenters = centerService.selectAll(pageable); @@ -55,4 +50,26 @@ public ResponseEntity getCenterById(@PathVariable("id") String id){ .build()); } + @GetMapping("/search") + public ResponseEntity getCenterBySearch(@RequestParam Map params + ,@PageableDefault(size = 20) Pageable pageable){ + + CenterSearchRequestDTO centerSearchRequestDTO = new CenterSearchRequestDTO(); + centerSearchRequestDTO.setId(params.get("id")); + centerSearchRequestDTO.setName(params.get("name")); + centerSearchRequestDTO.setAddress(params.get("address")); + + Map paramMap = new HashMap<>(); + paramMap.put("centerSearchRequestDTO", centerSearchRequestDTO); + paramMap.put("pageable", pageable); + + Page> responseCenters = centerService.selectBySearch(paramMap); + + return ResponseEntity.ok(ResponseMessage.builder() + .httpStatus(200) + .msg("검색 조회 성공") + .result(responseCenters) + .build()); + } + } diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSearchRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSearchRequestDTO.java new file mode 100644 index 00000000..f5aab488 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSearchRequestDTO.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.center.query.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString +public class CenterSearchRequestDTO { + private String id; + private String name; + private String address; +} diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java b/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java index e7111ca8..c9fa127f 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java @@ -2,6 +2,7 @@ import org.apache.ibatis.annotations.Mapper; import stanl_2.final_backend.domain.center.common.util.RequestList; +import stanl_2.final_backend.domain.center.query.dto.CenterSearchRequestDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectIdDTO; @@ -15,4 +16,9 @@ public interface CenterMapper { List> findCenterAll(RequestList requestList); int findCenterCount(); + + int findCenterBySearchCount(Map params); + + List> findCenterBySearch(Map params); + } diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterService.java b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterService.java index 5e32ebf3..6f4087e1 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterService.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterService.java @@ -3,6 +3,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.center.query.dto.CenterSearchRequestDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectIdDTO; @@ -14,4 +15,6 @@ public interface CenterService { CenterSelectIdDTO selectByCenterId(String id); Page> selectAll(Pageable pageable); + + Page> selectBySearch(Map params); } diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterServiceImpl.java index 07791035..44a5ad22 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterServiceImpl.java @@ -8,6 +8,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.center.common.util.RequestList; +import stanl_2.final_backend.domain.center.query.dto.CenterSearchRequestDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectIdDTO; import stanl_2.final_backend.domain.center.query.repository.CenterMapper; @@ -50,6 +51,18 @@ public Page> selectAll(Pageable pageable) { return new PageImpl<>(centerList, pageable, total); } + @Override + @Transactional + public Page> selectBySearch(Map params){ + + Pageable pageable = (Pageable) params.get("pageable"); + + List> centerList = centerMapper.findCenterBySearch(params); + + int total = centerMapper.findCenterBySearchCount(params); + + return new PageImpl<>(centerList, pageable, total); + } } diff --git a/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml b/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml index fa560e84..aa7f2c04 100644 --- a/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml @@ -47,7 +47,7 @@ WHERE A.CENT_ID = #{id} AND A.ACTIVE = TRUE - SELECT A.CENT_ID , A.CENT_NAME @@ -65,6 +65,32 @@ OFFSET #{pageable.offset} ROWS FETCH NEXT #{pageable.pageSize} ROWS ONLY; + + + + \ No newline at end of file From 326b1924193883b161c75299ed56f79e07d771ed Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Tue, 12 Nov 2024 19:40:08 +0900 Subject: [PATCH 031/563] =?UTF-8?q?fix:=20member=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20auth=20=ED=8C=8C=EC=9D=BC=20=EC=83=9D=EC=84=B1=20(#?= =?UTF-8?q?17)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AuthController.java | 37 ++++++ .../command/application/dto/SignupDTO.java | 29 +++++ .../service/AuthCommandService.java | 3 + .../service/AuthCommandServiceImpl.java | 12 +- .../query/controller/AuthController.java | 18 +++ .../query/controller/SampleController.java | 73 ----------- .../domain/member/query/dto/SampleDTO.java | 22 ---- .../domain/member/query/dto/SignupDTO.java | 29 +++++ .../member/query/repository/AuthMapper.java | 7 ++ .../member/query/repository/SampleMapper.java | 12 -- .../member/query/service/AuthService.java | 4 + .../member/query/service/AuthServiceImpl.java | 18 +++ .../member/query/service/SampleService.java | 9 -- .../query/service/SampleServiceImpl.java | 50 -------- .../security/config/ProdSecurityConfig.java | 117 ------------------ ...ProdUsernamePwdAuthenticationProvider.java | 43 ------- .../security/config/SecurityConfig.java | 99 --------------- .../UsernamePwdAuthenticationProvider.java | 37 ------ .../constants/ApplicationConstants.java | 18 --- .../security/filter/CsrfCookieFilter.java | 2 + .../filter/JWTTokenGeneratorFilter.java | 67 ---------- .../filter/JWTTokenValidatorFilter.java | 72 ----------- .../global/security/filter/TokenFilter.java | 88 ------------- .../service/MemberDetailsService.java | 6 - .../service/MemberDetailsServiceImpl.java | 26 ---- .../security/service/MemberPrincipal.java | 55 -------- 26 files changed, 156 insertions(+), 797 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/controller/AuthController.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/controller/SampleController.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/dto/SampleDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/dto/SignupDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/repository/AuthMapper.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/repository/SampleMapper.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/service/AuthService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/service/AuthServiceImpl.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/service/SampleService.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/service/SampleServiceImpl.java delete mode 100644 src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java delete mode 100644 src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java delete mode 100644 src/main/java/stanl_2/final_backend/global/security/config/SecurityConfig.java delete mode 100644 src/main/java/stanl_2/final_backend/global/security/config/UsernamePwdAuthenticationProvider.java delete mode 100644 src/main/java/stanl_2/final_backend/global/security/constants/ApplicationConstants.java delete mode 100644 src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java delete mode 100644 src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java delete mode 100644 src/main/java/stanl_2/final_backend/global/security/filter/TokenFilter.java delete mode 100644 src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsService.java delete mode 100644 src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsServiceImpl.java delete mode 100644 src/main/java/stanl_2/final_backend/global/security/service/MemberPrincipal.java diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java index 8ce2e887..6a696e52 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java @@ -1,10 +1,47 @@ package stanl_2.final_backend.domain.member.command.application.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +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.RestController; +import stanl_2.final_backend.domain.member.command.application.dto.SignupDTO; +import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; +import stanl_2.final_backend.domain.member.common.response.ResponseMessage; @RestController("commandAuthController") @RequestMapping("/api/v1/auth") public class AuthController { + private final AuthCommandService authCommandService; + + @Autowired + public AuthController(AuthCommandService authCommandService) { + this.authCommandService = authCommandService; + } + + @Operation(summary = "회원가입") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + }) + @PostMapping("signup") + public ResponseEntity signup(@RequestBody SignupDTO signupDTO){ + + authCommandService.signup(signupDTO); + + return ResponseEntity.ok(ResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + } + + } diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java new file mode 100644 index 00000000..32281c78 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java @@ -0,0 +1,29 @@ +package stanl_2.final_backend.domain.member.command.application.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class SignupDTO { + private String id; + private String loginId; + private String password; + private String name; + private String email; + private Integer age; + private String sex; + private String idenNo; + private String phone; + private String emergPhone; + private String address; + private String note; + private String position; + private String grade; + private String jobType; + private String military; + private String bankName; + private String account; +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java index b3137dd1..ebffda74 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java @@ -1,4 +1,7 @@ package stanl_2.final_backend.domain.member.command.application.service; +import stanl_2.final_backend.domain.member.command.application.dto.SignupDTO; + public interface AuthCommandService { + void signup(SignupDTO signupDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java index 43513aba..ec0d5f17 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java @@ -2,12 +2,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.member.command.application.dto.SignupDTO; import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; import stanl_2.final_backend.domain.member.command.domain.repository.AuthRepository; -import java.sql.Timestamp; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; @Service("commandAuthService") public class AuthCommandServiceImpl implements AuthCommandService { @@ -19,10 +21,14 @@ public AuthCommandServiceImpl(AuthRepository authRepository) { this.authRepository = authRepository; } - private Timestamp getCurrentTimestamp() { + private String getCurrentTimestamp() { ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); - return Timestamp.from(nowKst.toInstant()); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } + @Override + @Transactional + public void signup(SignupDTO signupDTO) { + } } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/controller/AuthController.java b/src/main/java/stanl_2/final_backend/domain/member/query/controller/AuthController.java new file mode 100644 index 00000000..ff7807c5 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/query/controller/AuthController.java @@ -0,0 +1,18 @@ +package stanl_2.final_backend.domain.member.query.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.member.query.service.AuthService; + +@RestController(value = "querySampleController") +@RequestMapping("/api/v1/auth") +public class AuthController { + + private final AuthService authService; + + @Autowired + public AuthController(AuthService authService) { + this.authService = authService; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/controller/SampleController.java b/src/main/java/stanl_2/final_backend/domain/member/query/controller/SampleController.java deleted file mode 100644 index 83d62c19..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/query/controller/SampleController.java +++ /dev/null @@ -1,73 +0,0 @@ -package stanl_2.final_backend.domain.member.query.controller; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; -import org.springframework.beans.factory.annotation.Autowired; -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.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import stanl_2.final_backend.domain.member.common.response.ResponseMessage; -import stanl_2.final_backend.domain.member.query.dto.SampleDTO; -import stanl_2.final_backend.domain.member.query.service.SampleService; - -@RestController(value = "querySampleController") -@RequestMapping("/api/v1/sample") -public class SampleController { - - private final SampleService sampleService; - - @Autowired - public SampleController(SampleService sampleService) { - this.sampleService = sampleService; - } - - /** - * [GET] http://localhost:7777/api/v1/sample/SAM_000000001 - * */ - @Operation(summary = "샘플 조회 테스트") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}), - @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", - content = @Content(mediaType = "application/json")) - }) - @GetMapping("{id}") - public ResponseEntity getTest(@PathVariable String id){ - - SampleDTO sampleDTO = sampleService.selectSampleInfo(id); - - return ResponseEntity.ok(ResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(sampleDTO) - .build()); - } - - /** - * [GET] http://localhost:7777/api/v1/sample/detail/SAM_000000001 - * */ - @Operation(summary = "샘플 상세 조회 테스트") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}), - @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", - content = @Content(mediaType = "application/json")) - }) - @GetMapping("/detail/{id}") - public ResponseEntity getDetailTest(@PathVariable String id) { - - String name = sampleService.selectSampleName(id); - - return ResponseEntity.ok(ResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(name) - .build()); - } - -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/dto/SampleDTO.java b/src/main/java/stanl_2/final_backend/domain/member/query/dto/SampleDTO.java deleted file mode 100644 index ffb84de5..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/query/dto/SampleDTO.java +++ /dev/null @@ -1,22 +0,0 @@ -package stanl_2.final_backend.domain.member.query.dto; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; - -import java.sql.Timestamp; - -@NoArgsConstructor -@AllArgsConstructor -@Getter -@ToString -public class SampleDTO { - private String id; - private String name; - private Integer num; - private Timestamp createdAt; - private Timestamp updatedAt; - private Timestamp deletedAt; - private Boolean active; -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/dto/SignupDTO.java b/src/main/java/stanl_2/final_backend/domain/member/query/dto/SignupDTO.java new file mode 100644 index 00000000..d918f85b --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/query/dto/SignupDTO.java @@ -0,0 +1,29 @@ +package stanl_2.final_backend.domain.member.query.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class SignupDTO { + private String id; + private String loginId; + private String password; + private String name; + private String email; + private Integer age; + private String sex; + private String idenNo; + private String phone; + private String emergPhone; + private String address; + private String note; + private String position; + private String grade; + private String jobType; + private String military; + private String bankName; + private String account; +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/repository/AuthMapper.java b/src/main/java/stanl_2/final_backend/domain/member/query/repository/AuthMapper.java new file mode 100644 index 00000000..8f478758 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/query/repository/AuthMapper.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.member.query.repository; + +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface AuthMapper { +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/repository/SampleMapper.java b/src/main/java/stanl_2/final_backend/domain/member/query/repository/SampleMapper.java deleted file mode 100644 index a5a813fa..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/query/repository/SampleMapper.java +++ /dev/null @@ -1,12 +0,0 @@ -package stanl_2.final_backend.domain.member.query.repository; - -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; -import stanl_2.final_backend.domain.member.query.dto.SampleDTO; - -@Mapper -public interface SampleMapper { - String selectNameById(@Param("id") String id); - - SampleDTO selectById(@Param("id") String id); -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthService.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthService.java new file mode 100644 index 00000000..7fb2608b --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthService.java @@ -0,0 +1,4 @@ +package stanl_2.final_backend.domain.member.query.service; + +public interface AuthService { +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthServiceImpl.java new file mode 100644 index 00000000..6da45a76 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthServiceImpl.java @@ -0,0 +1,18 @@ +package stanl_2.final_backend.domain.member.query.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.member.query.repository.AuthMapper; + +@Slf4j +@Service(value = "queryAuthService") +public class AuthServiceImpl implements AuthService{ + + private final AuthMapper authMapper; + + @Autowired + public AuthServiceImpl(AuthMapper authMapper) { + this.authMapper = authMapper; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/SampleService.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/SampleService.java deleted file mode 100644 index 30b83554..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/SampleService.java +++ /dev/null @@ -1,9 +0,0 @@ -package stanl_2.final_backend.domain.member.query.service; - -import stanl_2.final_backend.domain.member.query.dto.SampleDTO; - -public interface SampleService { - String selectSampleName(String id); - - SampleDTO selectSampleInfo(String id); -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/SampleServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/SampleServiceImpl.java deleted file mode 100644 index c0f8ea77..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/SampleServiceImpl.java +++ /dev/null @@ -1,50 +0,0 @@ -package stanl_2.final_backend.domain.member.query.service; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.member.common.exception.CommonException; -import stanl_2.final_backend.domain.member.common.exception.ErrorCode; -import stanl_2.final_backend.domain.member.query.dto.SampleDTO; -import stanl_2.final_backend.domain.member.query.repository.SampleMapper; - -@Slf4j -@Service(value = "querySampleService") -public class SampleServiceImpl implements SampleService { - - private final SampleMapper sampleMapper; - - @Autowired - public SampleServiceImpl(SampleMapper sampleMapper) { - this.sampleMapper = sampleMapper; - } - - @Override - @Transactional(readOnly = true) - public String selectSampleName(String id) { - - String name = sampleMapper.selectNameById(id);; - - if(name == null){ - throw new CommonException(ErrorCode.SAMPLE_NOT_FOUND); - } - - return name; - } - - @Override - @Transactional(readOnly = true) - public SampleDTO selectSampleInfo(String id) { - - SampleDTO sampleDTO = sampleMapper.selectById(id); - - if(sampleDTO == null){ - throw new CommonException(ErrorCode.SAMPLE_NOT_FOUND); - } - - return sampleDTO; - } - - -} diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java deleted file mode 100644 index 0882d701..00000000 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ /dev/null @@ -1,117 +0,0 @@ -package stanl_2.final_backend.global.security.config; - -import jakarta.servlet.http.HttpServletRequest; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.ProviderManager; -import org.springframework.security.authentication.password.CompromisedPasswordChecker; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.crypto.factory.PasswordEncoderFactories; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.password.HaveIBeenPwnedRestApiPasswordChecker; -import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; -import org.springframework.security.web.csrf.CookieCsrfTokenRepository; -import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; -import stanl_2.final_backend.global.security.constants.ApplicationConstants; -import stanl_2.final_backend.global.security.filter.CsrfCookieFilter; -import stanl_2.final_backend.global.security.filter.JWTTokenGeneratorFilter; -import stanl_2.final_backend.global.security.filter.JWTTokenValidatorFilter; -import stanl_2.final_backend.global.security.filter.TokenFilter; - -import java.util.Arrays; -import java.util.Collections; - -import static org.springframework.security.config.Customizer.withDefaults; - -@Configuration -@Profile("prod") -public class ProdSecurityConfig { - - private final ApplicationConstants applicationConstants; - - public ProdSecurityConfig(ApplicationConstants applicationConstants) { - this.applicationConstants = applicationConstants; - } - - @Bean - SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { - //CSRF 토큰 요청 속성을 사용하여 토큰 값을 헤더나 매개변수 값으로 해결하는 로직을 포함 - CsrfTokenRequestAttributeHandler csrfTokenRequestAttributeHandler = new CsrfTokenRequestAttributeHandler(); - http.sessionManagement(sessionConfig -> sessionConfig.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .cors(corsConfig -> corsConfig.configurationSource(new CorsConfigurationSource() { - @Override - public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { - CorsConfiguration config = new CorsConfiguration(); - config.setAllowedOrigins(Collections.singletonList("http://localhost:5173")); - config.setAllowedMethods(Collections.singletonList("*")); // 모든 유형의 http 메소드 트래픽 허용 - config.setAllowCredentials(true); // UI에서 백엔드로 user 자격 증명이나 기타 적용 가능한 쿠키 수락 - config.setAllowedHeaders(Collections.singletonList("*")); // 모든 종류의 헤더를 수락해도 괜찮다. - config.setExposedHeaders(Arrays.asList("Authorization")); // 헤터를 사용하여 JWT 토큰값 전송 - config.setMaxAge(3600L); // 1시간 - return config; - } - })) - .csrf(csrfConfig -> csrfConfig.csrfTokenRequestHandler(csrfTokenRequestAttributeHandler) - // 아래 API들에 대해서는 CSRF 보호를 무시하도록 지시(공개) - .ignoringRequestMatchers("/api/v1/member/register", "/api/v1/member/login") - // 로그인 작업 후 처음으로 CSRF 토큰을 생성하는데만 도움을 준다. - .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())) - // 로그인 시 사용(jwt 생성)2 - .addFilterAfter(new JWTTokenGeneratorFilter(applicationConstants), BasicAuthenticationFilter.class) - // 다른 api 접근시 사용(인증)1 - .addFilterBefore(new JWTTokenValidatorFilter(applicationConstants), BasicAuthenticationFilter.class) - // csrf 보호 필터3 - .addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class) - // 데이터 파싱 필터(파싱해서 request로 )4 - .addFilterAfter(new TokenFilter(applicationConstants), JWTTokenGeneratorFilter.class) - - - .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()) - .authorizeHttpRequests((requests -> requests - // 모두 접근 가능 - .requestMatchers("/api/v1/member/register", "/api/v1/member/login", "/ws-stomp/**").permitAll() - .anyRequest().authenticated())); - http.formLogin(withDefaults()); - http.httpBasic(withDefaults()); - return http.build(); - } - - - - - @Bean - public PasswordEncoder passwordEncoder() { - return PasswordEncoderFactories.createDelegatingPasswordEncoder(); - } - - /** - * 사용자 비밀 번호가 유출 되었는지 확인하는 메소드 - * From Spring Security 6.3부터 도입 - * @return - * */ - @Bean - public CompromisedPasswordChecker compromisedPasswordChecker() { - return new HaveIBeenPwnedRestApiPasswordChecker(); - } - - // 인증 메커니즘을 시작하는 역할 - @Bean - public AuthenticationManager authenticationManager(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder){ - // 인증 제공자 객체 - ProdUsernamePwdAuthenticationProvider authenticationProvider = - new ProdUsernamePwdAuthenticationProvider(userDetailsService, passwordEncoder); - - ProviderManager providerManager = new ProviderManager(authenticationProvider); - // provider manager는 Authentication 객체 내부의 비밀번호를 지우지 못하게 설정(유효성 검사) - providerManager.setEraseCredentialsAfterAuthentication(false); - return providerManager; - } - -} diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java deleted file mode 100644 index f37f8a17..00000000 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java +++ /dev/null @@ -1,43 +0,0 @@ -package stanl_2.final_backend.global.security.config; - -import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Profile; -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Component; -import stanl_2.final_backend.global.exception.CommonException; -import stanl_2.final_backend.global.exception.ErrorCode; - -@Component -@Profile("prod") -@RequiredArgsConstructor -public class ProdUsernamePwdAuthenticationProvider implements AuthenticationProvider { - - private final UserDetailsService userDetailsService; - private final PasswordEncoder passwordEncoder; - - - @Override - public Authentication authenticate(Authentication authentication) throws AuthenticationException { - String username = authentication.getName(); - String pwd = authentication.getCredentials().toString(); - - UserDetails userDetails = userDetailsService.loadUserByUsername(username); - if(passwordEncoder.matches(pwd, userDetails.getPassword())) { - return new UsernamePasswordAuthenticationToken(userDetails, pwd, userDetails.getAuthorities()); - }else{ - throw new CommonException(ErrorCode.LOGIN_FAILURE); - } - } - - // 추후에 여기에 OAuth2.0 추가 가능 할 듯 - @Override - public boolean supports(Class authentication) { - return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); - } -} diff --git a/src/main/java/stanl_2/final_backend/global/security/config/SecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/SecurityConfig.java deleted file mode 100644 index 964b1185..00000000 --- a/src/main/java/stanl_2/final_backend/global/security/config/SecurityConfig.java +++ /dev/null @@ -1,99 +0,0 @@ -package stanl_2.final_backend.global.security.config; - -import jakarta.servlet.http.HttpServletRequest; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.ProviderManager; -import org.springframework.security.authentication.password.CompromisedPasswordChecker; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.crypto.factory.PasswordEncoderFactories; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.password.HaveIBeenPwnedRestApiPasswordChecker; -import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; -import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; - -import java.util.Arrays; -import java.util.Collections; - -import static org.springframework.security.config.Customizer.withDefaults; - -@Configuration -@Profile("local") -public class SecurityConfig { - - private final ApplicationConstants applicationConstants; - - public SecurityConfig(ApplicationConstants applicationConstants) { - this.applicationConstants = applicationConstants; - } - - @Bean - SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { - //CSRF 토큰 요청 속성을 사용하여 토큰 값을 헤더나 매개변수 값으로 해결하는 로직을 포함 - CsrfTokenRequestAttributeHandler csrfTokenRequestAttributeHandler = new CsrfTokenRequestAttributeHandler(); - http.csrf(csrfConfig -> csrfConfig.disable()); - http.cors(corsConfig -> corsConfig.configurationSource(new CorsConfigurationSource() { - @Override - public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { - CorsConfiguration config = new CorsConfiguration(); - config.setAllowedOrigins(Collections.singletonList("http://localhost:5173")); - config.setAllowedMethods(Collections.singletonList("*")); // 모든 유형의 http 메소드 트래픽 허용 - config.setAllowCredentials(true); // UI에서 백엔드로 user 자격 증명이나 기타 적용 가능한 쿠키 수락 - config.setAllowedHeaders(Collections.singletonList("*")); // 모든 종류의 헤더를 수락해도 괜찮다. - config.setExposedHeaders(Arrays.asList("Authorization")); // 헤터를 사용하여 JWT 토큰값 전송 - config.setMaxAge(3600L); // 1시간 - return config; - } - })) - // 로그인 시 사용(jwt 생성)2 - .addFilterAfter(new JWTTokenGeneratorFilter(applicationConstants), BasicAuthenticationFilter.class) - // 다른 api 접근시 사용(인증)1 - .addFilterBefore(new JWTTokenValidatorFilter(applicationConstants), BasicAuthenticationFilter.class) - // 데이터 파싱 필터(파싱해서 request로 )4 - .addFilterAfter(new TokenFilter(applicationConstants), JWTTokenGeneratorFilter.class) - - .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()) - .authorizeHttpRequests((requests -> requests - .anyRequest().permitAll())); - http.formLogin(withDefaults()); - http.httpBasic(withDefaults()); - return http.build(); - } - - - - - @Bean - public PasswordEncoder passwordEncoder() { - return PasswordEncoderFactories.createDelegatingPasswordEncoder(); - } - - /** - * 사용자 비밀 번호가 유출 되었는지 확인하는 메소드 - * From Spring Security 6.3부터 도입 - * */ - @Bean - public CompromisedPasswordChecker compromisedPasswordChecker() { - return new HaveIBeenPwnedRestApiPasswordChecker(); - } - - // 인증 메커니즘을 시작하는 역할 - @Bean - public AuthenticationManager authenticationManager(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder){ - // 인증 제공자 객체 - UsernamePwdAuthenticationProvider authenticationProvider = - new UsernamePwdAuthenticationProvider(userDetailsService, passwordEncoder); - - ProviderManager providerManager = new ProviderManager(authenticationProvider); - // provider manager는 Authentication 객체 내부의 비밀번호를 지우지 못하게 설정(유효성 검사) - providerManager.setEraseCredentialsAfterAuthentication(false); - return providerManager; - } - -} diff --git a/src/main/java/stanl_2/final_backend/global/security/config/UsernamePwdAuthenticationProvider.java b/src/main/java/stanl_2/final_backend/global/security/config/UsernamePwdAuthenticationProvider.java deleted file mode 100644 index a0654fee..00000000 --- a/src/main/java/stanl_2/final_backend/global/security/config/UsernamePwdAuthenticationProvider.java +++ /dev/null @@ -1,37 +0,0 @@ -package stanl_2.final_backend.global.security.config; - -import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Profile; -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Component; - -@Component -@Profile("local") -@RequiredArgsConstructor -public class UsernamePwdAuthenticationProvider implements AuthenticationProvider { - - private final UserDetailsService userDetailsService; - private final PasswordEncoder passwordEncoder; - - - @Override - public Authentication authenticate(Authentication authentication) throws AuthenticationException { - String username = authentication.getName(); - String pwd = authentication.getCredentials().toString(); - UserDetails userDetails = userDetailsService.loadUserByUsername(username); - // 그냥 id만 맞으면 비밀번호 틀려도 통과 - return new UsernamePasswordAuthenticationToken(userDetails, pwd, userDetails.getAuthorities()); - } - - // 추후에 여기에 OAuth2.0 추가 가능 할 듯 - @Override - public boolean supports(Class authentication) { - return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); - } -} diff --git a/src/main/java/stanl_2/final_backend/global/security/constants/ApplicationConstants.java b/src/main/java/stanl_2/final_backend/global/security/constants/ApplicationConstants.java deleted file mode 100644 index 2e122bc8..00000000 --- a/src/main/java/stanl_2/final_backend/global/security/constants/ApplicationConstants.java +++ /dev/null @@ -1,18 +0,0 @@ -package stanl_2.final_backend.global.security.constants; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; - -@Component -@Getter -@RequiredArgsConstructor -public class ApplicationConstants { - - // 비밀키는 자동으로 생성하여 상수로 설정 - public final String JWT_SECRET_KEY = "JWT_SECRET"; - - private final String JWT_SECRET_DEFAULT_VALUE="jxgEQeXHuPq8VdbyYFNkANdudQ53YUn4"; - - public final String JWT_HEADER = "Authorization"; -} diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java index 0ab8a499..8f075a21 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java @@ -8,6 +8,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.web.filter.OncePerRequestFilter; +import stanl_2.final_backend.global.exception.CommonException; +import stanl_2.final_backend.global.exception.ErrorCode; import java.io.IOException; import java.util.Arrays; diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java deleted file mode 100644 index c3fdd977..00000000 --- a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java +++ /dev/null @@ -1,67 +0,0 @@ -package stanl_2.final_backend.global.security.filter; - -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.security.Keys; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.filter.OncePerRequestFilter; -import stanl_2.final_backend.global.security.constants.ApplicationConstants; - -import javax.crypto.SecretKey; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Date; -import java.util.stream.Collectors; - -@Slf4j -@RequiredArgsConstructor -public class JWTTokenGeneratorFilter extends OncePerRequestFilter { - - private final ApplicationConstants applicationConstants; - - @Override - protected void doFilterInternal(HttpServletRequest request, - HttpServletResponse response, - FilterChain filterChain) throws ServletException, IOException { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (null != authentication) { - // pk, nickname 넣기 - Member member = (Member) authentication.getPrincipal(); - - // 비밀키 생성 - String secret = applicationConstants.getJWT_SECRET_DEFAULT_VALUE(); - SecretKey secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)); - - String jwt = Jwts.builder() - .setIssuer("STANL2") - .setSubject("JWT Token") - .claim("username", authentication.getName()) - .claim("id", member.getId()) - .claim("nickname", member.getNickname()) - - .claim("authorities", authentication.getAuthorities().stream() - .map(GrantedAuthority::getAuthority).collect(Collectors.joining(","))) - .setIssuedAt(new Date()) - .setExpiration(new Date((new Date()).getTime() + 30000000)) - .signWith(secretKey) - .compact(); // Digital Signature 생성 - - // JWT 토큰을 응답 헤더에 추가 - response.setHeader(applicationConstants.getJWT_HEADER(), jwt); - log.error("{}", jwt); - } - filterChain.doFilter(request, response); - } - - @Override - protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { - return !request.getServletPath().equals("/api/v1/member/login"); - } -} diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java deleted file mode 100644 index 3e4a68e2..00000000 --- a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java +++ /dev/null @@ -1,72 +0,0 @@ -package stanl_2.final_backend.global.security.filter; - -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.security.Keys; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.crypto.SecretKey; -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -@Slf4j -@RequiredArgsConstructor -public class JWTTokenValidatorFilter extends OncePerRequestFilter { - - private final ApplicationConstants applicationConstants; - - @Override - protected void doFilterInternal(HttpServletRequest request, - HttpServletResponse response, - FilterChain filterChain) throws ServletException, IOException { - String jwt = request.getHeader(applicationConstants.getJWT_HEADER()); - if (null != jwt) { - try { - // 비밀키 가져오기 - String secret = applicationConstants.getJWT_SECRET_DEFAULT_VALUE(); - SecretKey secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)); - String jwtToken = jwt.substring(7); - // JWT 토큰 검증 - Claims claims = Jwts.parserBuilder() - .setSigningKey(secretKey) - .build() - .parseClaimsJws(jwtToken) - .getBody(); - - String username = String.valueOf(claims.get("username")); - String authorities = String.valueOf(claims.get("authorities")); - Authentication authentication = new UsernamePasswordAuthenticationToken(username, null, - AuthorityUtils.commaSeparatedStringToAuthorityList(authorities)); - - // SecurityContextHolder에 저장 - SecurityContextHolder.getContext().setAuthentication(authentication); - - } catch (Exception exception) { - throw new CommonException(ErrorCode.INVALID_TOKEN_ERROR); - } - } - filterChain.doFilter(request, response); - } - - @Override - protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { - String path = request.getServletPath(); - return path.equals("/api/v1/member/login") || - path.equals("/api/v1/member/register") || - path.equals("/api/v1/member") || - path.startsWith("/api/v1/member/sms") || // 와일드카드 경로 포함 - path.startsWith("/api/v1/member/mail") || - path.equals("/api/v1/member/otherprofile") || - path.equals("/api/v1/member/password"); - } -} diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/TokenFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/TokenFilter.java deleted file mode 100644 index 43c62ce8..00000000 --- a/src/main/java/stanl_2/final_backend/global/security/filter/TokenFilter.java +++ /dev/null @@ -1,88 +0,0 @@ -package stanl_2.final_backend.global.security.filter; - -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.security.Keys; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.crypto.SecretKey; -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -@RequiredArgsConstructor -@Slf4j -public class TokenFilter extends OncePerRequestFilter { - private final ApplicationConstants applicationConstants; - - @Override - public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException { - String token = request.getHeader("Authorization"); - - if(token == null || token.isEmpty() || !token.startsWith("Bearer ")){ - // 보안 X - filterChain.doFilter(request, response); // 토큰이 없으면 필터를 그냥 통과시킴 - - // 보안 O -// throw new CommonException(ErrorCode.NOT_FOUND_JWT_TOKEN); - }else { - String jwtToken = token.substring(7); - - // 시크릿 키로 JWT를 파싱 - SecretKey secretKey = Keys.hmacShaKeyFor(applicationConstants.getJWT_SECRET_DEFAULT_VALUE().getBytes(StandardCharsets.UTF_8)); - - // JWT 토큰에서 클레임을 추출 - Claims claims = Jwts.parserBuilder() - .setSigningKey(secretKey) - .build() - .parseClaimsJws(jwtToken) - .getBody(); - - // jwt 토큰에서 추출 - Long id = claims.get("id", Long.class); - String loginId = claims.get("loginId", String.class); - String nationality = claims.get("nationality", String.class); - String sex = claims.get("sex", String.class); - Integer point = claims.get("point", Integer.class); - String nickname = claims.get("nickname", String.class); -// String profile = claims.get("profile", String.class); -// String introduction = claims.get("introduction", String.class); - String language = claims.get("language", String.class); - - // request에 넣기 - request.setAttribute("id", id); - request.setAttribute("loginId", loginId); - request.setAttribute("nationality", nationality); - request.setAttribute("sex", sex); - request.setAttribute("point", point); - request.setAttribute("nickname", nickname); -// request.setAttribute("profile", profile); -// request.setAttribute("introduction", introduction); - request.setAttribute("language", language); - request.setAttribute("Authorization", token); - } - - - - filterChain.doFilter(request, response); - } - - @Override - protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { - String path = request.getServletPath(); - return path.equals("/api/v1/member/login") || - path.equals("/api/v1/member/register") || - path.startsWith("/api/v1/member/sms") || // 와일드카드 경로 포함 - path.startsWith("/api/v1/member/mail") || - path.equals("/api/v1/member") || - path.equals("/api/v1/member/password") || - path.equals("/api/v1/member/otherprofile") || - path.startsWith("/ws"); - } - -} diff --git a/src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsService.java b/src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsService.java deleted file mode 100644 index 4e0968ce..00000000 --- a/src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsService.java +++ /dev/null @@ -1,6 +0,0 @@ -package stanl_2.final_backend.global.security.service; - -import org.springframework.security.core.userdetails.UserDetailsService; - -public interface MemberDetailsService extends UserDetailsService { -} diff --git a/src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsServiceImpl.java b/src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsServiceImpl.java deleted file mode 100644 index a0f31cd8..00000000 --- a/src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsServiceImpl.java +++ /dev/null @@ -1,26 +0,0 @@ -package stanl_2.final_backend.global.security.service; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.stereotype.Service; - -// 사용자 정보 호출 -@Slf4j -@Service(value="memberDetailsServiceImpl") -@RequiredArgsConstructor -public class MemberDetailsServiceImpl implements MemberDetailsService { - - private final MemberRepository memberRepository; - - @Override - public UserDetails loadUserByUsername(String loginId) throws UsernameNotFoundException { - Member member = memberRepository.findByloginId(loginId); - if(member == null) { - log.info("D1"); - throw new UsernameNotFoundException(loginId); - } - return new MemberPrincipal(member); - } -} diff --git a/src/main/java/stanl_2/final_backend/global/security/service/MemberPrincipal.java b/src/main/java/stanl_2/final_backend/global/security/service/MemberPrincipal.java deleted file mode 100644 index 976d4531..00000000 --- a/src/main/java/stanl_2/final_backend/global/security/service/MemberPrincipal.java +++ /dev/null @@ -1,55 +0,0 @@ -package stanl_2.final_backend.global.security.service; - -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.Collection; -import java.util.Collections; - -// 사용자 인증 시 필요한 정보 제공 -@RequiredArgsConstructor -public class MemberPrincipal implements UserDetails { - - private final Member member; - - public Member getMember() { - return member; - } - - @Override - public Collection getAuthorities() { - return Collections.singletonList(new SimpleGrantedAuthority(member.getRole().name())); - } - - @Override - public String getPassword() { - return member.getPassword(); - } - - @Override - public String getUsername() { - return member.getLoginId(); - } - - @Override - public boolean isAccountNonExpired() { - return true; // 비즈니스 로직에 따라 설정 (true로 설정하면 계정 만료 미사용) - } - - @Override - public boolean isAccountNonLocked() { - return true; // 비즈니스 로직에 따라 설정 (true로 설정하면 계정 잠금 미사용) - } - - @Override - public boolean isCredentialsNonExpired() { - return true; // 비즈니스 로직에 따라 설정 (true로 설정하면 자격 증명 만료 미사용) - } - -// @Override -// public boolean isEnabled() { -// return member.isEnabled(); // 활성화 상태에 따라 true/false 반환 -// } -} From f33a3a994757b5d410574084bd7b9a1ddef7e95f Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 12 Nov 2024 19:45:12 +0900 Subject: [PATCH 032/563] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20Service=20=EC=84=B8=ED=8C=85=20=EC=A4=91=20(#26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/service/NoticeCommandService.java | 12 ++++++++++++ .../domain/service/NoticeCommandServiceImpl.java | 2 ++ 2 files changed, 14 insertions(+) create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/command/application/service/NoticeCommandService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/service/NoticeCommandService.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/service/NoticeCommandService.java new file mode 100644 index 00000000..cae0f38d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/service/NoticeCommandService.java @@ -0,0 +1,12 @@ +package stanl_2.final_backend.domain.notices.command.application.service; + +import stanl_2.final_backend.domain.notices.command.application.dto.NoticeModifyDTO; +import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; + +public interface NoticeCommandService { + void registerNotice(NoticeRegistDTO noticeRegistDTO); + + NoticeModifyDTO modifyNotice(String title, String tag, String classification, String content, String updatedAt); + + void deleteNotice(String id); +} diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java new file mode 100644 index 00000000..a8b97c03 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java @@ -0,0 +1,2 @@ +package stanl_2.final_backend.domain.notices.command.domain.service;public class NoticeCommandServiceImpl { +} From 368ab1f1cdc8ea91fa687510cc9b35912ba7c718 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 12 Nov 2024 20:05:17 +0900 Subject: [PATCH 033/563] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20Service=EC=84=B8=ED=8C=85=20=EC=A4=91=20(#26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SampleController.java | 22 +++--- .../service/SampleCommandServiceImpl.java | 8 +-- .../common/exception/ExceptionResponse.java | 22 ------ ...eption.java => SampleCommonException.java} | 6 +- .../{ErrorCode.java => SampleErrorCode.java} | 4 +- .../exception/SampleExceptionResponse.java | 22 ++++++ ...essage.java => SampleResponseMessage.java} | 2 +- .../query/controller/SampleController.java | 14 ++-- .../query/service/SampleServiceImpl.java | 8 +-- .../controller/CenterController.java | 7 +- .../service/CenterCommandServiceImpl.java | 9 ++- .../controller/ContractController.java | 10 +-- .../application/dto/NoticeModifyDTO.java | 11 +++ .../service/NoticeCommandService.java | 2 +- .../service/NoticeCommandServiceImpl.java | 69 ++++++++++++++++++- .../exception/NoticeCommonException.java | 17 +++++ .../common/exception/NoticeErrorCode.java | 51 ++++++++++++++ .../exception/NoticeExceptionResponse.java | 4 ++ .../response/NoticeResponseMessage.java | 14 ++++ 19 files changed, 232 insertions(+), 70 deletions(-) delete mode 100644 src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/ExceptionResponse.java rename src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/{CommonException.java => SampleCommonException.java} (62%) rename src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/{ErrorCode.java => SampleErrorCode.java} (97%) create mode 100644 src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/SampleExceptionResponse.java rename src/main/java/stanl_2/final_backend/domain/A_sample/common/response/{ResponseMessage.java => SampleResponseMessage.java} (85%) create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/common/exception/NoticeCommonException.java create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/common/exception/NoticeErrorCode.java create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/common/exception/NoticeExceptionResponse.java create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/common/response/NoticeResponseMessage.java diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java index 2d5921e4..8aea5c10 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java @@ -11,7 +11,7 @@ import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleRegistDTO; import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleModifyDTO; import stanl_2.final_backend.domain.A_sample.command.application.service.SampleCommandService; -import stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; @RestController("commandSampleController") @RequestMapping("/api/v1/sample") @@ -35,14 +35,14 @@ public SampleController(SampleCommandService sampleCommandService) { @Operation(summary = "샘플 요청 테스트") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postTest(@RequestBody SampleRegistDTO sampleRegistRequestDTO) { + public ResponseEntity postTest(@RequestBody SampleRegistDTO sampleRegistRequestDTO) { sampleCommandService.registerSample(sampleRegistRequestDTO); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(SampleResponseMessage.builder() .httpStatus(200) .msg("성공") .result(null) @@ -59,16 +59,16 @@ public ResponseEntity postTest(@RequestBody SampleRegistDTO sam @Operation(summary = "샘플 수정 테스트") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) }) @PutMapping("{id}") - public ResponseEntity putTest(@PathVariable String id, - @RequestBody SampleModifyDTO sampleModifyRequestDTO) { + public ResponseEntity putTest(@PathVariable String id, + @RequestBody SampleModifyDTO sampleModifyRequestDTO) { sampleModifyRequestDTO.setId(id); SampleModifyDTO sampleModifyDTO = sampleCommandService.modifySample(id, sampleModifyRequestDTO); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(SampleResponseMessage.builder() .httpStatus(200) .msg("성공") .result(sampleModifyDTO) @@ -81,14 +81,14 @@ public ResponseEntity putTest(@PathVariable String id, @Operation(summary = "샘플 삭제 테스트") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) }) @DeleteMapping("{id}") - public ResponseEntity deleteTest(@PathVariable String id) { + public ResponseEntity deleteTest(@PathVariable String id) { sampleCommandService.deleteSample(id); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(SampleResponseMessage.builder() .httpStatus(200) .msg("성공") .result(null) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java index 4187bbfb..4a68f8df 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java @@ -9,8 +9,8 @@ import stanl_2.final_backend.domain.A_sample.command.application.service.SampleCommandService; import stanl_2.final_backend.domain.A_sample.command.domain.aggregate.entity.Sample; import stanl_2.final_backend.domain.A_sample.command.domain.repository.SampleRepository; -import stanl_2.final_backend.domain.A_sample.common.exception.CommonException; -import stanl_2.final_backend.domain.A_sample.common.exception.ErrorCode; +import stanl_2.final_backend.domain.A_sample.common.exception.SampleCommonException; +import stanl_2.final_backend.domain.A_sample.common.exception.SampleErrorCode; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -47,7 +47,7 @@ public void registerSample(SampleRegistDTO sampleRegistRequestDTO) { public SampleModifyDTO modifySample(String id, SampleModifyDTO sampleModifyRequestDTO) { Sample sample = sampleRepository.findById(id) - .orElseThrow(() -> new CommonException(ErrorCode.SAMPLE_NOT_FOUND)); + .orElseThrow(() -> new SampleCommonException(SampleErrorCode.SAMPLE_NOT_FOUND)); sampleModifyRequestDTO.setId(id); Sample updateSample = modelMapper.map(sampleModifyRequestDTO, Sample.class); @@ -66,7 +66,7 @@ public SampleModifyDTO modifySample(String id, SampleModifyDTO sampleModifyReque public void deleteSample(String id) { Sample sample = sampleRepository.findById(id) - .orElseThrow(() -> new CommonException(ErrorCode.SAMPLE_NOT_FOUND)); + .orElseThrow(() -> new SampleCommonException(SampleErrorCode.SAMPLE_NOT_FOUND)); sample.setActive(false); sample.setDeletedAt(getCurrentTimestamp()); diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/ExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/ExceptionResponse.java deleted file mode 100644 index bd9c67ad..00000000 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/ExceptionResponse.java +++ /dev/null @@ -1,22 +0,0 @@ -package stanl_2.final_backend.domain.A_sample.common.exception; - -import lombok.Getter; -import org.springframework.http.HttpStatus; - -@Getter -public class ExceptionResponse { - private final Integer code; - private final String msg; - private final HttpStatus httpStatus; - - public ExceptionResponse(ErrorCode errorCode) { - this.code = errorCode.getCode(); - this.msg = errorCode.getMsg(); - this.httpStatus = errorCode.getHttpStatus(); - } - - public static ExceptionResponse of(ErrorCode errorCode) { - return new ExceptionResponse(errorCode); - } - -} diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/CommonException.java b/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/SampleCommonException.java similarity index 62% rename from src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/CommonException.java rename to src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/SampleCommonException.java index 2e2becf4..3777925c 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/CommonException.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/SampleCommonException.java @@ -5,12 +5,12 @@ @Getter @RequiredArgsConstructor -public class CommonException extends RuntimeException { - private final ErrorCode errorCode; +public class SampleCommonException extends RuntimeException { + private final SampleErrorCode sampleErrorCode; // 에러 발생시 ErroCode 별 메시지 @Override public String getMessage() { - return this.errorCode.getMsg(); + return this.sampleErrorCode.getMsg(); } } diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/ErrorCode.java b/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/SampleErrorCode.java similarity index 97% rename from src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/ErrorCode.java rename to src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/SampleErrorCode.java index 73bf4827..5706689d 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/ErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/SampleErrorCode.java @@ -6,7 +6,7 @@ @Getter @AllArgsConstructor -public enum ErrorCode { +public enum SampleErrorCode { /** * 400(Bad Request) @@ -40,9 +40,7 @@ public enum ErrorCode { */ SAMPLE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "sample 데이터를 찾지 못했습니다"), CENTER_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "center 데이터를 찾지 못했습니다."), - /** - * 500(Internal Server Error) * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. */ INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/SampleExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/SampleExceptionResponse.java new file mode 100644 index 00000000..cc5d7648 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/SampleExceptionResponse.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.A_sample.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class SampleExceptionResponse { + private final Integer code; + private final String msg; + private final HttpStatus httpStatus; + + public SampleExceptionResponse(SampleErrorCode sampleErrorCode) { + this.code = sampleErrorCode.getCode(); + this.msg = sampleErrorCode.getMsg(); + this.httpStatus = sampleErrorCode.getHttpStatus(); + } + + public static SampleExceptionResponse of(SampleErrorCode sampleErrorCode) { + return new SampleExceptionResponse(sampleErrorCode); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/common/response/ResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/A_sample/common/response/SampleResponseMessage.java similarity index 85% rename from src/main/java/stanl_2/final_backend/domain/A_sample/common/response/ResponseMessage.java rename to src/main/java/stanl_2/final_backend/domain/A_sample/common/response/SampleResponseMessage.java index 8cd45bcd..92a165f9 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/common/response/ResponseMessage.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/common/response/SampleResponseMessage.java @@ -7,7 +7,7 @@ @Builder @Getter @Setter -public class ResponseMessage { +public class SampleResponseMessage { private int httpStatus; private String msg; private Object result; diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java b/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java index fc0177cb..b9c4d82e 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java @@ -8,7 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.A_sample.query.dto.SampleDTO; import stanl_2.final_backend.domain.A_sample.query.service.SampleService; @@ -29,16 +29,16 @@ public SampleController(SampleService sampleService) { @Operation(summary = "샘플 조회 테스트") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}), + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) @GetMapping("{id}") - public ResponseEntity getTest(@PathVariable String id){ + public ResponseEntity getTest(@PathVariable String id){ SampleDTO sampleDTO = sampleService.selectSampleInfo(id); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(SampleResponseMessage.builder() .httpStatus(200) .msg("성공") .result(sampleDTO) @@ -51,16 +51,16 @@ public ResponseEntity getTest(@PathVariable String id){ @Operation(summary = "샘플 상세 조회 테스트") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}), + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) @GetMapping("/detail/{id}") - public ResponseEntity getDetailTest(@PathVariable String id) { + public ResponseEntity getDetailTest(@PathVariable String id) { String name = sampleService.selectSampleName(id); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(SampleResponseMessage.builder() .httpStatus(200) .msg("성공") .result(name) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleServiceImpl.java index 880be723..7fe42d99 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleServiceImpl.java @@ -4,8 +4,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.A_sample.common.exception.CommonException; -import stanl_2.final_backend.domain.A_sample.common.exception.ErrorCode; +import stanl_2.final_backend.domain.A_sample.common.exception.SampleCommonException; +import stanl_2.final_backend.domain.A_sample.common.exception.SampleErrorCode; import stanl_2.final_backend.domain.A_sample.query.dto.SampleDTO; import stanl_2.final_backend.domain.A_sample.query.repository.SampleMapper; @@ -27,7 +27,7 @@ public String selectSampleName(String id) { String name = sampleMapper.selectNameById(id);; if(name == null){ - throw new CommonException(ErrorCode.SAMPLE_NOT_FOUND); + throw new SampleCommonException(SampleErrorCode.SAMPLE_NOT_FOUND); } return name; @@ -40,7 +40,7 @@ public SampleDTO selectSampleInfo(String id) { SampleDTO sampleDTO = sampleMapper.selectById(id); if(sampleDTO == null){ - throw new CommonException(ErrorCode.SAMPLE_NOT_FOUND); + throw new SampleCommonException(SampleErrorCode.SAMPLE_NOT_FOUND); } return sampleDTO; diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java index ee46de80..e1f25a58 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java @@ -8,6 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterModifyRequestDTO; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistRequestDTO; import stanl_2.final_backend.domain.center.command.application.service.CenterCommandService; @@ -27,7 +28,7 @@ public CenterController(CenterCommandService centerCommandService) { @Operation(summary = "매장 등록") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) }) @PostMapping("") public ResponseEntity postTest(@RequestBody CenterRegistRequestDTO centerRegistRequestDTO){ @@ -46,7 +47,7 @@ public ResponseEntity postTest(@RequestBody CenterRegistRequestDTO centerRegi @Operation(summary = "매장 수정") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) }) @PutMapping("{id}") public ResponseEntity putTest(@PathVariable("id") String id, @@ -64,7 +65,7 @@ public ResponseEntity putTest(@PathVariable("id") String id, @Operation(summary = "샘플 삭제 테스트") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) }) @DeleteMapping("{id}") public ResponseEntity deleteTest(@PathVariable("id") String id){ diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java index 36281ab8..c8c87681 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java @@ -5,15 +5,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.A_sample.common.exception.CommonException; -import stanl_2.final_backend.domain.A_sample.common.exception.ErrorCode; +import stanl_2.final_backend.domain.A_sample.common.exception.SampleCommonException; +import stanl_2.final_backend.domain.A_sample.common.exception.SampleErrorCode; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterModifyRequestDTO; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistRequestDTO; import stanl_2.final_backend.domain.center.command.application.service.CenterCommandService; import stanl_2.final_backend.domain.center.command.domain.aggregate.entity.Center; import stanl_2.final_backend.domain.center.command.domain.repository.CenterRepository; -import java.sql.Timestamp; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -45,7 +44,7 @@ public void registCenter(CenterRegistRequestDTO centerRegistRequestDTO) { @Transactional public void modifyCenter(String id, CenterModifyRequestDTO centerModifyRequestDTO) { Center center = centerRepository.findById(id) - .orElseThrow(() -> new CommonException(ErrorCode.CENTER_NOT_FOUND)); + .orElseThrow(() -> new SampleCommonException(SampleErrorCode.CENTER_NOT_FOUND)); Center updateCenter = modelMapper.map(centerModifyRequestDTO, Center.class); @@ -62,7 +61,7 @@ public void modifyCenter(String id, CenterModifyRequestDTO centerModifyRequestDT public void deleteCenter(String id) { Center center = centerRepository.findById(id) - .orElseThrow(() -> new CommonException(ErrorCode.CENTER_NOT_FOUND)); + .orElseThrow(() -> new SampleCommonException(SampleErrorCode.CENTER_NOT_FOUND)); center.setActive(false); center.setDeletedAt(getCurrentTime()); diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java index cf37dc2e..1d9e60af 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java @@ -8,7 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.contract.command.application.dto.request.ContractRegistRequestDTO; import stanl_2.final_backend.domain.contract.command.application.service.ContractService; @@ -34,15 +34,15 @@ public ContractController(ContractService contractService) { @Operation(summary = "계약서 등록 테스트") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) }) @PostMapping("{id}") - public ResponseEntity postTest(@PathVariable String id, - @RequestBody ContractRegistRequestDTO contractRegistRequestDTO) { + public ResponseEntity postTest(@PathVariable String id, + @RequestBody ContractRegistRequestDTO contractRegistRequestDTO) { contractRegistRequestDTO.setMemId(id); contractService.registerContract(contractRegistRequestDTO); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(SampleResponseMessage.builder() .httpStatus(200) .msg("성공") .result(null) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java index d28c32b9..4f2c2a17 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java @@ -1,6 +1,17 @@ package stanl_2.final_backend.domain.notices.command.application.dto; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter public class NoticeModifyDTO { + private String id; + private String title; private String tag; diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/service/NoticeCommandService.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/service/NoticeCommandService.java index cae0f38d..f0b51c66 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/service/NoticeCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/service/NoticeCommandService.java @@ -6,7 +6,7 @@ public interface NoticeCommandService { void registerNotice(NoticeRegistDTO noticeRegistDTO); - NoticeModifyDTO modifyNotice(String title, String tag, String classification, String content, String updatedAt); + NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO); void deleteNotice(String id); } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java index a8b97c03..0b8f2120 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java @@ -1,2 +1,69 @@ -package stanl_2.final_backend.domain.notices.command.domain.service;public class NoticeCommandServiceImpl { +package stanl_2.final_backend.domain.notices.command.domain.service; + +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.notices.command.application.dto.NoticeModifyDTO; +import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; +import stanl_2.final_backend.domain.notices.command.application.service.NoticeCommandService; +import stanl_2.final_backend.domain.notices.command.domain.aggragate.entity.Notice; +import stanl_2.final_backend.domain.notices.command.domain.repository.NoticeRepository; +import stanl_2.final_backend.domain.notices.common.exception.NoticeCommonException; +import stanl_2.final_backend.domain.notices.common.exception.NoticeErrorCode; +import stanl_2.final_backend.global.exception.CommonException; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@Service("commandSampleService") +public class NoticeCommandServiceImpl implements NoticeCommandService { + + private final NoticeRepository noticeRepository; + + private final ModelMapper modelMapper; + + @Autowired + public NoticeCommandServiceImpl(NoticeRepository noticeRepository, ModelMapper modelMapper) { + this.noticeRepository = noticeRepository; + this.modelMapper = modelMapper; + } + + private String getCurrentTimestamp() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } + + @Override + @Transactional + public void registerNotice(NoticeRegistDTO noticeRegistDTO) { + Notice notice =modelMapper.map(noticeRegistDTO,Notice.class); + noticeRepository.save(notice); + } + + @Override + @Transactional + public NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO) { + Notice notice = noticeRepository.findById(id) + .orElseThrow(() -> new NoticeCommonException(NoticeErrorCode.NOTICE_NOT_FOUND)); + + noticeModifyDTO.setId(id); + + Notice updateNotice = modelMapper.map(noticeModifyDTO, Notice.class); + updateNotice.setCreatedAt(notice.getCreatedAt()); + updateNotice.setActive(notice.getActive()); + + noticeRepository.save(updateNotice); + + NoticeModifyDTO noticeModify = modelMapper.map(updateNotice,NoticeModifyDTO.class); + + return noticeModify; + } + + @Override + @Transactional + public void deleteNotice(String id) { + + } } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/common/exception/NoticeCommonException.java b/src/main/java/stanl_2/final_backend/domain/notices/common/exception/NoticeCommonException.java new file mode 100644 index 00000000..9c384472 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/common/exception/NoticeCommonException.java @@ -0,0 +1,17 @@ +package stanl_2.final_backend.domain.notices.common.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import stanl_2.final_backend.domain.A_sample.common.exception.SampleErrorCode; + +@Getter +@RequiredArgsConstructor +public class NoticeCommonException extends RuntimeException { + private final NoticeErrorCode noticeErrorCode; + + // 에러 발생시 ErroCode 별 메시지 + @Override + public String getMessage() { + return this.noticeErrorCode.getMsg(); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/notices/common/exception/NoticeErrorCode.java b/src/main/java/stanl_2/final_backend/domain/notices/common/exception/NoticeErrorCode.java new file mode 100644 index 00000000..f693efe3 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/common/exception/NoticeErrorCode.java @@ -0,0 +1,51 @@ +package stanl_2.final_backend.domain.notices.common.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum NoticeErrorCode { + + /** + * 400(Bad Request) + * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. + */ + + + + /** + * 401(Unauthorized) + * 비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만, + * 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다. + * 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다. + */ + + + /** + * 403(Forbidden) + * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. + * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다. + */ + + + + /** + * 404(Not Found) + * 서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다. + * 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있습니다. + * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. + * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. + */ + NOTICE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "notice 데이터를 찾지 못했습니다"), + /** + * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. + */ + INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); + + + private final Integer code; + private final HttpStatus httpStatus; + private final String msg; +} diff --git a/src/main/java/stanl_2/final_backend/domain/notices/common/exception/NoticeExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/notices/common/exception/NoticeExceptionResponse.java new file mode 100644 index 00000000..30ac7c4e --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/common/exception/NoticeExceptionResponse.java @@ -0,0 +1,4 @@ +package stanl_2.final_backend.domain.notices.common.exception; + +public class NoticeExceptionResponse { +} diff --git a/src/main/java/stanl_2/final_backend/domain/notices/common/response/NoticeResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/notices/common/response/NoticeResponseMessage.java new file mode 100644 index 00000000..ae17eed4 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/common/response/NoticeResponseMessage.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.notices.common.response; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class NoticeResponseMessage { + private int httpStatus; + private String msg; + private Object result; +} From 9dcd53b270f38d2c54f9b13ea0323cefd989ee56 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 12 Nov 2024 20:15:36 +0900 Subject: [PATCH 034/563] =?UTF-8?q?=EC=83=98=ED=94=8C=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95(#4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SampleController.java | 22 ++++++++-------- .../service/SampleCommandServiceImpl.java | 8 +++--- .../common/exception/ExceptionResponse.java | 22 ---------------- ...eption.java => SampleCommonException.java} | 6 ++--- .../{ErrorCode.java => SampleErrorCode.java} | 2 +- .../exception/SampleExceptionResponse.java | 22 ++++++++++++++++ ...essage.java => SampleResponseMessage.java} | 2 +- .../query/controller/SampleController.java | 26 +++++++++---------- ....java => SampleQueryQueryServiceImpl.java} | 14 +++++----- ...leService.java => SampleQueryService.java} | 2 +- .../controller/CenterController.java | 7 ++--- .../service/CenterCommandServiceImpl.java | 9 +++---- .../controller/ContractController.java | 10 +++---- 13 files changed, 76 insertions(+), 76 deletions(-) delete mode 100644 src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/ExceptionResponse.java rename src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/{CommonException.java => SampleCommonException.java} (62%) rename src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/{ErrorCode.java => SampleErrorCode.java} (98%) create mode 100644 src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/SampleExceptionResponse.java rename src/main/java/stanl_2/final_backend/domain/A_sample/common/response/{ResponseMessage.java => SampleResponseMessage.java} (85%) rename src/main/java/stanl_2/final_backend/domain/A_sample/query/service/{SampleServiceImpl.java => SampleQueryQueryServiceImpl.java} (67%) rename src/main/java/stanl_2/final_backend/domain/A_sample/query/service/{SampleService.java => SampleQueryService.java} (84%) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java index 2d5921e4..8aea5c10 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java @@ -11,7 +11,7 @@ import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleRegistDTO; import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleModifyDTO; import stanl_2.final_backend.domain.A_sample.command.application.service.SampleCommandService; -import stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; @RestController("commandSampleController") @RequestMapping("/api/v1/sample") @@ -35,14 +35,14 @@ public SampleController(SampleCommandService sampleCommandService) { @Operation(summary = "샘플 요청 테스트") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postTest(@RequestBody SampleRegistDTO sampleRegistRequestDTO) { + public ResponseEntity postTest(@RequestBody SampleRegistDTO sampleRegistRequestDTO) { sampleCommandService.registerSample(sampleRegistRequestDTO); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(SampleResponseMessage.builder() .httpStatus(200) .msg("성공") .result(null) @@ -59,16 +59,16 @@ public ResponseEntity postTest(@RequestBody SampleRegistDTO sam @Operation(summary = "샘플 수정 테스트") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) }) @PutMapping("{id}") - public ResponseEntity putTest(@PathVariable String id, - @RequestBody SampleModifyDTO sampleModifyRequestDTO) { + public ResponseEntity putTest(@PathVariable String id, + @RequestBody SampleModifyDTO sampleModifyRequestDTO) { sampleModifyRequestDTO.setId(id); SampleModifyDTO sampleModifyDTO = sampleCommandService.modifySample(id, sampleModifyRequestDTO); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(SampleResponseMessage.builder() .httpStatus(200) .msg("성공") .result(sampleModifyDTO) @@ -81,14 +81,14 @@ public ResponseEntity putTest(@PathVariable String id, @Operation(summary = "샘플 삭제 테스트") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) }) @DeleteMapping("{id}") - public ResponseEntity deleteTest(@PathVariable String id) { + public ResponseEntity deleteTest(@PathVariable String id) { sampleCommandService.deleteSample(id); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(SampleResponseMessage.builder() .httpStatus(200) .msg("성공") .result(null) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java index 4187bbfb..4a68f8df 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java @@ -9,8 +9,8 @@ import stanl_2.final_backend.domain.A_sample.command.application.service.SampleCommandService; import stanl_2.final_backend.domain.A_sample.command.domain.aggregate.entity.Sample; import stanl_2.final_backend.domain.A_sample.command.domain.repository.SampleRepository; -import stanl_2.final_backend.domain.A_sample.common.exception.CommonException; -import stanl_2.final_backend.domain.A_sample.common.exception.ErrorCode; +import stanl_2.final_backend.domain.A_sample.common.exception.SampleCommonException; +import stanl_2.final_backend.domain.A_sample.common.exception.SampleErrorCode; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -47,7 +47,7 @@ public void registerSample(SampleRegistDTO sampleRegistRequestDTO) { public SampleModifyDTO modifySample(String id, SampleModifyDTO sampleModifyRequestDTO) { Sample sample = sampleRepository.findById(id) - .orElseThrow(() -> new CommonException(ErrorCode.SAMPLE_NOT_FOUND)); + .orElseThrow(() -> new SampleCommonException(SampleErrorCode.SAMPLE_NOT_FOUND)); sampleModifyRequestDTO.setId(id); Sample updateSample = modelMapper.map(sampleModifyRequestDTO, Sample.class); @@ -66,7 +66,7 @@ public SampleModifyDTO modifySample(String id, SampleModifyDTO sampleModifyReque public void deleteSample(String id) { Sample sample = sampleRepository.findById(id) - .orElseThrow(() -> new CommonException(ErrorCode.SAMPLE_NOT_FOUND)); + .orElseThrow(() -> new SampleCommonException(SampleErrorCode.SAMPLE_NOT_FOUND)); sample.setActive(false); sample.setDeletedAt(getCurrentTimestamp()); diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/ExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/ExceptionResponse.java deleted file mode 100644 index bd9c67ad..00000000 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/ExceptionResponse.java +++ /dev/null @@ -1,22 +0,0 @@ -package stanl_2.final_backend.domain.A_sample.common.exception; - -import lombok.Getter; -import org.springframework.http.HttpStatus; - -@Getter -public class ExceptionResponse { - private final Integer code; - private final String msg; - private final HttpStatus httpStatus; - - public ExceptionResponse(ErrorCode errorCode) { - this.code = errorCode.getCode(); - this.msg = errorCode.getMsg(); - this.httpStatus = errorCode.getHttpStatus(); - } - - public static ExceptionResponse of(ErrorCode errorCode) { - return new ExceptionResponse(errorCode); - } - -} diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/CommonException.java b/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/SampleCommonException.java similarity index 62% rename from src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/CommonException.java rename to src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/SampleCommonException.java index 2e2becf4..3777925c 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/CommonException.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/SampleCommonException.java @@ -5,12 +5,12 @@ @Getter @RequiredArgsConstructor -public class CommonException extends RuntimeException { - private final ErrorCode errorCode; +public class SampleCommonException extends RuntimeException { + private final SampleErrorCode sampleErrorCode; // 에러 발생시 ErroCode 별 메시지 @Override public String getMessage() { - return this.errorCode.getMsg(); + return this.sampleErrorCode.getMsg(); } } diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/ErrorCode.java b/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/SampleErrorCode.java similarity index 98% rename from src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/ErrorCode.java rename to src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/SampleErrorCode.java index 73bf4827..14c6bef3 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/ErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/SampleErrorCode.java @@ -6,7 +6,7 @@ @Getter @AllArgsConstructor -public enum ErrorCode { +public enum SampleErrorCode { /** * 400(Bad Request) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/SampleExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/SampleExceptionResponse.java new file mode 100644 index 00000000..cc5d7648 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/common/exception/SampleExceptionResponse.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.A_sample.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class SampleExceptionResponse { + private final Integer code; + private final String msg; + private final HttpStatus httpStatus; + + public SampleExceptionResponse(SampleErrorCode sampleErrorCode) { + this.code = sampleErrorCode.getCode(); + this.msg = sampleErrorCode.getMsg(); + this.httpStatus = sampleErrorCode.getHttpStatus(); + } + + public static SampleExceptionResponse of(SampleErrorCode sampleErrorCode) { + return new SampleExceptionResponse(sampleErrorCode); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/common/response/ResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/A_sample/common/response/SampleResponseMessage.java similarity index 85% rename from src/main/java/stanl_2/final_backend/domain/A_sample/common/response/ResponseMessage.java rename to src/main/java/stanl_2/final_backend/domain/A_sample/common/response/SampleResponseMessage.java index 8cd45bcd..92a165f9 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/common/response/ResponseMessage.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/common/response/SampleResponseMessage.java @@ -7,7 +7,7 @@ @Builder @Getter @Setter -public class ResponseMessage { +public class SampleResponseMessage { private int httpStatus; private String msg; private Object result; diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java b/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java index fc0177cb..a4f0221b 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java @@ -8,19 +8,19 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.A_sample.query.dto.SampleDTO; -import stanl_2.final_backend.domain.A_sample.query.service.SampleService; +import stanl_2.final_backend.domain.A_sample.query.service.SampleQueryService; @RestController(value = "querySampleController") @RequestMapping("/api/v1/sample") public class SampleController { - private final SampleService sampleService; + private final SampleQueryService sampleQueryService; @Autowired - public SampleController(SampleService sampleService) { - this.sampleService = sampleService; + public SampleController(SampleQueryService sampleQueryService) { + this.sampleQueryService = sampleQueryService; } /** @@ -29,16 +29,16 @@ public SampleController(SampleService sampleService) { @Operation(summary = "샘플 조회 테스트") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}), + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) @GetMapping("{id}") - public ResponseEntity getTest(@PathVariable String id){ + public ResponseEntity getTest(@PathVariable String id){ - SampleDTO sampleDTO = sampleService.selectSampleInfo(id); + SampleDTO sampleDTO = sampleQueryService.selectSampleInfo(id); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(SampleResponseMessage.builder() .httpStatus(200) .msg("성공") .result(sampleDTO) @@ -51,16 +51,16 @@ public ResponseEntity getTest(@PathVariable String id){ @Operation(summary = "샘플 상세 조회 테스트") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}), + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) @GetMapping("/detail/{id}") - public ResponseEntity getDetailTest(@PathVariable String id) { + public ResponseEntity getDetailTest(@PathVariable String id) { - String name = sampleService.selectSampleName(id); + String name = sampleQueryService.selectSampleName(id); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(SampleResponseMessage.builder() .httpStatus(200) .msg("성공") .result(name) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryQueryServiceImpl.java similarity index 67% rename from src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleServiceImpl.java rename to src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryQueryServiceImpl.java index 880be723..f0bcf71c 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryQueryServiceImpl.java @@ -4,19 +4,19 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.A_sample.common.exception.CommonException; -import stanl_2.final_backend.domain.A_sample.common.exception.ErrorCode; +import stanl_2.final_backend.domain.A_sample.common.exception.SampleCommonException; +import stanl_2.final_backend.domain.A_sample.common.exception.SampleErrorCode; import stanl_2.final_backend.domain.A_sample.query.dto.SampleDTO; import stanl_2.final_backend.domain.A_sample.query.repository.SampleMapper; @Slf4j -@Service(value = "querySampleService") -public class SampleServiceImpl implements SampleService{ +@Service +public class SampleQueryQueryServiceImpl implements SampleQueryService { private final SampleMapper sampleMapper; @Autowired - public SampleServiceImpl(SampleMapper sampleMapper) { + public SampleQueryQueryServiceImpl(SampleMapper sampleMapper) { this.sampleMapper = sampleMapper; } @@ -27,7 +27,7 @@ public String selectSampleName(String id) { String name = sampleMapper.selectNameById(id);; if(name == null){ - throw new CommonException(ErrorCode.SAMPLE_NOT_FOUND); + throw new SampleCommonException(SampleErrorCode.SAMPLE_NOT_FOUND); } return name; @@ -40,7 +40,7 @@ public SampleDTO selectSampleInfo(String id) { SampleDTO sampleDTO = sampleMapper.selectById(id); if(sampleDTO == null){ - throw new CommonException(ErrorCode.SAMPLE_NOT_FOUND); + throw new SampleCommonException(SampleErrorCode.SAMPLE_NOT_FOUND); } return sampleDTO; diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleService.java b/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryService.java similarity index 84% rename from src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleService.java rename to src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryService.java index a53ea073..df522286 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleService.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryService.java @@ -2,7 +2,7 @@ import stanl_2.final_backend.domain.A_sample.query.dto.SampleDTO; -public interface SampleService { +public interface SampleQueryService { String selectSampleName(String id); SampleDTO selectSampleInfo(String id); diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java index ee46de80..e1f25a58 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java @@ -8,6 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterModifyRequestDTO; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistRequestDTO; import stanl_2.final_backend.domain.center.command.application.service.CenterCommandService; @@ -27,7 +28,7 @@ public CenterController(CenterCommandService centerCommandService) { @Operation(summary = "매장 등록") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) }) @PostMapping("") public ResponseEntity postTest(@RequestBody CenterRegistRequestDTO centerRegistRequestDTO){ @@ -46,7 +47,7 @@ public ResponseEntity postTest(@RequestBody CenterRegistRequestDTO centerRegi @Operation(summary = "매장 수정") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) }) @PutMapping("{id}") public ResponseEntity putTest(@PathVariable("id") String id, @@ -64,7 +65,7 @@ public ResponseEntity putTest(@PathVariable("id") String id, @Operation(summary = "샘플 삭제 테스트") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) }) @DeleteMapping("{id}") public ResponseEntity deleteTest(@PathVariable("id") String id){ diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java index 36281ab8..c8c87681 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java @@ -5,15 +5,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.A_sample.common.exception.CommonException; -import stanl_2.final_backend.domain.A_sample.common.exception.ErrorCode; +import stanl_2.final_backend.domain.A_sample.common.exception.SampleCommonException; +import stanl_2.final_backend.domain.A_sample.common.exception.SampleErrorCode; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterModifyRequestDTO; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistRequestDTO; import stanl_2.final_backend.domain.center.command.application.service.CenterCommandService; import stanl_2.final_backend.domain.center.command.domain.aggregate.entity.Center; import stanl_2.final_backend.domain.center.command.domain.repository.CenterRepository; -import java.sql.Timestamp; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -45,7 +44,7 @@ public void registCenter(CenterRegistRequestDTO centerRegistRequestDTO) { @Transactional public void modifyCenter(String id, CenterModifyRequestDTO centerModifyRequestDTO) { Center center = centerRepository.findById(id) - .orElseThrow(() -> new CommonException(ErrorCode.CENTER_NOT_FOUND)); + .orElseThrow(() -> new SampleCommonException(SampleErrorCode.CENTER_NOT_FOUND)); Center updateCenter = modelMapper.map(centerModifyRequestDTO, Center.class); @@ -62,7 +61,7 @@ public void modifyCenter(String id, CenterModifyRequestDTO centerModifyRequestDT public void deleteCenter(String id) { Center center = centerRepository.findById(id) - .orElseThrow(() -> new CommonException(ErrorCode.CENTER_NOT_FOUND)); + .orElseThrow(() -> new SampleCommonException(SampleErrorCode.CENTER_NOT_FOUND)); center.setActive(false); center.setDeletedAt(getCurrentTime()); diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java index cf37dc2e..1d9e60af 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java @@ -8,7 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.contract.command.application.dto.request.ContractRegistRequestDTO; import stanl_2.final_backend.domain.contract.command.application.service.ContractService; @@ -34,15 +34,15 @@ public ContractController(ContractService contractService) { @Operation(summary = "계약서 등록 테스트") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) }) @PostMapping("{id}") - public ResponseEntity postTest(@PathVariable String id, - @RequestBody ContractRegistRequestDTO contractRegistRequestDTO) { + public ResponseEntity postTest(@PathVariable String id, + @RequestBody ContractRegistRequestDTO contractRegistRequestDTO) { contractRegistRequestDTO.setMemId(id); contractService.registerContract(contractRegistRequestDTO); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(SampleResponseMessage.builder() .httpStatus(200) .msg("성공") .result(null) From b3c7921ef0eaaa9594626ce0e992e92463de48e3 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 13 Nov 2024 13:28:18 +0900 Subject: [PATCH 035/563] =?UTF-8?q?=EA=B3=B5=EC=A7=80=EC=82=AC=ED=95=AD=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1,=20=EC=88=98=EC=A0=95,=20=EC=82=AD=EC=A0=9C(?= =?UTF-8?q?#26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/NoticeController.java | 79 +++++++++++++++++++ .../application/dto/NoticeRegistDTO.java | 9 +++ .../domain/aggragate/entity/Notice.java | 4 + .../service/NoticeCommandServiceImpl.java | 9 ++- .../BooleanToStringConverterConfig.java | 17 ++++ 5 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java create mode 100644 src/main/java/stanl_2/final_backend/global/config/BooleanToStringConverterConfig.java diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java new file mode 100644 index 00000000..b78cf30d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java @@ -0,0 +1,79 @@ +package stanl_2.final_backend.domain.notices.command.application.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; +import stanl_2.final_backend.domain.notices.command.application.dto.NoticeModifyDTO; +import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; +import stanl_2.final_backend.domain.notices.command.application.service.NoticeCommandService; +import stanl_2.final_backend.domain.notices.common.response.NoticeResponseMessage; + +@RestController("commandNoticeController") +@RequestMapping("/api/v1/notice") +public class NoticeController { + + private final NoticeCommandService noticeCommandService; + + @Autowired + public NoticeController(NoticeCommandService noticeCommandService) { + this.noticeCommandService = noticeCommandService; + } + + @Operation(summary = "공지사항 작성") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = NoticeResponseMessage.class))}) + }) + @PostMapping("") + public ResponseEntity postNotice(@RequestBody NoticeRegistDTO noticeRegistDTO){ + noticeCommandService.registerNotice(noticeRegistDTO); + + return ResponseEntity.ok(NoticeResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + + } + @Operation(summary = "공지사항 수정 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) + }) + @PutMapping("{id}") + public ResponseEntity modifyNotice(@PathVariable String id, + @RequestBody NoticeModifyDTO noticeModifyRequestDTO){ + noticeModifyRequestDTO.setId(id); + NoticeModifyDTO noticeModifyDTO = noticeCommandService.modifyNotice(id,noticeModifyRequestDTO); + + return ResponseEntity.ok(NoticeResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(noticeModifyDTO) + .build()); + } + + @Operation(summary = "공지사항 삭제 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = NoticeResponseMessage.class))}) + }) + @DeleteMapping("{id}") + public ResponseEntity deleteNotice(@PathVariable String id) { + + noticeCommandService.deleteNotice(id); + + return ResponseEntity.ok(NoticeResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java index 0aa49af6..766989d3 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java @@ -1,5 +1,14 @@ package stanl_2.final_backend.domain.notices.command.application.dto; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter public class NoticeRegistDTO { private String title; diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java index 01291d10..5ab8eae1 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java @@ -6,6 +6,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.BooleanToStringConverterConfig; import stanl_2.final_backend.global.config.PrefixGeneratorConfig; import java.time.ZoneId; @@ -50,6 +51,7 @@ public class Notice { private String deletedAt; @Column(name = "ACTIVE", nullable = false) + @Convert(converter = BooleanToStringConverterConfig.class) private Boolean active = true; @Column(name = "MEM_ID", nullable = false) @@ -71,4 +73,6 @@ private String getCurrentTime() { ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } + + } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java index 0b8f2120..4c2b5f6b 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java @@ -11,13 +11,12 @@ import stanl_2.final_backend.domain.notices.command.domain.repository.NoticeRepository; import stanl_2.final_backend.domain.notices.common.exception.NoticeCommonException; import stanl_2.final_backend.domain.notices.common.exception.NoticeErrorCode; -import stanl_2.final_backend.global.exception.CommonException; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; -@Service("commandSampleService") +@Service("commandNoticeService") public class NoticeCommandServiceImpl implements NoticeCommandService { private final NoticeRepository noticeRepository; @@ -64,6 +63,12 @@ public NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO) @Override @Transactional public void deleteNotice(String id) { + Notice notice = noticeRepository.findById(id) + .orElseThrow(()-> new NoticeCommonException(NoticeErrorCode.NOTICE_NOT_FOUND)); + + notice.setActive(false); + notice.setDeletedAt(getCurrentTimestamp()); + noticeRepository.save(notice); } } diff --git a/src/main/java/stanl_2/final_backend/global/config/BooleanToStringConverterConfig.java b/src/main/java/stanl_2/final_backend/global/config/BooleanToStringConverterConfig.java new file mode 100644 index 00000000..2a9ebd60 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/config/BooleanToStringConverterConfig.java @@ -0,0 +1,17 @@ +package stanl_2.final_backend.global.config; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; + +@Converter(autoApply = true) +public class BooleanToStringConverterConfig implements AttributeConverter { + @Override + public String convertToDatabaseColumn(Boolean attribute) { + return (attribute != null && attribute) ? "true" : "false"; + } + + @Override + public Boolean convertToEntityAttribute(String dbData) { + return "true".equalsIgnoreCase(dbData); + } +} From ec7bcd74ec46b9e557bab3b6a3086d1dcac3e492 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 13 Nov 2024 13:43:49 +0900 Subject: [PATCH 036/563] =?UTF-8?q?fix:=20=EC=88=98=EC=A0=95DTO=EA=B3=BC?= =?UTF-8?q?=EB=A0=A8=20=EC=88=98=EC=A0=95=20=EC=82=AC=ED=95=AD(#26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/application/controller/NoticeController.java | 2 +- .../notices/command/application/dto/NoticeModifyDTO.java | 5 ----- .../command/domain/service/NoticeCommandServiceImpl.java | 3 ++- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java index b78cf30d..8af294c8 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java @@ -49,7 +49,7 @@ public ResponseEntity postNotice(@RequestBody NoticeRegis @PutMapping("{id}") public ResponseEntity modifyNotice(@PathVariable String id, @RequestBody NoticeModifyDTO noticeModifyRequestDTO){ - noticeModifyRequestDTO.setId(id); + NoticeModifyDTO noticeModifyDTO = noticeCommandService.modifyNotice(id,noticeModifyRequestDTO); return ResponseEntity.ok(NoticeResponseMessage.builder() diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java index 4f2c2a17..e4695591 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java @@ -10,8 +10,6 @@ @Setter @Getter public class NoticeModifyDTO { - private String id; - private String title; private String tag; @@ -19,7 +17,4 @@ public class NoticeModifyDTO { private String classification; private String content; - - private String updatedAt; - } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java index 4c2b5f6b..3dc37420 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java @@ -47,9 +47,10 @@ public NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO) Notice notice = noticeRepository.findById(id) .orElseThrow(() -> new NoticeCommonException(NoticeErrorCode.NOTICE_NOT_FOUND)); - noticeModifyDTO.setId(id); Notice updateNotice = modelMapper.map(noticeModifyDTO, Notice.class); + updateNotice.setId(notice.getId()); + updateNotice.setMemberId(notice.getMemberId()); updateNotice.setCreatedAt(notice.getCreatedAt()); updateNotice.setActive(notice.getActive()); From 239de6d5a050339b32aa36403dc9eb4b414dca82 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Wed, 13 Nov 2024 14:58:45 +0900 Subject: [PATCH 037/563] =?UTF-8?q?fix:=20=ED=95=84=EC=9A=94=ED=95=9C=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=B4=88=EC=95=88=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=EC=99=84=EB=A3=8C(#17)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/aggregate/entity/Member.java | 6 ++ .../domain/aggregate/entity/MemberRole.java | 4 + ...hRepository.java => MemberRepository.java} | 3 +- .../service/AuthCommandServiceImpl.java | 21 ++++- .../security/config/ProdSecurityConfig.java | 82 +++++++++++++++++ ...ProdUsernamePwdAuthenticationProvider.java | 46 ++++++++++ .../security/events/AuthorizationEvents.java | 4 +- .../security/filter/CsrfCookieFilter.java | 39 ++++---- .../filter/JWTTokenGeneratorFilter.java | 70 +++++++++++++++ .../filter/JWTTokenValidatorFilter.java | 90 +++++++++++++++++++ .../security/service/MemberDetails.java | 61 +++++++++++++ .../service/MemberDetailsService.java | 33 +++++++ src/main/resources/application-prod.yml | 3 +- 13 files changed, 435 insertions(+), 27 deletions(-) rename src/main/java/stanl_2/final_backend/domain/member/command/domain/repository/{AuthRepository.java => MemberRepository.java} (65%) create mode 100644 src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java create mode 100644 src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java create mode 100644 src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java create mode 100644 src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java create mode 100644 src/main/java/stanl_2/final_backend/global/security/service/MemberDetails.java create mode 100644 src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsService.java diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java index 9cb8007f..436fc3ec 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java @@ -11,6 +11,8 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; @NoArgsConstructor @AllArgsConstructor @@ -92,6 +94,10 @@ public class Member { @Column(name = "ACTIVE", nullable = false) private Boolean active = true; + @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL) + @JoinColumn(name = "MEM_ID") + private List roles = new ArrayList<>(); + /* 설명. updatedAt 자동화 */ // Insert 되기 전에 실행 @PrePersist diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/MemberRole.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/MemberRole.java index 62b7fbb9..955634a7 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/MemberRole.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/MemberRole.java @@ -27,4 +27,8 @@ public class MemberRole { @Column(name = "MEM_ROL_NAME", nullable = false) private String role; + + @ManyToOne + @JoinColumn(name = "MEM_ID", nullable = false, insertable = false, updatable = false) + private Member member; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/repository/AuthRepository.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/repository/MemberRepository.java similarity index 65% rename from src/main/java/stanl_2/final_backend/domain/member/command/domain/repository/AuthRepository.java rename to src/main/java/stanl_2/final_backend/domain/member/command/domain/repository/MemberRepository.java index 4850abd3..a630dac8 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/repository/AuthRepository.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/repository/MemberRepository.java @@ -3,5 +3,6 @@ import org.springframework.data.jpa.repository.JpaRepository; import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.Member; -public interface AuthRepository extends JpaRepository { +public interface MemberRepository extends JpaRepository { + Member findByLoginId(String loginId); } diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java index ec0d5f17..14e15ee0 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java @@ -1,11 +1,14 @@ package stanl_2.final_backend.domain.member.command.domain.service; +import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.member.command.application.dto.SignupDTO; import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; -import stanl_2.final_backend.domain.member.command.domain.repository.AuthRepository; +import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.Member; +import stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -14,11 +17,15 @@ @Service("commandAuthService") public class AuthCommandServiceImpl implements AuthCommandService { - private final AuthRepository authRepository; + private final MemberRepository memberRepository; + private final PasswordEncoder passwordEncoder; + private final ModelMapper modelMapper; @Autowired - public AuthCommandServiceImpl(AuthRepository authRepository) { - this.authRepository = authRepository; + public AuthCommandServiceImpl(MemberRepository memberRepository, PasswordEncoder passwordEncoder, ModelMapper modelMapper) { + this.memberRepository = memberRepository; + this.passwordEncoder = passwordEncoder; + this.modelMapper = modelMapper; } private String getCurrentTimestamp() { @@ -30,5 +37,11 @@ private String getCurrentTimestamp() { @Transactional public void signup(SignupDTO signupDTO) { + String hashPwd = passwordEncoder.encode(signupDTO.getPassword()); + signupDTO.setPassword(hashPwd); + + Member registerMember = modelMapper.map(signupDTO, Member.class); + + memberRepository.save(registerMember); } } diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java new file mode 100644 index 00000000..7c7f3035 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -0,0 +1,82 @@ +package stanl_2.final_backend.global.security.config; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.password.CompromisedPasswordChecker; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.factory.PasswordEncoderFactories; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.password.HaveIBeenPwnedRestApiPasswordChecker; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; +import org.springframework.security.web.csrf.CookieCsrfTokenRepository; +import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import stanl_2.final_backend.global.security.filter.CsrfCookieFilter; +import stanl_2.final_backend.global.security.filter.JWTTokenGeneratorFilter; +import stanl_2.final_backend.global.security.filter.JWTTokenValidatorFilter; + +import java.util.Arrays; +import java.util.Collections; + +import static org.springframework.security.config.Customizer.withDefaults; + +@Configuration +@Profile("prod") +public class ProdSecurityConfig { + + @Bean + SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { + + CsrfTokenRequestAttributeHandler csrfTokenRequestAttributeHandler = new CsrfTokenRequestAttributeHandler(); + http.sessionManagement(sessionConfig -> sessionConfig.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .cors(corsConfig -> corsConfig.configurationSource(new CorsConfigurationSource() { + @Override + public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowedOrigins(Collections.singletonList("http://localhost:5173")); + config.setAllowedMethods(Collections.singletonList("*")); + config.setAllowCredentials(true); + config.setExposedHeaders(Collections.singletonList("*")); + config.setExposedHeaders(Arrays.asList("Authorization")); + config.setMaxAge(3600L); + return config; + } + })) + .csrf(csrfConfig -> csrfConfig.csrfTokenRequestHandler(csrfTokenRequestAttributeHandler) + .ignoringRequestMatchers("/api/v1/auth/signup", "/api/v1/auth/signin") + .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())) + // 필터 순서 그대로 + .addFilterBefore(new JWTTokenValidatorFilter(), BasicAuthenticationFilter.class) + .addFilterAfter(new JWTTokenGeneratorFilter(), BasicAuthenticationFilter.class) + .addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class) + .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()) + .authorizeHttpRequests((requests -> requests + + .requestMatchers("/api/v1/auth/signup", "/api/v1/auth/signin").permitAll() + .anyRequest().authenticated())); + http.formLogin(withDefaults()); + http.httpBasic(withDefaults()); + return http.build(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return PasswordEncoderFactories.createDelegatingPasswordEncoder(); + } + + @Bean + public CompromisedPasswordChecker compromisedPasswordChecker() { + return new HaveIBeenPwnedRestApiPasswordChecker(); + } + + @Bean + public AuthenticationManager authenticationManager(){ + return null; + } +} diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java new file mode 100644 index 00000000..ce8bdfd5 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java @@ -0,0 +1,46 @@ +package stanl_2.final_backend.global.security.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; +import stanl_2.final_backend.global.exception.CommonException; +import stanl_2.final_backend.global.exception.ErrorCode; + +@Component +@Profile("prod") +public class ProdUsernamePwdAuthenticationProvider implements AuthenticationProvider { + + private final UserDetailsService userDetailsService; + private final PasswordEncoder passwordEncoder; + + @Autowired + public ProdUsernamePwdAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) { + this.userDetailsService = userDetailsService; + this.passwordEncoder = passwordEncoder; + } + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + String username = authentication.getName(); + String pwd = authentication.getCredentials().toString(); + + UserDetails userDetails = userDetailsService.loadUserByUsername(username); + if(passwordEncoder.matches(pwd, userDetails.getPassword())) { + return new UsernamePasswordAuthenticationToken(userDetails, pwd, userDetails.getAuthorities()); + } else { + throw new CommonException(ErrorCode.LOGIN_FAILURE); + } + } + + @Override + public boolean supports(Class authentication) { + return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); + } +} diff --git a/src/main/java/stanl_2/final_backend/global/security/events/AuthorizationEvents.java b/src/main/java/stanl_2/final_backend/global/security/events/AuthorizationEvents.java index d9fa79a9..14aa1361 100644 --- a/src/main/java/stanl_2/final_backend/global/security/events/AuthorizationEvents.java +++ b/src/main/java/stanl_2/final_backend/global/security/events/AuthorizationEvents.java @@ -4,6 +4,8 @@ import org.springframework.context.event.EventListener; import org.springframework.security.authorization.event.AuthorizationDeniedEvent; import org.springframework.stereotype.Component; +import stanl_2.final_backend.global.exception.CommonException; +import stanl_2.final_backend.global.exception.ErrorCode; @Component @Slf4j @@ -12,6 +14,6 @@ public class AuthorizationEvents { public void onFailure(AuthorizationDeniedEvent deniedEvent){ log.error("권한 없음 유저: {} due to: {}", deniedEvent.getAuthentication().get().getName() , deniedEvent.getAuthorizationDecision().toString()); - throw new CommonException(FORBIDDEN_ROLE); + throw new CommonException(ErrorCode.FORBIDDEN_ROLE); } } diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java index 8f075a21..9931de8f 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java @@ -12,8 +12,6 @@ import stanl_2.final_backend.global.exception.ErrorCode; import java.io.IOException; -import java.util.Arrays; -import java.util.Optional; @Slf4j public class CsrfCookieFilter extends OncePerRequestFilter { @@ -21,36 +19,37 @@ public class CsrfCookieFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - // CsrfToken은 Spring Security에서 CSRF 보호를 위한 토큰 - CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName()); + CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfCookieFilter.class.getName()); - if (csrfToken != null) { - // 기존 쿠키 확인 - Cookie[] cookies = request.getCookies(); - Optional csrfCookie = Optional.empty(); + if(csrfToken != null) { + Cookie csrfCookie = null; + // 요청에서 쿠키 배열 가져오기 (null 체크 추가) + Cookie[] cookies = request.getCookies(); if (cookies != null) { - csrfCookie = Arrays.stream(cookies) - .filter(cookie -> "XSRF-TOKEN".equals(cookie.getName())) - .findFirst(); + // 쿠키 배열에서 "XSRF-TOKEN" 이름을 가진 쿠키를 찾음 + for (Cookie cookie : cookies) { + if ("XSRF-TOKEN".equals(cookie.getName())) { + csrfCookie = cookie; + break; // 쿠키를 찾으면 반복문 종료 + } + } } - // 쿠키가 존재하지 않거나 토큰이 변경된 경우 쿠키 업데이트 - if (!csrfCookie.isPresent() || !csrfCookie.get().getValue().equals(csrfToken.getToken())) { - // 새로운 CSRF 토큰을 쿠키로 설정 + // 쿠키가 없거나, 토큰이 변경된 경우 새로운 쿠키 생성 + if (csrfCookie == null || !csrfCookie.getValue().equals(csrfToken.getToken())) { Cookie newCsrfCookie = new Cookie("XSRF-TOKEN", csrfToken.getToken()); newCsrfCookie.setPath("/"); - newCsrfCookie.setHttpOnly(false); // JS에서도 접근할 수 있도록 설정 - newCsrfCookie.setSecure(request.isSecure()); // HTTPS인 경우에만 secure 설정 - newCsrfCookie.setMaxAge(-1); // 세션이 종료될 때 쿠키가 삭제되도록 설정 - - response.addCookie(newCsrfCookie); // 응답에 쿠키 추가 + newCsrfCookie.setHttpOnly(false); // JavaScript에서 접근 가능하도록 설정 + newCsrfCookie.setSecure(request.isSecure()); // HTTPS 요청에서만 쿠키 전송 + newCsrfCookie.setMaxAge(-1); // 세션 종료 시 삭제 + response.addCookie(newCsrfCookie); // 응답에 쿠키 추가 } } else { throw new CommonException(ErrorCode.NOT_FOUND_CSRF_TOKEN); } - + // 다음 필터로 요청 전달 filterChain.doFilter(request, response); } } diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java new file mode 100644 index 00000000..7b8048e6 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java @@ -0,0 +1,70 @@ +package stanl_2.final_backend.global.security.filter; + +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; +import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.Member; + +import javax.crypto.SecretKey; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.stream.Collectors; + +@Slf4j +public class JWTTokenGeneratorFilter extends OncePerRequestFilter { + + @Value("${jwt.secret-key}") + private String jwtSecretKey; + + @Value("${jwt.header}") + private String jwtHeader; + + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + + if(authentication != null && authentication.isAuthenticated()){ + + Member member = (Member) authentication.getPrincipal(); + + // 비밀키 생성 + SecretKey secretKey = Keys.hmacShaKeyFor(jwtSecretKey.getBytes(StandardCharsets.UTF_8)); + + String jwt = Jwts.builder() + .setIssuer("STANL2") + .setSubject("JWT Token") + .claim("username", authentication.getName()) + .claim("id", member.getId()) + .claim("authorities", authentication.getAuthorities().stream() + .map(GrantedAuthority::getAuthority).collect(Collectors.joining(","))) + .setIssuedAt(new Date()) + .setExpiration(new Date((new Date()).getTime() + 30000000)) + .signWith(secretKey) + .compact(); + + // JWT 토큰을 응답 헤더에 추가 + response.setHeader(jwtHeader, jwt); + log.info("Generated JWT: {}", jwt); + } + filterChain.doFilter(request, response); + } + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { + return !request.getServletPath().equals("/api/v1/auth/signin"); + } +} diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java new file mode 100644 index 00000000..2483f397 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java @@ -0,0 +1,90 @@ +package stanl_2.final_backend.global.security.filter; + + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; +import stanl_2.final_backend.global.exception.CommonException; +import stanl_2.final_backend.global.exception.ErrorCode; + +import javax.crypto.SecretKey; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +@Slf4j +public class JWTTokenValidatorFilter extends OncePerRequestFilter { + + // 환경 변수 주입 + @Value("${jwt.secret-key}") + private String jwtSecretKey; + + @Value("${jwt.header}") + private String jwtHeader; + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + // 요청 헤더에서 JWT 토큰 가져오기 + String jwt = request.getHeader(jwtHeader); + + if (jwt != null && jwt.startsWith("Bearer ")) { + try { + // 비밀키를 사용하여 JWT 토큰을 검증 + SecretKey secretKey = Keys.hmacShaKeyFor(jwtSecretKey.getBytes(StandardCharsets.UTF_8)); + String jwtToken = jwt.substring(7); // "Bearer " 부분 제거 + + // JWT 토큰 파싱 및 검증 + Claims claims = Jwts.parserBuilder() + .setSigningKey(secretKey) + .build() + .parseClaimsJws(jwtToken) + .getBody(); + + // 클레임에서 사용자 정보 및 권한 추출 + String id = claims.get("id", String.class); + String username = claims.get("username", String.class); + String authorities = claims.get("authorities", String.class); + + // SecurityContext에 인증 정보 설정 + Authentication authentication = new UsernamePasswordAuthenticationToken( + username, null, + AuthorityUtils.commaSeparatedStringToAuthorityList(authorities) + ); + SecurityContextHolder.getContext().setAuthentication(authentication); + + log.info("Authenticated user: {}", username); + + // 추가적으로, 추출된 정보를 request 속성에 설정 + request.setAttribute("id", id); + request.setAttribute("authorities", authorities); + + } catch (Exception exception) { + log.error("Invalid JWT Token", exception); + throw new CommonException(ErrorCode.INVALID_TOKEN_ERROR); + } + } + + // 다음 필터로 요청 전달 + filterChain.doFilter(request, response); + } + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { + // 특정 경로에서는 필터를 적용하지 않음 + String path = request.getServletPath(); + return path.equals("/api/v1/member/signin") || + path.equals("/api/v1/member/signup"); + } +} diff --git a/src/main/java/stanl_2/final_backend/global/security/service/MemberDetails.java b/src/main/java/stanl_2/final_backend/global/security/service/MemberDetails.java new file mode 100644 index 00000000..25b83e03 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/security/service/MemberDetails.java @@ -0,0 +1,61 @@ +package stanl_2.final_backend.global.security.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.Member; + +import java.util.Collection; +import java.util.List; + +public class MemberDetails implements UserDetails { + + private final Member member; + + public MemberDetails(Member member) { + this.member = member; + } + + public Member getMember() { + return member; + } + + @Override + public Collection getAuthorities() { + List authorities = member.getRoles().stream() + .map(role -> new SimpleGrantedAuthority(role.getRole())) + .toList(); + return authorities; + } + + @Override + public String getPassword() { + return member.getPassword(); + } + + @Override + public String getUsername() { + return member.getLoginId(); + } + + @Override + public boolean isAccountNonExpired() { + return member.getActive(); + } + + @Override + public boolean isAccountNonLocked() { + return member.getActive(); + } + + @Override + public boolean isCredentialsNonExpired() { + return member.getActive(); + } + + @Override + public boolean isEnabled() { + return member.getActive(); + } +} diff --git a/src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsService.java b/src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsService.java new file mode 100644 index 00000000..c426c821 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsService.java @@ -0,0 +1,33 @@ +package stanl_2.final_backend.global.security.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.Member; +import stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; + +@Service(value = "MemberDetailsService") +public class MemberDetailsService implements UserDetailsService { + + private final MemberRepository memberRepository; + + @Autowired + public MemberDetailsService(MemberRepository memberRepository) { + this.memberRepository = memberRepository; + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + // JPA를 사용하여 로그인 ID로 회원 정보 조회 + Member member = memberRepository.findByLoginId(username); + + if (member == null) { + throw new UsernameNotFoundException("유저 정보가 없습니다" + username); + } + + // Member를 기반으로 UserDetails 생성 + return new MemberDetails(member); + } +} diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index bdb395a7..23faa6a8 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -5,4 +5,5 @@ spring: logging: level: - org.springframework.security: DEBUG +# org.springframework.security: DEBUG + org.springframework.security: TRACE \ No newline at end of file From 2239d1f5789e81a80e7a87ef023a9f269833cc89 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Wed, 13 Nov 2024 15:56:31 +0900 Subject: [PATCH 038/563] =?UTF-8?q?feat:=201=EC=B0=A8=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C(#1?= =?UTF-8?q?7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/application/dto/SignupDTO.java | 2 +- .../infrastructure/service/SampleService.java | 10 ----- .../query/controller/AuthController.java | 2 +- .../security/config/ProdSecurityConfig.java | 45 ++++++++++--------- .../security/filter/CsrfCookieFilter.java | 7 +++ .../filter/JWTTokenValidatorFilter.java | 4 +- 6 files changed, 34 insertions(+), 36 deletions(-) delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/infrastructure/service/SampleService.java diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java index 32281c78..949c73fc 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java @@ -8,7 +8,7 @@ @Getter @ToString public class SignupDTO { - private String id; +// private String id; private String loginId; private String password; private String name; diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/infrastructure/service/SampleService.java b/src/main/java/stanl_2/final_backend/domain/member/command/infrastructure/service/SampleService.java deleted file mode 100644 index 4982dcd5..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/command/infrastructure/service/SampleService.java +++ /dev/null @@ -1,10 +0,0 @@ -package stanl_2.final_backend.domain.member.command.infrastructure.service; - -import org.springframework.stereotype.Service; - -@Service("infrastructureSampleService") -public class SampleService { - - // 외부 시스템과의 통신이 필요하면 이 폴더에 파일 생성하여 통신 - -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/controller/AuthController.java b/src/main/java/stanl_2/final_backend/domain/member/query/controller/AuthController.java index ff7807c5..622f8d42 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/controller/AuthController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/controller/AuthController.java @@ -5,7 +5,7 @@ import org.springframework.web.bind.annotation.RestController; import stanl_2.final_backend.domain.member.query.service.AuthService; -@RestController(value = "querySampleController") +@RestController(value = "queryAuthController") @RequestMapping("/api/v1/auth") public class AuthController { diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index 7c7f3035..19908902 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -1,6 +1,5 @@ package stanl_2.final_backend.global.security.config; -import jakarta.servlet.http.HttpServletRequest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; @@ -14,6 +13,7 @@ import org.springframework.security.web.authentication.password.HaveIBeenPwnedRestApiPasswordChecker; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.csrf.CookieCsrfTokenRepository; +import org.springframework.security.web.csrf.CsrfFilter; import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; @@ -34,37 +34,38 @@ public class ProdSecurityConfig { SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { CsrfTokenRequestAttributeHandler csrfTokenRequestAttributeHandler = new CsrfTokenRequestAttributeHandler(); + http.sessionManagement(sessionConfig -> sessionConfig.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .cors(corsConfig -> corsConfig.configurationSource(new CorsConfigurationSource() { - @Override - public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { - CorsConfiguration config = new CorsConfiguration(); - config.setAllowedOrigins(Collections.singletonList("http://localhost:5173")); - config.setAllowedMethods(Collections.singletonList("*")); - config.setAllowCredentials(true); - config.setExposedHeaders(Collections.singletonList("*")); - config.setExposedHeaders(Arrays.asList("Authorization")); - config.setMaxAge(3600L); - return config; - } - })) + .cors(corsConfig -> corsConfig.configurationSource(corsConfigurationSource())) .csrf(csrfConfig -> csrfConfig.csrfTokenRequestHandler(csrfTokenRequestAttributeHandler) - .ignoringRequestMatchers("/api/v1/auth/signup", "/api/v1/auth/signin") + .ignoringRequestMatchers("/api/v1/auth/**", "/api/v1/sample/**") .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())) - // 필터 순서 그대로 + .authorizeHttpRequests(auth -> auth + .requestMatchers("/api/v1/auth/**", "/api/v1/sample/**").permitAll() + .anyRequest().authenticated()) + // 필터 순서: JWT 검증 -> CSRF -> JWT 생성 .addFilterBefore(new JWTTokenValidatorFilter(), BasicAuthenticationFilter.class) .addFilterAfter(new JWTTokenGeneratorFilter(), BasicAuthenticationFilter.class) - .addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class) - .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()) - .authorizeHttpRequests((requests -> requests - - .requestMatchers("/api/v1/auth/signup", "/api/v1/auth/signin").permitAll() - .anyRequest().authenticated())); + .addFilterAfter(new CsrfCookieFilter(), CsrfFilter.class) + .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()); http.formLogin(withDefaults()); http.httpBasic(withDefaults()); return http.build(); } + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowedOrigins(Collections.singletonList("http://localhost:5173")); + config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); + config.setAllowCredentials(true); + config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "X-XSRF-TOKEN")); + config.setExposedHeaders(Collections.singletonList("Authorization")); + config.setMaxAge(3600L); + + return request -> config; + } + @Bean public PasswordEncoder passwordEncoder() { return PasswordEncoderFactories.createDelegatingPasswordEncoder(); diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java index 9931de8f..07756bf5 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java @@ -52,4 +52,11 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse // 다음 필터로 요청 전달 filterChain.doFilter(request, response); } + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { + String path = request.getServletPath(); + return path.startsWith("/api/v1/auth") || + path.startsWith("/api/v1/sample"); + } } diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java index 2483f397..aab05ea2 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java @@ -84,7 +84,7 @@ protected void doFilterInternal(HttpServletRequest request, protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { // 특정 경로에서는 필터를 적용하지 않음 String path = request.getServletPath(); - return path.equals("/api/v1/member/signin") || - path.equals("/api/v1/member/signup"); + return path.startsWith("/api/v1/auth") || + path.startsWith("/api/v1/sample"); } } From 38a4f2fe1665ef023f85709630b2c18963b13be0 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Wed, 13 Nov 2024 16:36:31 +0900 Subject: [PATCH 039/563] =?UTF-8?q?refactor:=20=EA=B3=84=EC=95=BD=EC=84=9C?= =?UTF-8?q?=20Command=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20(#21)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ContractCommandController.java | 119 ++++++++++++++++++ .../controller/ContractController.java | 51 -------- ...RequestDTO.java => ContractModifyDTO.java} | 4 +- ...RequestDTO.java => ContractRegistDTO.java} | 4 +- .../response/ContractModifyResponseDTO.java | 31 ----- ...rvice.java => ContractCommandService.java} | 10 +- ...l.java => ContractCommandServiceImpl.java} | 27 ++-- ...tion.java => ContractCommonException.java} | 4 +- ...{ErrorCode.java => ContractErrorCode.java} | 2 +- ...se.java => ContractExceptionResponse.java} | 8 +- ...sage.java => ContractResponseMessage.java} | 2 +- .../query/service/ContractServiceImpl.java | 6 +- 12 files changed, 152 insertions(+), 116 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractCommandController.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java rename src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/{request/ContractModifyRequestDTO.java => ContractModifyDTO.java} (94%) rename src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/{request/ContractRegistRequestDTO.java => ContractRegistDTO.java} (93%) delete mode 100644 src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/response/ContractModifyResponseDTO.java rename src/main/java/stanl_2/final_backend/domain/contract/command/application/service/{ContractService.java => ContractCommandService.java} (54%) rename src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/{ContractServiceImpl.java => ContractCommandServiceImpl.java} (77%) rename src/main/java/stanl_2/final_backend/domain/contract/common/exception/{CommonException.java => ContractCommonException.java} (73%) rename src/main/java/stanl_2/final_backend/domain/contract/common/exception/{ErrorCode.java => ContractErrorCode.java} (98%) rename src/main/java/stanl_2/final_backend/domain/contract/common/exception/{ExceptionResponse.java => ContractExceptionResponse.java} (61%) rename src/main/java/stanl_2/final_backend/domain/contract/common/response/{ResponseMessage.java => ContractResponseMessage.java} (85%) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractCommandController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractCommandController.java new file mode 100644 index 00000000..d0abf091 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractCommandController.java @@ -0,0 +1,119 @@ +package stanl_2.final_backend.domain.contract.command.application.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; +import stanl_2.final_backend.domain.contract.command.application.dto.ContractModifyDTO; +import stanl_2.final_backend.domain.contract.command.application.dto.ContractRegistDTO; +import stanl_2.final_backend.domain.contract.command.application.service.ContractCommandService; +import stanl_2.final_backend.domain.contract.common.response.ContractResponseMessage; + +@RestController("contractController") +@RequestMapping("/api/v1/contract") +public class ContractCommandController { + + private final ContractCommandService contractCommandService; + + @Autowired + public ContractCommandController(ContractCommandService contractCommandService) { + this.contractCommandService = contractCommandService; + } + + /** + * [POST] http://localhost:7777/api/v1/contract + * Request + * { + * "name": "tes1", + * "num": 123 + * } + * */ + @Operation(summary = "계약서 등록 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) + }) + @PostMapping("{id}") + public ResponseEntity postTest(@PathVariable String id, + @RequestBody ContractRegistDTO contractRegistRequestDTO) { + contractRegistRequestDTO.setMemId(id); + contractCommandService.registerContract(contractRegistRequestDTO); + + return ResponseEntity.ok(SampleResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + } + + /** + * [PUT] http://localhost:8080/api/v1/contract/CON_000000011 + * Request + * { + * "name": "계약서 수정", + * "custName": "John Doe", + * "custIdenNo": "123456-7890123", + * "custAddrress": "123 Main Street, City, Country", + * "custEmail": "johndoe@example.com", + * "custPhone": "+1-234-567-8901", + * "compName": "Doe Industries", + * "custCla": "Premium", + * "custPurCond": "Full Payment", + * "seriNum": "A1B2C3D4", + * "seleOpti": "Extended Warranty", + * "downPay": 10000, + * "intePay": 500, + * "remPay": 15000, + * "consPay": 5000, + * "delvDate": "2024-12-15", + * "delvLoc": "Warehouse No. 3, Industrial Park", + * "state": "WAIT", + * "noOfVeh": "2", + * "memId": "MEM_000000001" + * } + * */ + @Operation(summary = "계약서 수정 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) + }) + @PutMapping("{id}") + public ResponseEntity putContract(@PathVariable String id, + @RequestBody ContractModifyDTO contractModifyRequestDTO) { + + contractModifyRequestDTO.setId(id); + ContractModifyDTO contractModifyDTO = contractCommandService.modifyContract(contractModifyRequestDTO); + + return ResponseEntity.ok(ContractResponseMessage.builder() + .httpStatus(200) + .msg("계약서가 수정되었습니다.") + .result(contractModifyDTO) + .build()); + } + + /** + * [DELETE] http://localhost:7777/api/v1/sample?mem_id=SAM_000001 + * */ + @Operation(summary = "샘플 삭제 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) + }) + @DeleteMapping("{id}") + public ResponseEntity deleteContract(@PathVariable String id) { + + // 회원 아이디 받아와야 함 + contractCommandService.deleteContract(id); + + return ResponseEntity.ok(ContractResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java deleted file mode 100644 index 1d9e60af..00000000 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java +++ /dev/null @@ -1,51 +0,0 @@ -package stanl_2.final_backend.domain.contract.command.application.controller; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; -import stanl_2.final_backend.domain.contract.command.application.dto.request.ContractRegistRequestDTO; -import stanl_2.final_backend.domain.contract.command.application.service.ContractService; - -@RestController("contractController") -@RequestMapping("/api/v1/contract") -public class ContractController { - - private final ContractService contractService; - - @Autowired - public ContractController(ContractService contractService) { - this.contractService = contractService; - } - - /** - * [POST] http://localhost:7777/api/v1/contract - * Request - * { - * "name": "tes1", - * "num": 123 - * } - * */ - @Operation(summary = "계약서 등록 테스트") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) - }) - @PostMapping("{id}") - public ResponseEntity postTest(@PathVariable String id, - @RequestBody ContractRegistRequestDTO contractRegistRequestDTO) { - contractRegistRequestDTO.setMemId(id); - contractService.registerContract(contractRegistRequestDTO); - - return ResponseEntity.ok(SampleResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(null) - .build()); - } -} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractModifyRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java similarity index 94% rename from src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractModifyRequestDTO.java rename to src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java index a61f7b72..e4594071 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractModifyRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java @@ -1,4 +1,4 @@ -package stanl_2.final_backend.domain.contract.command.application.dto.request; +package stanl_2.final_backend.domain.contract.command.application.dto; import lombok.*; @@ -7,7 +7,7 @@ @Setter @Getter @ToString -public class ContractModifyRequestDTO { +public class ContractModifyDTO { private String id; private String name; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractRegistRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java similarity index 93% rename from src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractRegistRequestDTO.java rename to src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java index f24d7255..0ca22c1c 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/request/ContractRegistRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java @@ -1,4 +1,4 @@ -package stanl_2.final_backend.domain.contract.command.application.dto.request; +package stanl_2.final_backend.domain.contract.command.application.dto; import lombok.*; @@ -7,7 +7,7 @@ @Setter @Getter @ToString -public class ContractRegistRequestDTO { +public class ContractRegistDTO { private String name; private String custName; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/response/ContractModifyResponseDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/response/ContractModifyResponseDTO.java deleted file mode 100644 index f4f19a6b..00000000 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/response/ContractModifyResponseDTO.java +++ /dev/null @@ -1,31 +0,0 @@ -package stanl_2.final_backend.domain.contract.command.application.dto.response; - -import lombok.*; - -@AllArgsConstructor -@NoArgsConstructor -@Setter -@Getter -@ToString -public class ContractModifyResponseDTO { - private String id; - private String name; - private String custName; - private String custIdenNo; - private String custAddrress; - private String custEmail; - private String custPhone; - private String compName; - private String custCla; - private String custPurCond; - private String seriNum; - private String seleOpti; - private Integer downPay; - private Integer intePay; - private Integer remPay; - private Integer consPay; - private String delvDate; - private String delvLoc; - private String state; - private String noOfVeh; -} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractService.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java similarity index 54% rename from src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractService.java rename to src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java index e93320cb..6c9da8b3 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractService.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java @@ -1,13 +1,13 @@ package stanl_2.final_backend.domain.contract.command.application.service; -import stanl_2.final_backend.domain.contract.command.application.dto.request.ContractModifyRequestDTO; -import stanl_2.final_backend.domain.contract.command.application.dto.request.ContractRegistRequestDTO; +import stanl_2.final_backend.domain.contract.command.application.dto.ContractModifyDTO; +import stanl_2.final_backend.domain.contract.command.application.dto.ContractRegistDTO; import stanl_2.final_backend.domain.contract.command.application.dto.response.ContractModifyResponseDTO; -public interface ContractService { - void registerContract(ContractRegistRequestDTO contractRegistRequestDTO); +public interface ContractCommandService { + void registerContract(ContractRegistDTO contractRegistRequestDTO); - ContractModifyResponseDTO modifyContract(ContractModifyRequestDTO contractModifyRequestDTO); + ContractModifyDTO modifyContract(ContractModifyDTO contractModifyRequestDTO); void deleteContract(String id); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java similarity index 77% rename from src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java rename to src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index a38683cc..93467e37 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -3,27 +3,26 @@ import org.modelmapper.ModelMapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.contract.command.application.dto.request.ContractModifyRequestDTO; -import stanl_2.final_backend.domain.contract.command.application.dto.request.ContractRegistRequestDTO; +import stanl_2.final_backend.domain.contract.command.application.dto.ContractModifyDTO; +import stanl_2.final_backend.domain.contract.command.application.dto.ContractRegistDTO; import stanl_2.final_backend.domain.contract.command.application.dto.response.ContractModifyResponseDTO; -import stanl_2.final_backend.domain.contract.command.application.service.ContractService; +import stanl_2.final_backend.domain.contract.command.application.service.ContractCommandService; import stanl_2.final_backend.domain.contract.command.domain.aggregate.entity.Contract; import stanl_2.final_backend.domain.contract.command.domain.repository.ContractRepository; -import stanl_2.final_backend.domain.contract.common.exception.CommonException; -import stanl_2.final_backend.domain.contract.common.exception.ErrorCode; +import stanl_2.final_backend.domain.contract.common.exception.ContractCommonException; +import stanl_2.final_backend.domain.contract.common.exception.ContractErrorCode; -import java.sql.Timestamp; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @Service("contractServiceImpl") -public class ContractServiceImpl implements ContractService { +public class ContractCommandServiceImpl implements ContractCommandService { private final ContractRepository contractRepository; private final ModelMapper modelMapper; - public ContractServiceImpl(ContractRepository contractRepository, ModelMapper modelMapper) { + public ContractCommandServiceImpl(ContractRepository contractRepository, ModelMapper modelMapper) { this.contractRepository = contractRepository; this.modelMapper = modelMapper; } @@ -35,7 +34,7 @@ private String getCurrentTime() { @Override @Transactional - public void registerContract(ContractRegistRequestDTO contractRegistRequestDTO) { + public void registerContract(ContractRegistDTO contractRegistRequestDTO) { // 회원인지 확인여부 및 값 가져오기 // 일련번호로 제품테이블의 총식별번호 찾아서 제품 가져오기 @@ -52,7 +51,7 @@ public void registerContract(ContractRegistRequestDTO contractRegistRequestDTO) @Override @Transactional - public ContractModifyResponseDTO modifyContract(ContractModifyRequestDTO contractModifyRequestDTO) { + public ContractModifyDTO modifyContract(ContractModifyDTO contractModifyRequestDTO) { // 회원인지 확인여부 및 값 가져오기 @@ -65,7 +64,7 @@ public ContractModifyResponseDTO modifyContract(ContractModifyRequestDTO contrac // 가져온 고객 정보에 수정된 값 넣기 Contract contract = contractRepository.findById(contractModifyRequestDTO.getId()) - .orElseThrow(() -> new CommonException(ErrorCode.CONTRACT_NOT_FOUND)); + .orElseThrow(() -> new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND)); Contract updateContract = modelMapper.map(contractModifyRequestDTO, Contract.class); updateContract.setCreatedAt(contract.getCreatedAt()); @@ -77,9 +76,9 @@ public ContractModifyResponseDTO modifyContract(ContractModifyRequestDTO contrac contractRepository.save(updateContract); - ContractModifyResponseDTO contractModifyResponseDTO = modelMapper.map(updateContract, ContractModifyResponseDTO.class); + ContractModifyDTO contractModifyDTO = modelMapper.map(updateContract, ContractModifyDTO.class); - return contractModifyResponseDTO; + return contractModifyDTO; } @Override @@ -89,7 +88,7 @@ public void deleteContract(String id) { // 회원 확인 Contract contract = contractRepository.findById(id) - .orElseThrow(() -> new CommonException(ErrorCode.CONTRACT_NOT_FOUND)); + .orElseThrow(() -> new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND)); contract.setActive(false); contract.setDeletedAt(getCurrentTime()); diff --git a/src/main/java/stanl_2/final_backend/domain/contract/common/exception/CommonException.java b/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ContractCommonException.java similarity index 73% rename from src/main/java/stanl_2/final_backend/domain/contract/common/exception/CommonException.java rename to src/main/java/stanl_2/final_backend/domain/contract/common/exception/ContractCommonException.java index 55d24ddd..da28b8c1 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/common/exception/CommonException.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ContractCommonException.java @@ -5,8 +5,8 @@ @Getter @RequiredArgsConstructor -public class CommonException extends RuntimeException { - private final ErrorCode errorCode; +public class ContractCommonException extends RuntimeException { + private final ContractErrorCode errorCode; // 에러 발생시 ErroCode 별 메시지 @Override diff --git a/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ErrorCode.java b/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ContractErrorCode.java similarity index 98% rename from src/main/java/stanl_2/final_backend/domain/contract/common/exception/ErrorCode.java rename to src/main/java/stanl_2/final_backend/domain/contract/common/exception/ContractErrorCode.java index d20039d7..bab81813 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ContractErrorCode.java @@ -6,7 +6,7 @@ @Getter @AllArgsConstructor -public enum ErrorCode { +public enum ContractErrorCode { /** * 400(Bad Request) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ContractExceptionResponse.java similarity index 61% rename from src/main/java/stanl_2/final_backend/domain/contract/common/exception/ExceptionResponse.java rename to src/main/java/stanl_2/final_backend/domain/contract/common/exception/ContractExceptionResponse.java index 70ac42a9..1f7f9b23 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ExceptionResponse.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ContractExceptionResponse.java @@ -4,19 +4,19 @@ import org.springframework.http.HttpStatus; @Getter -public class ExceptionResponse { +public class ContractExceptionResponse { private final Integer code; private final String msg; private final HttpStatus httpStatus; - public ExceptionResponse(ErrorCode errorCode) { + public ContractExceptionResponse(ContractErrorCode errorCode) { this.code = errorCode.getCode(); this.msg = errorCode.getMsg(); this.httpStatus = errorCode.getHttpStatus(); } - public static ExceptionResponse of(ErrorCode errorCode) { - return new ExceptionResponse(errorCode); + public static ContractExceptionResponse of(ContractErrorCode errorCode) { + return new ContractExceptionResponse(errorCode); } } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/common/response/ResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/contract/common/response/ContractResponseMessage.java similarity index 85% rename from src/main/java/stanl_2/final_backend/domain/contract/common/response/ResponseMessage.java rename to src/main/java/stanl_2/final_backend/domain/contract/common/response/ContractResponseMessage.java index f9d8d77e..bee55741 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/common/response/ResponseMessage.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/common/response/ContractResponseMessage.java @@ -7,7 +7,7 @@ @Builder @Getter @Setter -public class ResponseMessage { +public class ContractResponseMessage { private int httpStatus; private String msg; private Object result; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractServiceImpl.java index 000dbed5..11469837 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractServiceImpl.java @@ -2,8 +2,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import stanl_2.final_backend.domain.contract.common.exception.CommonException; -import stanl_2.final_backend.domain.contract.common.exception.ErrorCode; +import stanl_2.final_backend.domain.contract.common.exception.ContractCommonException; +import stanl_2.final_backend.domain.contract.common.exception.ContractErrorCode; import stanl_2.final_backend.domain.contract.query.dto.ContractDTO; import stanl_2.final_backend.domain.contract.query.repository.ContractMapper; @@ -25,7 +25,7 @@ public ContractDTO selectDetailContract(ContractDTO contractDTO) { ContractDTO responseContract = contractMapper.findContractByIdAndMemId(contractDTO); if (responseContract == null) { - throw new CommonException(ErrorCode.CONTRACT_NOT_FOUND); + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); } return responseContract; From fca487a47b13bab9f4d6d78402ef895a1b9e83d0 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Wed, 13 Nov 2024 19:24:23 +0900 Subject: [PATCH 040/563] =?UTF-8?q?feat:=201=EC=B0=A8=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20refreshToken,=20accessToken=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=EC=99=84=EB=A3=8C(#34)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AuthController.java | 25 ++++++- .../application/dto/SigninRequestDTO.java | 13 ++++ .../application/dto/SigninResponseDTO.java | 13 ++++ .../command/application/dto/SignupDTO.java | 1 - .../service/AuthCommandService.java | 4 + .../service/AuthCommandServiceImpl.java | 73 ++++++++++++++++++- .../security/config/ProdSecurityConfig.java | 13 +++- .../filter/JWTTokenGeneratorFilter.java | 46 ++++++------ .../security/service/MemberDetails.java | 1 - src/main/resources/application.yml | 2 +- 10 files changed, 157 insertions(+), 34 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninRequestDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninResponseDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java index 6a696e52..6ed6773a 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java @@ -7,10 +7,9 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; -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.RestController; +import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.member.command.application.dto.SigninRequestDTO; +import stanl_2.final_backend.domain.member.command.application.dto.SigninResponseDTO; import stanl_2.final_backend.domain.member.command.application.dto.SignupDTO; import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; import stanl_2.final_backend.domain.member.common.response.ResponseMessage; @@ -43,5 +42,23 @@ public ResponseEntity signup(@RequestBody SignupDTO signupDTO){ .build()); } + @Operation(summary = "로그인") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + }) + @PostMapping("signin") + public ResponseEntity signin(@RequestBody SigninRequestDTO signinRequestDTO){ + + // 로그인 서비스 호출하여 Access Token과 Refresh Token 발급 + SigninResponseDTO response = authCommandService.signin(signinRequestDTO); + + return ResponseEntity.ok(ResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(response) + .build()); + } + } diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninRequestDTO.java new file mode 100644 index 00000000..230ec928 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninRequestDTO.java @@ -0,0 +1,13 @@ +package stanl_2.final_backend.domain.member.command.application.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class SigninRequestDTO { + private String loginId; + private String password; +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninResponseDTO.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninResponseDTO.java new file mode 100644 index 00000000..bbde843d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninResponseDTO.java @@ -0,0 +1,13 @@ +package stanl_2.final_backend.domain.member.command.application.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class SigninResponseDTO { + private String accessToken; + private String refreshToken; +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java index 949c73fc..fd2e8078 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java @@ -8,7 +8,6 @@ @Getter @ToString public class SignupDTO { -// private String id; private String loginId; private String password; private String name; diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java index ebffda74..4d90b089 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java @@ -1,7 +1,11 @@ package stanl_2.final_backend.domain.member.command.application.service; +import stanl_2.final_backend.domain.member.command.application.dto.SigninRequestDTO; +import stanl_2.final_backend.domain.member.command.application.dto.SigninResponseDTO; import stanl_2.final_backend.domain.member.command.application.dto.SignupDTO; public interface AuthCommandService { void signup(SignupDTO signupDTO); + + SigninResponseDTO signin(SigninRequestDTO signinRequestDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java index 14e15ee0..0e6b2d3c 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java @@ -1,31 +1,52 @@ package stanl_2.final_backend.domain.member.command.domain.service; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.GrantedAuthority; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.member.command.application.dto.SigninRequestDTO; +import stanl_2.final_backend.domain.member.command.application.dto.SigninResponseDTO; import stanl_2.final_backend.domain.member.command.application.dto.SignupDTO; import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.Member; import stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; +import stanl_2.final_backend.global.exception.CommonException; +import stanl_2.final_backend.global.exception.ErrorCode; +import stanl_2.final_backend.global.security.service.MemberDetails; +import javax.crypto.SecretKey; +import java.nio.charset.StandardCharsets; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.util.stream.Collectors; @Service("commandAuthService") public class AuthCommandServiceImpl implements AuthCommandService { + @Value("${jwt.secret-key}") + private String jwtSecretKey; + private final MemberRepository memberRepository; private final PasswordEncoder passwordEncoder; private final ModelMapper modelMapper; + private final AuthenticationManager authenticationManager; @Autowired - public AuthCommandServiceImpl(MemberRepository memberRepository, PasswordEncoder passwordEncoder, ModelMapper modelMapper) { + public AuthCommandServiceImpl(MemberRepository memberRepository, PasswordEncoder passwordEncoder, ModelMapper modelMapper, AuthenticationManager authenticationManager) { this.memberRepository = memberRepository; this.passwordEncoder = passwordEncoder; this.modelMapper = modelMapper; + this.authenticationManager = authenticationManager; } private String getCurrentTimestamp() { @@ -44,4 +65,54 @@ public void signup(SignupDTO signupDTO) { memberRepository.save(registerMember); } + + + @Override + @Transactional + public SigninResponseDTO signin(SigninRequestDTO signinRequestDTO) { + + // 사용자 인증 + Authentication authentication = UsernamePasswordAuthenticationToken + .unauthenticated(signinRequestDTO.getLoginId(), signinRequestDTO.getPassword()); + + Authentication authenticationResponse = authenticationManager.authenticate(authentication); + + if (authenticationResponse == null || !authenticationResponse.isAuthenticated()) { + throw new CommonException(ErrorCode.LOGIN_FAILURE); + } + + // 인증 객체를 SecurityContext에 설정 + SecurityContextHolder.getContext().setAuthentication(authenticationResponse); + + // 사용자 정보 가져오기 + MemberDetails memberDetails = (MemberDetails) authenticationResponse.getPrincipal(); // 수정된 부분 + Member member = memberDetails.getMember(); // MemberDetails에서 Member를 얻어옴 + SecretKey secretKey = Keys.hmacShaKeyFor(jwtSecretKey.getBytes(StandardCharsets.UTF_8)); + + // Access Token (1시간) + String accessToken = Jwts.builder() + .setIssuer("STANL2") + .setSubject("Access Token") + .claim("id", member.getId()) // `id`만 포함 + .claim("authorities", authenticationResponse.getAuthorities().stream() + .map(GrantedAuthority::getAuthority).collect(Collectors.joining(","))) + .setIssuedAt(new java.util.Date()) + .setExpiration(new java.util.Date((new java.util.Date()).getTime() + 3600000)) // 1시간 유효 + .signWith(secretKey) + .compact(); + + // Refresh Token (7일) + String refreshToken = Jwts.builder() + .setIssuer("STANL2") + .setSubject("Refresh Token") + .claim("id", member.getId()) + .setIssuedAt(new java.util.Date()) + .setExpiration(new java.util.Date((new java.util.Date()).getTime() + 604800000)) // 7일 유효 + .signWith(secretKey) + .compact(); + + return new SigninResponseDTO(accessToken, refreshToken); + } + + } diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index 19908902..98acef29 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -4,9 +4,11 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.password.CompromisedPasswordChecker; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; @@ -77,7 +79,14 @@ public CompromisedPasswordChecker compromisedPasswordChecker() { } @Bean - public AuthenticationManager authenticationManager(){ - return null; + public AuthenticationManager authenticationManager(UserDetailsService userDetailsService){ + + ProdUsernamePwdAuthenticationProvider authenticationProvider = + new ProdUsernamePwdAuthenticationProvider(userDetailsService, passwordEncoder()); + + ProviderManager providerManager = new ProviderManager(authenticationProvider); + + providerManager.setEraseCredentialsAfterAuthentication(false); + return providerManager; } } diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java index 7b8048e6..7d6a9c01 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java @@ -9,16 +9,12 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.filter.OncePerRequestFilter; -import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.Member; import javax.crypto.SecretKey; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.Date; -import java.util.stream.Collectors; @Slf4j public class JWTTokenGeneratorFilter extends OncePerRequestFilter { @@ -35,34 +31,36 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - - if(authentication != null && authentication.isAuthenticated()){ + if (jwtHeader == null || jwtHeader.isEmpty()) { + log.error("JWT header is not configured properly"); + filterChain.doFilter(request, response); + return; + } - Member member = (Member) authentication.getPrincipal(); + String token = request.getHeader(jwtHeader); - // 비밀키 생성 + if (token != null && !token.isEmpty()) { SecretKey secretKey = Keys.hmacShaKeyFor(jwtSecretKey.getBytes(StandardCharsets.UTF_8)); + try { + Jwts.parserBuilder() + .setSigningKey(secretKey) + .build() + .parseClaimsJws(token); - String jwt = Jwts.builder() - .setIssuer("STANL2") - .setSubject("JWT Token") - .claim("username", authentication.getName()) - .claim("id", member.getId()) - .claim("authorities", authentication.getAuthorities().stream() - .map(GrantedAuthority::getAuthority).collect(Collectors.joining(","))) - .setIssuedAt(new Date()) - .setExpiration(new Date((new Date()).getTime() + 30000000)) - .signWith(secretKey) - .compact(); - - // JWT 토큰을 응답 헤더에 추가 - response.setHeader(jwtHeader, jwt); - log.info("Generated JWT: {}", jwt); + // 토큰이 유효한 경우 SecurityContext 설정 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication != null && authentication.isAuthenticated()) { + log.info("Valid JWT Token"); + } + } catch (Exception e) { + log.error("Invalid JWT Token", e); + } } + filterChain.doFilter(request, response); } + @Override protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { return !request.getServletPath().equals("/api/v1/auth/signin"); diff --git a/src/main/java/stanl_2/final_backend/global/security/service/MemberDetails.java b/src/main/java/stanl_2/final_backend/global/security/service/MemberDetails.java index 25b83e03..25ac096a 100644 --- a/src/main/java/stanl_2/final_backend/global/security/service/MemberDetails.java +++ b/src/main/java/stanl_2/final_backend/global/security/service/MemberDetails.java @@ -1,6 +1,5 @@ package stanl_2.final_backend.global.security.service; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 94f0cd80..5514a436 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -28,7 +28,7 @@ spring: hibernate.format_sql: true jwt: - secret-key: ${SECRET_KEY} + secret-key: ${JWT_SECRET_KEY} secret-default-value: ${JWT_SECRET_DEFAULT_VALUE} header: ${JWT_HEADER} From c7e54dd01cf7983c651348aae5f40194d78e4c1c Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 13 Nov 2024 20:14:39 +0900 Subject: [PATCH 041/563] =?UTF-8?q?fix:=20=EA=B3=B5=EC=A7=80=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20(#26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/NoticeController.java | 5 +- .../domain/aggragate/entity/Notice.java | 6 +- .../service/NoticeCommandServiceImpl.java | 2 +- .../notices/common/util/RequestList.java | 14 ++ .../query/config/MyBatisConfiguration.java | 9 ++ .../query/controller/NoticeController.java | 32 +++++ .../domain/notices/query/dto/NoticeDTO.java | 33 +++++ .../notices/query/dto/PaginationDTO.java | 15 +++ .../query/repository/NoticeMapper.java | 14 ++ .../notices/query/service/NoticeService.java | 12 ++ .../query/service/NoticeServiceImpl.java | 27 ++++ src/main/resources/application.yml | 1 + .../notices/query/repository/NoticeMapper.xml | 121 ++++++++++++++++++ 13 files changed, 284 insertions(+), 7 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/common/util/RequestList.java create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/query/config/MyBatisConfiguration.java create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/query/dto/PaginationDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java create mode 100644 src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java index 8af294c8..63a192d9 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java @@ -33,7 +33,6 @@ public NoticeController(NoticeCommandService noticeCommandService) { @PostMapping("") public ResponseEntity postNotice(@RequestBody NoticeRegistDTO noticeRegistDTO){ noticeCommandService.registerNotice(noticeRegistDTO); - return ResponseEntity.ok(NoticeResponseMessage.builder() .httpStatus(200) .msg("성공") @@ -41,7 +40,7 @@ public ResponseEntity postNotice(@RequestBody NoticeRegis .build()); } - @Operation(summary = "공지사항 수정 테스트") + @Operation(summary = "공지사항 수정") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) @@ -59,7 +58,7 @@ public ResponseEntity modifyNotice(@PathVariable String i .build()); } - @Operation(summary = "공지사항 삭제 테스트") + @Operation(summary = "공지사항 삭제") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = NoticeResponseMessage.class))}) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java index 5ab8eae1..ab8711f0 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java @@ -14,7 +14,7 @@ import java.time.format.DateTimeFormatter; @Entity -@Table(name="NOTICE") +@Table(name="TB_NOTICE") @AllArgsConstructor @NoArgsConstructor @Setter @@ -27,7 +27,7 @@ public class Notice { parameters = @org.hibernate.annotations.Parameter(name="prefix", value = "NOT") ) @Column(name = "NOT_ID") - private String id; + private String noticeId; @Column(name = "NOT_TTL") private String title; @@ -38,7 +38,7 @@ public class Notice { @Column(name = "NOT_CLA") private String classification; - @Column(columnDefinition = "TEXT", name = "content") + @Column(columnDefinition = "TEXT", name = "NOT_CONT") private String content; @Column(name = "CREATED_AT", nullable = false, updatable = false) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java index 3dc37420..fa3c3b32 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java @@ -49,7 +49,7 @@ public NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO) Notice updateNotice = modelMapper.map(noticeModifyDTO, Notice.class); - updateNotice.setId(notice.getId()); + updateNotice.setNoticeId(notice.getNoticeId()); updateNotice.setMemberId(notice.getMemberId()); updateNotice.setCreatedAt(notice.getCreatedAt()); updateNotice.setActive(notice.getActive()); diff --git a/src/main/java/stanl_2/final_backend/domain/notices/common/util/RequestList.java b/src/main/java/stanl_2/final_backend/domain/notices/common/util/RequestList.java new file mode 100644 index 00000000..b4030203 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/common/util/RequestList.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.notices.common.util; + +import lombok.*; +import org.springframework.data.domain.Pageable; + +@Builder +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class RequestList { + private T data; + private Pageable pageable; +} diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/config/MyBatisConfiguration.java b/src/main/java/stanl_2/final_backend/domain/notices/query/config/MyBatisConfiguration.java new file mode 100644 index 00000000..f420b5cc --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/config/MyBatisConfiguration.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.notices.query.config; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Configuration; + +@Configuration("noticeMybatisConfiguration") +@MapperScan(basePackages = "stanl_2.final_backend.domain.notices.query.repository") +public class MyBatisConfiguration { +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java new file mode 100644 index 00000000..718d36e2 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java @@ -0,0 +1,32 @@ +package stanl_2.final_backend.domain.notices.query.controller; + +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.center.common.response.ResponseMessage; +import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; +import stanl_2.final_backend.domain.notices.query.service.NoticeService; + +import java.util.List; + +@RestController("queryNoticeController") +@RequestMapping("/api/v1/notice") +public class NoticeController { + + private final NoticeService noticeService; + + @Autowired + public NoticeController(NoticeService noticeService) { + this.noticeService = noticeService; + } + + @GetMapping + public ResponseEntity> getNotices() { + List notices = noticeService.findAllNotices(); + return ResponseEntity.ok(notices); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java new file mode 100644 index 00000000..83403d55 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java @@ -0,0 +1,33 @@ +package stanl_2.final_backend.domain.notices.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class NoticeDTO { + private String noticeId; + + private String title; + + private String tag; + + private String classification; + + private String content; + + private String createdAt; + + private String updatedAt; + + private String deletedAt; + + private Boolean active; + + private String memberId; + +} diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/dto/PaginationDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/query/dto/PaginationDTO.java new file mode 100644 index 00000000..1d4a6708 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/dto/PaginationDTO.java @@ -0,0 +1,15 @@ +package stanl_2.final_backend.domain.notices.query.dto; + +import lombok.*; + +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class PaginationDTO { + private List data; + private int totalPages; + private long totalElements; +} diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java b/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java new file mode 100644 index 00000000..601d1cd8 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.notices.query.repository; + +import org.apache.ibatis.annotations.Mapper; +import org.springframework.data.repository.query.Param; +import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; + +import java.util.List; + + +@Mapper +public interface NoticeMapper { + List findAllNotices(); +} + diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java new file mode 100644 index 00000000..3e37ac37 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java @@ -0,0 +1,12 @@ +package stanl_2.final_backend.domain.notices.query.service; + + +import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; + +import java.util.List; + +public interface NoticeService { + + List findAllNotices(); + +} diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java new file mode 100644 index 00000000..ab081072 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java @@ -0,0 +1,27 @@ +package stanl_2.final_backend.domain.notices.query.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; +import stanl_2.final_backend.domain.notices.query.repository.NoticeMapper; + +import java.util.List; + +@Service("queryNoticeServiceImpl") +public class NoticeServiceImpl implements NoticeService{ + private final NoticeMapper noticeMapper; + + @Autowired + public NoticeServiceImpl(NoticeMapper noticeMapper) { + this.noticeMapper = noticeMapper; + } + + + @Override + @Transactional + public List findAllNotices() { + List notice = noticeMapper.findAllNotices(); + return notice; + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 94f0cd80..a6f2cd66 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -39,6 +39,7 @@ logging: org: springframework: security: ${SPRING_SECURITY_LOG_LEVEL:TRACE} + #server: # servlet: # session: diff --git a/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml b/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml new file mode 100644 index 00000000..5d21bbe2 --- /dev/null +++ b/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From c8e966c5aaa34c2180a3924ca3a2e2ff8e9faa6d Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 13 Nov 2024 20:39:54 +0900 Subject: [PATCH 042/563] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A7=80=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98=20(#26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/repository/NoticeRepository.java | 3 +++ .../query/controller/NoticeController.java | 14 +++++++++++--- .../domain/notices/query/dto/NoticeDTO.java | 14 ++++++++++++++ .../notices/query/service/NoticeService.java | 4 +++- .../notices/query/service/NoticeServiceImpl.java | 16 +++++++++------- 5 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/repository/NoticeRepository.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/repository/NoticeRepository.java index 105920ab..4c96292b 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/repository/NoticeRepository.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/repository/NoticeRepository.java @@ -1,7 +1,10 @@ package stanl_2.final_backend.domain.notices.command.domain.repository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import stanl_2.final_backend.domain.notices.command.domain.aggragate.entity.Notice; public interface NoticeRepository extends JpaRepository { + Page findAll(Pageable pageable); } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java index 718d36e2..5e3d751e 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java @@ -2,6 +2,9 @@ import io.swagger.v3.oas.annotations.Operation; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -25,8 +28,13 @@ public NoticeController(NoticeService noticeService) { } @GetMapping - public ResponseEntity> getNotices() { - List notices = noticeService.findAllNotices(); - return ResponseEntity.ok(notices); + public ResponseEntity> getNotices( + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "7") int size + ) { + Pageable pageable = PageRequest.of(page, size); + Page noticeDTOPage = noticeService.findAllNotices(pageable); + return ResponseEntity.ok(noticeDTOPage); } + } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java index 83403d55..b4e3f52c 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java @@ -4,6 +4,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import stanl_2.final_backend.domain.notices.command.domain.aggragate.entity.Notice; @AllArgsConstructor @NoArgsConstructor @@ -30,4 +31,17 @@ public class NoticeDTO { private String memberId; + + public NoticeDTO(Notice notice) { + this.noticeId = notice.getNoticeId(); + this.title = notice.getTitle(); + this.tag = notice.getTag(); + this.classification = notice.getClassification(); + this.content = notice.getContent(); + this.createdAt = notice.getCreatedAt(); + this.updatedAt = notice.getUpdatedAt(); + this.deletedAt = notice.getDeletedAt(); + this.active = notice.getActive(); + this.memberId = notice.getMemberId(); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java index 3e37ac37..a24d5b0c 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java @@ -1,12 +1,14 @@ package stanl_2.final_backend.domain.notices.query.service; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; import java.util.List; public interface NoticeService { - List findAllNotices(); + Page findAllNotices(Pageable pageable); } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java index ab081072..ec85b9db 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java @@ -1,27 +1,29 @@ package stanl_2.final_backend.domain.notices.query.service; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.notices.command.domain.repository.NoticeRepository; import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; import stanl_2.final_backend.domain.notices.query.repository.NoticeMapper; -import java.util.List; @Service("queryNoticeServiceImpl") public class NoticeServiceImpl implements NoticeService{ + private final NoticeRepository noticeRepository; private final NoticeMapper noticeMapper; @Autowired - public NoticeServiceImpl(NoticeMapper noticeMapper) { + public NoticeServiceImpl(NoticeMapper noticeMapper, NoticeRepository noticeRepository) { this.noticeMapper = noticeMapper; + this.noticeRepository = noticeRepository; } @Override - @Transactional - public List findAllNotices() { - List notice = noticeMapper.findAllNotices(); - return notice; + public Page findAllNotices(Pageable pageable) { + return noticeRepository.findAll(pageable) + .map(NoticeDTO::new); } } From 3222763f14e693fb126d7dc0b1dddeb40969d90f Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Thu, 14 Nov 2024 00:42:59 +0900 Subject: [PATCH 043/563] =?UTF-8?q?feat:=20csrf=ED=86=A0=ED=81=B0=EC=9D=84?= =?UTF-8?q?=20=ED=99=9C=EC=9A=A9=ED=95=9C=20=EB=8B=A4=EB=A5=B8=20api=20?= =?UTF-8?q?=EC=A0=91=EA=B7=BC=20except=20=EA=B6=8C=ED=95=9C=EC=B2=B4?= =?UTF-8?q?=ED=81=AC(#35)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AuthController.java | 27 +++++++- .../controller/MemberController.java | 41 ++++++++++++ .../service/MemberCommandService.java | 4 ++ .../service/AuthCommandServiceImpl.java | 13 ++-- .../service/MemberCommandServiceImpl.java | 17 +++++ .../security/config/ProdSecurityConfig.java | 16 +++-- .../security/filter/CsrfCookieFilter.java | 62 +++++++++---------- .../filter/JWTTokenGeneratorFilter.java | 10 +-- .../filter/JWTTokenValidatorFilter.java | 61 +++++++++++------- 9 files changed, 181 insertions(+), 70 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/application/service/MemberCommandService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/domain/service/MemberCommandServiceImpl.java diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java index 6ed6773a..f60ce4c4 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java @@ -5,9 +5,14 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; +import org.springframework.security.web.csrf.CsrfToken; import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; import stanl_2.final_backend.domain.member.command.application.dto.SigninRequestDTO; import stanl_2.final_backend.domain.member.command.application.dto.SigninResponseDTO; import stanl_2.final_backend.domain.member.command.application.dto.SignupDTO; @@ -48,15 +53,31 @@ public ResponseEntity signup(@RequestBody SignupDTO signupDTO){ content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) }) @PostMapping("signin") - public ResponseEntity signin(@RequestBody SigninRequestDTO signinRequestDTO){ + public ResponseEntity signin(@RequestBody SigninRequestDTO signinRequestDTO, + HttpServletResponse response){ // 로그인 서비스 호출하여 Access Token과 Refresh Token 발급 - SigninResponseDTO response = authCommandService.signin(signinRequestDTO); + SigninResponseDTO signinResponseDTO = authCommandService.signin(signinRequestDTO); + + + // CSRF 토큰 생성 및 쿠키에 추가 + ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); + CsrfToken csrfToken = (CsrfToken) attr.getRequest().getAttribute(CsrfToken.class.getName()); + + if (csrfToken != null) { + Cookie csrfCookie = new Cookie("XSRF-TOKEN", csrfToken.getToken()); + csrfCookie.setPath("/"); + csrfCookie.setHttpOnly(false); + csrfCookie.setSecure(attr.getRequest().isSecure()); + csrfCookie.setMaxAge(-1); // 세션 종료 시 삭제 + response.addCookie(csrfCookie); + } + return ResponseEntity.ok(ResponseMessage.builder() .httpStatus(200) .msg("성공") - .result(response) + .result(signinResponseDTO) .build()); } diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java new file mode 100644 index 00000000..30d606e5 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java @@ -0,0 +1,41 @@ +package stanl_2.final_backend.domain.member.command.application.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +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.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.member.command.application.service.MemberCommandService; +import stanl_2.final_backend.domain.member.common.response.ResponseMessage; +import stanl_2.final_backend.global.security.service.MemberDetails; + +@RestController("commandMemberController") +@RequestMapping("/api/v1/member") +public class MemberController { + + private final MemberCommandService memberCommandService; + + @Autowired + public MemberController(MemberCommandService memberCommandService) { + this.memberCommandService = memberCommandService; + } + + + @GetMapping("check") + public ResponseEntity check(@AuthenticationPrincipal MemberDetails memberDetails) { + if (memberDetails == null) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED) + .body(ResponseMessage.builder() + .httpStatus(401) + .msg("Unauthorized") + .build()); + } + return ResponseEntity.ok(ResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(memberDetails.getMember()) + .build()); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/MemberCommandService.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/MemberCommandService.java new file mode 100644 index 00000000..e1e93f57 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/MemberCommandService.java @@ -0,0 +1,4 @@ +package stanl_2.final_backend.domain.member.command.application.service; + +public interface MemberCommandService { +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java index 0e6b2d3c..85ec5de5 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java @@ -2,6 +2,7 @@ import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; +import jakarta.servlet.http.Cookie; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -11,8 +12,11 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.csrf.CsrfToken; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; import stanl_2.final_backend.domain.member.command.application.dto.SigninRequestDTO; import stanl_2.final_backend.domain.member.command.application.dto.SigninResponseDTO; import stanl_2.final_backend.domain.member.command.application.dto.SignupDTO; @@ -21,7 +25,6 @@ import stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; import stanl_2.final_backend.global.exception.CommonException; import stanl_2.final_backend.global.exception.ErrorCode; -import stanl_2.final_backend.global.security.service.MemberDetails; import javax.crypto.SecretKey; import java.nio.charset.StandardCharsets; @@ -85,15 +88,15 @@ public SigninResponseDTO signin(SigninRequestDTO signinRequestDTO) { SecurityContextHolder.getContext().setAuthentication(authenticationResponse); // 사용자 정보 가져오기 - MemberDetails memberDetails = (MemberDetails) authenticationResponse.getPrincipal(); // 수정된 부분 - Member member = memberDetails.getMember(); // MemberDetails에서 Member를 얻어옴 +// MemberDetails memberDetails = (MemberDetails) authenticationResponse.getPrincipal(); // 수정된 부분 +// Member member = memberDetails.getMember(); // MemberDetails에서 Member를 얻어옴 SecretKey secretKey = Keys.hmacShaKeyFor(jwtSecretKey.getBytes(StandardCharsets.UTF_8)); // Access Token (1시간) String accessToken = Jwts.builder() .setIssuer("STANL2") .setSubject("Access Token") - .claim("id", member.getId()) // `id`만 포함 +// .claim("id", member.getId()) // `id`만 포함 .claim("authorities", authenticationResponse.getAuthorities().stream() .map(GrantedAuthority::getAuthority).collect(Collectors.joining(","))) .setIssuedAt(new java.util.Date()) @@ -105,7 +108,7 @@ public SigninResponseDTO signin(SigninRequestDTO signinRequestDTO) { String refreshToken = Jwts.builder() .setIssuer("STANL2") .setSubject("Refresh Token") - .claim("id", member.getId()) +// .claim("id", member.getId()) .setIssuedAt(new java.util.Date()) .setExpiration(new java.util.Date((new java.util.Date()).getTime() + 604800000)) // 7일 유효 .signWith(secretKey) diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/MemberCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/MemberCommandServiceImpl.java new file mode 100644 index 00000000..8ee95370 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/MemberCommandServiceImpl.java @@ -0,0 +1,17 @@ +package stanl_2.final_backend.domain.member.command.domain.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.member.command.application.service.MemberCommandService; +import stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; + +@Service("commandMemberService") +public class MemberCommandServiceImpl implements MemberCommandService { + + private final MemberRepository memberRepository; + + @Autowired + public MemberCommandServiceImpl(MemberRepository memberRepository) { + this.memberRepository = memberRepository; + } +} diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index 98acef29..afc0ec50 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.global.security.config; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; @@ -32,6 +33,12 @@ @Profile("prod") public class ProdSecurityConfig { + @Value("${jwt.secret-key}") + private String jwtSecretKey; + + @Value("${jwt.header}") + private String jwtHeader; + @Bean SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { @@ -40,14 +47,15 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Excepti http.sessionManagement(sessionConfig -> sessionConfig.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .cors(corsConfig -> corsConfig.configurationSource(corsConfigurationSource())) .csrf(csrfConfig -> csrfConfig.csrfTokenRequestHandler(csrfTokenRequestAttributeHandler) - .ignoringRequestMatchers("/api/v1/auth/**", "/api/v1/sample/**") + .ignoringRequestMatchers("/api/v1/auth/signup", "/api/v1/auth/signin", "/api/v1/sample/**") .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())) .authorizeHttpRequests(auth -> auth .requestMatchers("/api/v1/auth/**", "/api/v1/sample/**").permitAll() - .anyRequest().authenticated()) + .anyRequest().permitAll()) +// .anyRequest().authenticated()) // 필터 순서: JWT 검증 -> CSRF -> JWT 생성 - .addFilterBefore(new JWTTokenValidatorFilter(), BasicAuthenticationFilter.class) - .addFilterAfter(new JWTTokenGeneratorFilter(), BasicAuthenticationFilter.class) + .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, jwtHeader), BasicAuthenticationFilter.class) + .addFilterAfter(new JWTTokenGeneratorFilter(jwtSecretKey, jwtHeader), BasicAuthenticationFilter.class) .addFilterAfter(new CsrfCookieFilter(), CsrfFilter.class) .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()); http.formLogin(withDefaults()); diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java index 07756bf5..44582f44 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java @@ -17,46 +17,44 @@ public class CsrfCookieFilter extends OncePerRequestFilter { @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - - CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfCookieFilter.class.getName()); - - if(csrfToken != null) { - Cookie csrfCookie = null; - - // 요청에서 쿠키 배열 가져오기 (null 체크 추가) - Cookie[] cookies = request.getCookies(); - if (cookies != null) { - // 쿠키 배열에서 "XSRF-TOKEN" 이름을 가진 쿠키를 찾음 - for (Cookie cookie : cookies) { - if ("XSRF-TOKEN".equals(cookie.getName())) { - csrfCookie = cookie; - break; // 쿠키를 찾으면 반복문 종료 - } - } - } + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName()); - // 쿠키가 없거나, 토큰이 변경된 경우 새로운 쿠키 생성 - if (csrfCookie == null || !csrfCookie.getValue().equals(csrfToken.getToken())) { - Cookie newCsrfCookie = new Cookie("XSRF-TOKEN", csrfToken.getToken()); - newCsrfCookie.setPath("/"); - newCsrfCookie.setHttpOnly(false); // JavaScript에서 접근 가능하도록 설정 - newCsrfCookie.setSecure(request.isSecure()); // HTTPS 요청에서만 쿠키 전송 - newCsrfCookie.setMaxAge(-1); // 세션 종료 시 삭제 - response.addCookie(newCsrfCookie); // 응답에 쿠키 추가 - } - } else { + if (csrfToken == null) { + log.error("CSRF 토큰을 찾을 수 없습니다."); + throw new CommonException(ErrorCode.NOT_FOUND_CSRF_TOKEN); + } + + // 쿠키에서 CSRF 토큰 가져오기 + String csrfCookieValue = getCsrfTokenFromCookie(request); + log.info("쿠키에서 가져온 CSRF 토큰: {}", csrfCookieValue); + log.info("현재 요청의 CSRF 토큰: {}", csrfToken.getToken()); + + if (csrfCookieValue == null || !csrfCookieValue.equals(csrfToken.getToken())) { + log.error("CSRF 토큰 불일치 - 쿠키와 요청의 토큰이 일치하지 않습니다."); throw new CommonException(ErrorCode.NOT_FOUND_CSRF_TOKEN); } - // 다음 필터로 요청 전달 filterChain.doFilter(request, response); } + private String getCsrfTokenFromCookie(HttpServletRequest request) { + Cookie[] cookies = request.getCookies(); + if (cookies != null) { + for (Cookie cookie : cookies) { + if ("XSRF-TOKEN".equals(cookie.getName())) { + return cookie.getValue(); + } + } + } + return null; + } + @Override - protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { + protected boolean shouldNotFilter(HttpServletRequest request) { String path = request.getServletPath(); - return path.startsWith("/api/v1/auth") || - path.startsWith("/api/v1/sample"); + return path.startsWith("/api/v1/auth") || path.startsWith("/api/v1/sample"); } } diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java index 7d6a9c01..716b0c83 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java @@ -19,11 +19,13 @@ @Slf4j public class JWTTokenGeneratorFilter extends OncePerRequestFilter { - @Value("${jwt.secret-key}") - private String jwtSecretKey; + private final String jwtSecretKey; + private final String jwtHeader; - @Value("${jwt.header}") - private String jwtHeader; + public JWTTokenGeneratorFilter(String jwtSecretKey, String jwtHeader) { + this.jwtSecretKey = jwtSecretKey; + this.jwtHeader = jwtHeader; + } @Override diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java index aab05ea2..5b8e11d8 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java @@ -1,6 +1,5 @@ package stanl_2.final_backend.global.security.filter; - import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; @@ -25,12 +24,13 @@ @Slf4j public class JWTTokenValidatorFilter extends OncePerRequestFilter { - // 환경 변수 주입 - @Value("${jwt.secret-key}") - private String jwtSecretKey; + private final String jwtSecretKey; + private final String jwtHeader; - @Value("${jwt.header}") - private String jwtHeader; + public JWTTokenValidatorFilter(String jwtSecretKey, String jwtHeader) { + this.jwtHeader = jwtHeader; + this.jwtSecretKey = jwtSecretKey; + } @Override protected void doFilterInternal(HttpServletRequest request, @@ -39,6 +39,7 @@ protected void doFilterInternal(HttpServletRequest request, // 요청 헤더에서 JWT 토큰 가져오기 String jwt = request.getHeader(jwtHeader); + // 토큰이 존재하고 "Bearer "로 시작하는지 확인 if (jwt != null && jwt.startsWith("Bearer ")) { try { // 비밀키를 사용하여 JWT 토큰을 검증 @@ -52,23 +53,19 @@ protected void doFilterInternal(HttpServletRequest request, .parseClaimsJws(jwtToken) .getBody(); - // 클레임에서 사용자 정보 및 권한 추출 - String id = claims.get("id", String.class); - String username = claims.get("username", String.class); - String authorities = claims.get("authorities", String.class); - - // SecurityContext에 인증 정보 설정 - Authentication authentication = new UsernamePasswordAuthenticationToken( - username, null, - AuthorityUtils.commaSeparatedStringToAuthorityList(authorities) - ); - SecurityContextHolder.getContext().setAuthentication(authentication); + // 클레임에서 토큰 타입 확인 + String subject = claims.getSubject(); - log.info("Authenticated user: {}", username); - - // 추가적으로, 추출된 정보를 request 속성에 설정 - request.setAttribute("id", id); - request.setAttribute("authorities", authorities); + // Access Token 처리 + if ("Access Token".equals(subject)) { + handleAccessToken(claims, request); + } + // Refresh Token은 인증 처리가 아닌, 토큰 재발급용으로만 사용 + else if ("Refresh Token".equals(subject)) { + log.info("Received Refresh Token"); + } else { + throw new CommonException(ErrorCode.INVALID_TOKEN_ERROR); + } } catch (Exception exception) { log.error("Invalid JWT Token", exception); @@ -80,6 +77,26 @@ protected void doFilterInternal(HttpServletRequest request, filterChain.doFilter(request, response); } + private void handleAccessToken(Claims claims, HttpServletRequest request) { + // 클레임에서 사용자 정보 및 권한 추출 + String id = claims.get("id", String.class); + String username = claims.get("username", String.class); + String authorities = claims.get("authorities", String.class); + + // SecurityContext에 인증 정보 설정 + Authentication authentication = new UsernamePasswordAuthenticationToken( + username, null, + AuthorityUtils.commaSeparatedStringToAuthorityList(authorities) + ); + SecurityContextHolder.getContext().setAuthentication(authentication); + + log.info("Authenticated user: {}", username); + + // 추가적으로, 추출된 정보를 request 속성에 설정 + request.setAttribute("id", id); + request.setAttribute("authorities", authorities); + } + @Override protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { // 특정 경로에서는 필터를 적용하지 않음 From 50a801371c918ad5979a2922325f6ce595713d20 Mon Sep 17 00:00:00 2001 From: DongHo Bang <74201427+Bang1999@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:33:38 +0900 Subject: [PATCH 044/563] Update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 45d78aff..0aa68ae9 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ # Final_Backend 파이널 백엔드 프로젝트 -http://localhost:7777/swagger-ui/index.html +
+http://localhost:8080/swagger-ui/index.html +
+http://motive-backend-env.eba-n6hhmwaa.ap-northeast-2.elasticbeanstalk.com/ From dfac44f1c66dce18a861800d36b260875dffbeb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=84=9D?= <93479041+minseokKim6823@users.noreply.github.com> Date: Thu, 14 Nov 2024 12:03:43 +0900 Subject: [PATCH 045/563] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0aa68ae9..1a4c98d3 100644 --- a/README.md +++ b/README.md @@ -3,4 +3,4 @@
http://localhost:8080/swagger-ui/index.html
-http://motive-backend-env.eba-n6hhmwaa.ap-northeast-2.elasticbeanstalk.com/ +http://motive-backend-env.eba-n6hhmwaa.ap-northeast-2.elasticbeanstalk.com/swagger-ui/index.html From c2505c4215a6efddcbbb10090ff71efbf33570d4 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 14 Nov 2024 12:23:23 +0900 Subject: [PATCH 046/563] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=A0=84=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80=20(#4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/NoticeController.java | 9 +++-- .../domain/notices/query/dto/NoticeDTO.java | 2 ++ .../query/repository/NoticeMapper.java | 3 +- .../notices/query/service/NoticeService.java | 1 - .../query/service/NoticeServiceImpl.java | 3 +- .../notices/query/repository/NoticeMapper.xml | 33 +------------------ 6 files changed, 10 insertions(+), 41 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java index 5e3d751e..fb368b3d 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java @@ -1,6 +1,5 @@ package stanl_2.final_backend.domain.notices.query.controller; -import io.swagger.v3.oas.annotations.Operation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -10,20 +9,18 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import stanl_2.final_backend.domain.center.common.response.ResponseMessage; import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; +import stanl_2.final_backend.domain.notices.query.repository.NoticeMapper; import stanl_2.final_backend.domain.notices.query.service.NoticeService; -import java.util.List; @RestController("queryNoticeController") @RequestMapping("/api/v1/notice") public class NoticeController { - private final NoticeService noticeService; @Autowired - public NoticeController(NoticeService noticeService) { + public NoticeController(NoticeService noticeService, NoticeMapper noticeMapper) { this.noticeService = noticeService; } @@ -33,7 +30,9 @@ public ResponseEntity> getNotices( @RequestParam(defaultValue = "7") int size ) { Pageable pageable = PageRequest.of(page, size); + Page noticeDTOPage = noticeService.findAllNotices(pageable); + return ResponseEntity.ok(noticeDTOPage); } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java index b4e3f52c..a7fac9ef 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java @@ -5,6 +5,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; import stanl_2.final_backend.domain.notices.command.domain.aggragate.entity.Notice; +import stanl_2.final_backend.domain.notices.query.repository.NoticeMapper; @AllArgsConstructor @NoArgsConstructor @@ -31,6 +32,7 @@ public class NoticeDTO { private String memberId; + public NoticeDTO(Notice notice) { this.noticeId = notice.getNoticeId(); diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java b/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java index 601d1cd8..7bcc89b2 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java @@ -1,7 +1,6 @@ package stanl_2.final_backend.domain.notices.query.repository; import org.apache.ibatis.annotations.Mapper; -import org.springframework.data.repository.query.Param; import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; import java.util.List; @@ -10,5 +9,7 @@ @Mapper public interface NoticeMapper { List findAllNotices(); + + int findNoticeCount(); } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java index a24d5b0c..a421cebc 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java @@ -5,7 +5,6 @@ import org.springframework.data.domain.Pageable; import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; -import java.util.List; public interface NoticeService { diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java index ec85b9db..1782121f 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java @@ -12,11 +12,9 @@ @Service("queryNoticeServiceImpl") public class NoticeServiceImpl implements NoticeService{ private final NoticeRepository noticeRepository; - private final NoticeMapper noticeMapper; @Autowired public NoticeServiceImpl(NoticeMapper noticeMapper, NoticeRepository noticeRepository) { - this.noticeMapper = noticeMapper; this.noticeRepository = noticeRepository; } @@ -26,4 +24,5 @@ public Page findAllNotices(Pageable pageable) { return noticeRepository.findAll(pageable) .map(NoticeDTO::new); } + } diff --git a/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml b/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml index 5d21bbe2..56ef22d7 100644 --- a/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml @@ -17,6 +17,7 @@ + - - - - - + + @@ -71,6 +79,31 @@ OFFSET #{pageable.offset} ROWS FETCH NEXT #{pageable.pageSize} ROWS ONLY; + + + + SELECT + A.MEM_ID + FROM TB_MEMBER A + WHERE A.MEM_LOGIN_ID = #{ loginId } + + \ No newline at end of file From 2979ff7c97f4b1648ea666ceae22bcff5dcbcf50 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Thu, 14 Nov 2024 13:36:20 +0900 Subject: [PATCH 049/563] =?UTF-8?q?feat:=20=EC=9D=BC=EC=A0=95=ED=91=9C=20c?= =?UTF-8?q?rud=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20(#25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ScheduleController.java | 32 ++--- ...RequestDTO.java => ScheduleModifyDTO.java} | 4 +- ...RequestDTO.java => ScheduleRegistDTO.java} | 4 +- .../service/ScheduleCommandService.java | 8 +- .../service/ScheduleCommandServiceImpl.java | 42 +++--- .../common/exception/ExceptionResponse.java | 22 --- ...tion.java => ScheduleCommonException.java} | 6 +- ...{ErrorCode.java => ScheduleErrorCode.java} | 7 +- .../exception/ScheduleExceptionResponse.java | 22 +++ ...sage.java => ScheduleResponseMessage.java} | 2 +- .../query/controller/ScheduleController.java | 50 +++---- .../schedule/query/dto/ScheduleDTO.java | 7 +- .../dto/ScheduleDetailDTO.java} | 8 +- .../query/dto/ScheduleYearMonthDTO.java | 7 +- .../query/repository/ScheduleMapper.java | 7 +- ...Service.java => ScheduleQueryService.java} | 5 +- .../service/ScheduleQueryServiceImpl.java | 129 ++++++++++++++++++ .../query/service/ScheduleServiceImpl.java | 78 ----------- .../query/repository/ScheduleMapper.xml | 22 +-- 19 files changed, 252 insertions(+), 210 deletions(-) rename src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/{request/ScheduleModifyRequestDTO.java => ScheduleModifyDTO.java} (87%) rename src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/{request/ScheduleRegistRequestDTO.java => ScheduleRegistDTO.java} (87%) delete mode 100644 src/main/java/stanl_2/final_backend/domain/schedule/common/exception/ExceptionResponse.java rename src/main/java/stanl_2/final_backend/domain/schedule/common/exception/{CommonException.java => ScheduleCommonException.java} (61%) rename src/main/java/stanl_2/final_backend/domain/schedule/common/exception/{ErrorCode.java => ScheduleErrorCode.java} (88%) create mode 100644 src/main/java/stanl_2/final_backend/domain/schedule/common/exception/ScheduleExceptionResponse.java rename src/main/java/stanl_2/final_backend/domain/schedule/common/response/{ResponseMessage.java => ScheduleResponseMessage.java} (85%) rename src/main/java/stanl_2/final_backend/domain/schedule/{command/application/dto/response/ScheduleModifyResponseDTO.java => query/dto/ScheduleDetailDTO.java} (50%) rename src/main/java/stanl_2/final_backend/domain/schedule/query/service/{ScheduleService.java => ScheduleQueryService.java} (66%) create mode 100644 src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleServiceImpl.java diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java index b62cae21..848cc231 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java @@ -8,10 +8,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.schedule.command.application.dto.request.ScheduleModifyRequestDTO; -import stanl_2.final_backend.domain.schedule.command.application.dto.request.ScheduleRegistRequestDTO; +import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleModifyDTO; +import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleRegistDTO; import stanl_2.final_backend.domain.schedule.command.application.service.ScheduleCommandService; -import stanl_2.final_backend.domain.schedule.common.response.ResponseMessage; +import stanl_2.final_backend.domain.schedule.common.response.ScheduleResponseMessage; @RestController("commandScheduleController") @RequestMapping("/api/v1/schedule") @@ -27,14 +27,14 @@ public ScheduleController(ScheduleCommandService scheduleCommandService) { @Operation(summary = "일정 등록 api") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "일정 등록 성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity registSchedule(@RequestBody ScheduleRegistRequestDTO scheduleRegistRequestDTO){ + public ResponseEntity registSchedule(@RequestBody ScheduleRegistDTO scheduleRegistDTO){ - Boolean answer = scheduleCommandService.registSchedule(scheduleRegistRequestDTO); + Boolean answer = scheduleCommandService.registSchedule(scheduleRegistDTO); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(ScheduleResponseMessage.builder() .httpStatus(200) .msg("성공") .result(answer) @@ -45,16 +45,16 @@ public ResponseEntity registSchedule(@RequestBody ScheduleRegis @Operation(summary = "일정 수정 api") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "일정 수정 성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) }) @PutMapping("{id}") - public ResponseEntity modifySchedule(@PathVariable String id, - @RequestBody ScheduleModifyRequestDTO scheduleModifyRequestDTO){ + public ResponseEntity modifySchedule(@PathVariable String id, + @RequestBody ScheduleModifyDTO scheduleModifyDTO){ - scheduleModifyRequestDTO.setId(id); - Boolean answer = scheduleCommandService.modifySchedule(scheduleModifyRequestDTO); + scheduleModifyDTO.setId(id); + Boolean answer = scheduleCommandService.modifySchedule(scheduleModifyDTO); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(ScheduleResponseMessage.builder() .httpStatus(200) .msg("성공") .result(answer) @@ -64,14 +64,14 @@ public ResponseEntity modifySchedule(@PathVariable String id, @Operation(summary = "일정 삭제 api") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "일정 삭제 성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) }) @DeleteMapping("{id}") - public ResponseEntity deleteSchedule(@PathVariable String id){ + public ResponseEntity deleteSchedule(@PathVariable String id){ Boolean answer = scheduleCommandService.deleteSchedule(id); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(ScheduleResponseMessage.builder() .httpStatus(200) .msg("성공") .result(answer) diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/request/ScheduleModifyRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleModifyDTO.java similarity index 87% rename from src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/request/ScheduleModifyRequestDTO.java rename to src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleModifyDTO.java index 878de409..e5a314d2 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/request/ScheduleModifyRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleModifyDTO.java @@ -1,4 +1,4 @@ -package stanl_2.final_backend.domain.schedule.command.application.dto.request; +package stanl_2.final_backend.domain.schedule.command.application.dto; import lombok.AllArgsConstructor; import lombok.Getter; @@ -9,7 +9,7 @@ @NoArgsConstructor @Getter @Setter -public class ScheduleModifyRequestDTO { +public class ScheduleModifyDTO { private String id; private String name; diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/request/ScheduleRegistRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleRegistDTO.java similarity index 87% rename from src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/request/ScheduleRegistRequestDTO.java rename to src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleRegistDTO.java index d1d6d660..79f4de4f 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/request/ScheduleRegistRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleRegistDTO.java @@ -1,4 +1,4 @@ -package stanl_2.final_backend.domain.schedule.command.application.dto.request; +package stanl_2.final_backend.domain.schedule.command.application.dto; import lombok.AllArgsConstructor; import lombok.Getter; @@ -9,7 +9,7 @@ @NoArgsConstructor @Getter @Setter -public class ScheduleRegistRequestDTO { +public class ScheduleRegistDTO { private String name; private String content; diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/service/ScheduleCommandService.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/service/ScheduleCommandService.java index b211cd05..d45e319c 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/service/ScheduleCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/service/ScheduleCommandService.java @@ -1,13 +1,13 @@ package stanl_2.final_backend.domain.schedule.command.application.service; -import stanl_2.final_backend.domain.schedule.command.application.dto.request.ScheduleModifyRequestDTO; -import stanl_2.final_backend.domain.schedule.command.application.dto.request.ScheduleRegistRequestDTO; +import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleModifyDTO; +import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleRegistDTO; public interface ScheduleCommandService { - Boolean registSchedule(ScheduleRegistRequestDTO scheduleRegistRequestDTO); + Boolean registSchedule(ScheduleRegistDTO scheduleRegistDTO); - Boolean modifySchedule(ScheduleModifyRequestDTO scheduleModifyRequestDTO); + Boolean modifySchedule(ScheduleModifyDTO scheduleModifyDTO); Boolean deleteSchedule(String scheduleId); } \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java index ed23b4d2..2cb8a780 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java @@ -7,21 +7,20 @@ import org.springframework.data.mapping.MappingException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.schedule.command.application.dto.request.ScheduleModifyRequestDTO; -import stanl_2.final_backend.domain.schedule.command.application.dto.request.ScheduleRegistRequestDTO; +import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleModifyDTO; +import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleRegistDTO; import stanl_2.final_backend.domain.schedule.command.application.service.ScheduleCommandService; import stanl_2.final_backend.domain.schedule.command.domain.aggregate.entity.Schedule; import stanl_2.final_backend.domain.schedule.command.domain.repository.ScheduleRepository; -import stanl_2.final_backend.domain.schedule.common.exception.CommonException; -import stanl_2.final_backend.domain.schedule.common.exception.ErrorCode; +import stanl_2.final_backend.domain.schedule.common.exception.ScheduleCommonException; +import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; -import java.sql.Timestamp; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @Slf4j -@Service("commandScheduleServiceImpl") +@Service public class ScheduleCommandServiceImpl implements ScheduleCommandService { private final ScheduleRepository scheduleRepository; @@ -39,35 +38,44 @@ private String getCurrentTime() { } @Override @Transactional - public Boolean registSchedule(ScheduleRegistRequestDTO scheduleRegistRequestDTO) { + public Boolean registSchedule(ScheduleRegistDTO scheduleRegistDTO) { try { - Schedule schedule = modelMapper.map(scheduleRegistRequestDTO, Schedule.class); + Schedule schedule = modelMapper.map(scheduleRegistDTO, Schedule.class); scheduleRepository.save(schedule); return true; } catch (DataIntegrityViolationException e){ // DB 무결정 제약 조건 (NOT NULL, UNIQUE) 위반 - throw new CommonException(ErrorCode.DATA_INTEGRITY_VIOLATION); + throw new ScheduleCommonException(ScheduleErrorCode.DATA_INTEGRITY_VIOLATION); } catch (MappingException e){ // ModelMapper 매핑 오류 - throw new CommonException(ErrorCode.MAPPING_ERROR); + throw new ScheduleCommonException(ScheduleErrorCode.MAPPING_ERROR); } catch (Exception e) { // 서버 오류 - throw new CommonException(ErrorCode.INTERNAL_SERVER_ERROR); + throw new ScheduleCommonException(ScheduleErrorCode.INTERNAL_SERVER_ERROR); } } @Override @Transactional - public Boolean modifySchedule(ScheduleModifyRequestDTO scheduleModifyRequestDTO) { + public Boolean modifySchedule(ScheduleModifyDTO scheduleModifyDTO) { - Schedule schedule = scheduleRepository.findById(scheduleModifyRequestDTO.getId()) - .orElseThrow(() -> new CommonException(ErrorCode.SCHEDULE_NOT_FOUND)); + if(scheduleModifyDTO.getMemberId() == null){ + // 향후 memberException으로 바꿀 예정 + throw new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND); + } + + Schedule schedule = scheduleRepository.findById(scheduleModifyDTO.getId()) + .orElseThrow(() -> new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND)); + + if(scheduleModifyDTO.getMemberId() != schedule.getMemberId()){ + throw new ScheduleCommonException(ScheduleErrorCode.AUTHORIZATION_VIOLATION); + } try { - Schedule updateSchedule = modelMapper.map(scheduleModifyRequestDTO, Schedule.class); + Schedule updateSchedule = modelMapper.map(scheduleModifyDTO, Schedule.class); updateSchedule.setCreatedAt(schedule.getCreatedAt()); updateSchedule.setActive(schedule.getActive()); @@ -75,7 +83,7 @@ public Boolean modifySchedule(ScheduleModifyRequestDTO scheduleModifyRequestDTO) return true; } catch (MappingException e){ - throw new CommonException(ErrorCode.MAPPING_ERROR); + throw new ScheduleCommonException(ScheduleErrorCode.MAPPING_ERROR); } } @@ -83,7 +91,7 @@ public Boolean modifySchedule(ScheduleModifyRequestDTO scheduleModifyRequestDTO) public Boolean deleteSchedule(String scheduleId) { Schedule schedule = scheduleRepository.findById(scheduleId) - .orElseThrow(() -> new CommonException(ErrorCode.SCHEDULE_NOT_FOUND)); + .orElseThrow(() -> new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND)); schedule.setActive(false); schedule.setDeletedAt(getCurrentTime()); diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/common/exception/ExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/schedule/common/exception/ExceptionResponse.java deleted file mode 100644 index c5ee4b35..00000000 --- a/src/main/java/stanl_2/final_backend/domain/schedule/common/exception/ExceptionResponse.java +++ /dev/null @@ -1,22 +0,0 @@ -package stanl_2.final_backend.domain.schedule.common.exception; - -import lombok.Getter; -import org.springframework.http.HttpStatus; - -@Getter -public class ExceptionResponse { - private final Integer code; - private final String msg; - private final HttpStatus httpStatus; - - public ExceptionResponse(ErrorCode errorCode) { - this.code = errorCode.getCode(); - this.msg = errorCode.getMsg(); - this.httpStatus = errorCode.getHttpStatus(); - } - - public static ExceptionResponse of(ErrorCode errorCode) { - return new ExceptionResponse(errorCode); - } - -} diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/common/exception/CommonException.java b/src/main/java/stanl_2/final_backend/domain/schedule/common/exception/ScheduleCommonException.java similarity index 61% rename from src/main/java/stanl_2/final_backend/domain/schedule/common/exception/CommonException.java rename to src/main/java/stanl_2/final_backend/domain/schedule/common/exception/ScheduleCommonException.java index 870ef028..b5050ec7 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/common/exception/CommonException.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/common/exception/ScheduleCommonException.java @@ -5,12 +5,12 @@ @Getter @RequiredArgsConstructor -public class CommonException extends RuntimeException { - private final ErrorCode errorCode; +public class ScheduleCommonException extends RuntimeException { + private final ScheduleErrorCode scheduleErrorCode; // 에러 발생시 ErroCode 별 메시지 @Override public String getMessage() { - return this.errorCode.getMsg(); + return this.scheduleErrorCode.getMsg(); } } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/common/exception/ErrorCode.java b/src/main/java/stanl_2/final_backend/domain/schedule/common/exception/ScheduleErrorCode.java similarity index 88% rename from src/main/java/stanl_2/final_backend/domain/schedule/common/exception/ErrorCode.java rename to src/main/java/stanl_2/final_backend/domain/schedule/common/exception/ScheduleErrorCode.java index 739aada9..587a0967 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/common/exception/ErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/common/exception/ScheduleErrorCode.java @@ -6,7 +6,7 @@ @Getter @AllArgsConstructor -public enum ErrorCode { +public enum ScheduleErrorCode { /** * 400(Bad Request) @@ -14,6 +14,7 @@ public enum ErrorCode { */ DATA_INTEGRITY_VIOLATION(40001, HttpStatus.BAD_REQUEST, "데이터 무결 위반하였습니다."), CONSTRAINT_VIOLATION(40002, HttpStatus.BAD_REQUEST, "제약 조건 위반하였습니다."), + AUTHORIZATION_VIOLATION(40003, HttpStatus.BAD_REQUEST, "본인의 일정에만 접근 가능합니다."), @@ -48,7 +49,9 @@ public enum ErrorCode { * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. */ INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."), - MAPPING_ERROR(50001, HttpStatus.INTERNAL_SERVER_ERROR, "ModleMapper 매핑 오류입니다."); + MAPPING_ERROR(50001, HttpStatus.INTERNAL_SERVER_ERROR, "ModleMapper 매핑 오류입니다."), + DATA_ACCESS_ERROR(50002, HttpStatus.INTERNAL_SERVER_ERROR, "데이터베이스 접근 중 오류가 발생했습니다."); + private final Integer code; diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/common/exception/ScheduleExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/schedule/common/exception/ScheduleExceptionResponse.java new file mode 100644 index 00000000..1165f5d0 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/schedule/common/exception/ScheduleExceptionResponse.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.schedule.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class ScheduleExceptionResponse { + private final Integer code; + private final String msg; + private final HttpStatus httpStatus; + + public ScheduleExceptionResponse(ScheduleErrorCode scheduleErrorCode) { + this.code = scheduleErrorCode.getCode(); + this.msg = scheduleErrorCode.getMsg(); + this.httpStatus = scheduleErrorCode.getHttpStatus(); + } + + public static ScheduleExceptionResponse of(ScheduleErrorCode scheduleErrorCode) { + return new ScheduleExceptionResponse(scheduleErrorCode); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/common/response/ResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/schedule/common/response/ScheduleResponseMessage.java similarity index 85% rename from src/main/java/stanl_2/final_backend/domain/schedule/common/response/ResponseMessage.java rename to src/main/java/stanl_2/final_backend/domain/schedule/common/response/ScheduleResponseMessage.java index a3cf0529..ca583c8c 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/common/response/ResponseMessage.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/common/response/ScheduleResponseMessage.java @@ -7,7 +7,7 @@ @Builder @Getter @Setter -public class ResponseMessage { +public class ScheduleResponseMessage { private Integer httpStatus; private String msg; private Object result; diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java index 25425913..17c989ee 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java @@ -7,15 +7,15 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; -import org.springframework.security.core.parameters.P; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import stanl_2.final_backend.domain.schedule.common.response.ResponseMessage; +import stanl_2.final_backend.domain.schedule.common.response.ScheduleResponseMessage; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; +import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDetailDTO; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleYearMonthDTO; -import stanl_2.final_backend.domain.schedule.query.service.ScheduleService; +import stanl_2.final_backend.domain.schedule.query.service.ScheduleQueryService; import java.util.List; @@ -23,24 +23,24 @@ @RequestMapping("/api/v1/schedule") public class ScheduleController { - private final ScheduleService scheduleService; + private final ScheduleQueryService scheduleQueryService; @Autowired - public ScheduleController(ScheduleService scheduleService) { - this.scheduleService = scheduleService; + public ScheduleController(ScheduleQueryService scheduleQueryService) { + this.scheduleQueryService = scheduleQueryService; } @Operation(summary = "일정 전체 조회 api") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "일정 조회 성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) }) @GetMapping("{memberId}") - public ResponseEntity selectAllSchedule(@PathVariable("memberId") String memberId){ + public ResponseEntity selectAllSchedule(@PathVariable("memberId") String memberId){ - List schedules = scheduleService.selectAllSchedule(memberId); + List schedules = scheduleQueryService.selectAllSchedule(memberId); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(ScheduleResponseMessage.builder() .httpStatus(200) .msg("성공") .result(schedules) @@ -51,21 +51,21 @@ public ResponseEntity selectAllSchedule(@PathVariable("memberId @Operation(summary = "일정 조건별(년&일) 전체 조회 api") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "일정 조건별(년&일) 전체 조회 성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) }) @GetMapping("{memberId}/{year}/{month}") - public ResponseEntity selectMonthSchedule(@PathVariable("memberId") String memberId, - @PathVariable("year") String year, - @PathVariable("month") String month){ + public ResponseEntity selectMonthSchedule(@PathVariable("memberId") String memberId, + @PathVariable("year") String year, + @PathVariable("month") String month){ ScheduleYearMonthDTO scheduleYearMonthDTO = new ScheduleYearMonthDTO(); scheduleYearMonthDTO.setMemberId(memberId); scheduleYearMonthDTO.setYear(year); scheduleYearMonthDTO.setMonth(month); - List yearMonthSchedule = scheduleService.selectYearMonthSchedule(scheduleYearMonthDTO); + List yearMonthSchedule = scheduleQueryService.selectYearMonthSchedule(scheduleYearMonthDTO); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(ScheduleResponseMessage.builder() .httpStatus(200) .msg("성공") .result(yearMonthSchedule) @@ -76,22 +76,22 @@ public ResponseEntity selectMonthSchedule(@PathVariable("member @Operation(summary = "일정 상세 조회 api") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "일정 상세 조회 성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) }) @GetMapping("{memberId}/{scheduleId}") - public ResponseEntity selectDetailSchedule(@PathVariable("memberId") String memberId, - @PathVariable("scheduleId") String scheduleId){ + public ResponseEntity selectDetailSchedule(@PathVariable("memberId") String memberId, + @PathVariable("scheduleId") String scheduleId){ - ScheduleDTO scheduleDTO = new ScheduleDTO(); - scheduleDTO.setMemberId(memberId); - scheduleDTO.setId(scheduleId); + ScheduleDetailDTO scheduleDetailDTO = new ScheduleDetailDTO(); + scheduleDetailDTO.setMemberId(memberId); + scheduleDetailDTO.setId(scheduleId); - ScheduleDTO responseSchedule = scheduleService.selectDetailSchedule(scheduleDTO); + ScheduleDetailDTO responseDetailSchedule = scheduleQueryService.selectDetailSchedule(scheduleDetailDTO); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(ScheduleResponseMessage.builder() .httpStatus(200) .msg("성공") - .result(responseSchedule) + .result(responseDetailSchedule) .build()); } } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java index f8503699..c6d35a8a 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java @@ -13,12 +13,9 @@ public class ScheduleDTO { private String id; private String name; - private String content; private String startAt; private String endAt; - private String createdAt; - private String updatedAt; - private String deletedAt; - private Boolean active; private String memberId; + + private String month; } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/response/ScheduleModifyResponseDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDetailDTO.java similarity index 50% rename from src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/response/ScheduleModifyResponseDTO.java rename to src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDetailDTO.java index 6aa22e5d..56e4fec2 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/response/ScheduleModifyResponseDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDetailDTO.java @@ -1,4 +1,4 @@ -package stanl_2.final_backend.domain.schedule.command.application.dto.response; +package stanl_2.final_backend.domain.schedule.query.dto; import lombok.*; @@ -7,16 +7,12 @@ @Getter @Setter @ToString -public class ScheduleModifyResponseDTO { +public class ScheduleDetailDTO { private String id; private String name; private String content; private String startAt; private String endAt; - private String createdAt; - private String updatedAt; - private String deletedAt; - private Boolean active; private String memberId; } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleYearMonthDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleYearMonthDTO.java index 0dc0fa27..a227b076 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleYearMonthDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleYearMonthDTO.java @@ -10,15 +10,12 @@ public class ScheduleYearMonthDTO { private String id; - private String content; + private String name; private String startAt; private String endAt; - private String createdAt; - private String updatedAt; - private String deletedAt; - private Boolean active; private String memberId; private String year; private String month; + private String yearMonth; } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java index 65200e0d..ea34e92e 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java @@ -2,6 +2,7 @@ import org.apache.ibatis.annotations.Mapper; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; +import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDetailDTO; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleYearMonthDTO; import java.util.List; @@ -10,9 +11,9 @@ @Mapper public interface ScheduleMapper { - List findSchedulesByMemberIdAndSrtAt(Map arg); + List findSchedulesByMemberIdAndSrtAt(ScheduleDTO scheduleDTO); - List findSchedulesByMemberIdAndYearMonth(Map arg); + List findSchedulesByMemberIdAndYearMonth(ScheduleYearMonthDTO scheduleYearMonthDTO); - ScheduleDTO findScheduleByMemberIdAndScheduleId(ScheduleDTO scheduleDTO); + ScheduleDetailDTO findScheduleByMemberIdAndScheduleId(ScheduleDetailDTO scheduleDetailDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleService.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryService.java similarity index 66% rename from src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleService.java rename to src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryService.java index 88e93ab6..d579db05 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleService.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryService.java @@ -1,14 +1,15 @@ package stanl_2.final_backend.domain.schedule.query.service; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; +import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDetailDTO; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleYearMonthDTO; import java.util.List; -public interface ScheduleService { +public interface ScheduleQueryService { List selectAllSchedule(String memberId); List selectYearMonthSchedule(ScheduleYearMonthDTO scheduleYearMonthDTO); - ScheduleDTO selectDetailSchedule(ScheduleDTO scheduleDTO); + ScheduleDetailDTO selectDetailSchedule(ScheduleDetailDTO scheduleDetailDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java new file mode 100644 index 00000000..0355f9f4 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java @@ -0,0 +1,129 @@ +package stanl_2.final_backend.domain.schedule.query.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.schedule.common.exception.ScheduleCommonException; +import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; +import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; +import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDetailDTO; +import stanl_2.final_backend.domain.schedule.query.dto.ScheduleYearMonthDTO; +import stanl_2.final_backend.domain.schedule.query.repository.ScheduleMapper; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; + +@Slf4j +@Service +public class ScheduleQueryServiceImpl implements ScheduleQueryService { + + private final ScheduleMapper scheduleMapper; + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } + + @Autowired + public ScheduleQueryServiceImpl(ScheduleMapper scheduleMapper) { + this.scheduleMapper = scheduleMapper; + } + + @Override + @Transactional(readOnly = true) + public List selectAllSchedule(String memberId) { + + if(memberId == null || memberId.trim().isEmpty()){ + // 향후 Member의 ErrorCode로 수정할 예정 + throw new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND); + } + + String currentMonth = getCurrentTime().substring(0,7); + + ScheduleDTO scheduleDTO = new ScheduleDTO(); + scheduleDTO.setMemberId(memberId); + scheduleDTO.setMonth(currentMonth); + + try { + List scheduleList = scheduleMapper.findSchedulesByMemberIdAndSrtAt(scheduleDTO); + + if(scheduleList == null || scheduleList.isEmpty()){ + throw new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND); + } + + return scheduleList; + } catch(DataAccessException e){ + throw new ScheduleCommonException(ScheduleErrorCode.DATA_ACCESS_ERROR); + } catch(NullPointerException e){ + throw new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND); + } catch(Exception e){ + throw new ScheduleCommonException(ScheduleErrorCode.INTERNAL_SERVER_ERROR); + } + } + + @Override + @Transactional(readOnly = true) + public List selectYearMonthSchedule(ScheduleYearMonthDTO scheduleYearMonthDTO) { + + if(scheduleYearMonthDTO.getMemberId() == null || scheduleYearMonthDTO.getMemberId().trim().isEmpty()){ + // 향후 Member의 ErrorCode로 수정할 예정 + throw new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND); + } + + if(scheduleYearMonthDTO.getId() == null || scheduleYearMonthDTO.getId().trim().isEmpty()){ + throw new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND); + } + + scheduleYearMonthDTO.setYearMonth(scheduleYearMonthDTO.getYear() + "-" + scheduleYearMonthDTO.getMonth()); + + + try { + List scheduleList = + scheduleMapper.findSchedulesByMemberIdAndYearMonth(scheduleYearMonthDTO); + + if(scheduleList == null || scheduleList.isEmpty()){ + throw new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND); + } + + return scheduleList; + } catch(DataAccessException e){ + throw new ScheduleCommonException(ScheduleErrorCode.DATA_ACCESS_ERROR); + } catch(NullPointerException e){ + throw new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND); + } catch(Exception e){ + throw new ScheduleCommonException(ScheduleErrorCode.INTERNAL_SERVER_ERROR); + } + } + + @Override + @Transactional(readOnly = true) + public ScheduleDetailDTO selectDetailSchedule(ScheduleDetailDTO scheduleDetailDTO) { + + if(scheduleDetailDTO.getMemberId() == null || scheduleDetailDTO.getMemberId().trim().isEmpty()){ + // 향후 Member의 ErrorCode로 수정할 예정 + throw new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND); + } + + if(scheduleDetailDTO.getId() == null || scheduleDetailDTO.getId().trim().isEmpty()){ + throw new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND); + } + + try { + ScheduleDetailDTO responseDetailSchedule + = scheduleMapper.findScheduleByMemberIdAndScheduleId(scheduleDetailDTO); + + return responseDetailSchedule; + } catch(DataAccessException e){ + throw new ScheduleCommonException(ScheduleErrorCode.DATA_ACCESS_ERROR); + } catch(NullPointerException e){ + throw new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND); + } catch(Exception e){ + throw new ScheduleCommonException(ScheduleErrorCode.INTERNAL_SERVER_ERROR); + } + } + + +} diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleServiceImpl.java deleted file mode 100644 index 62623e27..00000000 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleServiceImpl.java +++ /dev/null @@ -1,78 +0,0 @@ -package stanl_2.final_backend.domain.schedule.query.service; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; -import stanl_2.final_backend.domain.schedule.query.dto.ScheduleYearMonthDTO; -import stanl_2.final_backend.domain.schedule.query.repository.ScheduleMapper; - -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@Slf4j -@Service("querScheduleServiceImpl") -public class ScheduleServiceImpl implements ScheduleService{ - - private final ScheduleMapper scheduleMapper; - private String getCurrentTime() { - ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); - return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); - } - - @Autowired - public ScheduleServiceImpl(ScheduleMapper scheduleMapper) { - this.scheduleMapper = scheduleMapper; - } - - @Override - @Transactional(readOnly = true) - public List selectAllSchedule(String memberId) { - - String currentMonth = getCurrentTime().substring(0,7); - - Map arg = new HashMap<>(); - - arg.put("memberId",memberId); - arg.put("month",currentMonth); - - List scheduleList = scheduleMapper.findSchedulesByMemberIdAndSrtAt(arg); - - return scheduleList; - } - - @Override - @Transactional(readOnly = true) - public List selectYearMonthSchedule(ScheduleYearMonthDTO scheduleYearMonthDTO) { - - String memberId = scheduleYearMonthDTO.getMemberId(); - String year = scheduleYearMonthDTO.getYear(); - String month = scheduleYearMonthDTO.getMonth(); - - String checkDate = year + "-" + month; - - Map arg = new HashMap<>(); - - arg.put("memberId", memberId); - arg.put("yearMonth", checkDate); - - List scheduleList = scheduleMapper.findSchedulesByMemberIdAndYearMonth(arg); - - return scheduleList; - } - - @Override - @Transactional(readOnly = true) - public ScheduleDTO selectDetailSchedule(ScheduleDTO scheduleDTO) { - - ScheduleDTO responseSchedule = scheduleMapper.findScheduleByMemberIdAndScheduleId(scheduleDTO); - - return responseSchedule; - } - -} diff --git a/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml b/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml index f2896c3b..4c9c8b2e 100644 --- a/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml @@ -15,17 +15,13 @@ - SELECT A.SCH_ID, A.SCH_NAME, - A.SCH_CONT, A.SCH_SRT_AT, A.SCH_END_AT, - A.CREATED_AT, - A.UPDATED_AT, - A.DELETED_AT, - A.ACTIVE, A.MEM_ID FROM TB_SCHEDULE A WHERE MEM_ID = #{ memberId } @@ -33,17 +29,13 @@ AND ACTIVE = TRUE; - SELECT A.SCH_ID, A.SCH_NAME, - A.SCH_CONT, A.SCH_SRT_AT, A.SCH_END_AT, - A.CREATED_AT, - A.UPDATED_AT, - A.DELETED_AT, - A.ACTIVE, A.MEM_ID FROM TB_SCHEDULE A WHERE MEM_ID = #{ memberId } @@ -52,17 +44,13 @@ + + SELECT A.CONR_ID, @@ -73,17 +123,128 @@ AND A.ACTIVE = TRUE; - SELECT A.CONR_ID, + A.CONR_NAME, A.CONR_CUST_NAME, A.CONR_COMP_NAME, A.CONR_STAT, A.CREATED_URL, - A.ACTIVE + A.ACTIVE, + B.PROD_NAME FROM TB_CONTRACT A + JOIN TB_PRODUCT B + ON A.PROD_ID = B.PROD_ID WHERE A.MEM_ID = #{ memId } - AND A.ACTIVE = TRUE; + AND A.ACTIVE = TRUE + ORDER BY A.CREATED_AT DESC + LIMIT #{pageSize} OFFSET #{offset} + + + + + + + \ No newline at end of file From f6139c589a6c821c6382a3256ba184f7bed43297 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Fri, 15 Nov 2024 02:03:40 +0900 Subject: [PATCH 055/563] =?UTF-8?q?feat:=20=EC=88=98=EC=A3=BC=EC=84=9C=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EA=B5=AC=ED=98=84(#40)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/OrderCommandController.java | 56 +++++++++++++ .../application/dto/OrderRegistDTO.java | 16 ++++ .../service/OrderCommandService.java | 7 ++ .../domain/aggregate/entity/Order.java | 80 +++++++++++++++++++ .../domain/repository/OrderRepository.java | 7 ++ .../service/OrderCommandServiceImpl.java | 40 ++++++++++ .../exception/OrderCommonException.java | 16 ++++ .../common/exception/OrderErrorCode.java | 52 ++++++++++++ .../exception/OrderExceptionResponse.java | 22 +++++ .../common/response/OrderResponseMessage.java | 14 ++++ src/main/resources/sql/ddl.sql | 70 ++++++++-------- 11 files changed, 347 insertions(+), 33 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderCommandController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderRegistDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/order/command/application/service/OrderCommandService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/order/command/domain/aggregate/entity/Order.java create mode 100644 src/main/java/stanl_2/final_backend/domain/order/command/domain/repository/OrderRepository.java create mode 100644 src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java create mode 100644 src/main/java/stanl_2/final_backend/domain/order/common/exception/OrderCommonException.java create mode 100644 src/main/java/stanl_2/final_backend/domain/order/common/exception/OrderErrorCode.java create mode 100644 src/main/java/stanl_2/final_backend/domain/order/common/exception/OrderExceptionResponse.java create mode 100644 src/main/java/stanl_2/final_backend/domain/order/common/response/OrderResponseMessage.java diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderCommandController.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderCommandController.java new file mode 100644 index 00000000..d6df2e62 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderCommandController.java @@ -0,0 +1,56 @@ +package stanl_2.final_backend.domain.order.command.application.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; +import stanl_2.final_backend.domain.order.command.application.dto.OrderRegistDTO; +import stanl_2.final_backend.domain.order.command.application.service.OrderCommandService; +import stanl_2.final_backend.domain.order.common.response.OrderResponseMessage; + +@RestController +@RequestMapping("/api/v1/order") +public class OrderCommandController { + + private final OrderCommandService orderCommandService; + + @Autowired + public OrderCommandController(OrderCommandService orderCommandService) { + this.orderCommandService = orderCommandService; + } + + /** + * [POST] http://localhost:8080/api/v1/order + * Request + * { + * "title": "241115 셀토스 계약 주문", + * "content": "\n\n\n \n \n 자동차 매매 계약서\n \n\n\n
\n
\n

기아 자동차 매매 계약서

\n
\n
\n

계약 정보

\n \n \n \n
계약 번호KL-JS계약일2022-11-17계약장소서울 강남구
담당자유혜진전화번호010-7158-8796
\n
\n
\n

고객사항

\n
성명홍길동상호
주민등록번호****-*******사업자등록번호
주소**********사업자등록주소OOOOOOOO
전화(휴대폰)010-****-****구분개인
E-mail****@****.com구매유형현금
\n
\n
\n

차량사항

\n
차종Q4 e-tron 40일련번호2Y2Y
선택옵션AO대수1대
인도예정일2023년 인도인도장소서울지점
특약사항- 특약사항 내용이 여기에 표시됩니다.
\n
\n
\n

금액사항

\n
차량가격66,700,000원
계약금900,000원
중도금100,000원
인도금65,700,000원
탁송료65,700,000원
\n
\n
\n

본 계약서 주요 내용을 확인하고 계약을 체결하였음을 확인합니다.

\n
\n
매수인 (서명): ****
\n
매도인 (서명): ****
\n
\n
\n
\n\n", + * "conrId": "CON_000000001", + * "memId": "MEM_000000001" + * } + * */ + @Operation(summary = "수주서 등록 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "수주서 등록 성공", + content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) + }) + @PostMapping("") + public ResponseEntity postOrder(@RequestBody OrderRegistDTO orderRegistDTO) { + + orderCommandService.registerOrder(orderRegistDTO); + + return ResponseEntity.ok(OrderResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + } + + + +} diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderRegistDTO.java new file mode 100644 index 00000000..e72380dc --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderRegistDTO.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.order.command.application.dto; + +import jakarta.persistence.Column; +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class OrderRegistDTO { + private String title; + private String content; + private String conrId; + private String memId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/service/OrderCommandService.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/service/OrderCommandService.java new file mode 100644 index 00000000..51d7e89b --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/service/OrderCommandService.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.order.command.application.service; + +import stanl_2.final_backend.domain.order.command.application.dto.OrderRegistDTO; + +public interface OrderCommandService { + void registerOrder(OrderRegistDTO orderRegistDTO); +} diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/domain/aggregate/entity/Order.java b/src/main/java/stanl_2/final_backend/domain/order/command/domain/aggregate/entity/Order.java new file mode 100644 index 00000000..94b13dae --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/order/command/domain/aggregate/entity/Order.java @@ -0,0 +1,80 @@ +package stanl_2.final_backend.domain.order.command.domain.aggregate.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "TB_ORDER") +public class Order { + + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "ORD") + ) + @Column(name = "ORD_ID") + private String id; + + @Column(name = "ORD_TTL", nullable = false) + private String title; + + @Lob + @Column(name = "ORD_CONT", nullable = false, columnDefinition = "TEXT") + private String content; + + @Column(name = "ACTIVE", nullable = false) + private Boolean active = true; + + @Column(name = "CREATED_AT", nullable = false, updatable = false) + private String createdAt; + + @Column(name = "UPDATED_AT", nullable = false) + private String updatedAt; + + @Column(name = "DELETED_AT") + private String deletedAt; + + @Column(name = "ORD_STAT", nullable = false) + private String status; + + @Column(name = "CONR_ID", nullable = false) + private String conrId; + + @Column(name = "ADMIN_ID") + private String adminId; + + @Column(name = "MEM_ID", nullable = false) + private String memId; + + // Insert 되기 전에 실행 + @PrePersist + private void prePersist() { + this.createdAt = getCurrentTime(); + this.updatedAt = this.createdAt; + } + + // Update 되기 전에 실행 + @PreUpdate + private void preUpdate() { + this.updatedAt = getCurrentTime(); + } + + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/domain/repository/OrderRepository.java b/src/main/java/stanl_2/final_backend/domain/order/command/domain/repository/OrderRepository.java new file mode 100644 index 00000000..3b8342cc --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/order/command/domain/repository/OrderRepository.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.order.command.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import stanl_2.final_backend.domain.order.command.domain.aggregate.entity.Order; + +public interface OrderRepository extends JpaRepository { +} diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java new file mode 100644 index 00000000..a967eea6 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java @@ -0,0 +1,40 @@ +package stanl_2.final_backend.domain.order.command.domain.service; + +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.contract.command.domain.aggregate.entity.Contract; +import stanl_2.final_backend.domain.contract.command.domain.repository.ContractRepository; +import stanl_2.final_backend.domain.order.command.application.dto.OrderRegistDTO; +import stanl_2.final_backend.domain.order.command.application.service.OrderCommandService; +import stanl_2.final_backend.domain.order.command.domain.aggregate.entity.Order; +import stanl_2.final_backend.domain.order.command.domain.repository.OrderRepository; + +@Service +public class OrderCommandServiceImpl implements OrderCommandService { + + private final OrderRepository orderRepository; + private final ContractRepository contractRepository; + private final ModelMapper modelMapper; + + public OrderCommandServiceImpl(OrderRepository orderRepository, ContractRepository contractRepository, ModelMapper modelMapper) { + this.orderRepository = orderRepository; + this.contractRepository = contractRepository; + this.modelMapper = modelMapper; + } + + @Override + @Transactional + public void registerOrder(OrderRegistDTO orderRegistDTO) { + // 회원인지 확인여부 + + // 회원의 계약서가 맞는지 확인 +// Contract contract = contractRepository.findById(orderRegistDTO.getConrId()) +// .orElseThrow(() -> new Contrac) + + Order order = modelMapper.map(orderRegistDTO, Order.class); + order.setStatus("WAIT"); + + orderRepository.save(order); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/order/common/exception/OrderCommonException.java b/src/main/java/stanl_2/final_backend/domain/order/common/exception/OrderCommonException.java new file mode 100644 index 00000000..6e81a006 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/order/common/exception/OrderCommonException.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.order.common.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class OrderCommonException extends RuntimeException { + private final OrderErrorCode sampleErrorCode; + + // 에러 발생시 ErroCode 별 메시지 + @Override + public String getMessage() { + return this.sampleErrorCode.getMsg(); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/order/common/exception/OrderErrorCode.java b/src/main/java/stanl_2/final_backend/domain/order/common/exception/OrderErrorCode.java new file mode 100644 index 00000000..068d864c --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/order/common/exception/OrderErrorCode.java @@ -0,0 +1,52 @@ +package stanl_2.final_backend.domain.order.common.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum OrderErrorCode { + + /** + * 400(Bad Request) + * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. + */ + + + + /** + * 401(Unauthorized) + * 비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만, + * 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다. + * 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다. + */ + + + /** + * 403(Forbidden) + * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. + * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다. + */ + + + + /** + * 404(Not Found) + * 서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다. + * 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있습니다. + * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. + * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. + */ + SAMPLE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "sample 데이터를 찾지 못했습니다"), + CENTER_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "center 데이터를 찾지 못했습니다."), + /** + * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. + */ + INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); + + + private final Integer code; + private final HttpStatus httpStatus; + private final String msg; +} diff --git a/src/main/java/stanl_2/final_backend/domain/order/common/exception/OrderExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/order/common/exception/OrderExceptionResponse.java new file mode 100644 index 00000000..cac1fb2e --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/order/common/exception/OrderExceptionResponse.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.order.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class OrderExceptionResponse { + private final Integer code; + private final String msg; + private final HttpStatus httpStatus; + + public OrderExceptionResponse(OrderErrorCode sampleErrorCode) { + this.code = sampleErrorCode.getCode(); + this.msg = sampleErrorCode.getMsg(); + this.httpStatus = sampleErrorCode.getHttpStatus(); + } + + public static OrderExceptionResponse of(OrderErrorCode sampleErrorCode) { + return new OrderExceptionResponse(sampleErrorCode); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/order/common/response/OrderResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/order/common/response/OrderResponseMessage.java new file mode 100644 index 00000000..00c7e773 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/order/common/response/OrderResponseMessage.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.order.common.response; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class OrderResponseMessage { + private int httpStatus; + private String msg; + private Object result; +} \ No newline at end of file diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index fa3912d7..95d8d110 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -176,6 +176,7 @@ CREATE TABLE tb_order ( ORD_ID VARCHAR(255) NOT NULL, ORD_TTL VARCHAR(255) NOT NULL, + ORD_CONT TEXT NOT NULL, ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, CREATED_AT CHAR(19) NOT NULL, UPDATED_AT CHAR(19) NOT NULL, @@ -183,10 +184,10 @@ CREATE TABLE tb_order ORD_STAT VARCHAR(255) NOT NULL DEFAULT 'WAIT', CONR_ID VARCHAR(255) NOT NULL, MEM_ID VARCHAR(255) NOT NULL, - MEM_ID2 VARCHAR(255) NOT NULL, + ADMIN_ID VARCHAR(255) NULL, PRIMARY KEY (ORD_ID), FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE, - FOREIGN KEY (MEM_ID2) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE, + FOREIGN KEY (ADMIN_ID) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE, FOREIGN KEY (CONR_ID) REFERENCES tb_contract (CONR_ID) ON DELETE CASCADE ); @@ -464,16 +465,16 @@ VALUES ('MEM_000000001', 101, 'pwd1234', '김철수', 'chulsoo@example.com', 35, '2024-01-10 12:00:00', TRUE); INSERT INTO tb_member_role (MEM_ROL_ID, MEM_ROL_NAME, MEM_ID) -VALUES ('ROL_000000001', '팀장', 'MEM_000000001'), - ('ROL_000000002', '영업사원', 'MEM_000000002'), - ('ROL_000000003', '기술지원', 'MEM_000000003'), - ('ROL_000000004', '관리자', 'MEM_000000004'), - ('ROL_000000005', '고객관리', 'MEM_000000005'), - ('ROL_000000006', '인턴', 'MEM_000000009'), - ('ROL_000000007', '파트타임', 'MEM_000000007'), - ('ROL_000000008', '정규직', 'MEM_000000008'), - ('ROL_000000009', '계약직', 'MEM_000000010'), - ('ROL_000000010', '프로젝트 매니저', 'MEM_000000006'); +VALUES ('ROL_000000001', '영업 사원', 'MEM_000000001'), + ('ROL_000000002', '영업 사원', 'MEM_000000002'), + ('ROL_000000003', '영업 사원', 'MEM_000000003'), + ('ROL_000000004', '영업 사원', 'MEM_000000004'), + ('ROL_000000005', '영업 관리자', 'MEM_000000005'), + ('ROL_000000006', '영업 관리자', 'MEM_000000009'), + ('ROL_000000007', '영업 관리자', 'MEM_000000007'), + ('ROL_000000008', '영업 담당자', 'MEM_000000008'), + ('ROL_000000009', '영업 담당자', 'MEM_000000010'), + ('ROL_000000010', '영업 담당자', 'MEM_000000006'); INSERT INTO tb_customer_info (CUST_ID, CUST_NAME, CUST_AGE, CUST_SEX, CUST_PHO, CUST_EMER_PHO, CUST_EMA, ACTIVE, MEM_ID) VALUES ('CUS_000000001', '홍길동', 45, 'MALE', '010-1111-2222', '010-2222-3333', 'gildong@example.com', TRUE, @@ -598,27 +599,30 @@ VALUES '/contracts/10', NULL, TRUE, '2024-01-28 23:00:00', '2024-01-29 23:59:00', NULL, 'MEM_000000010', 'CEN_000000010', 'CUS_000000010', 'PRO_000000010'); -INSERT INTO tb_order (ORD_ID, ORD_TTL, ACTIVE, CREATED_AT, UPDATED_AT, ORD_STAT, CONR_ID, MEM_ID, MEM_ID2) -VALUES ('ORD_000000001', '쏘렌토 주문', TRUE, '2024-01-12 09:00:00', '2024-01-12 10:00:00', 'WAIT', 'CON_000000001', - 'MEM_000000001', 'MEM_000000002'), - ('ORD_000000002', '스포티지 주문', TRUE, '2024-01-13 10:00:00', '2024-01-13 11:00:00', 'CONFIRMED', 'CON_000000002', - 'MEM_000000002', 'MEM_000000003'), - ('ORD_000000003', 'K7 주문', TRUE, '2024-01-14 11:00:00', '2024-01-14 12:00:00', 'DELIVERED', 'CON_000000003', - 'MEM_000000003', 'MEM_000000004'), - ('ORD_000000004', '셀토스 주문', TRUE, '2024-01-15 12:00:00', '2024-01-15 13:00:00', 'CANCELLED', 'CON_000000004', - 'MEM_000000004', 'MEM_000000005'), - ('ORD_000000005', 'K3 주문', TRUE, '2024-01-16 14:00:00', '2024-01-16 15:00:00', 'WAIT', 'CON_000000005', - 'MEM_000000005', 'MEM_000000006'), - ('ORD_000000006', '모하비 주문', TRUE, '2024-01-17 09:00:00', '2024-01-17 10:00:00', 'DELIVERED', 'CON_000000006', - 'MEM_000000006', 'MEM_000000007'), - ('ORD_000000007', 'K8 주문', TRUE, '2024-01-18 10:00:00', '2024-01-18 11:00:00', 'WAIT', 'CON_000000007', - 'MEM_000000007', 'MEM_000000008'), - ('ORD_000000008', '스팅어 주문', TRUE, '2024-01-19 12:00:00', '2024-01-19 13:00:00', 'CONFIRMED', 'CON_000000008', - 'MEM_000000008', 'MEM_000000009'), - ('ORD_000000009', '니로 주문', TRUE, '2024-01-20 14:00:00', '2024-01-20 15:00:00', 'CANCELLED', 'CON_000000009', - 'MEM_000000009', 'MEM_000000010'), - ('ORD_000000010', 'K5 주문', TRUE, '2024-01-21 15:00:00', '2024-01-21 16:00:00', 'WAIT', 'CON_000000010', - 'MEM_000000010', 'MEM_000000001'); +INSERT INTO tb_order ( + ORD_ID, ORD_TTL, ORD_CONT, ACTIVE, CREATED_AT, UPDATED_AT, DELETED_AT, ORD_STAT, CONR_ID, MEM_ID, ADMIN_ID +) VALUES +-- 주문 1 +('ORD_000000001', '쏘렌토 계약 주문', '쏘렌토 차량 계약 완료 후 주문 처리', TRUE, '2024-01-10 10:00:00', '2024-01-10 11:00:00', NULL, 'CONFIRMED', 'CON_000000001', 'MEM_000000001', 'MEM_000000005'), +-- 주문 2 +('ORD_000000002', '스포티지 구매 주문', '스포티지 고객 상담 후 주문 확정', TRUE, '2024-01-11 12:00:00', '2024-01-11 13:00:00', NULL, 'WAIT', 'CON_000000002', 'MEM_000000002', 'MEM_000000005'), +-- 주문 3 +('ORD_000000003', 'K7 리스 주문', 'K7 리스 계약 완료 후 주문 생성', TRUE, '2024-01-12 09:00:00', '2024-01-12 10:00:00', NULL, 'CONFIRMED', 'CON_000000003', 'MEM_000000003', 'MEM_000000005'), +-- 주문 4 +('ORD_000000004', '셀토스 추가 주문', '셀토스 고객 추가 옵션 주문', TRUE, '2024-01-13 10:30:00', '2024-01-13 11:30:00', NULL, 'CANCELLED', 'CON_000000004', 'MEM_000000004', 'MEM_000000005'), +-- 주문 5 +('ORD_000000005', 'K3 계약 주문', 'K3 차량 계약 후 최종 주문', TRUE, '2024-01-14 11:00:00', '2024-01-14 12:00:00', NULL, 'DELIVERED', 'CON_000000005', 'MEM_000000001', 'MEM_000000006'), +-- 주문 6 +('ORD_000000006', '모하비 계약 수정 주문', '모하비 계약 변경에 따른 주문 수정', TRUE, '2024-01-15 13:00:00', '2024-01-15 14:00:00', NULL, 'WAIT', 'CON_000000006', 'MEM_000000002', 'MEM_000000006'), +-- 주문 7 +('ORD_000000007', 'K8 리스 주문', 'K8 차량 리스 계약 주문', TRUE, '2024-01-16 14:30:00', '2024-01-16 15:30:00', NULL, 'CONFIRMED', 'CON_000000007', 'MEM_000000003', 'MEM_000000006'), +-- 주문 8 +('ORD_000000008', '스팅어 구매 주문', '스팅어 차량 구매 계약 후 주문 확정', TRUE, '2024-01-17 16:00:00', '2024-01-17 17:00:00', NULL, 'DELIVERED', 'CON_000000008', 'MEM_000000004', 'MEM_000000006'), +-- 주문 9 +('ORD_000000009', '니로 전기차 주문', '니로 전기차 계약 주문', TRUE, '2024-01-18 17:30:00', '2024-01-18 18:30:00', NULL, 'WAIT', 'CON_000000009', 'MEM_000000001', 'MEM_000000010'), +-- 주문 10 +('ORD_000000010', 'K5 재고 주문', 'K5 재고 차량 추가 주문', TRUE, '2024-01-19 09:00:00', '2024-01-19 10:00:00', NULL, 'CONFIRMED', 'CON_000000010', 'MEM_000000001', 'MEM_00000009'); + INSERT INTO tb_problem (PROB_ID, PROB_TTL, PROB_CONT, CREATED_AT, UPDATED_AT, ACTIVE, CUST_ID, MEM_ID, PROD_ID) VALUES ('PROB_000000001', '쏘렌토 엔진 문제', '엔진에서 소음 발생', '2024-01-12 14:00:00', '2024-01-12 15:00:00', TRUE, From 964569e8b868a7627b2452e6b0606de98f83d9af Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Fri, 15 Nov 2024 02:40:32 +0900 Subject: [PATCH 056/563] =?UTF-8?q?feat:=20=EC=88=98=EC=A3=BC=EC=84=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=82=AD=EC=A0=9C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84(#40)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/OrderCommandController.java | 55 ++++++++++++++++++- .../application/dto/OrderModifyDTO.java | 15 +++++ .../application/dto/OrderRegistDTO.java | 1 - .../service/OrderCommandService.java | 5 ++ .../service/OrderCommandServiceImpl.java | 50 ++++++++++++++++- .../common/exception/OrderErrorCode.java | 3 +- 6 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderModifyDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderCommandController.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderCommandController.java index d6df2e62..9c58cf0c 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderCommandController.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderCommandController.java @@ -9,6 +9,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; +import stanl_2.final_backend.domain.order.command.application.dto.OrderModifyDTO; import stanl_2.final_backend.domain.order.command.application.dto.OrderRegistDTO; import stanl_2.final_backend.domain.order.command.application.service.OrderCommandService; import stanl_2.final_backend.domain.order.common.response.OrderResponseMessage; @@ -46,11 +47,63 @@ public ResponseEntity postOrder(@RequestBody OrderRegistDT return ResponseEntity.ok(OrderResponseMessage.builder() .httpStatus(200) - .msg("성공") + .msg("수주서가 성공적으로 등록되었습니다.") .result(null) .build()); } + /** + * [PUT] http://localhost:8080/api/v1/order/ORD_000000011 + * Request + * { + * "title": "241115 셀토스 계약 주문 수정!!", + * "content": "\n\n\n \n \n 자동차 매매 계약서\n \n\n\n
\n
\n

기아 자동차 매매 계약서

\n
\n
\n

계약 정보

\n \n \n \n
계약 번호KL-JS계약일2022-11-17계약장소서울 강남구
담당자유혜진전화번호010-7158-8796
\n
\n
\n

고객사항

\n
성명홍길동상호
주민등록번호****-*******사업자등록번호
주소**********사업자등록주소OOOOOOOO
전화(휴대폰)010-****-****구분개인
E-mail****@****.com구매유형현금
\n
\n
\n

차량사항

\n
차종Q4 e-tron 40일련번호2Y2Y
선택옵션AO대수1대
인도예정일2023년 인도인도장소서울지점
특약사항- 특약사항 내용이 여기에 표시됩니다.
\n
\n
\n

금액사항

\n
차량가격66,700,000원
계약금900,000원
중도금100,000원
인도금65,700,000원
탁송료65,700,000원
\n
\n
\n

본 계약서 주요 내용을 확인하고 계약을 체결하였음을 확인합니다.

\n
\n
매수인 (서명): ****
\n
매도인 (서명): ****
\n
\n
\n
\n\n", + * "memId": "MEM_000000001" + * } + * */ + @Operation(summary = "수주서 수정 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "수주서 수정 성공", + content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) + }) + @PutMapping("{id}") + public ResponseEntity putOrder(@PathVariable String id, + @RequestBody OrderModifyDTO orderModifyDTO) { + + orderModifyDTO.setId(id); + OrderModifyDTO orderModifyResponseDTO = orderCommandService.modifyOrder(orderModifyDTO); + + return ResponseEntity.ok(OrderResponseMessage.builder() + .httpStatus(200) + .msg("수주서가 성공적으로 수정되었습니다.") + .result(orderModifyResponseDTO) + .build()); + } + /** + * [PUT] http://localhost:8080/api/v1/order/ORD_000000011 + * Request + * { + * "title": "241115 셀토스 계약 주문 수정!!", + * "content": "\n\n\n \n \n 자동차 매매 계약서\n \n\n\n
\n
\n

기아 자동차 매매 계약서

\n
\n
\n

계약 정보

\n \n \n \n
계약 번호KL-JS계약일2022-11-17계약장소서울 강남구
담당자유혜진전화번호010-7158-8796
\n
\n
\n

고객사항

\n
성명홍길동상호
주민등록번호****-*******사업자등록번호
주소**********사업자등록주소OOOOOOOO
전화(휴대폰)010-****-****구분개인
E-mail****@****.com구매유형현금
\n
\n
\n

차량사항

\n
차종Q4 e-tron 40일련번호2Y2Y
선택옵션AO대수1대
인도예정일2023년 인도인도장소서울지점
특약사항- 특약사항 내용이 여기에 표시됩니다.
\n
\n
\n

금액사항

\n
차량가격66,700,000원
계약금900,000원
중도금100,000원
인도금65,700,000원
탁송료65,700,000원
\n
\n
\n

본 계약서 주요 내용을 확인하고 계약을 체결하였음을 확인합니다.

\n
\n
매수인 (서명): ****
\n
매도인 (서명): ****
\n
\n
\n
\n\n", + * "memId": "MEM_000000001" + * } + * */ + @Operation(summary = "수주서 삭제 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "수주서 삭제 성공", + content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) + }) + @DeleteMapping("/{id}") + public ResponseEntity deleteTest(@PathVariable("id") String id) { + + orderCommandService.deleteOrder(id); + + return ResponseEntity.ok(OrderResponseMessage.builder() + .httpStatus(200) + .msg("수주서를 성공적으로 삭제하였습니다.") + .result(null) + .build()); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderModifyDTO.java new file mode 100644 index 00000000..4097939e --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderModifyDTO.java @@ -0,0 +1,15 @@ +package stanl_2.final_backend.domain.order.command.application.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class OrderModifyDTO { + private String id; + private String title; + private String content; + private String memId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderRegistDTO.java index e72380dc..20655611 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderRegistDTO.java @@ -1,6 +1,5 @@ package stanl_2.final_backend.domain.order.command.application.dto; -import jakarta.persistence.Column; import lombok.*; @AllArgsConstructor diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/service/OrderCommandService.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/service/OrderCommandService.java index 51d7e89b..35665d2f 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/application/service/OrderCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/service/OrderCommandService.java @@ -1,7 +1,12 @@ package stanl_2.final_backend.domain.order.command.application.service; +import stanl_2.final_backend.domain.order.command.application.dto.OrderModifyDTO; import stanl_2.final_backend.domain.order.command.application.dto.OrderRegistDTO; public interface OrderCommandService { void registerOrder(OrderRegistDTO orderRegistDTO); + + OrderModifyDTO modifyOrder(OrderModifyDTO orderModifyDTO); + + void deleteOrder(String id); } diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java index a967eea6..cb5006bc 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java @@ -3,12 +3,18 @@ import org.modelmapper.ModelMapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.contract.command.domain.aggregate.entity.Contract; import stanl_2.final_backend.domain.contract.command.domain.repository.ContractRepository; +import stanl_2.final_backend.domain.order.command.application.dto.OrderModifyDTO; import stanl_2.final_backend.domain.order.command.application.dto.OrderRegistDTO; import stanl_2.final_backend.domain.order.command.application.service.OrderCommandService; import stanl_2.final_backend.domain.order.command.domain.aggregate.entity.Order; import stanl_2.final_backend.domain.order.command.domain.repository.OrderRepository; +import stanl_2.final_backend.domain.order.common.exception.OrderCommonException; +import stanl_2.final_backend.domain.order.common.exception.OrderErrorCode; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; @Service public class OrderCommandServiceImpl implements OrderCommandService { @@ -23,6 +29,11 @@ public OrderCommandServiceImpl(OrderRepository orderRepository, ContractReposito this.modelMapper = modelMapper; } + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } + @Override @Transactional public void registerOrder(OrderRegistDTO orderRegistDTO) { @@ -37,4 +48,41 @@ public void registerOrder(OrderRegistDTO orderRegistDTO) { orderRepository.save(order); } + + @Override + @Transactional + public OrderModifyDTO modifyOrder(OrderModifyDTO orderModifyDTO) { + // 회원인지 확인여부 + + Order order = orderRepository.findById(orderModifyDTO.getId()) + .orElseThrow(() -> new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND)); + + Order updateOrder = modelMapper.map(orderModifyDTO, Order.class); + updateOrder.setCreatedAt(order.getCreatedAt()); + updateOrder.setUpdatedAt(order.getUpdatedAt()); + updateOrder.setStatus(order.getStatus()); + updateOrder.setActive(order.getActive()); + updateOrder.setConrId(order.getConrId()); + + orderRepository.save(updateOrder); + + OrderModifyDTO orderModifyResponse = modelMapper.map(updateOrder, OrderModifyDTO.class); + + return orderModifyResponse; + } + + @Override + @Transactional + public void deleteOrder(String id) { + + // 회원 확인 + + Order order = orderRepository.findById(id) + .orElseThrow(() -> new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND)); + + order.setActive(false); + order.setDeletedAt(getCurrentTime()); + + orderRepository.save(order); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/order/common/exception/OrderErrorCode.java b/src/main/java/stanl_2/final_backend/domain/order/common/exception/OrderErrorCode.java index 068d864c..f5e5bb4a 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/common/exception/OrderErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/order/common/exception/OrderErrorCode.java @@ -38,8 +38,7 @@ public enum OrderErrorCode { * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. */ - SAMPLE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "sample 데이터를 찾지 못했습니다"), - CENTER_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "center 데이터를 찾지 못했습니다."), + ORDER_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "수주서를 찾지 못했습니다"), /** * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. */ From 9869e764f6bb32ec486916237d8d4ec3a2a1bad8 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Fri, 15 Nov 2024 09:42:22 +0900 Subject: [PATCH 057/563] =?UTF-8?q?feat:=20=ED=98=84=EC=9E=AC=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EC=A0=95=EB=B3=B4=20=EB=A9=94=EC=86=8C?= =?UTF-8?q?=EB=93=9C=20=EB=A0=88=EB=B2=A8=EC=97=90=EC=84=9C=20=EC=B6=9C?= =?UTF-8?q?=EB=A0=A5(#41)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/MemberController.java | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java index 9f76176f..06383f39 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java @@ -4,6 +4,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -24,21 +25,24 @@ public MemberController(MemberCommandService memberCommandService) { this.memberCommandService = memberCommandService; } - - @GetMapping("authorities") - public ResponseEntity check(Principal principal) { - if (principal == null || "anonymousUser".equals(principal.getName())) { + @GetMapping("/authorities") + public ResponseEntity check(Authentication authentication) { + if (authentication == null || !authentication.isAuthenticated() || "anonymousUser".equals(authentication.getName())) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(MemberResponseMessage.builder() - .httpStatus(401) - .msg("Unauthorized") - .build()); + .body(MemberResponseMessage.builder() + .httpStatus(401) + .msg("Unauthorized") + .build()); } + // 인증된 사용자 정보 출력 + System.out.println("인증된 사용자: " + authentication.getName()); + return ResponseEntity.ok(MemberResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result("인증된 사용자: " + principal) - .build()); + .httpStatus(200) + .msg("성공") + .result("인증된 사용자: " + authentication.getName()) + .build()); } + } From 2514b73ea2f09f90e228ce3f63a87702aa36d06b Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Fri, 15 Nov 2024 10:53:44 +0900 Subject: [PATCH 058/563] =?UTF-8?q?refactor:=20ResponseMessage=20=EC=88=98?= =?UTF-8?q?=EC=A0=95(#44)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ContractCommandController.java | 36 ++++++++++++++----- .../query/controller/ContractController.java | 22 ++++++------ 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractCommandController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractCommandController.java index d0abf091..eaa6eeac 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractCommandController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractCommandController.java @@ -8,7 +8,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.contract.command.application.dto.ContractModifyDTO; import stanl_2.final_backend.domain.contract.command.application.dto.ContractRegistDTO; import stanl_2.final_backend.domain.contract.command.application.service.ContractCommandService; @@ -26,25 +25,44 @@ public ContractCommandController(ContractCommandService contractCommandService) } /** - * [POST] http://localhost:7777/api/v1/contract + * [POST] http://localhost:8080/api/v1/contract * Request * { - * "name": "tes1", - * "num": 123 - * } + * "name": "Sample Contract", + * "custName": "John Doe", + * "custIdenNo": "123456-7890123", + * "custAddrress": "123 Main Street, City, Country", + * "custEmail": "johndoe@example.com", + * "custPhone": "+1-234-567-8901", + * "compName": "Doe Industries", + * "custCla": "Premium", + * "custPurCond": "Full Payment", + * "seriNum": "A1B2C3D4", + * "seleOpti": "Extended Warranty", + * "downPay": 10000, + * "intePay": 500, + * "remPay": 15000, + * "consPay": 5000, + * "delvDate": "2024-12-15", + * "delvLoc": "Warehouse No. 3, Industrial Park", + * "state": "WAIT", + * "noOfVeh": "2", + * "createdUrl": "\n\n\n \n \n 자동차 매매 계약서\n \n\n\n
\n
\n

기아 자동차 매매 계약서

\n
\n
\n

계약 정보

\n \n \n \n
계약 번호KL-JS계약일2022-11-17계약장소서울 강남구
담당자유혜진전화번호010-7158-8796
\n
\n
\n

고객사항

\n
성명홍길동상호
주민등록번호****-*******사업자등록번호
주소**********사업자등록주소OOOOOOOO
전화(휴대폰)010-****-****구분개인
E-mail****@****.com구매유형현금
\n
\n
\n

차량사항

\n
차종Q4 e-tron 40일련번호2Y2Y
선택옵션AO대수1대
인도예정일2023년 인도인도장소서울지점
특약사항- 특약사항 내용이 여기에 표시됩니다.
\n
\n
\n

금액사항

\n
차량가격66,700,000원
계약금900,000원
중도금100,000원
인도금65,700,000원
탁송료65,700,000원
\n
\n
\n

본 계약서 주요 내용을 확인하고 계약을 체결하였음을 확인합니다.

\n
\n
매수인 (서명): ****
\n
매도인 (서명): ****
\n
\n
\n
\n\n", + * "memId": "MEM_000000001" + * } * */ @Operation(summary = "계약서 등록 테스트") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) @PostMapping("{id}") - public ResponseEntity postTest(@PathVariable String id, + public ResponseEntity postTest(@PathVariable String id, @RequestBody ContractRegistDTO contractRegistRequestDTO) { contractRegistRequestDTO.setMemId(id); contractCommandService.registerContract(contractRegistRequestDTO); - return ResponseEntity.ok(SampleResponseMessage.builder() + return ResponseEntity.ok(ContractResponseMessage.builder() .httpStatus(200) .msg("성공") .result(null) @@ -97,7 +115,7 @@ public ResponseEntity putContract(@PathVariable String } /** - * [DELETE] http://localhost:7777/api/v1/sample?mem_id=SAM_000001 + * [DELETE] http://localhost:8080/api/v1/contract/CON_000000011 * */ @Operation(summary = "샘플 삭제 테스트") @ApiResponses(value = { diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java index 0cc77e81..80de0579 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java @@ -11,9 +11,7 @@ import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.contract.common.exception.ContractCommonException; -import stanl_2.final_backend.domain.contract.common.exception.ContractErrorCode; -import stanl_2.final_backend.domain.contract.query.dto.ContractSearchDTO; +import stanl_2.final_backend.domain.contract.common.response.ContractResponseMessage; import stanl_2.final_backend.domain.contract.query.dto.ContractSeletIdDTO; import stanl_2.final_backend.domain.contract.query.service.ContractQueryService; import stanl_2.final_backend.domain.schedule.common.response.ResponseMessage; @@ -38,14 +36,14 @@ public ContractController(ContractQueryService contractQueryService) { @Operation(summary = "계약서 전체 조회 api") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "계약서 전채 조회 성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) @GetMapping("{memId}") - public ResponseEntity getAllContract(@PathVariable("memId") String memId, + public ResponseEntity getAllContract(@PathVariable("memId") String memId, @PageableDefault(size = 10) Pageable pageable) { Page> responseContracts = contractQueryService.selectAll(memId, pageable); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(ContractResponseMessage.builder() .httpStatus(200) .msg("계약서 전체 조회 성공") .result(responseContracts) @@ -58,10 +56,10 @@ public ResponseEntity getAllContract(@PathVariable("memId") Str @Operation(summary = "계약서 상세 조회 api") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "계약서 상세 조회 성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) @GetMapping("{id}/{memId}") - public ResponseEntity getDetailContract(@PathVariable("id") String id, + public ResponseEntity getDetailContract(@PathVariable("id") String id, @PathVariable("memId") String memId) { ContractSeletIdDTO contractDTO = new ContractSeletIdDTO(); @@ -70,7 +68,7 @@ public ResponseEntity getDetailContract(@PathVariable("id") Str ContractSeletIdDTO responseContract = contractQueryService.selectDetailContract(contractDTO); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(ContractResponseMessage.builder() .httpStatus(200) .msg("계약서 상세조회 성공") .result(responseContract) @@ -83,10 +81,10 @@ public ResponseEntity getDetailContract(@PathVariable("id") Str @Operation(summary = "계약서 검색 조회 api") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "계약서 검색 조회 성공", - content = {@Content(schema = @Schema(implementation = ResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) @GetMapping("/search") - public ResponseEntity getContractBySearch(@RequestParam Map params, + public ResponseEntity getContractBySearch(@RequestParam Map params, @PageableDefault(size = 10) Pageable pageable) { Map paramMap = new HashMap<>(); paramMap.put("memId", params.get("memId")); @@ -104,7 +102,7 @@ public ResponseEntity getContractBySearch(@RequestParam Map> responseContracts = contractQueryService.selectBySearch(paramMap); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(ContractResponseMessage.builder() .httpStatus(200) .msg("계약서 검색 조회 성공") .result(responseContracts) From 79fac4c99a12956e8d79d5c8f8f731121c1cc587 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Fri, 15 Nov 2024 11:05:21 +0900 Subject: [PATCH 059/563] =?UTF-8?q?feat:=20merge=20=EC=A0=84=20commit=20(#?= =?UTF-8?q?25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/schedule/query/repository/ScheduleMapper.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml b/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml index ae57edf5..b2b3842f 100644 --- a/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml @@ -15,8 +15,8 @@ - SELECT A.SCH_ID, A.SCH_NAME, @@ -25,8 +25,8 @@ A.MEM_ID FROM TB_SCHEDULE A WHERE A.MEM_ID = #{ memberId } - AND A.SCH_SRT_AT LIKE CONCAT(#{ month },'%') - + + AND DATE_FORMAT(A.SCH_SRT_AT, '%Y-%m') = #{ month } AND A.ACTIVE = TRUE From 6513e97955d97b622942650ba2d063529b6d8756 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Fri, 15 Nov 2024 11:25:29 +0900 Subject: [PATCH 060/563] =?UTF-8?q?feat:=20swagger=20security=20=ED=99=98?= =?UTF-8?q?=EA=B2=BD=20=EA=B5=AC=EC=B6=95(#38)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 + .../controller/MemberController.java | 2 - .../global/config/SwaggerConfig.java | 59 +++++++++---------- .../security/config/DevSecurityConfig.java | 4 +- .../security/config/ProdSecurityConfig.java | 7 ++- 5 files changed, 38 insertions(+), 36 deletions(-) diff --git a/build.gradle b/build.gradle index 24a2496f..1f9b7eb1 100644 --- a/build.gradle +++ b/build.gradle @@ -43,6 +43,8 @@ dependencies { //swagger implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0' + + // modelmapper implementation 'org.modelmapper:modelmapper:3.2.0' // jwt 토큰 라이브러리 추가 diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java index 06383f39..4998e68f 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java @@ -11,8 +11,6 @@ import stanl_2.final_backend.domain.member.command.application.service.MemberCommandService; import stanl_2.final_backend.domain.member.common.response.MemberResponseMessage; -import java.security.Principal; - @Slf4j @RestController("commandMemberController") @RequestMapping("/api/v1/member") diff --git a/src/main/java/stanl_2/final_backend/global/config/SwaggerConfig.java b/src/main/java/stanl_2/final_backend/global/config/SwaggerConfig.java index 77d6feae..c8da78e2 100644 --- a/src/main/java/stanl_2/final_backend/global/config/SwaggerConfig.java +++ b/src/main/java/stanl_2/final_backend/global/config/SwaggerConfig.java @@ -1,47 +1,44 @@ package stanl_2.final_backend.global.config; import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; import io.swagger.v3.oas.annotations.info.Info; -import lombok.RequiredArgsConstructor; -import org.springdoc.core.customizers.OpenApiCustomizer; -import org.springdoc.core.models.GroupedOpenApi; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.security.SecurityScheme; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; +@Configuration @OpenAPIDefinition( info = @Info( title = "Motive_BE API 명세서", description = "Motive 프로젝트 API 명세서", version = "v1" - ) + ), + security = {@SecurityRequirement(name = "Bearer Authentication")} +) +@SecurityScheme( + name = "Bearer Authentication", + type = SecuritySchemeType.HTTP, + scheme = "bearer", + bearerFormat = "JWT" ) -@Configuration -@RequiredArgsConstructor public class SwaggerConfig { -// @Bean -// @Profile("!Prod") -// public GroupedOpenApi GroupedOpenApi(){ -// -// String jwtSchemaName = "Bear 토큰 입력"; -// String[] paths = {"../../domain/center/**"}; -// -// return GroupedOpenApi -// .builder() -// .group("영업 매장 관련 API") -// .pathsToMatch(paths) -// .addOpenApiCustomizer(buildSecurityOpenApi()).build(); -// } - -// public OpenApiCustomizer buildSecurityOpenApi() { -// // jwt token을 한번 설정하면 header에 값을 넣어주는 코드 -// return OpenApi -> OpenApi.addSecurityItem(new SecurityRequirement().addList("jwt token")) -// .getComponents().addSecuritySchemes("jwt token", new SecurityScheme() -// .name("Authorization") -// .type(SecurityScheme.Type.HTTP) -// .in(SecurityScheme.In.HEADER) -// .bearerFormat("JWT") -// .scheme("bearer")); -// } + @Bean + public OpenAPI customOpenAPI() { + return new OpenAPI() + .components(new Components() + .addSecuritySchemes("Bearer Authentication", + new io.swagger.v3.oas.models.security.SecurityScheme() + .type(io.swagger.v3.oas.models.security.SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT") + ) + ) + .addSecurityItem(new io.swagger.v3.oas.models.security.SecurityRequirement() + .addList("Bearer Authentication")); + } } diff --git a/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java index 576145de..e5570330 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java @@ -42,8 +42,10 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Excepti http.csrf(csrfConfig -> csrfConfig.disable()); http.cors(corsConfig -> corsConfig.configurationSource(corsConfigurationSource())) .authorizeHttpRequests(auth -> auth + // Swagger 관련 URL 접근 허용 + .requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**").permitAll() + // 인증 없이 접근 가능한 API 설정 .requestMatchers("/api/v1/auth/**", "/api/v1/sample/**").permitAll() - // [Example] member는 ADMIN 권한만 접근 가능 설정 예시 .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasRole("ADMIN") .anyRequest().authenticated()) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index aa656100..da67b7da 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -48,11 +48,14 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Excepti http.sessionManagement(sessionConfig -> sessionConfig.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .cors(corsConfig -> corsConfig.configurationSource(corsConfigurationSource())) .csrf(csrfConfig -> csrfConfig.csrfTokenRequestHandler(csrfTokenRequestAttributeHandler) - .ignoringRequestMatchers("/api/v1/auth/signup", "/api/v1/auth/signin", "/api/v1/auth", "/api/v1/sample/**") + .ignoringRequestMatchers("/api/v1/auth/signup", "/api/v1/auth/signin", "/api/v1/auth", "/api/v1/sample/**", + "/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**") .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())) .authorizeHttpRequests(auth -> auth + // Swagger 관련 URL 접근 허용 + .requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**").permitAll() + // 인증 없이 접근 가능한 API 설정 .requestMatchers("/api/v1/auth/**", "/api/v1/sample/**").permitAll() - // [Example] member는 ADMIN 권한만 접근 가능 설정 예시 .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasRole("ADMIN") .anyRequest().authenticated()) From 998efa97e562484d631457401634ae045d358abc Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Fri, 15 Nov 2024 12:22:18 +0900 Subject: [PATCH 061/563] =?UTF-8?q?feat:=20=EC=9D=BC=EC=A0=95=ED=91=9C=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EB=B6=80=EB=B6=84=20mybatis=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/ContractController.java | 1 - .../domain/aggregate/entity/Schedule.java | 2 +- .../schedule/query/dto/ScheduleDTO.java | 3 +- .../query/repository/ScheduleMapper.java | 2 +- .../service/ScheduleQueryServiceImpl.java | 12 +++-- src/main/resources/sql/ddl.sql | 24 ++++----- .../query/repository/ScheduleMapper.xml | 52 ++++++++++++++----- 7 files changed, 61 insertions(+), 35 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java index 80de0579..bdacd47f 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java @@ -14,7 +14,6 @@ import stanl_2.final_backend.domain.contract.common.response.ContractResponseMessage; import stanl_2.final_backend.domain.contract.query.dto.ContractSeletIdDTO; import stanl_2.final_backend.domain.contract.query.service.ContractQueryService; -import stanl_2.final_backend.domain.schedule.common.response.ResponseMessage; import java.util.HashMap; import java.util.Map; diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java index 30240ef4..cece1870 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java @@ -52,7 +52,7 @@ public class Schedule { private String deletedAt; @Column(name = "ACTIVE", nullable = false) - private Boolean active = true; + private Boolean active = 1; @Column(name = "MEM_ID", nullable = false) private String memberId; diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java index c6d35a8a..6ac54133 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java @@ -16,6 +16,5 @@ public class ScheduleDTO { private String startAt; private String endAt; private String memberId; - - private String month; + private String active; } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java index ea34e92e..1dec886b 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java @@ -11,7 +11,7 @@ @Mapper public interface ScheduleMapper { - List findSchedulesByMemberIdAndSrtAt(ScheduleDTO scheduleDTO); + List findSchedulesByMemberIdAndSrtAt(Map arg); List findSchedulesByMemberIdAndYearMonth(ScheduleYearMonthDTO scheduleYearMonthDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java index 195b4bd7..944e53bd 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java @@ -15,7 +15,9 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.util.HashMap; import java.util.List; +import java.util.Map; @Slf4j @Service @@ -43,12 +45,14 @@ public List selectAllSchedule(String memberId) { String currentMonth = getCurrentTime().substring(0,7); - ScheduleDTO scheduleDTO = new ScheduleDTO(); - scheduleDTO.setMemberId(memberId); - scheduleDTO.setMonth(currentMonth); + Map arg = new HashMap<>(); + arg.put("memberId",memberId); + arg.put("month",currentMonth); - List scheduleList = scheduleMapper.findSchedulesByMemberIdAndSrtAt(scheduleDTO); + List scheduleList = scheduleMapper.findSchedulesByMemberIdAndSrtAt(arg); + + log.info("값 확인:{}",scheduleList); // Mapping 오류 체크 고려하기 return scheduleList; diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index d71b3120..6a6a5c1e 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -715,25 +715,25 @@ VALUES ('FIL_000000001', '쏘렌토_계약서.pdf', '/files/contract1.pdf', 'pdf INSERT INTO tb_schedule (SCH_ID, SCH_NAME, SCH_CONT, SCH_SRT_AT, SCH_END_AT, CREATED_AT, UPDATED_AT, DELETED_AT, ACTIVE, MEM_ID) -VALUES ('SCH_000000001', '쏘렌토 신차 발표회', '신차 발표회 준비 및 진행', '2024-02-01 09:00:00', '2024-02-01 12:00:00', +VALUES ('SCH_000000001', '쏘렌토 신차 발표회', '신차 발표회 준비 및 진행', '2024-11-01 09:00:00', '2024-11-01 12:00:00', '2024-01-20 10:00:00', '2024-01-20 10:30:00', NULL, TRUE, 'MEM_000000001'), - ('SCH_000000002', '스포티지 고객 상담', '신차 구매 고객 상담 일정', '2024-11-05 10:00:00', '2024-02-05 11:30:00', - '2024-01-21 11:00:00', '2024-01-21 11:30:00', NULL, TRUE, 'MEM_000000002'), - ('SCH_000000003', 'K7 프로모션 회의', '프로모션 기획 및 준비 회의', '2024-02-10 13:00:00', '2024-02-10 15:00:00', - '2024-01-22 12:00:00', '2024-01-22 12:30:00', NULL, TRUE, 'MEM_000000003'), - ('SCH_000000004', '셀토스 시승 이벤트', '고객 대상 시승 이벤트', '2024-02-12 14:00:00', '2024-02-12 17:00:00', + ('SCH_000000002', '스포티지 고객 상담', '신차 구매 고객 상담 일정', '2024-11-05 10:00:00', '2024-11-05 11:30:00', + '2024-01-21 11:00:00', '2024-01-21 11:30:00', NULL, TRUE, 'MEM_000000001'), + ('SCH_000000003', 'K7 프로모션 회의', '프로모션 기획 및 준비 회의', '2024-11-10 13:00:00', '2024-11-10 15:00:00', + '2024-01-22 12:00:00', '2024-01-22 12:30:00', NULL, TRUE, 'MEM_000000001'), + ('SCH_000000004', '셀토스 시승 이벤트', '고객 대상 시승 이벤트', '2024-11-12 14:00:00', '2024-11-12 17:00:00', '2024-01-23 13:00:00', '2024-01-23 13:30:00', NULL, TRUE, 'MEM_000000004'), - ('SCH_000000005', 'K3 서비스 교육', '서비스 센터 직원 교육', '2024-02-15 09:00:00', '2024-02-15 12:00:00', + ('SCH_000000005', 'K3 서비스 교육', '서비스 센터 직원 교육', '2024-11-15 09:00:00', '2024-11-15 12:00:00', '2024-01-24 14:00:00', '2024-01-24 14:30:00', NULL, TRUE, 'MEM_000000005'), - ('SCH_000000006', '모하비 유지보수 회의', '유지보수 전략 논의', '2024-02-17 10:00:00', '2024-02-17 11:00:00', + ('SCH_000000006', '모하비 유지보수 회의', '유지보수 전략 논의', '2024-11-17 10:00:00', '2024-11-17 11:00:00', '2024-01-25 15:00:00', '2024-01-25 15:30:00', NULL, TRUE, 'MEM_000000006'), - ('SCH_000000007', 'K8 내부 검토', '신차 내부 디자인 검토', '2024-02-20 11:00:00', '2024-02-20 13:00:00', + ('SCH_000000007', 'K8 내부 검토', '신차 내부 디자인 검토', '2024-11-20 11:00:00', '2024-11-20 13:00:00', '2024-01-26 16:00:00', '2024-01-26 16:30:00', NULL, TRUE, 'MEM_000000007'), - ('SCH_000000008', '스팅어 기술 세미나', '스팅어 기술 세미나 및 발표', '2024-02-22 14:00:00', '2024-02-22 16:00:00', + ('SCH_000000008', '스팅어 기술 세미나', '스팅어 기술 세미나 및 발표', '2024-11-22 14:00:00', '2024-11-22 16:00:00', '2024-01-27 17:00:00', '2024-01-27 17:30:00', NULL, TRUE, 'MEM_000000008'), - ('SCH_000000009', '니로 전기차 테스트', '니로 전기차 충전 테스트', '2024-02-25 09:00:00', '2024-02-25 11:00:00', + ('SCH_000000009', '니로 전기차 테스트', '니로 전기차 충전 테스트', '2024-11-25 09:00:00', '2024-11-25 11:00:00', '2024-01-28 18:00:00', '2024-01-28 18:30:00', NULL, TRUE, 'MEM_000000009'), - ('SCH_000000010', 'K5 재고 점검', 'K5 재고 관리 및 점검', '2024-02-28 15:00:00', '2024-02-28 17:00:00', + ('SCH_000000010', 'K5 재고 점검', 'K5 재고 관리 및 점검', '2024-11-28 15:00:00', '2024-11-28 17:00:00', '2024-01-29 19:00:00', '2024-01-29 19:30:00', NULL, TRUE, 'MEM_000000010'); INSERT INTO tb_alarm (ALR_ID, ALR_MSG, ALR_URL, ALR_READ_STAT, CREATED_AT, MEM_ID) diff --git a/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml b/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml index b2b3842f..d340611e 100644 --- a/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml @@ -3,34 +3,45 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> - + - + - - - + + + - SELECT A.SCH_ID, A.SCH_NAME, A.SCH_SRT_AT, A.SCH_END_AT, + A.ACTIVE, A.MEM_ID FROM TB_SCHEDULE A - WHERE A.MEM_ID = #{ memberId } - - AND DATE_FORMAT(A.SCH_SRT_AT, '%Y-%m') = #{ month } - AND A.ACTIVE = TRUE + WHERE MEM_ID = #{ memberId } + AND SCH_SRT_AT LIKE CONCAT(#{ month },'%') + AND ACTIVE = TRUE - SELECT A.SCH_ID, @@ -41,11 +52,24 @@ FROM TB_SCHEDULE A WHERE A.MEM_ID = #{ memberId } AND A.SCH_SRT_AT LIKE CONCAT(#{ yearMonth },'%') + AND A.ACTIVE = TRUE - SELECT A.SCH_ID, A.SCH_NAME, From a1e6ff3c6680acca8221ad667e35df8a89e3f4a3 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Fri, 15 Nov 2024 12:44:04 +0900 Subject: [PATCH 062/563] =?UTF-8?q?fix:=20swagger=20csrf=ED=86=A0=ED=81=B0?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9=EC=A4=91(#38)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/config/SwaggerConfig.java | 36 ++++++++++++++++++- .../exception/GlobalExceptionHandler.java | 8 ++--- .../response/GlobalResponseMessage.java | 2 +- .../security/filter/CsrfCookieFilter.java | 9 +++-- .../security/service/MemberDetails.java | 4 --- .../service/MemberDetailsService.java | 1 - 6 files changed, 47 insertions(+), 13 deletions(-) rename src/main/java/stanl_2/final_backend/global/{common => }/response/GlobalResponseMessage.java (78%) diff --git a/src/main/java/stanl_2/final_backend/global/config/SwaggerConfig.java b/src/main/java/stanl_2/final_backend/global/config/SwaggerConfig.java index c8da78e2..f3e03480 100644 --- a/src/main/java/stanl_2/final_backend/global/config/SwaggerConfig.java +++ b/src/main/java/stanl_2/final_backend/global/config/SwaggerConfig.java @@ -7,8 +7,16 @@ import io.swagger.v3.oas.annotations.security.SecurityScheme; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.HandlerInterceptor; +import jakarta.servlet.http.Cookie; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.Arrays; @Configuration @OpenAPIDefinition( @@ -28,7 +36,7 @@ public class SwaggerConfig { @Bean - public OpenAPI customOpenAPI() { + public OpenAPI OpenAPI() { return new OpenAPI() .components(new Components() .addSecuritySchemes("Bearer Authentication", @@ -41,4 +49,30 @@ public OpenAPI customOpenAPI() { .addSecurityItem(new io.swagger.v3.oas.models.security.SecurityRequirement() .addList("Bearer Authentication")); } + + @Bean + public WebMvcConfigurer swaggerCsrfConfigurer() { + return new WebMvcConfigurer() { + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new HandlerInterceptor() { + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + Cookie[] cookies = request.getCookies(); + if (cookies != null) { + String csrfToken = Arrays.stream(cookies) + .filter(cookie -> "XSRF-TOKEN".equals(cookie.getName())) + .map(Cookie::getValue) + .findFirst() + .orElse(null); + if (csrfToken != null) { + response.addHeader("X-XSRF-TOKEN", csrfToken); + } + } + return true; + } + }).addPathPatterns("/swagger-ui/**"); + } + }; + } } diff --git a/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java b/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java index 5b376bf5..cace7cc7 100644 --- a/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java @@ -45,7 +45,7 @@ public ResponseEntity handleArgumentNotValidException(M // 사용자 정의 예외 처리 @ExceptionHandler(value = {GlobalCommonException.class}) - public ResponseEntity handleCustomException(GlobalCommonException e) { + public ResponseEntity handleCustomException(GlobalCommonException e) { log.error("handleCustomException() in GlobalExceptionHandler: {}", e.getMessage()); GlobalExceptionResponse response = new GlobalExceptionResponse(e.getErrorCode()); @@ -54,7 +54,7 @@ public ResponseEntity handleCustomException(GlobalCommonException e) { // 서버 내부 오류시 작동 @ExceptionHandler(value = {Exception.class}) - public ResponseEntity handleServerException(Exception e) { + public ResponseEntity handleServerException(Exception e) { log.info("occurred exception in handleServerError = {}", e.getMessage()); e.printStackTrace(); GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.INTERNAL_SERVER_ERROR).getErrorCode()); @@ -63,14 +63,14 @@ public ResponseEntity handleServerException(Exception e) { // 데이터 무결성 위반 예외 처리기 추가 @ExceptionHandler(value = {DataIntegrityViolationException.class}) - public ResponseEntity handleDataIntegrityViolationException(DataIntegrityViolationException e) { + public ResponseEntity handleDataIntegrityViolationException(DataIntegrityViolationException e) { log.error("handleDataIntegrityViolationException() in GlobalExceptionHandler : {}", e.getMessage()); GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.DATA_INTEGRITY_VIOLATION).getErrorCode()); return new ResponseEntity<>(response, response.getHttpStatus()); } @ExceptionHandler(value = {MethodArgumentNotValidException.class}) - public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { + public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { log.error("유효성 검사 실패: {}", e.getMessage()); GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.VALIDATION_FAIL).getErrorCode()); return new ResponseEntity<>(response, response.getHttpStatus()); diff --git a/src/main/java/stanl_2/final_backend/global/common/response/GlobalResponseMessage.java b/src/main/java/stanl_2/final_backend/global/response/GlobalResponseMessage.java similarity index 78% rename from src/main/java/stanl_2/final_backend/global/common/response/GlobalResponseMessage.java rename to src/main/java/stanl_2/final_backend/global/response/GlobalResponseMessage.java index d9f94c64..5b60ef96 100644 --- a/src/main/java/stanl_2/final_backend/global/common/response/GlobalResponseMessage.java +++ b/src/main/java/stanl_2/final_backend/global/response/GlobalResponseMessage.java @@ -1,4 +1,4 @@ -package stanl_2.final_backend.global.common.response; +package stanl_2.final_backend.global.response; import lombok.*; diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java index f4bd721a..c84245e1 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java @@ -55,6 +55,11 @@ private String getCsrfTokenFromCookie(HttpServletRequest request) { @Override protected boolean shouldNotFilter(HttpServletRequest request) { String path = request.getServletPath(); - return path.startsWith("/api/v1/auth") || path.startsWith("/api/v1/sample"); + return path.startsWith("/api/v1/auth") || + path.startsWith("/api/v1/sample") || + path.startsWith("/swagger-ui/") || + path.startsWith("/v3/api-docs") || + path.startsWith("/swagger-resources") || + path.startsWith("/webjars"); } -} +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/global/security/service/MemberDetails.java b/src/main/java/stanl_2/final_backend/global/security/service/MemberDetails.java index 332e7f9c..d684061d 100644 --- a/src/main/java/stanl_2/final_backend/global/security/service/MemberDetails.java +++ b/src/main/java/stanl_2/final_backend/global/security/service/MemberDetails.java @@ -22,10 +22,6 @@ public Member getMember() { @Override public Collection getAuthorities() { -// List authorities = member.getRoles().stream() -// .map(role -> new SimpleGrantedAuthority(role.getRole())) -// .toList(); -// return authorities; return member.getRoles().stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role.getRole())) .collect(Collectors.toList()); diff --git a/src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsService.java b/src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsService.java index 89c48418..91091482 100644 --- a/src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsService.java +++ b/src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsService.java @@ -24,7 +24,6 @@ public MemberDetailsService(MemberRepository memberRepository) { public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // JPA를 사용하여 로그인 ID로 회원 정보 조회 Member member = memberRepository.findByLoginId(username); - log.info("@@@@!!! {}", member.toString()); if (member == null) { throw new UsernameNotFoundException("유저 정보가 없습니다" + username); } From bf90aea0529a767c5fb94f8161e6e983832e9721 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 15 Nov 2024 14:28:16 +0900 Subject: [PATCH 063/563] =?UTF-8?q?fix:=20active=201(true)=20or=200(false)?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/aggragate/entity/Notice.java | 2 - .../query/controller/NoticeController.java | 45 ++++++++++++++++--- .../domain/notices/query/dto/SearchDTO.java | 24 ++++++++++ .../query/repository/NoticeMapper.java | 12 +++++ .../notices/query/service/NoticeService.java | 3 ++ .../query/service/NoticeServiceImpl.java | 15 ++++++- .../BooleanToStringConverterConfig.java | 17 ------- .../notices/query/repository/NoticeMapper.xml | 38 +++++++++------- 8 files changed, 115 insertions(+), 41 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/query/dto/SearchDTO.java delete mode 100644 src/main/java/stanl_2/final_backend/global/config/BooleanToStringConverterConfig.java diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java index ab8711f0..9c9b9966 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java @@ -6,7 +6,6 @@ import lombok.NoArgsConstructor; import lombok.Setter; import org.hibernate.annotations.GenericGenerator; -import stanl_2.final_backend.global.config.BooleanToStringConverterConfig; import stanl_2.final_backend.global.config.PrefixGeneratorConfig; import java.time.ZoneId; @@ -51,7 +50,6 @@ public class Notice { private String deletedAt; @Column(name = "ACTIVE", nullable = false) - @Convert(converter = BooleanToStringConverterConfig.class) private Boolean active = true; @Column(name = "MEM_ID", nullable = false) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java index 1ebeacc3..c58d1b47 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java @@ -1,15 +1,19 @@ package stanl_2.final_backend.domain.notices.query.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.notices.common.response.NoticeResponseMessage; import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; +import stanl_2.final_backend.domain.notices.query.dto.SearchDTO; import stanl_2.final_backend.domain.notices.query.service.NoticeService; @RestController("queryNoticeController") @@ -22,8 +26,13 @@ public NoticeController(NoticeService noticeService) { this.noticeService = noticeService; } - @GetMapping - public ResponseEntity> getNotices( + @Operation(summary = "공지사항 전체 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = NoticeResponseMessage.class))}) + }) + @GetMapping("/all") + public ResponseEntity> getAllNotices( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size ) { @@ -35,4 +44,28 @@ public ResponseEntity> getNotices( return ResponseEntity.ok(noticeDTOPage); } + + @Operation(summary = "공지사항 조건별 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = NoticeResponseMessage.class))}) + }) + @GetMapping + public ResponseEntity> getNotices( + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "10") int size, + @RequestParam(required = false) String title, + @RequestParam(required = false) String tag, + @RequestParam(required = false) String memberId, + @RequestParam(required = false) String classification, + @RequestParam(required = false) String startDate, + @RequestParam(required = false) String endDate + + ) { + Pageable pageable = PageRequest.of(page, size); + SearchDTO searchDTO = new SearchDTO(title, tag, classification, memberId, startDate, endDate); + Page noticeDTOPage = noticeService.findNotices(pageable,searchDTO); + + return ResponseEntity.ok(noticeDTOPage); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/dto/SearchDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/query/dto/SearchDTO.java new file mode 100644 index 00000000..cfdf8093 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/dto/SearchDTO.java @@ -0,0 +1,24 @@ +package stanl_2.final_backend.domain.notices.query.dto; + +import lombok.*; + +@NoArgsConstructor +@Data +public class SearchDTO { + private String noticeId; + private String title; + private String tag; + private String memberId; + private String classification; + private String startDate; + private String endDate; + + public SearchDTO(String title, String tag, String memberId, String classification, String startDate, String endDate) { + this.title = title; + this.tag = tag; + this.memberId = memberId; + this.classification = classification; + this.startDate = startDate; + this.endDate = endDate; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java b/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java index 122f7260..bb0dda9a 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java @@ -3,6 +3,7 @@ import org.apache.ibatis.annotations.Mapper; import org.springframework.data.repository.query.Param; import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; +import stanl_2.final_backend.domain.notices.query.dto.SearchDTO; import java.util.List; @@ -12,5 +13,16 @@ public interface NoticeMapper { List findAllNotices(@Param("offset") int offset, @Param("size") int size); int findNoticeCount(); + + List findNotices( + @Param("offset") int offset, + @Param("size") int size, + @Param("searchDTO") SearchDTO searchDTO + ); + + int findNoticesCount(@Param("searchDTO") SearchDTO searchDTO); + NoticeDTO findNotice(@Param("noticeId") String noticeId); } + + diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java index a421cebc..9057133e 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java @@ -4,10 +4,13 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; +import stanl_2.final_backend.domain.notices.query.dto.SearchDTO; public interface NoticeService { Page findAllNotices(Pageable pageable); + Page findNotices(Pageable pageable, SearchDTO searchDTO); + } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java index 8e236720..1455a318 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java @@ -5,7 +5,9 @@ import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; +import stanl_2.final_backend.domain.notices.query.dto.SearchDTO; import stanl_2.final_backend.domain.notices.query.repository.NoticeMapper; import java.util.List; @@ -20,7 +22,7 @@ public NoticeServiceImpl(NoticeMapper noticeMapper) { this.noticeMapper = noticeMapper; } - + @Transactional @Override public Page findAllNotices(Pageable pageable) { int offset = Math.toIntExact(pageable.getOffset()); @@ -31,4 +33,15 @@ public Page findAllNotices(Pageable pageable) { return new PageImpl<>(notices, pageable, totalElements); } + @Transactional + @Override + public Page findNotices(Pageable pageable, SearchDTO searchDTO) { + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); + List notices = noticeMapper.findNotices(offset,size,searchDTO); + int totalElements = noticeMapper.findNoticesCount(searchDTO); + + return new PageImpl<>(notices, pageable, totalElements); + } + } diff --git a/src/main/java/stanl_2/final_backend/global/config/BooleanToStringConverterConfig.java b/src/main/java/stanl_2/final_backend/global/config/BooleanToStringConverterConfig.java deleted file mode 100644 index 2a9ebd60..00000000 --- a/src/main/java/stanl_2/final_backend/global/config/BooleanToStringConverterConfig.java +++ /dev/null @@ -1,17 +0,0 @@ -package stanl_2.final_backend.global.config; - -import jakarta.persistence.AttributeConverter; -import jakarta.persistence.Converter; - -@Converter(autoApply = true) -public class BooleanToStringConverterConfig implements AttributeConverter { - @Override - public String convertToDatabaseColumn(Boolean attribute) { - return (attribute != null && attribute) ? "true" : "false"; - } - - @Override - public Boolean convertToEntityAttribute(String dbData) { - return "true".equalsIgnoreCase(dbData); - } -} diff --git a/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml b/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml index 15def641..9396362c 100644 --- a/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml @@ -17,8 +17,16 @@ + + + + + + + + - SELECT A.NOT_ID, A.NOT_TTL, @@ -44,18 +52,18 @@ - SELECT - A.NOT_ID, - A.NOT_TTL, - A.NOT_TAG, - A.NOT_CLA, - A.NOT_CONT, - A.CREATED_AT, - A.UPDATED_AT, - A.DELETED_AT, - A.ACTIVE, - A.MEM_ID + A.NOT_ID, + A.NOT_TTL, + A.NOT_TAG, + A.NOT_CLA, + A.NOT_CONT, + A.CREATED_AT, + A.UPDATED_AT, + A.DELETED_AT, + A.ACTIVE, + A.MEM_ID FROM TB_NOTICE A A.ACTIVE != 'false' @@ -69,18 +77,18 @@ AND A.MEM_ID = #{memberId} - AND A.NOT_TTL = #{title} + AND A.NOT_TTL LIKE CONCAT('%', #{title}, '%') AND A.CREATED_AT BETWEEN #{startDate} AND #{endDate} ORDER BY A.CREATED_AT DESC - OFFSET #{pageable.offset} ROWS FETCH NEXT #{pageable.pageSize} ROWS ONLY; + LIMIT #{size} OFFSET #{offset} - SELECT COUNT(*) AS CNT FROM TB_NOTICE A From 401bcb1d355dd3081f5ef07b96269fd2d01ef327 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Fri, 15 Nov 2024 14:36:13 +0900 Subject: [PATCH 064/563] =?UTF-8?q?feat:=20dev=20merge=EC=A0=84=20commit?= =?UTF-8?q?=20(#25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../schedule/command/domain/aggregate/entity/Schedule.java | 2 +- .../final_backend/domain/schedule/query/dto/ScheduleDTO.java | 1 - .../schedule/query/service/ScheduleQueryServiceImpl.java | 3 --- .../domain/schedule/query/repository/ScheduleMapper.xml | 5 ----- 4 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java index cece1870..30240ef4 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java @@ -52,7 +52,7 @@ public class Schedule { private String deletedAt; @Column(name = "ACTIVE", nullable = false) - private Boolean active = 1; + private Boolean active = true; @Column(name = "MEM_ID", nullable = false) private String memberId; diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java index 6ac54133..167be695 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java @@ -16,5 +16,4 @@ public class ScheduleDTO { private String startAt; private String endAt; private String memberId; - private String active; } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java index 944e53bd..539810d0 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java @@ -51,8 +51,6 @@ public List selectAllSchedule(String memberId) { List scheduleList = scheduleMapper.findSchedulesByMemberIdAndSrtAt(arg); - - log.info("값 확인:{}",scheduleList); // Mapping 오류 체크 고려하기 return scheduleList; @@ -69,7 +67,6 @@ public List selectYearMonthSchedule(ScheduleYearMonthDTO s scheduleYearMonthDTO.setYearMonth(scheduleYearMonthDTO.getYear() + "-" + scheduleYearMonthDTO.getMonth()); - log.info("값 출력: {}", scheduleYearMonthDTO); List scheduleList = scheduleMapper.findSchedulesByMemberIdAndYearMonth(scheduleYearMonthDTO); diff --git a/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml b/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml index d340611e..5c497220 100644 --- a/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml @@ -6,12 +6,8 @@ - - - - @@ -21,7 +17,6 @@ A.SCH_NAME, A.SCH_SRT_AT, A.SCH_END_AT, - A.ACTIVE, A.MEM_ID FROM TB_SCHEDULE A WHERE MEM_ID = #{ memberId } From ddde23a2485a4cca12a020ebd73d942c14b49212 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Fri, 15 Nov 2024 15:40:26 +0900 Subject: [PATCH 065/563] =?UTF-8?q?feat:=20=EC=9D=BC=EC=A0=95=ED=91=9C=20C?= =?UTF-8?q?RUD=20=EC=B5=9C=EC=A2=85=20=EA=B5=AC=ED=98=84=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20(#25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/dto/ScheduleModifyDTO.java | 1 + .../application/dto/ScheduleRegistDTO.java | 1 + .../domain/aggregate/entity/Schedule.java | 3 ++ .../schedule/query/dto/ScheduleDTO.java | 1 + .../schedule/query/dto/ScheduleDetailDTO.java | 1 + .../query/dto/ScheduleYearMonthDTO.java | 2 +- .../query/repository/ScheduleMapper.java | 4 +- .../service/ScheduleQueryServiceImpl.java | 13 +++-- src/main/resources/sql/ddl.sql | 46 +++++++++--------- .../query/repository/ScheduleMapper.xml | 48 +++++++++---------- 10 files changed, 66 insertions(+), 54 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleModifyDTO.java index e5a314d2..21df0c27 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleModifyDTO.java @@ -14,6 +14,7 @@ public class ScheduleModifyDTO { private String id; private String name; private String content; + private String tag; private String startAt; private String endAt; private String memberId; diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleRegistDTO.java index 79f4de4f..989aa839 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleRegistDTO.java @@ -13,6 +13,7 @@ public class ScheduleRegistDTO { private String name; private String content; + private String tag; private String startAt; private String endAt; private String memberId; diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java index 30240ef4..72afca5b 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java @@ -36,6 +36,9 @@ public class Schedule { @Column(name = "SCH_CONT", nullable = false) private String content; + @Column(name = "SCH_TAG", nullable = false) + private String tag = "CONSULTATION"; + @Column(name = "SCH_SRT_AT", nullable = false) private String startAt; diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java index 167be695..92908e73 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java @@ -13,6 +13,7 @@ public class ScheduleDTO { private String id; private String name; + private String tag; private String startAt; private String endAt; private String memberId; diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDetailDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDetailDTO.java index 56e4fec2..f6d689a8 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDetailDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDetailDTO.java @@ -12,6 +12,7 @@ public class ScheduleDetailDTO { private String id; private String name; private String content; + private String tag; private String startAt; private String endAt; private String memberId; diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleYearMonthDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleYearMonthDTO.java index a227b076..35f9fede 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleYearMonthDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleYearMonthDTO.java @@ -11,11 +11,11 @@ public class ScheduleYearMonthDTO { private String id; private String name; + private String tag; private String startAt; private String endAt; private String memberId; private String year; private String month; - private String yearMonth; } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java index 1dec886b..ee9490ae 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java @@ -13,7 +13,7 @@ public interface ScheduleMapper { List findSchedulesByMemberIdAndSrtAt(Map arg); - List findSchedulesByMemberIdAndYearMonth(ScheduleYearMonthDTO scheduleYearMonthDTO); + List findSchedulesByMemberIdAndYearMonth(Map arg); - ScheduleDetailDTO findScheduleByMemberIdAndScheduleId(ScheduleDetailDTO scheduleDetailDTO); + ScheduleDetailDTO findScheduleByMemberIdAndScheduleId(Map arg); } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java index 539810d0..b8f797e2 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java @@ -65,11 +65,14 @@ public List selectYearMonthSchedule(ScheduleYearMonthDTO s throw new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND); } - scheduleYearMonthDTO.setYearMonth(scheduleYearMonthDTO.getYear() + "-" + scheduleYearMonthDTO.getMonth()); + String yearMonth = scheduleYearMonthDTO.getYear() + "-" + scheduleYearMonthDTO.getMonth(); + Map arg = new HashMap<>(); + arg.put("memberId", scheduleYearMonthDTO.getMemberId()); + arg.put("yearMonth", yearMonth); List scheduleList = - scheduleMapper.findSchedulesByMemberIdAndYearMonth(scheduleYearMonthDTO); + scheduleMapper.findSchedulesByMemberIdAndYearMonth(arg); // Mapping 오류 체크 고려하기 @@ -89,8 +92,12 @@ public ScheduleDetailDTO selectDetailSchedule(ScheduleDetailDTO scheduleDetailDT throw new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND); } + Map arg = new HashMap<>(); + arg.put("memberId", scheduleDetailDTO.getMemberId()); + arg.put("scheduleId", scheduleDetailDTO.getId()); + ScheduleDetailDTO responseDetailSchedule - = scheduleMapper.findScheduleByMemberIdAndScheduleId(scheduleDetailDTO); + = scheduleMapper.findScheduleByMemberIdAndScheduleId(arg); // Mapping 오류 체크 고려하기 diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index 6a6a5c1e..7f477d32 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -291,6 +291,7 @@ CREATE TABLE tb_schedule SCH_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', SCH_NAME VARCHAR(255) NOT NULL, SCH_CONT VARCHAR(255) NOT NULL, + SCH_TAG VARCHAR(255) NOT NULL, SCH_SRT_AT CHAR(19) NOT NULL, SCH_END_AT CHAR(19) NOT NULL, CREATED_AT CHAR(19) NOT NULL, @@ -713,28 +714,29 @@ VALUES ('FIL_000000001', '쏘렌토_계약서.pdf', '/files/contract1.pdf', 'pdf ('FIL_000000010', 'K5_고객이벤트_배너.jpg', '/files/banner10.jpg', 'image', TRUE, '2024-01-19 18:00:00', 'MEM_000000010', 'NOT_000000010'); -INSERT INTO tb_schedule (SCH_ID, SCH_NAME, SCH_CONT, SCH_SRT_AT, SCH_END_AT, CREATED_AT, UPDATED_AT, DELETED_AT, ACTIVE, - MEM_ID) -VALUES ('SCH_000000001', '쏘렌토 신차 발표회', '신차 발표회 준비 및 진행', '2024-11-01 09:00:00', '2024-11-01 12:00:00', - '2024-01-20 10:00:00', '2024-01-20 10:30:00', NULL, TRUE, 'MEM_000000001'), - ('SCH_000000002', '스포티지 고객 상담', '신차 구매 고객 상담 일정', '2024-11-05 10:00:00', '2024-11-05 11:30:00', - '2024-01-21 11:00:00', '2024-01-21 11:30:00', NULL, TRUE, 'MEM_000000001'), - ('SCH_000000003', 'K7 프로모션 회의', '프로모션 기획 및 준비 회의', '2024-11-10 13:00:00', '2024-11-10 15:00:00', - '2024-01-22 12:00:00', '2024-01-22 12:30:00', NULL, TRUE, 'MEM_000000001'), - ('SCH_000000004', '셀토스 시승 이벤트', '고객 대상 시승 이벤트', '2024-11-12 14:00:00', '2024-11-12 17:00:00', - '2024-01-23 13:00:00', '2024-01-23 13:30:00', NULL, TRUE, 'MEM_000000004'), - ('SCH_000000005', 'K3 서비스 교육', '서비스 센터 직원 교육', '2024-11-15 09:00:00', '2024-11-15 12:00:00', - '2024-01-24 14:00:00', '2024-01-24 14:30:00', NULL, TRUE, 'MEM_000000005'), - ('SCH_000000006', '모하비 유지보수 회의', '유지보수 전략 논의', '2024-11-17 10:00:00', '2024-11-17 11:00:00', - '2024-01-25 15:00:00', '2024-01-25 15:30:00', NULL, TRUE, 'MEM_000000006'), - ('SCH_000000007', 'K8 내부 검토', '신차 내부 디자인 검토', '2024-11-20 11:00:00', '2024-11-20 13:00:00', - '2024-01-26 16:00:00', '2024-01-26 16:30:00', NULL, TRUE, 'MEM_000000007'), - ('SCH_000000008', '스팅어 기술 세미나', '스팅어 기술 세미나 및 발표', '2024-11-22 14:00:00', '2024-11-22 16:00:00', - '2024-01-27 17:00:00', '2024-01-27 17:30:00', NULL, TRUE, 'MEM_000000008'), - ('SCH_000000009', '니로 전기차 테스트', '니로 전기차 충전 테스트', '2024-11-25 09:00:00', '2024-11-25 11:00:00', - '2024-01-28 18:00:00', '2024-01-28 18:30:00', NULL, TRUE, 'MEM_000000009'), - ('SCH_000000010', 'K5 재고 점검', 'K5 재고 관리 및 점검', '2024-11-28 15:00:00', '2024-11-28 17:00:00', - '2024-01-29 19:00:00', '2024-01-29 19:30:00', NULL, TRUE, 'MEM_000000010'); +INSERT INTO tb_schedule (SCH_ID, SCH_NAME, SCH_CONT, SCH_TAG, SCH_SRT_AT, SCH_END_AT, CREATED_AT, UPDATED_AT, DELETED_AT, ACTIVE, MEM_ID) +VALUES + ('SCH_000000001', '쏘렌토 신차 발표회', '신차 발표회 준비 및 진행', 'MEETING', '2024-11-01 09:00:00', '2024-11-01 12:00:00', + '2024-01-20 10:00:00', '2024-01-20 10:30:00', NULL, TRUE, 'MEM_000000001'), + ('SCH_000000002', '스포티지 고객 상담', '신차 구매 고객 상담 일정', 'CONSULTATION', '2024-11-05 10:00:00', '2024-11-05 11:30:00', + '2024-01-21 11:00:00', '2024-01-21 11:30:00', NULL, TRUE, 'MEM_000000002'), + ('SCH_000000003', 'K7 프로모션 회의', '프로모션 기획 및 준비 회의', 'MEETING', '2024-11-10 13:00:00', '2024-11-10 15:00:00', + '2024-01-22 12:00:00', '2024-01-22 12:30:00', NULL, TRUE, 'MEM_000000003'), + ('SCH_000000004', '셀토스 시승 이벤트', '고객 대상 시승 이벤트', 'CONSULTATION', '2024-11-12 14:00:00', '2024-11-12 17:00:00', + '2024-01-23 13:00:00', '2024-01-23 13:30:00', NULL, TRUE, 'MEM_000000004'), + ('SCH_000000005', 'K3 서비스 교육', '서비스 센터 직원 교육', 'TRAINING', '2024-11-15 09:00:00', '2024-11-15 12:00:00', + '2024-01-24 14:00:00', '2024-01-24 14:30:00', NULL, TRUE, 'MEM_000000005'), + ('SCH_000000006', '모하비 유지보수 회의', '유지보수 전략 논의', 'MEETING', '2024-11-17 10:00:00', '2024-11-17 11:00:00', + '2024-01-25 15:00:00', '2024-01-25 15:30:00', NULL, TRUE, 'MEM_000000006'), + ('SCH_000000007', 'K8 내부 검토', '신차 내부 디자인 검토', 'MEETING', '2024-11-20 11:00:00', '2024-11-20 13:00:00', + '2024-01-26 16:00:00', '2024-01-26 16:30:00', NULL, TRUE, 'MEM_000000007'), + ('SCH_000000008', '스팅어 기술 세미나', '스팅어 기술 세미나 및 발표', 'TRAINING', '2024-11-22 14:00:00', '2024-11-22 16:00:00', + '2024-01-27 17:00:00', '2024-01-27 17:30:00', NULL, TRUE, 'MEM_000000008'), + ('SCH_000000009', '연말 휴가', '연말 휴가 계획', 'VACATION', '2024-11-25 09:00:00', '2024-11-25 17:00:00', + '2024-01-28 18:00:00', '2024-01-28 18:30:00', NULL, TRUE, 'MEM_000000009'), + ('SCH_000000010', 'K5 재고 점검', 'K5 재고 관리 및 점검', 'MEETING', '2024-11-28 15:00:00', '2024-11-28 17:00:00', + '2024-01-29 19:00:00', '2024-01-29 19:30:00', NULL, TRUE, 'MEM_000000010'); + INSERT INTO tb_alarm (ALR_ID, ALR_MSG, ALR_URL, ALR_READ_STAT, CREATED_AT, MEM_ID) VALUES ('ALR_000000001', '신차 출시 공지 알림', '/notices/1', FALSE, '2024-01-20 12:00:00', 'MEM_000000001'), diff --git a/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml b/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml index 5c497220..da3d3079 100644 --- a/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml @@ -6,6 +6,7 @@ + @@ -15,6 +16,7 @@ SELECT A.SCH_ID, A.SCH_NAME, + A.SCH_TAG, A.SCH_SRT_AT, A.SCH_END_AT, A.MEM_ID @@ -27,54 +29,48 @@ - + - - - - SELECT A.SCH_ID, A.SCH_NAME, + A.SCH_TAG, A.SCH_SRT_AT, A.SCH_END_AT, A.MEM_ID - FROM TB_SCHEDULE A - WHERE A.MEM_ID = #{ memberId } - AND A.SCH_SRT_AT LIKE CONCAT(#{ yearMonth },'%') - - AND A.ACTIVE = TRUE + FROM TB_SCHEDULE A + WHERE A.MEM_ID = #{ memberId } + AND A.SCH_SRT_AT LIKE CONCAT(#{ yearMonth },'%') + AND A.ACTIVE = TRUE + - - - - SELECT - A.SCH_ID, - A.SCH_NAME, - A.SCH_CONT, - A.SCH_SRT_AT, - A.SCH_END_AT, - A.MEM_ID - FROM TB_SCHEDULE A - WHERE MEM_ID = #{ memberId } - AND SCH_ID = #{ id } - AND ACTIVE = TRUE + A.SCH_ID, + A.SCH_NAME, + A.SCH_CONT, + A.SCH_TAG, + A.SCH_SRT_AT, + A.SCH_END_AT, + A.MEM_ID + FROM TB_SCHEDULE A + WHERE A.MEM_ID = #{ memberId } + AND A.SCH_ID = #{ scheduleId } + AND A.ACTIVE = TRUE \ No newline at end of file From 3589650500f1014fdca0572caae255608abed144 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Fri, 15 Nov 2024 15:46:38 +0900 Subject: [PATCH 066/563] =?UTF-8?q?fix:=20=EC=8B=9C=ED=81=90=EB=A6=AC?= =?UTF-8?q?=ED=8B=B0=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD(#34)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AuthController.java | 59 ++++++------ .../command/application/dto/RefreshDTO.java | 14 +++ .../service/AuthCommandService.java | 9 +- .../service/AuthCommandServiceImpl.java | 92 +++++++++++++------ .../global/exception/GlobalErrorCode.java | 2 +- .../security/config/DevSecurityConfig.java | 19 +++- .../security/config/ProdSecurityConfig.java | 35 +++++-- ...ProdUsernamePwdAuthenticationProvider.java | 11 --- .../security/events/AuthorizationEvents.java | 2 +- .../security/filter/CsrfCookieFilter.java | 20 ++-- .../filter/JWTTokenGeneratorFilter.java | 69 -------------- .../filter/JWTTokenValidatorFilter.java | 25 +++-- 12 files changed, 183 insertions(+), 174 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/application/dto/RefreshDTO.java delete mode 100644 src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java index c34fb04f..837f9e97 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java @@ -7,19 +7,20 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; -import stanl_2.final_backend.domain.member.command.application.dto.GrantDTO; -import stanl_2.final_backend.domain.member.command.application.dto.SigninRequestDTO; -import stanl_2.final_backend.domain.member.command.application.dto.SigninResponseDTO; -import stanl_2.final_backend.domain.member.command.application.dto.SignupDTO; +import stanl_2.final_backend.domain.member.command.application.dto.*; import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; import stanl_2.final_backend.domain.member.common.response.MemberResponseMessage; +import java.util.Map; + +@Slf4j @RestController("commandAuthController") @RequestMapping("/api/v1/auth") public class AuthController { @@ -66,38 +67,38 @@ public ResponseEntity grantAuthority(@RequestBody GrantDT } + @PostMapping("signin") @Operation(summary = "로그인") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = MemberResponseMessage.class))}) }) - @PostMapping("signin") - public ResponseEntity signin(@RequestBody SigninRequestDTO signinRequestDTO, - HttpServletResponse response){ - - // 로그인 서비스 호출하여 Access Token과 Refresh Token 발급 - SigninResponseDTO signinResponseDTO = authCommandService.signin(signinRequestDTO); - - - // CSRF 토큰 생성 및 쿠키에 추가 - ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); - CsrfToken csrfToken = (CsrfToken) attr.getRequest().getAttribute(CsrfToken.class.getName()); - - if (csrfToken != null) { - Cookie csrfCookie = new Cookie("XSRF-TOKEN", csrfToken.getToken()); - csrfCookie.setPath("/"); - csrfCookie.setHttpOnly(false); - csrfCookie.setSecure(attr.getRequest().isSecure()); - csrfCookie.setMaxAge(-1); // 세션 종료 시 삭제 - response.addCookie(csrfCookie); - } - + public ResponseEntity signin(@RequestBody SigninRequestDTO signinRequestDTO) { + // 서비스에서 로그인 및 토큰 생성 처리 + SigninResponseDTO responseDTO = authCommandService.signin(signinRequestDTO); + + return ResponseEntity.ok( + MemberResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(responseDTO) + .build() + ); + } + @Operation(summary = "토큰 갱신") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = MemberResponseMessage.class))}) + }) + @PostMapping("refresh") + public ResponseEntity refresh(@RequestBody RefreshDTO refreshDTO) { + RefreshDTO newAccessToken = authCommandService.refreshAccessToken(refreshDTO.getRefreshToken()); return ResponseEntity.ok(MemberResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(signinResponseDTO) - .build()); + .httpStatus(200) + .msg("성공") + .result(newAccessToken) + .build()); } diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/RefreshDTO.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/RefreshDTO.java new file mode 100644 index 00000000..1f153a87 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/RefreshDTO.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.member.command.application.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +@Builder +public class RefreshDTO { + private String refreshToken; + private String newAccessToken; +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java index 34069e1f..56d01c4a 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java @@ -1,14 +1,13 @@ package stanl_2.final_backend.domain.member.command.application.service; -import stanl_2.final_backend.domain.member.command.application.dto.GrantDTO; -import stanl_2.final_backend.domain.member.command.application.dto.SigninRequestDTO; -import stanl_2.final_backend.domain.member.command.application.dto.SigninResponseDTO; -import stanl_2.final_backend.domain.member.command.application.dto.SignupDTO; +import stanl_2.final_backend.domain.member.command.application.dto.*; public interface AuthCommandService { void signup(SignupDTO signupDTO); - SigninResponseDTO signin(SigninRequestDTO signinRequestDTO); + RefreshDTO refreshAccessToken(String refreshToken); void grantAuthority(GrantDTO grantDTO); + + SigninResponseDTO signin(SigninRequestDTO signinRequestDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java index b5664b28..c9b3db5a 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.member.command.domain.service; +import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; import lombok.extern.slf4j.Slf4j; @@ -14,10 +15,7 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.member.command.application.dto.GrantDTO; -import stanl_2.final_backend.domain.member.command.application.dto.SigninRequestDTO; -import stanl_2.final_backend.domain.member.command.application.dto.SigninResponseDTO; -import stanl_2.final_backend.domain.member.command.application.dto.SignupDTO; +import stanl_2.final_backend.domain.member.command.application.dto.*; import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.Member; import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.MemberRole; @@ -29,6 +27,7 @@ import javax.crypto.SecretKey; import java.nio.charset.StandardCharsets; +import java.util.Date; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -80,16 +79,12 @@ public void signup(SignupDTO signupDTO) { memberRepository.save(registerMember); } - @Override @Transactional public SigninResponseDTO signin(SigninRequestDTO signinRequestDTO) { - - // 사용자 인증을 위한 Authentication 객체 생성 - Authentication authentication = UsernamePasswordAuthenticationToken - .unauthenticated(signinRequestDTO.getLoginId(), signinRequestDTO.getPassword()); - - // AuthenticationManager를 사용해 인증 처리 + // 사용자 인증 + Authentication authentication = new UsernamePasswordAuthenticationToken( + signinRequestDTO.getLoginId(), signinRequestDTO.getPassword()); Authentication authenticationResponse = authenticationManager.authenticate(authentication); if (authenticationResponse == null || !authenticationResponse.isAuthenticated()) { @@ -99,35 +94,74 @@ public SigninResponseDTO signin(SigninRequestDTO signinRequestDTO) { // 인증된 사용자 정보를 SecurityContext에 설정 SecurityContextHolder.getContext().setAuthentication(authenticationResponse); - // 사용자 정보 가져오기 -// MemberDetails memberDetails = (MemberDetails) authenticationResponse.getPrincipal(); // 수정된 부분 -// Member member = memberDetails.getMember(); // MemberDetails에서 Member를 얻어옴 + // 권한 정보 추출 + String authorities = authenticationResponse.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .collect(Collectors.joining(",")); + + // JWT 토큰 생성 SecretKey secretKey = Keys.hmacShaKeyFor(jwtSecretKey.getBytes(StandardCharsets.UTF_8)); + String accessToken = generateAccessToken(authenticationResponse.getName(), authorities, secretKey); + String refreshToken = generateRefreshToken(secretKey); - // Access Token (1시간) - String accessToken = Jwts.builder() + return new SigninResponseDTO(accessToken, refreshToken); + } + + private String generateAccessToken(String username, String authorities, SecretKey secretKey) { + return Jwts.builder() .setIssuer("STANL2") .setSubject("Access Token") -// .claim("id", member.getId()) // `id`만 포함 - .claim("username", signinRequestDTO.getLoginId()) - .claim("authorities", authenticationResponse.getAuthorities().stream() - .map(GrantedAuthority::getAuthority).collect(Collectors.joining(","))) - .setIssuedAt(new java.util.Date()) - .setExpiration(new java.util.Date((new java.util.Date()).getTime() + 3600000)) // 1시간 유효 + .claim("username", username) + .claim("authorities", authorities) // 권한 정보를 클레임에 추가 + .setIssuedAt(new Date()) + .setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1시간 유효 .signWith(secretKey) .compact(); + } - // Refresh Token (7일) - String refreshToken = Jwts.builder() + private String generateRefreshToken(SecretKey secretKey) { + return Jwts.builder() .setIssuer("STANL2") .setSubject("Refresh Token") -// .claim("id", member.getId()) - .setIssuedAt(new java.util.Date()) - .setExpiration(new java.util.Date((new java.util.Date()).getTime() + 604800000)) // 7일 유효 + .setIssuedAt(new Date()) + .setExpiration(new Date(System.currentTimeMillis() + 604800000)) // 7일 유효 .signWith(secretKey) .compact(); + } - return new SigninResponseDTO(accessToken, refreshToken); + @Override + public RefreshDTO refreshAccessToken(String refreshToken) { + SecretKey secretKey = Keys.hmacShaKeyFor(jwtSecretKey.getBytes(StandardCharsets.UTF_8)); + + + // Refresh Token을 검증하고 클레임을 추출 + Claims claims = Jwts.parserBuilder() + .setSigningKey(secretKey) + .build() + .parseClaimsJws(refreshToken) + .getBody(); + + // 토큰의 주제가 "Refresh Token"인지 확인 + if (!"Refresh Token".equals(claims.getSubject())) { + throw new GlobalCommonException(GlobalErrorCode.INVALID_TOKEN_ERROR); + } + + // 현재 인증된 사용자 가져오기 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null || !authentication.isAuthenticated()) { + throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + } + + // 새로운 Access Token 생성 및 반환 + return RefreshDTO.builder() + .newAccessToken( + generateAccessToken(authentication.getName(), + authentication.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .collect(Collectors.joining(",")), + secretKey) + ) + .build(); } @Override @@ -143,6 +177,4 @@ public void grantAuthority(GrantDTO grantDTO) { memberRoleRepository.save(newMemberRole); } - - } diff --git a/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java b/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java index 480935fb..cd3a3ae1 100644 --- a/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java +++ b/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java @@ -38,7 +38,7 @@ public enum GlobalErrorCode { * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다. */ - FORBIDDEN_ROLE(40300, HttpStatus.FORBIDDEN, "권한이 존재하지 않습니다."), + UNAUTHORIZED(40300, HttpStatus.FORBIDDEN, "권한이 존재하지 않습니다."), /** diff --git a/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java index e5570330..c4099834 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java @@ -18,7 +18,6 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; -import stanl_2.final_backend.global.security.filter.JWTTokenGeneratorFilter; import stanl_2.final_backend.global.security.filter.JWTTokenValidatorFilter; import java.util.Arrays; @@ -36,22 +35,32 @@ public class DevSecurityConfig { @Value("${jwt.header}") private String jwtHeader; + private AuthenticationManager authenticationManager; + @Bean SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { http.csrf(csrfConfig -> csrfConfig.disable()); http.cors(corsConfig -> corsConfig.configurationSource(corsConfigurationSource())) .authorizeHttpRequests(auth -> auth - // Swagger 관련 URL 접근 허용 - .requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**").permitAll() // 인증 없이 접근 가능한 API 설정 - .requestMatchers("/api/v1/auth/**", "/api/v1/sample/**").permitAll() + .requestMatchers( + "/swagger-ui/**", + "/v3/api-docs/**", + "/swagger-resources/**", + "/webjars/**", + "/api/v1/auth/signin", + "/api/v1/auth/signup", + "/api/v1/auth/refresh", + "/api/v1/sample/**", + "/api/v1/auth" + ).permitAll() // [Example] member는 ADMIN 권한만 접근 가능 설정 예시 .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasRole("ADMIN") .anyRequest().authenticated()) // 필터 순서: JWT 검증 -> CSRF -> JWT 생성 .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, jwtHeader), BasicAuthenticationFilter.class) - .addFilterAfter(new JWTTokenGeneratorFilter(jwtSecretKey, jwtHeader), BasicAuthenticationFilter.class) +// .addFilterAfter(new JWTTokenGeneratorFilter(jwtSecretKey, authenticationManager), BasicAuthenticationFilter.class) .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()); http.formLogin(withDefaults()); http.httpBasic(withDefaults()); diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index da67b7da..baa96e55 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -14,6 +14,7 @@ import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.password.HaveIBeenPwnedRestApiPasswordChecker; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.csrf.CookieCsrfTokenRepository; @@ -22,7 +23,6 @@ import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import stanl_2.final_backend.global.security.filter.CsrfCookieFilter; -import stanl_2.final_backend.global.security.filter.JWTTokenGeneratorFilter; import stanl_2.final_backend.global.security.filter.JWTTokenValidatorFilter; import java.util.Arrays; @@ -41,28 +41,45 @@ public class ProdSecurityConfig { private String jwtHeader; @Bean - SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { + SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception { CsrfTokenRequestAttributeHandler csrfTokenRequestAttributeHandler = new CsrfTokenRequestAttributeHandler(); http.sessionManagement(sessionConfig -> sessionConfig.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .cors(corsConfig -> corsConfig.configurationSource(corsConfigurationSource())) .csrf(csrfConfig -> csrfConfig.csrfTokenRequestHandler(csrfTokenRequestAttributeHandler) - .ignoringRequestMatchers("/api/v1/auth/signup", "/api/v1/auth/signin", "/api/v1/auth", "/api/v1/sample/**", - "/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**") + .ignoringRequestMatchers( + "/api/v1/auth/signup", + "/api/v1/auth/signin", + "/api/v1/auth/refresh", + "/api/v1/sample/**", + "/swagger-ui/**", + "/v3/api-docs/**", + "/swagger-resources/**", + "/webjars/**", + "/api/v1/auth" + ) .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())) .authorizeHttpRequests(auth -> auth - // Swagger 관련 URL 접근 허용 - .requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**").permitAll() // 인증 없이 접근 가능한 API 설정 - .requestMatchers("/api/v1/auth/**", "/api/v1/sample/**").permitAll() + .requestMatchers( + "/swagger-ui/**", + "/v3/api-docs/**", + "/swagger-resources/**", + "/webjars/**", + "/api/v1/auth/signin", + "/api/v1/auth/signup", + "/api/v1/auth/refresh", + "/api/v1/sample/**", + "/api/v1/auth" + ).permitAll() // [Example] member는 ADMIN 권한만 접근 가능 설정 예시 .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasRole("ADMIN") .anyRequest().authenticated()) // .anyRequest().authenticated()) // 필터 순서: JWT 검증 -> CSRF -> JWT 생성 - .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, jwtHeader), BasicAuthenticationFilter.class) - .addFilterAfter(new JWTTokenGeneratorFilter(jwtSecretKey, jwtHeader), BasicAuthenticationFilter.class) + .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, jwtHeader), UsernamePasswordAuthenticationFilter.class) +// .addFilterAfter(new JWTTokenGeneratorFilter(jwtSecretKey, authenticationManager), BasicAuthenticationFilter.class) .addFilterAfter(new CsrfCookieFilter(), CsrfFilter.class) .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()); http.formLogin(withDefaults()); diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java index bbaa16b8..755d1051 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java @@ -39,17 +39,6 @@ public Authentication authenticate(Authentication authentication) throws Authent log.error("UserDetails is null for username: {}", username); throw new UsernameNotFoundException("User not found"); } - log.info("$$$ ", userDetails); - log.info("$$$ ", userDetails.getUsername()); - log.info("$$$ ", userDetails.getPassword()); - log.info("$$$ ", userDetails.getAuthorities()); - - log.warn("~~~~~~~~~~~~~~~~~~~~~~~~`"); - log.info("UserDetails loaded: {}", userDetails != null ? userDetails.toString() : "null"); - log.info("Username: {}", userDetails != null ? userDetails.getUsername() : "null"); - log.info("Password: {}", userDetails != null ? userDetails.getPassword() : "null"); - log.info("Authorities: {}", userDetails != null ? userDetails.getAuthorities() : "null"); - if(passwordEncoder.matches(pwd, userDetails.getPassword())) { return new UsernamePasswordAuthenticationToken(userDetails, pwd, userDetails.getAuthorities()); diff --git a/src/main/java/stanl_2/final_backend/global/security/events/AuthorizationEvents.java b/src/main/java/stanl_2/final_backend/global/security/events/AuthorizationEvents.java index 653ded62..fa35dafc 100644 --- a/src/main/java/stanl_2/final_backend/global/security/events/AuthorizationEvents.java +++ b/src/main/java/stanl_2/final_backend/global/security/events/AuthorizationEvents.java @@ -14,6 +14,6 @@ public class AuthorizationEvents { public void onFailure(AuthorizationDeniedEvent deniedEvent){ log.error("권한 없음 유저: {} due to: {}", deniedEvent.getAuthentication().get().getName() , deniedEvent.getAuthorizationDecision().toString()); - throw new GlobalCommonException(GlobalErrorCode.FORBIDDEN_ROLE); + throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); } } diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java index c84245e1..7138d740 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java @@ -33,8 +33,13 @@ protected void doFilterInternal(HttpServletRequest request, log.info("현재 요청의 CSRF 토큰: {}", csrfToken.getToken()); if (csrfCookieValue == null || !csrfCookieValue.equals(csrfToken.getToken())) { - log.error("CSRF 토큰 불일치 - 쿠키와 요청의 토큰이 일치하지 않습니다."); - throw new GlobalCommonException(GlobalErrorCode.NOT_FOUND_CSRF_TOKEN); + log.warn("CSRF 토큰 불일치 - 새로운 CSRF 쿠키 설정"); + Cookie csrfCookie = new Cookie("XSRF-TOKEN", csrfToken.getToken()); + csrfCookie.setPath("/"); + csrfCookie.setHttpOnly(false); + csrfCookie.setSecure(request.isSecure()); + csrfCookie.setMaxAge(-1); // 세션 종료 시 삭제 + response.addCookie(csrfCookie); } filterChain.doFilter(request, response); @@ -55,11 +60,14 @@ private String getCsrfTokenFromCookie(HttpServletRequest request) { @Override protected boolean shouldNotFilter(HttpServletRequest request) { String path = request.getServletPath(); - return path.startsWith("/api/v1/auth") || - path.startsWith("/api/v1/sample") || - path.startsWith("/swagger-ui/") || + return path.equals("/api/v1/auth/signin") || + path.equals("/api/v1/auth/signup") || + path.equals("/api/v1/auth/refresh") || + path.startsWith("/swagger-ui") || path.startsWith("/v3/api-docs") || path.startsWith("/swagger-resources") || - path.startsWith("/webjars"); + path.startsWith("/webjars") || + path.startsWith("/api/v1/sample") || + path.equals("/api/v1/auth"); } } \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java deleted file mode 100644 index bb9fb7ce..00000000 --- a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenGeneratorFilter.java +++ /dev/null @@ -1,69 +0,0 @@ -package stanl_2.final_backend.global.security.filter; - -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.security.Keys; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.crypto.SecretKey; -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -@Slf4j -public class JWTTokenGeneratorFilter extends OncePerRequestFilter { - - private final String jwtSecretKey; - private final String jwtHeader; - - public JWTTokenGeneratorFilter(String jwtSecretKey, String jwtHeader) { - this.jwtSecretKey = jwtSecretKey; - this.jwtHeader = jwtHeader; - } - - - @Override - protected void doFilterInternal(HttpServletRequest request, - HttpServletResponse response, - FilterChain filterChain) throws ServletException, IOException { - - if (jwtHeader == null || jwtHeader.isEmpty()) { - log.error("JWT header is not configured properly"); - filterChain.doFilter(request, response); - return; - } - - String token = request.getHeader(jwtHeader); - - if (token != null && !token.isEmpty()) { - SecretKey secretKey = Keys.hmacShaKeyFor(jwtSecretKey.getBytes(StandardCharsets.UTF_8)); - try { - Jwts.parserBuilder() - .setSigningKey(secretKey) - .build() - .parseClaimsJws(token); - - // 토큰이 유효한 경우 SecurityContext 설정 - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication != null && authentication.isAuthenticated()) { - log.info("Valid JWT Token"); - } - } catch (Exception e) { - log.error("Invalid JWT Token", e); - } - } - - filterChain.doFilter(request, response); - } - - - @Override - protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { - return !request.getServletPath().equals("/api/v1/auth/signin"); - } -} diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java index c9b878d1..81bd48d5 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java @@ -10,6 +10,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.filter.OncePerRequestFilter; @@ -19,6 +20,7 @@ import javax.crypto.SecretKey; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.List; @Slf4j public class JWTTokenValidatorFilter extends OncePerRequestFilter { @@ -78,30 +80,37 @@ else if ("Refresh Token".equals(subject)) { private void handleAccessToken(Claims claims, HttpServletRequest request) { // 클레임에서 사용자 정보 및 권한 추출 -// String id = claims.get("id", String.class); String username = claims.get("username", String.class); String authorities = claims.get("authorities", String.class); + List authorityList = AuthorityUtils.commaSeparatedStringToAuthorityList(authorities); + // SecurityContext에 인증 정보 설정 Authentication authentication = new UsernamePasswordAuthenticationToken( - username, null, - AuthorityUtils.commaSeparatedStringToAuthorityList(authorities) + username, null, authorityList ); SecurityContextHolder.getContext().setAuthentication(authentication); - log.info("Authenticated user: {}", username); + log.info("Authenticated user: {}, authorities: {}", username, authorities); - // 추가적으로, 추출된 정보를 request 속성에 설정 -// request.setAttribute("id", id); + // 요청 속성에 사용자 정보 추가 (선택 사항) request.setAttribute("username", username); request.setAttribute("authorities", authorities); } + @Override protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { // 특정 경로에서는 필터를 적용하지 않음 String path = request.getServletPath(); - return path.startsWith("/api/v1/auth") || - path.startsWith("/api/v1/sample"); + return path.equals("/api/v1/auth/signin") || + path.equals("/api/v1/auth/signup") || + path.equals("/api/v1/auth/refresh") || + path.startsWith("/swagger-ui") || + path.startsWith("/v3/api-docs") || + path.startsWith("/swagger-resources") || + path.startsWith("/webjars") || + path.startsWith("/api/v1/sample") || + path.equals("/api/v1/auth"); } } From 2e6419c33a7d602abd404b28763a94e3f5551d40 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Fri, 15 Nov 2024 16:12:07 +0900 Subject: [PATCH 067/563] =?UTF-8?q?fix:=20=EC=98=88=EC=99=B8=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=A0=95=EB=A6=AC(#49)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/application/controller/MemberController.java | 2 +- .../final_backend/global/exception/GlobalErrorCode.java | 1 + .../global/security/config/DevSecurityConfig.java | 4 ---- .../global/security/config/ProdSecurityConfig.java | 5 +---- .../config/ProdUsernamePwdAuthenticationProvider.java | 3 ++- .../global/security/service/MemberDetailsService.java | 4 +++- 6 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java index 4998e68f..4844e35d 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java @@ -34,7 +34,7 @@ public ResponseEntity check(Authentication authentication } // 인증된 사용자 정보 출력 - System.out.println("인증된 사용자: " + authentication.getName()); + log.info("인증된 사용자: {}", authentication.getName()); return ResponseEntity.ok(MemberResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java b/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java index cd3a3ae1..fa5ec6ff 100644 --- a/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java +++ b/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java @@ -49,6 +49,7 @@ public enum GlobalErrorCode { * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. */ USERDETAILS_NOT_FOUND(40400, HttpStatus.NOT_FOUND, "User Details를 찾을 수 없습니다."), + USER_NOT_FOUND(40401, HttpStatus.NOT_FOUND, "유저 정보가 없습니다."), /** * 500(Internal Server Error) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java index c4099834..512cafd8 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java @@ -35,8 +35,6 @@ public class DevSecurityConfig { @Value("${jwt.header}") private String jwtHeader; - private AuthenticationManager authenticationManager; - @Bean SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { @@ -58,9 +56,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Excepti // [Example] member는 ADMIN 권한만 접근 가능 설정 예시 .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasRole("ADMIN") .anyRequest().authenticated()) - // 필터 순서: JWT 검증 -> CSRF -> JWT 생성 .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, jwtHeader), BasicAuthenticationFilter.class) -// .addFilterAfter(new JWTTokenGeneratorFilter(jwtSecretKey, authenticationManager), BasicAuthenticationFilter.class) .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()); http.formLogin(withDefaults()); http.httpBasic(withDefaults()); diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index baa96e55..a794987b 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -16,7 +16,6 @@ import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.password.HaveIBeenPwnedRestApiPasswordChecker; -import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.csrf.CookieCsrfTokenRepository; import org.springframework.security.web.csrf.CsrfFilter; import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; @@ -76,10 +75,8 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication // [Example] member는 ADMIN 권한만 접근 가능 설정 예시 .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasRole("ADMIN") .anyRequest().authenticated()) -// .anyRequest().authenticated()) - // 필터 순서: JWT 검증 -> CSRF -> JWT 생성 + // 필터 순서: JWT 검증 -> CSRF .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, jwtHeader), UsernamePasswordAuthenticationFilter.class) -// .addFilterAfter(new JWTTokenGeneratorFilter(jwtSecretKey, authenticationManager), BasicAuthenticationFilter.class) .addFilterAfter(new CsrfCookieFilter(), CsrfFilter.class) .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()); http.formLogin(withDefaults()); diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java index 755d1051..3fbea478 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java @@ -14,6 +14,7 @@ import org.springframework.stereotype.Component; import stanl_2.final_backend.global.exception.GlobalCommonException; import stanl_2.final_backend.global.exception.GlobalErrorCode; +import stanl_2.final_backend.global.exception.GlobalExceptionResponse; @Slf4j @Component @@ -37,7 +38,7 @@ public Authentication authenticate(Authentication authentication) throws Authent UserDetails userDetails = userDetailsService.loadUserByUsername(username); if (userDetails == null) { log.error("UserDetails is null for username: {}", username); - throw new UsernameNotFoundException("User not found"); + throw new GlobalCommonException(GlobalErrorCode.USER_NOT_FOUND); } if(passwordEncoder.matches(pwd, userDetails.getPassword())) { diff --git a/src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsService.java b/src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsService.java index 91091482..f6fbc818 100644 --- a/src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsService.java +++ b/src/main/java/stanl_2/final_backend/global/security/service/MemberDetailsService.java @@ -8,6 +8,8 @@ import org.springframework.stereotype.Service; import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.Member; import stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; +import stanl_2.final_backend.global.exception.GlobalCommonException; +import stanl_2.final_backend.global.exception.GlobalErrorCode; @Slf4j @Service(value = "MemberDetailsService") @@ -25,7 +27,7 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx // JPA를 사용하여 로그인 ID로 회원 정보 조회 Member member = memberRepository.findByLoginId(username); if (member == null) { - throw new UsernameNotFoundException("유저 정보가 없습니다" + username); + throw new GlobalCommonException(GlobalErrorCode.USER_NOT_FOUND); } // Member를 기반으로 UserDetails 생성 From e38aecc01ff255feeacbc42cd974d9d405516ced Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 15 Nov 2024 18:23:07 +0900 Subject: [PATCH 068/563] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A7=80=20=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=EB=B3=84=20=EA=B2=80=EC=83=89=20(#26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/notices/query/dto/SearchDTO.java | 5 ++++- .../query/repository/NoticeMapper.java | 2 +- .../query/service/NoticeServiceImpl.java | 5 +++-- .../notices/query/repository/NoticeMapper.xml | 21 +++++++++---------- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/dto/SearchDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/query/dto/SearchDTO.java index cfdf8093..ab6334ed 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/dto/SearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/dto/SearchDTO.java @@ -2,8 +2,11 @@ import lombok.*; +@AllArgsConstructor @NoArgsConstructor -@Data +@Getter +@Setter +@ToString public class SearchDTO { private String noticeId; private String title; diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java b/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java index bb0dda9a..3c963b0e 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java @@ -20,7 +20,7 @@ List findNotices( @Param("searchDTO") SearchDTO searchDTO ); - int findNoticesCount(@Param("searchDTO") SearchDTO searchDTO); + Integer findNoticesCount(@Param("searchDTO") SearchDTO searchDTO); NoticeDTO findNotice(@Param("noticeId") String noticeId); } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java index 1455a318..e89a8bb7 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java @@ -39,9 +39,10 @@ public Page findNotices(Pageable pageable, SearchDTO searchDTO) { int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); List notices = noticeMapper.findNotices(offset,size,searchDTO); - int totalElements = noticeMapper.findNoticesCount(searchDTO); + Integer count = noticeMapper.findNoticesCount(searchDTO); + int noticeCount = (count != null) ? noticeMapper.findNoticesCount(searchDTO) : 0; - return new PageImpl<>(notices, pageable, totalElements); + return new PageImpl<>(notices, pageable, noticeCount); } } diff --git a/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml b/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml index 9396362c..799a940d 100644 --- a/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml @@ -22,7 +22,6 @@ - @@ -67,20 +66,20 @@ FROM TB_NOTICE A A.ACTIVE != 'false' - - AND A.NOT_TAG = #{tag} + + AND A.NOT_TAG = #{searchDTO.tag} - - AND A.NOT_CLA = #{classification} + + AND A.NOT_CLA = #{searchDTO.classification} - - AND A.MEM_ID = #{memberId} + + AND A.MEM_ID = #{searchDTO.memberId} - - AND A.NOT_TTL LIKE CONCAT('%', #{title}, '%') + + AND A.NOT_TTL LIKE CONCAT('%', #{searchDTO.title}, '%') - - AND A.CREATED_AT BETWEEN #{startDate} AND #{endDate} + + AND A.CREATED_AT BETWEEN #{searchDTO.startDate} AND #{searchDTO.endDate} ORDER BY A.CREATED_AT DESC From 8f99d4d53a0667771cc077b280aab5aa90356fe2 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Fri, 15 Nov 2024 18:28:31 +0900 Subject: [PATCH 069/563] =?UTF-8?q?fix:=20=EC=8B=9C=ED=81=90=EB=A6=AC?= =?UTF-8?q?=ED=8B=B0(#38)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AuthController.java | 7 -- .../service/AuthCommandServiceImpl.java | 10 ++- .../global/config/SwaggerConfig.java | 34 --------- .../global/exception/GlobalErrorCode.java | 3 +- .../global/security/config/CorsConfig.java | 2 - .../security/config/DevSecurityConfig.java | 1 - .../security/config/ProdSecurityConfig.java | 6 +- ...ProdUsernamePwdAuthenticationProvider.java | 4 +- .../security/filter/CsrfCookieFilter.java | 73 ------------------- .../filter/JWTTokenValidatorFilter.java | 10 ++- .../security/service/MemberDetails.java | 9 --- 11 files changed, 18 insertions(+), 141 deletions(-) delete mode 100644 src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java index 837f9e97..596b8bac 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java @@ -5,21 +5,14 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; -import org.springframework.security.web.csrf.CsrfToken; import org.springframework.web.bind.annotation.*; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; import stanl_2.final_backend.domain.member.command.application.dto.*; import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; import stanl_2.final_backend.domain.member.common.response.MemberResponseMessage; -import java.util.Map; - @Slf4j @RestController("commandAuthController") @RequestMapping("/api/v1/auth") diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java index c9b3db5a..903ed6df 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java @@ -3,6 +3,9 @@ import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; @@ -13,6 +16,7 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.csrf.CsrfToken; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.member.command.application.dto.*; @@ -112,9 +116,9 @@ private String generateAccessToken(String username, String authorities, SecretKe .setIssuer("STANL2") .setSubject("Access Token") .claim("username", username) - .claim("authorities", authorities) // 권한 정보를 클레임에 추가 + .claim("authorities", authorities) .setIssuedAt(new Date()) - .setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1시간 유효 + .setExpiration(new Date(System.currentTimeMillis() + 1800000)) // 30분 유효 .signWith(secretKey) .compact(); } @@ -124,7 +128,7 @@ private String generateRefreshToken(SecretKey secretKey) { .setIssuer("STANL2") .setSubject("Refresh Token") .setIssuedAt(new Date()) - .setExpiration(new Date(System.currentTimeMillis() + 604800000)) // 7일 유효 + .setExpiration(new Date(System.currentTimeMillis() + 86400000)) // 24시간 유효 .signWith(secretKey) .compact(); } diff --git a/src/main/java/stanl_2/final_backend/global/config/SwaggerConfig.java b/src/main/java/stanl_2/final_backend/global/config/SwaggerConfig.java index f3e03480..d9365116 100644 --- a/src/main/java/stanl_2/final_backend/global/config/SwaggerConfig.java +++ b/src/main/java/stanl_2/final_backend/global/config/SwaggerConfig.java @@ -7,16 +7,8 @@ import io.swagger.v3.oas.annotations.security.SecurityScheme; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.HandlerInterceptor; -import jakarta.servlet.http.Cookie; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -import java.util.Arrays; @Configuration @OpenAPIDefinition( @@ -49,30 +41,4 @@ public OpenAPI OpenAPI() { .addSecurityItem(new io.swagger.v3.oas.models.security.SecurityRequirement() .addList("Bearer Authentication")); } - - @Bean - public WebMvcConfigurer swaggerCsrfConfigurer() { - return new WebMvcConfigurer() { - @Override - public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(new HandlerInterceptor() { - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - Cookie[] cookies = request.getCookies(); - if (cookies != null) { - String csrfToken = Arrays.stream(cookies) - .filter(cookie -> "XSRF-TOKEN".equals(cookie.getName())) - .map(Cookie::getValue) - .findFirst() - .orElse(null); - if (csrfToken != null) { - response.addHeader("X-XSRF-TOKEN", csrfToken); - } - } - return true; - } - }).addPathPatterns("/swagger-ui/**"); - } - }; - } } diff --git a/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java b/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java index fa5ec6ff..f3ed639a 100644 --- a/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java +++ b/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java @@ -31,7 +31,7 @@ public enum GlobalErrorCode { */ LOGIN_FAILURE(40100, HttpStatus.UNAUTHORIZED, "로그인에 실패했습니다"), INVALID_TOKEN_ERROR(40101, HttpStatus.UNAUTHORIZED, "유효하지 않은 토큰입니다."), - NOT_FOUND_CSRF_TOKEN(40102, HttpStatus.UNAUTHORIZED, "CSRF 토큰이 요류입니다."), + INVALID_CSRF_TOKEN(40102, HttpStatus.UNAUTHORIZED, "유효하지 않은 CSRF토큰입니다."), /** * 403(Forbidden) @@ -50,6 +50,7 @@ public enum GlobalErrorCode { */ USERDETAILS_NOT_FOUND(40400, HttpStatus.NOT_FOUND, "User Details를 찾을 수 없습니다."), USER_NOT_FOUND(40401, HttpStatus.NOT_FOUND, "유저 정보가 없습니다."), + NOT_FOUND_CSRF_TOKEN(40402, HttpStatus.NOT_FOUND, "CSRF토큰이 없습니다."), /** * 500(Internal Server Error) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/CorsConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/CorsConfig.java index c47bef41..32871306 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/CorsConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/CorsConfig.java @@ -1,7 +1,5 @@ package stanl_2.final_backend.global.security.config; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; diff --git a/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java index 512cafd8..a6f6f5da 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java @@ -1,6 +1,5 @@ package stanl_2.final_backend.global.security.config; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index a794987b..53ebaa0f 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -17,11 +17,9 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.password.HaveIBeenPwnedRestApiPasswordChecker; import org.springframework.security.web.csrf.CookieCsrfTokenRepository; -import org.springframework.security.web.csrf.CsrfFilter; import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; -import stanl_2.final_backend.global.security.filter.CsrfCookieFilter; import stanl_2.final_backend.global.security.filter.JWTTokenValidatorFilter; import java.util.Arrays; @@ -57,8 +55,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication "/swagger-resources/**", "/webjars/**", "/api/v1/auth" - ) - .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())) + ).csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())) .authorizeHttpRequests(auth -> auth // 인증 없이 접근 가능한 API 설정 .requestMatchers( @@ -77,7 +74,6 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication .anyRequest().authenticated()) // 필터 순서: JWT 검증 -> CSRF .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, jwtHeader), UsernamePasswordAuthenticationFilter.class) - .addFilterAfter(new CsrfCookieFilter(), CsrfFilter.class) .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()); http.formLogin(withDefaults()); http.httpBasic(withDefaults()); diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java index 3fbea478..f7b95157 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdUsernamePwdAuthenticationProvider.java @@ -9,12 +9,10 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; import stanl_2.final_backend.global.exception.GlobalCommonException; import stanl_2.final_backend.global.exception.GlobalErrorCode; -import stanl_2.final_backend.global.exception.GlobalExceptionResponse; @Slf4j @Component @@ -37,7 +35,7 @@ public Authentication authenticate(Authentication authentication) throws Authent UserDetails userDetails = userDetailsService.loadUserByUsername(username); if (userDetails == null) { - log.error("UserDetails is null for username: {}", username); + log.error("현재 null인 userdetail의 username: {}", username); throw new GlobalCommonException(GlobalErrorCode.USER_NOT_FOUND); } diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java deleted file mode 100644 index 7138d740..00000000 --- a/src/main/java/stanl_2/final_backend/global/security/filter/CsrfCookieFilter.java +++ /dev/null @@ -1,73 +0,0 @@ -package stanl_2.final_backend.global.security.filter; - -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.web.csrf.CsrfToken; -import org.springframework.web.filter.OncePerRequestFilter; -import stanl_2.final_backend.global.exception.GlobalCommonException; -import stanl_2.final_backend.global.exception.GlobalErrorCode; - -import java.io.IOException; - -@Slf4j -public class CsrfCookieFilter extends OncePerRequestFilter { - - @Override - protected void doFilterInternal(HttpServletRequest request, - HttpServletResponse response, - FilterChain filterChain) throws ServletException, IOException { - CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName()); - - if (csrfToken == null) { - log.error("CSRF 토큰을 찾을 수 없습니다."); - throw new GlobalCommonException(GlobalErrorCode.NOT_FOUND_CSRF_TOKEN); - } - - // 쿠키에서 CSRF 토큰 가져오기 - String csrfCookieValue = getCsrfTokenFromCookie(request); - log.info("쿠키에서 가져온 CSRF 토큰: {}", csrfCookieValue); - log.info("현재 요청의 CSRF 토큰: {}", csrfToken.getToken()); - - if (csrfCookieValue == null || !csrfCookieValue.equals(csrfToken.getToken())) { - log.warn("CSRF 토큰 불일치 - 새로운 CSRF 쿠키 설정"); - Cookie csrfCookie = new Cookie("XSRF-TOKEN", csrfToken.getToken()); - csrfCookie.setPath("/"); - csrfCookie.setHttpOnly(false); - csrfCookie.setSecure(request.isSecure()); - csrfCookie.setMaxAge(-1); // 세션 종료 시 삭제 - response.addCookie(csrfCookie); - } - - filterChain.doFilter(request, response); - } - - private String getCsrfTokenFromCookie(HttpServletRequest request) { - Cookie[] cookies = request.getCookies(); - if (cookies != null) { - for (Cookie cookie : cookies) { - if ("XSRF-TOKEN".equals(cookie.getName())) { - return cookie.getValue(); - } - } - } - return null; - } - - @Override - protected boolean shouldNotFilter(HttpServletRequest request) { - String path = request.getServletPath(); - return path.equals("/api/v1/auth/signin") || - path.equals("/api/v1/auth/signup") || - path.equals("/api/v1/auth/refresh") || - path.startsWith("/swagger-ui") || - path.startsWith("/v3/api-docs") || - path.startsWith("/swagger-resources") || - path.startsWith("/webjars") || - path.startsWith("/api/v1/sample") || - path.equals("/api/v1/auth"); - } -} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java index 81bd48d5..33ec83dc 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java @@ -63,13 +63,15 @@ protected void doFilterInternal(HttpServletRequest request, } // Refresh Token은 인증 처리가 아닌, 토큰 재발급용으로만 사용 else if ("Refresh Token".equals(subject)) { - log.info("Received Refresh Token"); + log.info("$Refresh Token$"); } else { throw new GlobalCommonException(GlobalErrorCode.INVALID_TOKEN_ERROR); } + + } catch (Exception exception) { - log.error("Invalid JWT Token", exception); + log.error("유효한 JWT Token", exception); throw new GlobalCommonException(GlobalErrorCode.INVALID_TOKEN_ERROR); } } @@ -89,9 +91,11 @@ private void handleAccessToken(Claims claims, HttpServletRequest request) { Authentication authentication = new UsernamePasswordAuthenticationToken( username, null, authorityList ); + + // SecurityContextHolder에 인증 정보 설정 SecurityContextHolder.getContext().setAuthentication(authentication); - log.info("Authenticated user: {}, authorities: {}", username, authorities); + log.info("Authenticated 유저: {}, authorities: {}", username, authorities); // 요청 속성에 사용자 정보 추가 (선택 사항) request.setAttribute("username", username); diff --git a/src/main/java/stanl_2/final_backend/global/security/service/MemberDetails.java b/src/main/java/stanl_2/final_backend/global/security/service/MemberDetails.java index d684061d..8f2e35b6 100644 --- a/src/main/java/stanl_2/final_backend/global/security/service/MemberDetails.java +++ b/src/main/java/stanl_2/final_backend/global/security/service/MemberDetails.java @@ -57,13 +57,4 @@ public boolean isEnabled() { return member.getActive(); } - @Override - public String toString() { - return "MemberDetails{" + - "username='" + getUsername() + '\'' + - ", password='" + getPassword() + '\'' + - ", authorities=" + getAuthorities() + - '}'; - } - } From 7997e89d85b14a00ee852f2b0d8e9d5dd878ee8c Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 15 Nov 2024 18:59:33 +0900 Subject: [PATCH 070/563] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A7=80=20Id?= =?UTF-8?q?=EA=B8=B0=EB=B0=98=20=EA=B2=80=EC=83=89=20(#26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notices/query/controller/NoticeController.java | 13 +++++++++++++ .../domain/notices/query/service/NoticeService.java | 1 + .../notices/query/service/NoticeServiceImpl.java | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java index c58d1b47..18b9f277 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java @@ -68,4 +68,17 @@ public ResponseEntity> getNotices( return ResponseEntity.ok(noticeDTOPage); } + + @Operation(summary = "공지사항 Id로 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = NoticeResponseMessage.class))}) + }) + @GetMapping("{noticeId}") + public ResponseEntity getNotice(@PathVariable String noticeId){ + NoticeDTO noticeDTO = noticeService.findNotice(noticeId); + return ResponseEntity.ok(noticeDTO); + } + + } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java index 9057133e..5117c080 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java @@ -12,5 +12,6 @@ public interface NoticeService { Page findAllNotices(Pageable pageable); Page findNotices(Pageable pageable, SearchDTO searchDTO); + NoticeDTO findNotice(String noticeId); } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java index e89a8bb7..1bca9daf 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java @@ -6,6 +6,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.notices.command.domain.aggragate.entity.Notice; import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; import stanl_2.final_backend.domain.notices.query.dto.SearchDTO; import stanl_2.final_backend.domain.notices.query.repository.NoticeMapper; @@ -45,4 +46,10 @@ public Page findNotices(Pageable pageable, SearchDTO searchDTO) { return new PageImpl<>(notices, pageable, noticeCount); } + @Override + public NoticeDTO findNotice(String noticeId) { + NoticeDTO notice = noticeMapper.findNotice(noticeId); + return notice; + } + } From 580f06f9a315c570e760114d96dc5a7654fdaa5c Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Fri, 15 Nov 2024 21:37:45 +0900 Subject: [PATCH 071/563] =?UTF-8?q?feat:=20=EA=B0=9C=EC=9D=B8=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=96=91=EB=B0=A9=ED=96=A5=20=EC=95=94=ED=98=B8?= =?UTF-8?q?=ED=99=94(#17)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AuthController.java | 8 +- .../service/AuthCommandService.java | 4 +- .../service/AuthCommandServiceImpl.java | 49 +++++----- .../security/config/DevSecurityConfig.java | 2 +- .../security/config/ProdSecurityConfig.java | 24 +---- .../filter/JWTTokenValidatorFilter.java | 91 +++++++------------ .../final_backend/global/utils/AESUtils.java | 50 ++++++++++ src/main/resources/application.yml | 16 ++-- 8 files changed, 133 insertions(+), 111 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/global/utils/AESUtils.java diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java index 596b8bac..fcece68d 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java @@ -13,6 +13,8 @@ import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; import stanl_2.final_backend.domain.member.common.response.MemberResponseMessage; +import java.security.GeneralSecurityException; + @Slf4j @RestController("commandAuthController") @RequestMapping("/api/v1/auth") @@ -31,7 +33,7 @@ public AuthController(AuthCommandService authCommandService) { content = {@Content(schema = @Schema(implementation = MemberResponseMessage.class))}) }) @PostMapping("signup") - public ResponseEntity signup(@RequestBody SignupDTO signupDTO){ + public ResponseEntity signup(@RequestBody SignupDTO signupDTO) throws GeneralSecurityException { authCommandService.signup(signupDTO); @@ -67,7 +69,7 @@ public ResponseEntity grantAuthority(@RequestBody GrantDT content = {@Content(schema = @Schema(implementation = MemberResponseMessage.class))}) }) public ResponseEntity signin(@RequestBody SigninRequestDTO signinRequestDTO) { - // 서비스에서 로그인 및 토큰 생성 처리 + SigninResponseDTO responseDTO = authCommandService.signin(signinRequestDTO); return ResponseEntity.ok( @@ -86,7 +88,9 @@ public ResponseEntity signin(@RequestBody SigninRequestDT }) @PostMapping("refresh") public ResponseEntity refresh(@RequestBody RefreshDTO refreshDTO) { + RefreshDTO newAccessToken = authCommandService.refreshAccessToken(refreshDTO.getRefreshToken()); + return ResponseEntity.ok(MemberResponseMessage.builder() .httpStatus(200) .msg("성공") diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java index 56d01c4a..b21356f6 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java @@ -2,8 +2,10 @@ import stanl_2.final_backend.domain.member.command.application.dto.*; +import java.security.GeneralSecurityException; + public interface AuthCommandService { - void signup(SignupDTO signupDTO); + void signup(SignupDTO signupDTO) throws GeneralSecurityException; RefreshDTO refreshAccessToken(String refreshToken); diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java index 903ed6df..16019cd0 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java @@ -3,9 +3,6 @@ import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; @@ -16,7 +13,6 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.csrf.CsrfToken; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.member.command.application.dto.*; @@ -28,9 +24,11 @@ import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.global.exception.GlobalCommonException; import stanl_2.final_backend.global.exception.GlobalErrorCode; +import stanl_2.final_backend.global.utils.AESUtils; import javax.crypto.SecretKey; import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; import java.util.Date; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -50,6 +48,7 @@ public class AuthCommandServiceImpl implements AuthCommandService { private final ModelMapper modelMapper; private final AuthenticationManager authenticationManager; private final AuthQueryService authQueryService; + private final AESUtils aesUtils; @Autowired public AuthCommandServiceImpl(MemberRepository memberRepository, @@ -57,13 +56,15 @@ public AuthCommandServiceImpl(MemberRepository memberRepository, PasswordEncoder passwordEncoder, ModelMapper modelMapper, AuthenticationManager authenticationManager, - AuthQueryService authQueryService) { + AuthQueryService authQueryService, + AESUtils aesUtils) { this.memberRepository = memberRepository; this.memberRoleRepository = memberRoleRepository; this.passwordEncoder = passwordEncoder; this.modelMapper = modelMapper; this.authenticationManager = authenticationManager; this.authQueryService = authQueryService; + this.aesUtils = aesUtils; } private String getCurrentTimestamp() { @@ -73,10 +74,18 @@ private String getCurrentTimestamp() { @Override @Transactional - public void signup(SignupDTO signupDTO) { + public void signup(SignupDTO signupDTO) throws GeneralSecurityException { String hashPwd = passwordEncoder.encode(signupDTO.getPassword()); signupDTO.setPassword(hashPwd); + signupDTO.setName(aesUtils.encrypt(signupDTO.getName())); + signupDTO.setEmail(aesUtils.encrypt(signupDTO.getEmail())); + signupDTO.setIdenNo(aesUtils.encrypt(signupDTO.getIdenNo())); + signupDTO.setPhone(aesUtils.encrypt(signupDTO.getPhone())); + signupDTO.setEmergPhone(aesUtils.encrypt(signupDTO.getEmergPhone())); + signupDTO.setAddress(aesUtils.encrypt(signupDTO.getAddress())); + signupDTO.setBankName(aesUtils.encrypt(signupDTO.getBankName())); + signupDTO.setAccount(aesUtils.encrypt(signupDTO.getAccount())); Member registerMember = modelMapper.map(signupDTO, Member.class); @@ -95,7 +104,7 @@ public SigninResponseDTO signin(SigninRequestDTO signinRequestDTO) { throw new GlobalCommonException(GlobalErrorCode.LOGIN_FAILURE); } - // 인증된 사용자 정보를 SecurityContext에 설정 + // 인증된 사용자 정보를 SecurityContext에 저장 SecurityContextHolder.getContext().setAuthentication(authenticationResponse); // 권한 정보 추출 @@ -106,7 +115,7 @@ public SigninResponseDTO signin(SigninRequestDTO signinRequestDTO) { // JWT 토큰 생성 SecretKey secretKey = Keys.hmacShaKeyFor(jwtSecretKey.getBytes(StandardCharsets.UTF_8)); String accessToken = generateAccessToken(authenticationResponse.getName(), authorities, secretKey); - String refreshToken = generateRefreshToken(secretKey); + String refreshToken = generateRefreshToken(authenticationResponse.getName(), authorities,secretKey); return new SigninResponseDTO(accessToken, refreshToken); } @@ -123,10 +132,12 @@ private String generateAccessToken(String username, String authorities, SecretKe .compact(); } - private String generateRefreshToken(SecretKey secretKey) { + private String generateRefreshToken(String username, String authorities, SecretKey secretKey) { return Jwts.builder() .setIssuer("STANL2") .setSubject("Refresh Token") + .claim("username", username) + .claim("authorities", authorities) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + 86400000)) // 24시간 유효 .signWith(secretKey) @@ -150,21 +161,17 @@ public RefreshDTO refreshAccessToken(String refreshToken) { throw new GlobalCommonException(GlobalErrorCode.INVALID_TOKEN_ERROR); } - // 현재 인증된 사용자 가져오기 - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication == null || !authentication.isAuthenticated()) { - throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + // 사용자 정보 추출 + String username = claims.get("username", String.class); + String authorities = claims.get("authorities", String.class); + + if (username == null || authorities == null) { + throw new GlobalCommonException(GlobalErrorCode.INVALID_TOKEN_ERROR); } - // 새로운 Access Token 생성 및 반환 + // 새로운 Access Token 생성 return RefreshDTO.builder() - .newAccessToken( - generateAccessToken(authentication.getName(), - authentication.getAuthorities().stream() - .map(GrantedAuthority::getAuthority) - .collect(Collectors.joining(",")), - secretKey) - ) + .newAccessToken(generateAccessToken(username, authorities, secretKey)) .build(); } diff --git a/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java index a6f6f5da..0c20bb6e 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java @@ -55,7 +55,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Excepti // [Example] member는 ADMIN 권한만 접근 가능 설정 예시 .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasRole("ADMIN") .anyRequest().authenticated()) - .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, jwtHeader), BasicAuthenticationFilter.class) + .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey), BasicAuthenticationFilter.class) .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()); http.formLogin(withDefaults()); http.httpBasic(withDefaults()); diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index 53ebaa0f..42414d4b 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -9,14 +9,12 @@ import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.password.CompromisedPasswordChecker; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.password.HaveIBeenPwnedRestApiPasswordChecker; -import org.springframework.security.web.csrf.CookieCsrfTokenRepository; import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; @@ -34,28 +32,12 @@ public class ProdSecurityConfig { @Value("${jwt.secret-key}") private String jwtSecretKey; - @Value("${jwt.header}") - private String jwtHeader; - @Bean SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception { CsrfTokenRequestAttributeHandler csrfTokenRequestAttributeHandler = new CsrfTokenRequestAttributeHandler(); - - http.sessionManagement(sessionConfig -> sessionConfig.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .cors(corsConfig -> corsConfig.configurationSource(corsConfigurationSource())) - .csrf(csrfConfig -> csrfConfig.csrfTokenRequestHandler(csrfTokenRequestAttributeHandler) - .ignoringRequestMatchers( - "/api/v1/auth/signup", - "/api/v1/auth/signin", - "/api/v1/auth/refresh", - "/api/v1/sample/**", - "/swagger-ui/**", - "/v3/api-docs/**", - "/swagger-resources/**", - "/webjars/**", - "/api/v1/auth" - ).csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())) + http.cors(corsConfig -> corsConfig.configurationSource(corsConfigurationSource())) + .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> auth // 인증 없이 접근 가능한 API 설정 .requestMatchers( @@ -73,7 +55,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasRole("ADMIN") .anyRequest().authenticated()) // 필터 순서: JWT 검증 -> CSRF - .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, jwtHeader), UsernamePasswordAuthenticationFilter.class) + .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey), UsernamePasswordAuthenticationFilter.class) .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()); http.formLogin(withDefaults()); http.httpBasic(withDefaults()); diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java index 33ec83dc..af07b72d 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java @@ -1,6 +1,7 @@ package stanl_2.final_backend.global.security.filter; import io.jsonwebtoken.Claims; +import io.jsonwebtoken.JwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; import jakarta.servlet.FilterChain; @@ -8,100 +9,77 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpHeaders; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.filter.OncePerRequestFilter; -import stanl_2.final_backend.global.exception.GlobalCommonException; -import stanl_2.final_backend.global.exception.GlobalErrorCode; import javax.crypto.SecretKey; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; @Slf4j public class JWTTokenValidatorFilter extends OncePerRequestFilter { private final String jwtSecretKey; - private final String jwtHeader; - public JWTTokenValidatorFilter(String jwtSecretKey, String jwtHeader) { - this.jwtHeader = jwtHeader; + public JWTTokenValidatorFilter(String jwtSecretKey) { this.jwtSecretKey = jwtSecretKey; } @Override - protected void doFilterInternal(HttpServletRequest request, - HttpServletResponse response, - FilterChain filterChain) throws ServletException, IOException { - // 요청 헤더에서 JWT 토큰 가져오기 - String jwt = request.getHeader(jwtHeader); + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { - // 토큰이 존재하고 "Bearer "로 시작하는지 확인 - if (jwt != null && jwt.startsWith("Bearer ")) { + String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION); + + if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { + String token = authorizationHeader.substring(7); try { - // 비밀키를 사용하여 JWT 토큰을 검증 SecretKey secretKey = Keys.hmacShaKeyFor(jwtSecretKey.getBytes(StandardCharsets.UTF_8)); - String jwtToken = jwt.substring(7); // "Bearer " 부분 제거 - - // JWT 토큰 파싱 및 검증 Claims claims = Jwts.parserBuilder() .setSigningKey(secretKey) .build() - .parseClaimsJws(jwtToken) + .parseClaimsJws(token) .getBody(); - // 클레임에서 토큰 타입 확인 - String subject = claims.getSubject(); + // 사용자 정보 추출 + String username = claims.get("username", String.class); + String authorities = claims.get("authorities", String.class); - // Access Token 처리 - if ("Access Token".equals(subject)) { - handleAccessToken(claims, request); - } - // Refresh Token은 인증 처리가 아닌, 토큰 재발급용으로만 사용 - else if ("Refresh Token".equals(subject)) { - log.info("$Refresh Token$"); - } else { - throw new GlobalCommonException(GlobalErrorCode.INVALID_TOKEN_ERROR); + // 예외 처리: authorities가 null인 경우 + if (authorities == null || authorities.isEmpty()) { + log.error("Invalid token: authorities are missing"); + SecurityContextHolder.clearContext(); + filterChain.doFilter(request, response); + return; } + List grantedAuthorities = Arrays.stream(authorities.split(",")) + .map(SimpleGrantedAuthority::new) + .collect(Collectors.toList()); - - } catch (Exception exception) { - log.error("유효한 JWT Token", exception); - throw new GlobalCommonException(GlobalErrorCode.INVALID_TOKEN_ERROR); + // SecurityContext에 인증 정보 설정 + if (username != null) { + Authentication authentication = new UsernamePasswordAuthenticationToken( + username, null, grantedAuthorities); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } catch (JwtException e) { + logger.error("Invalid JWT token", e); + SecurityContextHolder.clearContext(); } } - // 다음 필터로 요청 전달 filterChain.doFilter(request, response); } - private void handleAccessToken(Claims claims, HttpServletRequest request) { - // 클레임에서 사용자 정보 및 권한 추출 - String username = claims.get("username", String.class); - String authorities = claims.get("authorities", String.class); - - List authorityList = AuthorityUtils.commaSeparatedStringToAuthorityList(authorities); - - // SecurityContext에 인증 정보 설정 - Authentication authentication = new UsernamePasswordAuthenticationToken( - username, null, authorityList - ); - - // SecurityContextHolder에 인증 정보 설정 - SecurityContextHolder.getContext().setAuthentication(authentication); - - log.info("Authenticated 유저: {}, authorities: {}", username, authorities); - - // 요청 속성에 사용자 정보 추가 (선택 사항) - request.setAttribute("username", username); - request.setAttribute("authorities", authorities); - } - @Override protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { @@ -109,7 +87,6 @@ protected boolean shouldNotFilter(HttpServletRequest request) throws ServletExce String path = request.getServletPath(); return path.equals("/api/v1/auth/signin") || path.equals("/api/v1/auth/signup") || - path.equals("/api/v1/auth/refresh") || path.startsWith("/swagger-ui") || path.startsWith("/v3/api-docs") || path.startsWith("/swagger-resources") || diff --git a/src/main/java/stanl_2/final_backend/global/utils/AESUtils.java b/src/main/java/stanl_2/final_backend/global/utils/AESUtils.java new file mode 100644 index 00000000..484d5a38 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/utils/AESUtils.java @@ -0,0 +1,50 @@ +package stanl_2.final_backend.global.utils; + +import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; +import org.apache.tomcat.util.codec.binary.Base64; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; + +@Slf4j +@Component +public class AESUtils { + + @Value("${encryption.algorithm}") + private String algorithm; + + @Value("${encryption.transformation}") + private String transformation; + + @Value("${encryption.secret-key}") + private String secretKeyValue; + + /** + * AES 대칭키를 사용하여 문자열을 암호화합니다. + */ + public String encrypt(String data) throws GeneralSecurityException { + Cipher cipher = Cipher.getInstance(transformation); + SecretKey secretKey = new SecretKeySpec(secretKeyValue.getBytes(StandardCharsets.UTF_8), algorithm); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8)); + return Base64.encodeBase64String(encryptedBytes); + } + + /** + * AES 대칭키를 사용하여 암호화된 문자열을 복호화합니다. + */ + public String decrypt(String encryptedData) throws GeneralSecurityException { + Cipher cipher = Cipher.getInstance(transformation); + SecretKey secretKey = new SecretKeySpec(secretKeyValue.getBytes(StandardCharsets.UTF_8), algorithm); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + byte[] decodedBytes = Base64.decodeBase64(encryptedData); + byte[] decryptedBytes = cipher.doFinal(decodedBytes); + return new String(decryptedBytes, StandardCharsets.UTF_8); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 2b514a12..eee7170c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -16,7 +16,7 @@ spring: devtools: livereload: - enabled: true + enabled: false restart: enabled: false @@ -29,18 +29,18 @@ spring: jwt: secret-key: ${JWT_SECRET_KEY} - secret-default-value: ${JWT_SECRET_DEFAULT_VALUE} header: ${JWT_HEADER} +encryption: + algorithm: ${ALGORITHM} + transformation: ${TRANSFORMATION} + secret-key: ${SECRET_DEFAULT_KEY} + + logging: pattern: console: ${LOGPATTERN_CONSOLE:%green(%d{HH:mm:ss.SSS}) %blue(%-5level) %red([%thread]) %yellow(%logger{15}) - %msg%n} level: org: springframework: - security: ${SPRING_SECURITY_LOG_LEVEL:TRACE} - -#server: -# servlet: -# session: -# timeout: ${SESSION_TIMEOUT:20m} \ No newline at end of file + security: ${SPRING_SECURITY_LOG_LEVEL:TRACE} \ No newline at end of file From 37520b0d30e1216e9ce42e4beab080dff700a231 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Fri, 15 Nov 2024 23:00:47 +0900 Subject: [PATCH 072/563] =?UTF-8?q?refactor:=20=EC=88=98=EC=A3=BC=EC=84=9C?= =?UTF-8?q?=20=EB=B3=80=EC=88=98=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/controller/OrderCommandController.java | 9 ++++----- .../order/command/application/dto/OrderModifyDTO.java | 4 ++-- .../order/command/application/dto/OrderRegistDTO.java | 4 ++-- .../order/command/domain/aggregate/entity/Order.java | 6 +++--- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderCommandController.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderCommandController.java index 9c58cf0c..5d11e08f 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderCommandController.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderCommandController.java @@ -8,7 +8,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.order.command.application.dto.OrderModifyDTO; import stanl_2.final_backend.domain.order.command.application.dto.OrderRegistDTO; import stanl_2.final_backend.domain.order.command.application.service.OrderCommandService; @@ -35,7 +34,7 @@ public OrderCommandController(OrderCommandService orderCommandService) { * "memId": "MEM_000000001" * } * */ - @Operation(summary = "수주서 등록 테스트") + @Operation(summary = "수주서 등록") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "수주서 등록 성공", content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) @@ -62,7 +61,7 @@ public ResponseEntity postOrder(@RequestBody OrderRegistDT * "memId": "MEM_000000001" * } * */ - @Operation(summary = "수주서 수정 테스트") + @Operation(summary = "수주서 수정") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "수주서 수정 성공", content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) @@ -71,7 +70,7 @@ public ResponseEntity postOrder(@RequestBody OrderRegistDT public ResponseEntity putOrder(@PathVariable String id, @RequestBody OrderModifyDTO orderModifyDTO) { - orderModifyDTO.setId(id); + orderModifyDTO.setOrderId(id); OrderModifyDTO orderModifyResponseDTO = orderCommandService.modifyOrder(orderModifyDTO); return ResponseEntity.ok(OrderResponseMessage.builder() @@ -90,7 +89,7 @@ public ResponseEntity putOrder(@PathVariable String id, * "memId": "MEM_000000001" * } * */ - @Operation(summary = "수주서 삭제 테스트") + @Operation(summary = "수주서 삭제") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "수주서 삭제 성공", content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderModifyDTO.java index 4097939e..145eaddd 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderModifyDTO.java @@ -8,8 +8,8 @@ @Getter @ToString public class OrderModifyDTO { - private String id; + private String orderId; private String title; private String content; - private String memId; + private String memberId; } diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderRegistDTO.java index 20655611..eb81b6e8 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderRegistDTO.java @@ -10,6 +10,6 @@ public class OrderRegistDTO { private String title; private String content; - private String conrId; - private String memId; + private String contractId; + private String memberId; } diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/domain/aggregate/entity/Order.java b/src/main/java/stanl_2/final_backend/domain/order/command/domain/aggregate/entity/Order.java index 94b13dae..123163f1 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/domain/aggregate/entity/Order.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/domain/aggregate/entity/Order.java @@ -27,7 +27,7 @@ public class Order { parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "ORD") ) @Column(name = "ORD_ID") - private String id; + private String orderId; @Column(name = "ORD_TTL", nullable = false) private String title; @@ -52,13 +52,13 @@ public class Order { private String status; @Column(name = "CONR_ID", nullable = false) - private String conrId; + private String contractId; @Column(name = "ADMIN_ID") private String adminId; @Column(name = "MEM_ID", nullable = false) - private String memId; + private String memberId; // Insert 되기 전에 실행 @PrePersist From d54f3a5ac0f7cc1d27c5cc402e8ee4e07286f3c5 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Sat, 16 Nov 2024 00:35:49 +0900 Subject: [PATCH 073/563] =?UTF-8?q?feat:=20=EC=88=98=EC=A3=BC=EC=84=9C=20?= =?UTF-8?q?=EC=A0=84=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84(#4?= =?UTF-8?q?3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ndController.java => OrderController.java} | 6 +-- .../service/OrderCommandServiceImpl.java | 4 +- .../query/controller/OrderController.java | 53 +++++++++++++++++++ .../order/query/dto/OrderSelectAllDTO.java | 21 ++++++++ .../order/query/repository/OrderMapper.java | 13 +++++ .../query/service/OrderQueryService.java | 11 ++++ .../query/service/OrderQueryServiceImpl.java | 40 ++++++++++++++ .../order/query/repository/OrderMapper.xml | 51 ++++++++++++++++++ 8 files changed, 194 insertions(+), 5 deletions(-) rename src/main/java/stanl_2/final_backend/domain/order/command/application/controller/{OrderCommandController.java => OrderController.java} (99%) create mode 100644 src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectAllDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java create mode 100644 src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java create mode 100644 src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderCommandController.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderController.java similarity index 99% rename from src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderCommandController.java rename to src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderController.java index 5d11e08f..3360fa30 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderCommandController.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderController.java @@ -13,14 +13,14 @@ import stanl_2.final_backend.domain.order.command.application.service.OrderCommandService; import stanl_2.final_backend.domain.order.common.response.OrderResponseMessage; -@RestController +@RestController("OrderCommandController") @RequestMapping("/api/v1/order") -public class OrderCommandController { +public class OrderController { private final OrderCommandService orderCommandService; @Autowired - public OrderCommandController(OrderCommandService orderCommandService) { + public OrderController(OrderCommandService orderCommandService) { this.orderCommandService = orderCommandService; } diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java index cb5006bc..015c3b35 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java @@ -54,7 +54,7 @@ public void registerOrder(OrderRegistDTO orderRegistDTO) { public OrderModifyDTO modifyOrder(OrderModifyDTO orderModifyDTO) { // 회원인지 확인여부 - Order order = orderRepository.findById(orderModifyDTO.getId()) + Order order = orderRepository.findById(orderModifyDTO.getOrderId()) .orElseThrow(() -> new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND)); Order updateOrder = modelMapper.map(orderModifyDTO, Order.class); @@ -62,7 +62,7 @@ public OrderModifyDTO modifyOrder(OrderModifyDTO orderModifyDTO) { updateOrder.setUpdatedAt(order.getUpdatedAt()); updateOrder.setStatus(order.getStatus()); updateOrder.setActive(order.getActive()); - updateOrder.setConrId(order.getConrId()); + updateOrder.setContractId(order.getContractId()); orderRepository.save(updateOrder); diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java b/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java new file mode 100644 index 00000000..4697bca9 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java @@ -0,0 +1,53 @@ +package stanl_2.final_backend.domain.order.query.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; +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.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.order.common.response.OrderResponseMessage; +import stanl_2.final_backend.domain.order.query.dto.OrderSelectAllDTO; +import stanl_2.final_backend.domain.order.query.service.OrderQueryService; + +import java.util.Map; + +@RestController("queryOrderController") +@RequestMapping("/api/v1/order") +public class OrderController { + + private final OrderQueryService orderQueryService; + + @Autowired + public OrderController(OrderQueryService orderQueryService) { + this.orderQueryService = orderQueryService; + } + + @Operation(summary = "수주서 전제 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "수주서 전제 조회 성공", + content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) + }) + @GetMapping("{memberId}") + public ResponseEntity getAllOrder(@PathVariable("memberId") String memberId, + @PageableDefault(size = 10)Pageable pageable) { + + // 회원 아이디 받아 오는건 나중에 수정할 예정 + + Page responseOrders = orderQueryService.selectAll(memberId, pageable); + + return ResponseEntity.ok(OrderResponseMessage.builder() + .httpStatus(200) + .msg("수주서 전체 조회 성공") + .result(responseOrders) + .build()); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectAllDTO.java new file mode 100644 index 00000000..485db207 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectAllDTO.java @@ -0,0 +1,21 @@ +package stanl_2.final_backend.domain.order.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class OrderSelectAllDTO { + private String orderId; + private String title; + private String content; + private String status; + private String contractName; + private String adminName; + private String memberName; + private String productName; +} diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java b/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java new file mode 100644 index 00000000..6ca8e824 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java @@ -0,0 +1,13 @@ +package stanl_2.final_backend.domain.order.query.repository; + +import org.apache.ibatis.annotations.Mapper; +import stanl_2.final_backend.domain.order.query.dto.OrderSelectAllDTO; + +import java.util.List; + +@Mapper +public interface OrderMapper { + List findAllOrderByMemberId(int offset, int pageSize, String memberId); + + int findOrderCountByMemberId(String memberId); +} diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java new file mode 100644 index 00000000..b409fee0 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java @@ -0,0 +1,11 @@ +package stanl_2.final_backend.domain.order.query.service; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import stanl_2.final_backend.domain.order.query.dto.OrderSelectAllDTO; + +import java.util.Map; + +public interface OrderQueryService { + Page selectAll(String memberId, Pageable pageable); +} diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java new file mode 100644 index 00000000..e916c153 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java @@ -0,0 +1,40 @@ +package stanl_2.final_backend.domain.order.query.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.order.common.exception.OrderCommonException; +import stanl_2.final_backend.domain.order.common.exception.OrderErrorCode; +import stanl_2.final_backend.domain.order.query.dto.OrderSelectAllDTO; +import stanl_2.final_backend.domain.order.query.repository.OrderMapper; + +import java.util.List; +@Service +@Transactional(readOnly = true) +public class OrderQueryServiceImpl implements OrderQueryService { + + private final OrderMapper orderMapper; + + @Autowired + public OrderQueryServiceImpl(OrderMapper orderMapper) { + this.orderMapper = orderMapper; + } + + @Override + public Page selectAll(String memberId, Pageable pageable) { + int offset = Math.toIntExact(pageable.getOffset()); + int pageSize = pageable.getPageSize(); + List orders = orderMapper.findAllOrderByMemberId(offset, pageSize, memberId); + + if (orders == null || orders.isEmpty()) { + throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); + } + + int totalElements = orderMapper.findOrderCountByMemberId(memberId); + + return new PageImpl<>(orders, pageable, totalElements); + } +} diff --git a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml new file mode 100644 index 00000000..f9e0f4f2 --- /dev/null +++ b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From afaa2334107d3bcb456be0323f0322814baad41d Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Sat, 16 Nov 2024 00:40:03 +0900 Subject: [PATCH 074/563] =?UTF-8?q?feat:=20=EC=95=8C=EB=9E=8C=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EC=9E=A1=EA=B8=B0(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AlarmController.java | 32 +++++++++++ .../service/AlarmCommandService.java | 4 ++ .../service/AlarmCommandServiceImpl.java | 10 ++++ .../exception/AlarmCommonException.java | 17 ++++++ .../common/exception/AlarmErrorCode.java | 53 +++++++++++++++++++ .../exception/AlarmExceptionResponse.java | 22 ++++++++ .../common/response/AlarmResponseMessage.java | 14 +++++ .../service/ScheduleCommandServiceImpl.java | 2 +- 8 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/command/application/controller/AlarmController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/command/application/service/AlarmCommandService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java create mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmCommonException.java create mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmErrorCode.java create mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmExceptionResponse.java create mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/common/response/AlarmResponseMessage.java diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/application/controller/AlarmController.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/controller/AlarmController.java new file mode 100644 index 00000000..2fae0751 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/controller/AlarmController.java @@ -0,0 +1,32 @@ +package stanl_2.final_backend.domain.alarm.command.application.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.alarm.command.application.service.AlarmCommandService; +import stanl_2.final_backend.domain.alarm.common.response.AlarmResponseMessage; +import stanl_2.final_backend.domain.schedule.common.response.ScheduleResponseMessage; + +@RestController("commandAlarmController") +@RequestMapping("/api/v1/alarm") +public class AlarmController { + + private final AlarmCommandService alarmCommandService; + + @Autowired + public AlarmController(AlarmCommandService alarmCommandService) { + this.alarmCommandService = alarmCommandService; + } + + @PostMapping("") + public ResponseEntity registAlarm(){ + + return ResponseEntity.ok(AlarmResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(" ") + .build()); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/application/service/AlarmCommandService.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/service/AlarmCommandService.java new file mode 100644 index 00000000..53c1ce4a --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/service/AlarmCommandService.java @@ -0,0 +1,4 @@ +package stanl_2.final_backend.domain.alarm.command.application.service; + +public interface AlarmCommandService { +} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java new file mode 100644 index 00000000..bcb92270 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java @@ -0,0 +1,10 @@ +package stanl_2.final_backend.domain.alarm.command.domain.service; + +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.alarm.command.application.service.AlarmCommandService; + +@Service +public class AlarmCommandServiceImpl implements AlarmCommandService { + + +} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmCommonException.java b/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmCommonException.java new file mode 100644 index 00000000..a86154c2 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmCommonException.java @@ -0,0 +1,17 @@ +package stanl_2.final_backend.domain.alarm.common.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; + +@Getter +@RequiredArgsConstructor +public class AlarmCommonException extends RuntimeException { + private final AlarmErrorCode alarmErrorCode; + + // 에러 발생시 ErroCode 별 메시지 + @Override + public String getMessage() { + return this.alarmErrorCode.getMsg(); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmErrorCode.java b/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmErrorCode.java new file mode 100644 index 00000000..b0a92704 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmErrorCode.java @@ -0,0 +1,53 @@ +package stanl_2.final_backend.domain.alarm.common.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum AlarmErrorCode { + + /** + * 400(Bad Request) + * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. + */ + + + + /** + * 401(Unauthorized) + * 비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만, + * 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다. + * 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다. + */ + + + /** + * 403(Forbidden) + * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. + * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다. + */ + + + /** + * 404(Not Found) + * 서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다. + * 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있습니다. + * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. + * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. + */ + + + /** + * 500(Internal Server Error) + * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. + */ + INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); + + + + private final Integer code; + private final HttpStatus httpStatus; + private final String msg; +} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmExceptionResponse.java new file mode 100644 index 00000000..075a2bbd --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmExceptionResponse.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.alarm.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class AlarmExceptionResponse { + private final Integer code; + private final String msg; + private final HttpStatus httpStatus; + + public AlarmExceptionResponse(AlarmErrorCode alarmErrorCode) { + this.code = alarmErrorCode.getCode(); + this.msg = alarmErrorCode.getMsg(); + this.httpStatus = alarmErrorCode.getHttpStatus(); + } + + public static AlarmExceptionResponse of(AlarmErrorCode alarmErrorCode) { + return new AlarmExceptionResponse(alarmErrorCode); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/common/response/AlarmResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/alarm/common/response/AlarmResponseMessage.java new file mode 100644 index 00000000..b5f9d40d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/alarm/common/response/AlarmResponseMessage.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.alarm.common.response; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class AlarmResponseMessage { + private Integer httpStatus; + private String msg; + private Object result; +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java index b652f1cc..d5c6b942 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java @@ -75,7 +75,7 @@ public Boolean modifySchedule(ScheduleModifyDTO scheduleModifyDTO) { .orElseThrow(() -> new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND)); if(!scheduleModifyDTO.getMemberId().equals(schedule.getMemberId())){ - // 권한 오류 + // 권한 오ㅋ throw new ScheduleCommonException(ScheduleErrorCode.AUTHORIZATION_VIOLATION); } From 72040604612433b64cb5ae8028c001aa71030670 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Sat, 16 Nov 2024 01:06:04 +0900 Subject: [PATCH 075/563] =?UTF-8?q?feat:=20=EC=88=98=EC=A3=BC=EC=84=9C=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84(#43)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/OrderController.java | 25 ++++++++++++-- .../order/query/dto/OrderSelectIdDTO.java | 24 ++++++++++++++ .../order/query/repository/OrderMapper.java | 3 ++ .../query/service/OrderQueryService.java | 3 ++ .../query/service/OrderQueryServiceImpl.java | 13 ++++++++ .../order/query/repository/OrderMapper.xml | 33 +++++++++++++++++++ 6 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectIdDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java b/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java index 4697bca9..210e3485 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java @@ -16,10 +16,9 @@ import org.springframework.web.bind.annotation.RestController; import stanl_2.final_backend.domain.order.common.response.OrderResponseMessage; import stanl_2.final_backend.domain.order.query.dto.OrderSelectAllDTO; +import stanl_2.final_backend.domain.order.query.dto.OrderSelectIdDTO; import stanl_2.final_backend.domain.order.query.service.OrderQueryService; -import java.util.Map; - @RestController("queryOrderController") @RequestMapping("/api/v1/order") public class OrderController { @@ -50,4 +49,26 @@ public ResponseEntity getAllOrder(@PathVariable("memberId" .result(responseOrders) .build()); } + + @Operation(summary = "수주서 상세 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "수주서 상세 조회 성공", + content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) + }) + @GetMapping("{id}/{memberId}") + public ResponseEntity getDetailOrder(@PathVariable("id") String orderId, + @PathVariable("memberId") String memberId) { + + OrderSelectIdDTO orderSelectIdDTO = new OrderSelectIdDTO(); + orderSelectIdDTO.setOrderId(orderId); + orderSelectIdDTO.setMemberId(memberId); + + OrderSelectIdDTO responseOrder = orderQueryService.selectDetailOrder(orderSelectIdDTO); + + return ResponseEntity.ok(OrderResponseMessage.builder() + .httpStatus(200) + .msg("수주서 상세 조회 성공") + .result(responseOrder) + .build()); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectIdDTO.java b/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectIdDTO.java new file mode 100644 index 00000000..91c5a92a --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectIdDTO.java @@ -0,0 +1,24 @@ +package stanl_2.final_backend.domain.order.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class OrderSelectIdDTO { + private String orderId; + private String title; + private String content; + private Boolean active; + private String status; + private String createdAt; + private String updatedAt; + private String deletedAt; + private String contractId; + private String adminId; + private String memberId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java b/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java index 6ca8e824..9d1a8c77 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java @@ -2,6 +2,7 @@ import org.apache.ibatis.annotations.Mapper; import stanl_2.final_backend.domain.order.query.dto.OrderSelectAllDTO; +import stanl_2.final_backend.domain.order.query.dto.OrderSelectIdDTO; import java.util.List; @@ -10,4 +11,6 @@ public interface OrderMapper { List findAllOrderByMemberId(int offset, int pageSize, String memberId); int findOrderCountByMemberId(String memberId); + + OrderSelectIdDTO findOrderByIdAndMemberId(String orderId, String memberId); } diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java index b409fee0..d155d8c1 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java @@ -3,9 +3,12 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import stanl_2.final_backend.domain.order.query.dto.OrderSelectAllDTO; +import stanl_2.final_backend.domain.order.query.dto.OrderSelectIdDTO; import java.util.Map; public interface OrderQueryService { Page selectAll(String memberId, Pageable pageable); + + OrderSelectIdDTO selectDetailOrder(OrderSelectIdDTO orderSelectIdDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java index e916c153..26a8b899 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java @@ -9,6 +9,7 @@ import stanl_2.final_backend.domain.order.common.exception.OrderCommonException; import stanl_2.final_backend.domain.order.common.exception.OrderErrorCode; import stanl_2.final_backend.domain.order.query.dto.OrderSelectAllDTO; +import stanl_2.final_backend.domain.order.query.dto.OrderSelectIdDTO; import stanl_2.final_backend.domain.order.query.repository.OrderMapper; import java.util.List; @@ -37,4 +38,16 @@ public Page selectAll(String memberId, Pageable pageable) { return new PageImpl<>(orders, pageable, totalElements); } + + @Override + public OrderSelectIdDTO selectDetailOrder(OrderSelectIdDTO orderSelectIdDTO) { + + OrderSelectIdDTO order = orderMapper.findOrderByIdAndMemberId(orderSelectIdDTO.getOrderId(), orderSelectIdDTO.getMemberId()); + + if(order == null) { + throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); + } + + return order; + } } diff --git a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml index f9e0f4f2..10ca524a 100644 --- a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml @@ -15,6 +15,20 @@ + + + + + + + + + + + + + + + + \ No newline at end of file From b6030ae197dc62b976c4c3cc05a7f0d68dffb401 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Sat, 16 Nov 2024 11:35:12 +0900 Subject: [PATCH 076/563] =?UTF-8?q?feat:=20=EC=88=98=EC=A3=BC=EC=84=9C=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=EC=A1=B0=ED=9A=8C=20(#43)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/OrderController.java | 31 +++++++- .../order/query/dto/OrderSelectAllDTO.java | 1 - .../order/query/dto/OrderSelectSearchDTO.java | 35 +++++++++ .../order/query/repository/OrderMapper.java | 10 ++- .../query/service/OrderQueryService.java | 3 + .../query/service/OrderQueryServiceImpl.java | 29 +++++++ .../order/query/repository/OrderMapper.xml | 75 +++++++++++++++++-- 7 files changed, 173 insertions(+), 11 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectSearchDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java b/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java index 210e3485..edd37ba6 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java @@ -10,13 +10,11 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; 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.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.order.common.response.OrderResponseMessage; import stanl_2.final_backend.domain.order.query.dto.OrderSelectAllDTO; import stanl_2.final_backend.domain.order.query.dto.OrderSelectIdDTO; +import stanl_2.final_backend.domain.order.query.dto.OrderSelectSearchDTO; import stanl_2.final_backend.domain.order.query.service.OrderQueryService; @RestController("queryOrderController") @@ -71,4 +69,29 @@ public ResponseEntity getDetailOrder(@PathVariable("id") S .result(responseOrder) .build()); } + + @Operation(summary = "수주서 검색 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "수주서 검색 조회 성공", + content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) + }) + @GetMapping("/search/{memId}") + public ResponseEntity getSearchOrder(@RequestParam(required = false) String title, + @RequestParam(required = false) String status, + @RequestParam(required = false) String adminId, + @RequestParam(required = false) String memberId, + @RequestParam(required = false) String startDate, + @RequestParam(required = false) String endDate, + @RequestParam(required = false) String memId, + @PageableDefault(size = 10) Pageable pageable) { + OrderSelectSearchDTO orderSelectSearchDTO = new OrderSelectSearchDTO(title, status, adminId, memberId, memId, startDate, endDate); + + Page responseOrders = orderQueryService.selectSearchOrders(orderSelectSearchDTO, pageable); + + return ResponseEntity.ok(OrderResponseMessage.builder() + .httpStatus(200) + .msg("수주서 검색 조회 성공") + .result(responseOrders) + .build()); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectAllDTO.java index 485db207..f6ac7908 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectAllDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectAllDTO.java @@ -12,7 +12,6 @@ public class OrderSelectAllDTO { private String orderId; private String title; - private String content; private String status; private String contractName; private String adminName; diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectSearchDTO.java new file mode 100644 index 00000000..bcfd21b9 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectSearchDTO.java @@ -0,0 +1,35 @@ +package stanl_2.final_backend.domain.order.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class OrderSelectSearchDTO { + private String orderId; + private String title; + private String status; + private String contractName; + private String adminId; + private String memberId; + private String memId; + private String adminName; + private String memberName; + private String productName; + private String startDate; + private String endDate; + + public OrderSelectSearchDTO(String title, String status, String adminId, String memberId, String memId, String startDate, String endDate) { + this.title = title; + this.status = status; + this.adminId = adminId; + this.memId = memId; + this.memberId = memberId; + this.startDate = startDate; + this.endDate = endDate; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java b/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java index 9d1a8c77..665cf16b 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java @@ -3,14 +3,22 @@ import org.apache.ibatis.annotations.Mapper; import stanl_2.final_backend.domain.order.query.dto.OrderSelectAllDTO; import stanl_2.final_backend.domain.order.query.dto.OrderSelectIdDTO; +import stanl_2.final_backend.domain.order.query.dto.OrderSelectSearchDTO; import java.util.List; +import java.util.Map; @Mapper public interface OrderMapper { - List findAllOrderByMemberId(int offset, int pageSize, String memberId); + List findSearchOrderByMemberId(int offset, int pageSize, OrderSelectSearchDTO orderSelectSearchDTO); int findOrderCountByMemberId(String memberId); OrderSelectIdDTO findOrderByIdAndMemberId(String orderId, String memberId); + + List findAllOrderByMemberId(int offset, int pageSize, String memberId); + + int findOrderSearchCountByMemberId(Map map); + + List findSearchOrderByMemberId(Map map); } diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java index d155d8c1..4d77a61e 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java @@ -4,6 +4,7 @@ import org.springframework.data.domain.Pageable; import stanl_2.final_backend.domain.order.query.dto.OrderSelectAllDTO; import stanl_2.final_backend.domain.order.query.dto.OrderSelectIdDTO; +import stanl_2.final_backend.domain.order.query.dto.OrderSelectSearchDTO; import java.util.Map; @@ -11,4 +12,6 @@ public interface OrderQueryService { Page selectAll(String memberId, Pageable pageable); OrderSelectIdDTO selectDetailOrder(OrderSelectIdDTO orderSelectIdDTO); + + Page selectSearchOrders(OrderSelectSearchDTO orderSelectSearchDTO, Pageable pageable); } diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java index 26a8b899..7a8f66fb 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java @@ -10,9 +10,13 @@ import stanl_2.final_backend.domain.order.common.exception.OrderErrorCode; import stanl_2.final_backend.domain.order.query.dto.OrderSelectAllDTO; import stanl_2.final_backend.domain.order.query.dto.OrderSelectIdDTO; +import stanl_2.final_backend.domain.order.query.dto.OrderSelectSearchDTO; import stanl_2.final_backend.domain.order.query.repository.OrderMapper; +import java.util.HashMap; import java.util.List; +import java.util.Map; + @Service @Transactional(readOnly = true) public class OrderQueryServiceImpl implements OrderQueryService { @@ -50,4 +54,29 @@ public OrderSelectIdDTO selectDetailOrder(OrderSelectIdDTO orderSelectIdDTO) { return order; } + + @Override + public Page selectSearchOrders(OrderSelectSearchDTO orderSelectSearchDTO, Pageable pageable) { + + Map map = new HashMap<>(); + map.put("memId", orderSelectSearchDTO.getMemId()); + map.put("title", orderSelectSearchDTO.getTitle()); + map.put("status", orderSelectSearchDTO.getStatus()); + map.put("adminId", orderSelectSearchDTO.getAdminId()); + map.put("memberId", orderSelectSearchDTO.getMemberId()); + map.put("startDate", orderSelectSearchDTO.getStartDate()); + map.put("endDate", orderSelectSearchDTO.getEndDate()); + map.put("pageSize", pageable.getPageSize()); + map.put("offset", pageable.getOffset()); + + List orders = orderMapper.findSearchOrderByMemberId(map); + + if (orders == null || orders.isEmpty()) { + throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); + } + + int totalElements = orderMapper.findOrderSearchCountByMemberId(map); + + return new PageImpl<>(orders, pageable, totalElements); + } } diff --git a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml index 10ca524a..eb371a51 100644 --- a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml @@ -7,10 +7,9 @@ - - + @@ -33,11 +32,10 @@ SELECT A.ORD_ID, A.ORD_TTL, - A.ORD_CONT, A.ORD_STAT, B.CONR_NAME, - D.MEM_NAME, - C.MEM_NAME, + D.MEM_NAME AS ADMIN_NAME, + C.MEM_NAME AS MEM_NAME, E.PROD_NAME FROM TB_ORDER A LEFT JOIN TB_CONTRACT B @@ -81,4 +79,71 @@ AND A.ORD_ID = #{orderId} + + + + + \ No newline at end of file From 915fefc56b134f72f273baaa3c3f7e6324d4ee92 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Sat, 16 Nov 2024 13:08:20 +0900 Subject: [PATCH 077/563] =?UTF-8?q?refactor:=20=EA=B3=84=EC=95=BD=EC=84=9C?= =?UTF-8?q?=20=EB=B3=80=EC=88=98=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ContractCommandController.java | 4 +- .../application/dto/ContractModifyDTO.java | 44 +++--- .../application/dto/ContractRegistDTO.java | 36 ++--- .../domain/aggregate/entity/Contract.java | 44 +++--- .../service/ContractCommandServiceImpl.java | 10 +- .../contract/query/dto/ContractSearchDTO.java | 46 +++--- .../query/dto/ContractSelectAllDTO.java | 16 +-- .../query/dto/ContractSeletIdDTO.java | 44 +++--- .../query/repository/ContractMapper.xml | 132 ++++++++---------- 9 files changed, 181 insertions(+), 195 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractCommandController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractCommandController.java index eaa6eeac..13d25f4d 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractCommandController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractCommandController.java @@ -59,7 +59,7 @@ public ContractCommandController(ContractCommandService contractCommandService) @PostMapping("{id}") public ResponseEntity postTest(@PathVariable String id, @RequestBody ContractRegistDTO contractRegistRequestDTO) { - contractRegistRequestDTO.setMemId(id); + contractRegistRequestDTO.setMemberId(id); contractCommandService.registerContract(contractRegistRequestDTO); return ResponseEntity.ok(ContractResponseMessage.builder() @@ -104,7 +104,7 @@ public ResponseEntity postTest(@PathVariable String id, public ResponseEntity putContract(@PathVariable String id, @RequestBody ContractModifyDTO contractModifyRequestDTO) { - contractModifyRequestDTO.setId(id); + contractModifyRequestDTO.setContractId(id); ContractModifyDTO contractModifyDTO = contractCommandService.modifyContract(contractModifyRequestDTO); return ResponseEntity.ok(ContractResponseMessage.builder() diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java index e80d8c7a..d6afbb40 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java @@ -9,32 +9,32 @@ @ToString public class ContractModifyDTO { - private String id; + private String contractId; private String name; - private String custName; - private String custIdenNo; - private String custAddrress; - private String custEmail; - private String custPhone; - private String compName; - private String custCla; - private String custPurCond; - private String seriNum; - private String seleOpti; - private Integer downPay; - private Integer intePay; - private Integer remPay; - private Integer consPay; - private String delvDate; - private String delvLoc; + private String customerName; + private String customerIdentifiNo; + private String customerAddrress; + private String customerEmail; + private String customerPhone; + private String companyName; + private String customerClassifcation; + private String customerPurchaseCondition; + private String serialNum; + private String selectOption; + private Integer downPayment; + private Integer intermediatePayment; + private Integer remremainderPaymentPay; + private Integer consignmentPayment; + private String delveryDate; + private String delveryLocation; private String status; - private String noOfVeh; + private String NumberOfVehicles; private String createdUrl; private Boolean active; private String createdAt; private String updatedAt; - private String memId; - private String centId; - private String custId; - private String prodId; + private String memberId; + private String centerId; + private String customerId; + private String productId; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java index 4e91e703..81c1f7a8 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java @@ -10,24 +10,24 @@ public class ContractRegistDTO { private String name; - private String custName; - private String custIdenNo; - private String custAddrress; - private String custEmail; - private String custPhone; - private String compName; - private String custCla; - private String custPurCond; - private String seriNum; - private String seleOpti; - private Integer downPay; - private Integer intePay; - private Integer remPay; - private Integer consPay; - private String delvDate; - private String delvLoc; + private String customerName; + private String customerIdentifiNo; + private String customerAddrress; + private String customerEmail; + private String customerPhone; + private String companyName; + private String customerClassifcation; + private String customerPurchaseCondition; + private String serialNum; + private String selectOption; + private Integer downPayment; + private Integer intermediatePayment; + private Integer remainderPayment; + private Integer consignmentPayment; + private String delveryDate; + private String delveryLocation; private String status; - private String noOfVeh; + private String NumberOfVehicles; private String createdUrl; - private String memId; + private String memberId; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java index 7f335b41..da98e9e0 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java @@ -29,60 +29,60 @@ public class Contract { parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "CON") ) @Column(name = "CONR_ID") - private String id; + private String contractId; @Column(name = "CONR_NAME", nullable = false) private String name; @Column(name = "CONR_CUST_NAME", nullable = false) - private String custName; + private String customerName; @Column(name = "CONR_CUST_IDEN_NO", nullable = false) - private String custIdenNo; + private String customerIdentifiNo; @Column(name = "CONR_CUST_ADR", nullable = false) - private String custAddrress; + private String customerAddrress; @Column(name = "CONR_CUST_EMA", nullable = false) - private String custEmail; + private String customerEmail; @Column(name = "CONR_CUST_PHO", nullable = false) - private String custPhone; + private String customerPhone; @Column(name = "CONR_COMP_NAME") - private String compName; + private String companyName; @Column(name = "CONR_CUST_CLA", nullable = false) @ColumnDefault("'PERSONAL'") - private String custCla; + private String customerClassifcation; @Column(name = "CONR_CUST_PUR_COND", nullable = false) @ColumnDefault("'CASH'") - private String custPurCond; + private String customerPurchaseCondition; @Column(name = "CONR_SERI_NUM", nullable = false) - private String seriNum; + private String serialNum; @Column(name = "CONR_SELE_OPTI", nullable = false) - private String seleOpti; + private String selectOption; @Column(name = "CONR_DOWN_PAY", nullable = false) - private Integer downPay; + private Integer downPayment; @Column(name = "CONR_INTE_PAY", nullable = false) - private Integer intePay; + private Integer intermediatePayment; @Column(name = "CONR_REM_PAY", nullable = false) - private Integer remPay; + private Integer remainderPayment; @Column(name = "CONR_CONS_PAY", nullable = false) - private Integer consPay; + private Integer consignmentPayment; @Column(name = "CONR_DELV_DATE") - private String delvDate; + private String delveryDate; @Column(name = "CONR_DELV_LOC") - private String delvLoc; + private String delveryLocation; @Column(name = "CONR_STAT", nullable = false) @ColumnDefault("'WAIT'") @@ -90,7 +90,7 @@ public class Contract { @Column(name = "CONR_NO_OF_VEH", nullable = false) @ColumnDefault("1") - private String noOfVeh; + private String NumberOfVehicles; @Lob @Column(name = "CREATED_URL", nullable = false, columnDefinition = "TEXT") @@ -113,16 +113,16 @@ public class Contract { private String deletedAt; @Column(name = "MEM_ID", nullable = false) - private String memId; + private String memberId; @Column(name = "CENT_ID", nullable = false) - private String centId; + private String centerId; @Column(name = "CUST_ID") - private String custId; + private String customerId; @Column(name = "PROD_ID", nullable = false) - private String prodId; + private String productId; /* 설명. updatedAt 자동화 */ // Insert 되기 전에 실행 diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index 68e9cbce..1b391999 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -42,8 +42,8 @@ public void registerContract(ContractRegistDTO contractRegistRequestDTO) { Contract contract = modelMapper.map(contractRegistRequestDTO, Contract.class); - contract.setCentId("CEN_000000001"); // 회원의 매장번호 넣기 - contract.setProdId("PRO_000000001"); // 제품 번호 넣기 + contract.setCenterId("CEN_000000001"); // 회원의 매장번호 넣기 + contract.setProductId("PRO_000000001"); // 제품 번호 넣기 contractRepository.save(contract); } @@ -62,15 +62,15 @@ public ContractModifyDTO modifyContract(ContractModifyDTO contractModifyRequestD // 가져온 고객 정보에 수정된 값 넣기 - Contract contract = contractRepository.findById(contractModifyRequestDTO.getId()) + Contract contract = contractRepository.findById(contractModifyRequestDTO.getContractId()) .orElseThrow(() -> new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND)); Contract updateContract = modelMapper.map(contractModifyRequestDTO, Contract.class); updateContract.setCreatedAt(contract.getCreatedAt()); updateContract.setUpdatedAt(contract.getUpdatedAt()); updateContract.setActive(contract.isActive()); - updateContract.setCentId(contract.getCentId()); - updateContract.setProdId(contract.getProdId()); + updateContract.setCenterId(contract.getCenterId()); + updateContract.setProductId(contract.getProductId()); updateContract.setCreatedUrl(contract.getCreatedUrl()); contractRepository.save(updateContract); diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java index e3c4d0e6..86f9cc1f 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java @@ -9,37 +9,37 @@ @ToString public class ContractSearchDTO { - private String id; + private String contractId; private String name; - private String custName; - private String custIdenNo; - private String custAddrress; - private String custEmail; - private String custPhone; - private String compName; - private String custCla; - private String custPurCond; - private String seriNum; - private String seleOpti; - private Integer downPay; - private Integer intePay; - private Integer remPay; - private Integer consPay; - private String delvDate; - private String delvLoc; + private String customerName; + private String customerIdentifiNo; + private String customerAddrress; + private String customerEmail; + private String customerPhone; + private String companyName; + private String customerClassifcation; + private String customerPurchaseCondition; + private String serialNum; + private String selectOption; + private Integer downPayment; + private Integer intermediatePayment; + private Integer remainderPayment; + private Integer consignmentPayment; + private String delveryDate; + private String ddelveryLocationelvLoc; private String status; - private String noOfVeh; + private String NumberOfVehicles; private String createdUrl; private String updatedUrl; private boolean active; private String createdAt; private String updatedAt; private String deletedAt; - private String memId; - private String centId; - private String custId; - private String prodId; + private String memberId; + private String centerId; + private String customerId; + private String productId; private String startAt; private String endAt; - private String prodName; + private String productName; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java index c466eae1..fb8f34cb 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java @@ -9,17 +9,17 @@ @ToString public class ContractSelectAllDTO { - private String id; + private String contractId; private String name; - private String custName; - private String compName; + private String customerName; + private String companyName; private String status; private String createdUrl; private String updatedUrl; private boolean active; - private String memId; - private String centId; - private String custId; - private String prodId; - private String prodName; + private String memberId; + private String centerId; + private String customerId; + private String productId; + private String productName; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java index be0152ed..d40cd9a6 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java @@ -9,34 +9,34 @@ @ToString public class ContractSeletIdDTO { - private String id; + private String contractId; private String name; - private String custName; - private String custIdenNo; - private String custAddrress; - private String custEmail; - private String custPhone; - private String compName; - private String custCla; - private String custPurCond; - private String seriNum; - private String seleOpti; - private Integer downPay; - private Integer intePay; - private Integer remPay; - private Integer consPay; - private String delvDate; - private String delvLoc; + private String customerName; + private String customerIdentifiNo; + private String customerAddrress; + private String customerEmail; + private String customerPhone; + private String companyName; + private String customerClassifcation; + private String customerPurchaseCondition; + private String serialNum; + private String selectOption; + private Integer downPayment; + private Integer intermediatePayment; + private Integer remainderPayment; + private Integer consignmentPayment; + private String delveryDate; + private String delveryLocation; private String status; - private String noOfVeh; + private String NumberOfVehicles; private String createdUrl; private String updatedUrl; private boolean active; private String createdAt; private String updatedAt; private String deletedAt; - private String memId; - private String centId; - private String custId; - private String prodId; + private String memberId; + private String centerId; + private String customerId; + private String productId; } diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index c1804cb0..dd749d79 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -5,86 +5,72 @@ - + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - + - - - - + + + + - + - - - - - - - - - - - - - - - - + + + + + - - - - - - - - + + + + + - + - - + + - - - - - + + + + + @@ -136,7 +122,7 @@ FROM TB_CONTRACT A JOIN TB_PRODUCT B ON A.PROD_ID = B.PROD_ID - WHERE A.MEM_ID = #{ memId } + WHERE A.MEM_ID = #{ memberId } AND A.ACTIVE = TRUE ORDER BY A.CREATED_AT DESC LIMIT #{pageSize} OFFSET #{offset} @@ -157,29 +143,29 @@ B.PROD_NAME FROM TB_CONTRACT A JOIN TB_PRODUCT B ON A.PROD_ID = B.PROD_ID - WHERE A.MEM_ID = #{memId} + WHERE A.MEM_ID = #{memberId} AND A.ACTIVE = TRUE - AND A.CENT_ID LIKE CONCAT('%', #{centId}, '%') + AND A.CENT_ID LIKE CONCAT('%', #{centerId}, '%') AND A.CONR_NAME LIKE CONCAT('%', #{name}, '%') - AND A.CONR_CUST_NAME LIKE CONCAT('%', #{custName}, '%') + AND A.CONR_CUST_NAME LIKE CONCAT('%', #{customerName}, '%') - AND A.CONR_CUST_CLA LIKE CONCAT('%', #{custCla}, '%') + AND A.CONR_CUST_CLA LIKE CONCAT('%', #{customerClassifcation}, '%') AND A.CONR_STATUS = #{status} - AND A.CONR_COMP_NAME LIKE CONCAT('%', #{compName}, '%') + AND A.CONR_COMP_NAME LIKE CONCAT('%', #{companyName}, '%') - AND A.CONR_CUST_PUR_COND LIKE CONCAT('%', #{custPurCond}, '%') + AND A.CONR_CUST_PUR_COND LIKE CONCAT('%', #{customerPurchaseCondition}, '%') AND A.CREATED_AT BETWEEN #{startAt} AND #{endAt} @@ -191,7 +177,7 @@ AND A.CREATED_AT < #{endAt} - AND B.PROD_NAME LIKE CONCAT('%', #{prodName}, '%') + AND B.PROD_NAME LIKE CONCAT('%', #{productName}, '%') ORDER BY A.CREATED_AT DESC LIMIT #{pageable.pageSize} OFFSET #{pageable.offset} @@ -201,7 +187,7 @@ SELECT COUNT(*) AS CNT FROM TB_CONTRACT A - WHERE A.MEM_ID = #{ memId } + WHERE A.MEM_ID = #{ memberId } AND A.ACTIVE = TRUE @@ -209,29 +195,29 @@ SELECT COUNT(*) FROM TB_CONTRACT A JOIN TB_PRODUCT B ON A.PROD_ID = B.PROD_ID - WHERE A.MEM_ID = #{memId} + WHERE A.MEM_ID = #{memberId} AND A.ACTIVE = TRUE - AND A.CENT_ID LIKE CONCAT('%', #{centId}, '%') + AND A.CENT_ID LIKE CONCAT('%', #{centerId}, '%') AND A.CONR_NAME LIKE CONCAT('%', #{name}, '%') - AND A.CONR_CUST_NAME LIKE CONCAT('%', #{custName}, '%') + AND A.CONR_CUST_NAME LIKE CONCAT('%', #{customerName}, '%') - AND A.CONR_CUST_CLA LIKE CONCAT('%', #{custCla}, '%') + AND A.CONR_CUST_CLA LIKE CONCAT('%', #{customerClassifcation}, '%') AND A.CONR_STATUS = #{status} - AND A.CONR_COMP_NAME LIKE CONCAT('%', #{compName}, '%') + AND A.CONR_COMP_NAME LIKE CONCAT('%', #{companyName}, '%') - AND A.CONR_CUST_PUR_COND LIKE CONCAT('%', #{custPurCond}, '%') + AND A.CONR_CUST_PUR_COND LIKE CONCAT('%', #{customerPurchaseCondition}, '%') AND A.CREATED_AT BETWEEN #{startAt} AND #{endAt} @@ -243,7 +229,7 @@ AND A.CREATED_AT < #{endAt} - AND B.PROD_NAME LIKE CONCAT('%', #{prodName}, '%') + AND B.PROD_NAME LIKE CONCAT('%', #{productName}, '%') From ff0c5791d7d0b59df3f56bdb7c110614a890f2d8 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Sat, 16 Nov 2024 13:21:34 +0900 Subject: [PATCH 078/563] =?UTF-8?q?refactor:=20=EA=B3=84=EC=95=BD=EC=84=9C?= =?UTF-8?q?=20=EB=B3=80=EC=88=98=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/dto/ContractModifyDTO.java | 4 +-- .../application/dto/ContractRegistDTO.java | 2 +- .../domain/aggregate/entity/Contract.java | 2 +- .../query/controller/ContractController.java | 28 +++++++++---------- .../query/repository/ContractMapper.xml | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java index d6afbb40..a0e88191 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java @@ -23,12 +23,12 @@ public class ContractModifyDTO { private String selectOption; private Integer downPayment; private Integer intermediatePayment; - private Integer remremainderPaymentPay; + private Integer remainderPayment; private Integer consignmentPayment; private String delveryDate; private String delveryLocation; private String status; - private String NumberOfVehicles; + private String numberOfVehicles; private String createdUrl; private Boolean active; private String createdAt; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java index 81c1f7a8..2921af20 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java @@ -27,7 +27,7 @@ public class ContractRegistDTO { private String delveryDate; private String delveryLocation; private String status; - private String NumberOfVehicles; + private String numberOfVehicles; private String createdUrl; private String memberId; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java index da98e9e0..11965457 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java @@ -90,7 +90,7 @@ public class Contract { @Column(name = "CONR_NO_OF_VEH", nullable = false) @ColumnDefault("1") - private String NumberOfVehicles; + private String numberOfVehicles; @Lob @Column(name = "CREATED_URL", nullable = false, columnDefinition = "TEXT") diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java index bdacd47f..a3678051 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java @@ -37,10 +37,10 @@ public ContractController(ContractQueryService contractQueryService) { @ApiResponse(responseCode = "200", description = "계약서 전채 조회 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) - @GetMapping("{memId}") - public ResponseEntity getAllContract(@PathVariable("memId") String memId, + @GetMapping("{memberId}") + public ResponseEntity getAllContract(@PathVariable("memberId") String memberId, @PageableDefault(size = 10) Pageable pageable) { - Page> responseContracts = contractQueryService.selectAll(memId, pageable); + Page> responseContracts = contractQueryService.selectAll(memberId, pageable); return ResponseEntity.ok(ContractResponseMessage.builder() .httpStatus(200) @@ -57,13 +57,13 @@ public ResponseEntity getAllContract(@PathVariable("mem @ApiResponse(responseCode = "200", description = "계약서 상세 조회 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) - @GetMapping("{id}/{memId}") + @GetMapping("{id}/{memberId}") public ResponseEntity getDetailContract(@PathVariable("id") String id, - @PathVariable("memId") String memId) { + @PathVariable("memberId") String memberId) { ContractSeletIdDTO contractDTO = new ContractSeletIdDTO(); - contractDTO.setId(id); - contractDTO.setMemId(memId); + contractDTO.setContractId(id); + contractDTO.setMemberId(memberId); ContractSeletIdDTO responseContract = contractQueryService.selectDetailContract(contractDTO); @@ -86,17 +86,17 @@ public ResponseEntity getDetailContract(@PathVariable(" public ResponseEntity getContractBySearch(@RequestParam Map params, @PageableDefault(size = 10) Pageable pageable) { Map paramMap = new HashMap<>(); - paramMap.put("memId", params.get("memId")); - paramMap.put("centId", params.get("centId")); + paramMap.put("memberId", params.get("memberId")); + paramMap.put("centerId", params.get("centerId")); paramMap.put("name", params.get("name")); paramMap.put("startAt", params.get("startAt")); paramMap.put("endAt", params.get("endAt")); - paramMap.put("custName", params.get("custName")); - paramMap.put("custCla", params.get("custCla")); - paramMap.put("prodId", params.get("prodId")); + paramMap.put("customerName", params.get("customerName")); + paramMap.put("customerClassifcation", params.get("customerClassifcation")); + paramMap.put("productId", params.get("productId")); paramMap.put("status", params.get("status")); - paramMap.put("compName", params.get("compName")); - paramMap.put("custPurCond", params.get("custCond")); + paramMap.put("companyName", params.get("companyName")); + paramMap.put("customerPurchaseCondition", params.get("customerPurchaseCondition")); paramMap.put("pageable", pageable); Page> responseContracts = contractQueryService.selectBySearch(paramMap); diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index dd749d79..90e29a7d 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -24,7 +24,7 @@ - + From 75db93abb96ced706ecbfb911b85369b8a98303b Mon Sep 17 00:00:00 2001 From: giuseog Date: Sat, 16 Nov 2024 13:23:33 +0900 Subject: [PATCH 079/563] =?UTF-8?q?feat:=20=EC=A0=9C=ED=92=88=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EA=B5=AC=ED=98=84(#31)=20->=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=EC=BB=A8=EB=B2=A4=EC=85=98=EC=9D=80=20=ED=95=9C?= =?UTF-8?q?=EB=B2=88=EC=97=90=20=EB=A7=9E=EC=B6=9C=20=EC=98=88=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../center/query/repository/CenterMapper.java | 4 +- .../response/ProductResponseMessage.java | 14 ++ .../product/common/util/RequestList.java | 14 ++ .../query/controller/ProductController.java | 73 +++++++++ .../query/dto/ProductSearchRequestDTO.java | 14 ++ .../query/dto/ProductSelectAllDTO.java | 16 ++ .../product/query/dto/ProductSelectIdDTO.java | 43 ++++++ .../query/repository/ProductMapper.java | 21 +++ .../product/query/service/ProductService.java | 17 +++ .../query/service/ProductServiceImpl.java | 63 ++++++++ src/main/resources/sql/ddl.sql | 69 +++++---- .../query/repository/ProductMapper.xml | 144 ++++++++++++++++++ 12 files changed, 455 insertions(+), 37 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/product/common/response/ProductResponseMessage.java create mode 100644 src/main/java/stanl_2/final_backend/domain/product/common/util/RequestList.java create mode 100644 src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSearchRequestDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectAllDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectIdDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java create mode 100644 src/main/java/stanl_2/final_backend/domain/product/query/service/ProductService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/product/query/service/ProductServiceImpl.java create mode 100644 src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java b/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java index c9fa127f..66fc6cbe 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java @@ -15,9 +15,9 @@ public interface CenterMapper { List> findCenterAll(RequestList requestList); - int findCenterCount(); + Integer findCenterCount(); - int findCenterBySearchCount(Map params); + Integer findCenterBySearchCount(Map params); List> findCenterBySearch(Map params); diff --git a/src/main/java/stanl_2/final_backend/domain/product/common/response/ProductResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/product/common/response/ProductResponseMessage.java new file mode 100644 index 00000000..108883e2 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/product/common/response/ProductResponseMessage.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.product.common.response; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class ProductResponseMessage { + private int httpStatus; + private String msg; + private Object result; +} diff --git a/src/main/java/stanl_2/final_backend/domain/product/common/util/RequestList.java b/src/main/java/stanl_2/final_backend/domain/product/common/util/RequestList.java new file mode 100644 index 00000000..9a861d2c --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/product/common/util/RequestList.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.product.common.util; + +import lombok.*; +import org.springframework.data.domain.Pageable; + +@Builder +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class RequestList { + private T data; + private Pageable pageable; +} diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java b/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java new file mode 100644 index 00000000..bbd63f6c --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java @@ -0,0 +1,73 @@ +package stanl_2.final_backend.domain.product.query.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.product.common.response.ProductResponseMessage; +import stanl_2.final_backend.domain.product.query.dto.ProductSearchRequestDTO; +import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; +import stanl_2.final_backend.domain.product.query.service.ProductService; + +import java.util.HashMap; +import java.util.Map; + +@RestController("queryProductController") +@RequestMapping("/api/v1/product") +public class ProductController { + + private final ProductService productService; + + @Autowired + public ProductController(ProductService productService) { + this.productService = productService; + } + + @GetMapping("") + public ResponseEntity getProductAll(@PageableDefault(size = 20) Pageable pageable){ + + Page> responseProducts = productService.selectAll(pageable); + + return ResponseEntity.ok(ProductResponseMessage.builder() + .httpStatus(200) + .msg("조회 성공") + .result(responseProducts) + .build()); + } + + @GetMapping("{id}") + public ResponseEntity getProductById(@PathVariable("id") String id){ + + ProductSelectIdDTO productSelectIdDTO = productService.selectByProductId(id); + + return ResponseEntity.ok(ProductResponseMessage.builder() + .httpStatus(200) + .msg("상세 조회 성공") + .result(productSelectIdDTO) + .build()); + } + + @GetMapping("/search") + public ResponseEntity getProductBySearch(@RequestParam Map params + ,@PageableDefault(size = 20) Pageable pageable){ + + ProductSearchRequestDTO productSearchRequestDTO = new ProductSearchRequestDTO(); + productSearchRequestDTO.setId(params.get("id")); + productSearchRequestDTO.setName(params.get("name")); + productSearchRequestDTO.setSerialNumber(params.get("serialNumber")); + + Map paramMap = new HashMap<>(); + paramMap.put("productSearchRequestDTO", productSearchRequestDTO); + paramMap.put("pageable", pageable); + + Page> responseProducts = productService.selectProductBySearch(paramMap); + + return ResponseEntity.ok(ProductResponseMessage.builder() + .httpStatus(200) + .msg("검색 조회 성공") + .result(responseProducts) + .build()); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSearchRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSearchRequestDTO.java new file mode 100644 index 00000000..cd14943c --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSearchRequestDTO.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.product.query.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString +public class ProductSearchRequestDTO { + private String id; + private String name; + private String serialNumber; +} diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectAllDTO.java new file mode 100644 index 00000000..60e5a488 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectAllDTO.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.product.query.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString +public class ProductSelectAllDTO { + private String id; + private String name; + private String serialNumber; + private String cost; + private String stock; +} diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectIdDTO.java b/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectIdDTO.java new file mode 100644 index 00000000..b81b6ce7 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectIdDTO.java @@ -0,0 +1,43 @@ +package stanl_2.final_backend.domain.product.query.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString +public class ProductSelectIdDTO { + private String id; + private String name; + private String serialNumber; + private String cost; + private String stock; + + /* 설명. product option 받아올 필드 */ + private Character country; + private Character manufacturer; + private Character vehicleType; + private Character chassis; + private Character detailType; + private Character bodyType; + private Character safetyDevice; + private Character engineCapacity; + private Character secretCode; + private Character productYear; + private Character productPlant; + private Character engine; + private Character mission; + private Character trim; + private Character externalColor; + private Character internalColor; + private Character headUpDisplay; + private Character navigation; + private Character driveWise; + private Character smartConnect; + private Character style; + private Character myComfortPackage; + private Character outdoorPackage; + private Character sunRoof; + private Character sound; +} diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java b/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java new file mode 100644 index 00000000..6ace42e4 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java @@ -0,0 +1,21 @@ +package stanl_2.final_backend.domain.product.query.repository; + +import org.apache.ibatis.annotations.Mapper; +import stanl_2.final_backend.domain.product.common.util.RequestList; +import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; + +import java.util.List; +import java.util.Map; + +@Mapper +public interface ProductMapper { + List> findProductAll(RequestList requestList); + + int findProductCount(); + + ProductSelectIdDTO findProductById(String id); + + List> findProductBySearch(Map paramMap); + + int findProductBySearchCount(Map paramMap); +} diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductService.java b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductService.java new file mode 100644 index 00000000..74e876f5 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductService.java @@ -0,0 +1,17 @@ +package stanl_2.final_backend.domain.product.query.service; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; + +import java.util.Map; + +@Service +public interface ProductService { + Page> selectAll(Pageable pageable); + + ProductSelectIdDTO selectByProductId(String id); + + Page> selectProductBySearch(Map paramMap); +} diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductServiceImpl.java new file mode 100644 index 00000000..ced78f15 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductServiceImpl.java @@ -0,0 +1,63 @@ +package stanl_2.final_backend.domain.product.query.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.product.common.util.RequestList; +import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; +import stanl_2.final_backend.domain.product.query.repository.ProductMapper; + +import java.util.List; +import java.util.Map; + +@Service +public class ProductServiceImpl implements ProductService{ + + private final ProductMapper productMapper; + + @Autowired + public ProductServiceImpl(ProductMapper productMapper) { + this.productMapper = productMapper; + } + + @Override + @Transactional + public Page> selectAll(Pageable pageable) { + RequestList requestList = RequestList.builder() + .pageable(pageable) + .build(); + + List> productList = productMapper.findProductAll(requestList); + + int total = productMapper.findProductCount(); + + return new PageImpl<>(productList, pageable, total); + + } + + @Override + @Transactional + public ProductSelectIdDTO selectByProductId(String id) { + + /* 설명. 상세 조회 시, Mapper에서 product와 productOption join해서 보여줄 것 */ + ProductSelectIdDTO productSelectIdDTO = productMapper.findProductById(id); + + return productSelectIdDTO; + } + + @Override + @Transactional + public Page> selectProductBySearch(Map paramMap) { + + Pageable pageable = (Pageable) paramMap.get("pageable"); + + List> productList = productMapper.findProductBySearch(paramMap); + + int total = productMapper.findProductBySearchCount(paramMap); + + return new PageImpl<>(productList, pageable, total); + } +} diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index fa3912d7..c43ee5b2 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -114,7 +114,7 @@ CREATE TABLE tb_product UPDATED_AT CHAR(19) NOT NULL DEFAULT 'CURRENT_TIMESTAMP', DELETED_AT CHAR(19) NULL, ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - PRIMARY KEY (PROD_ID, PROD_SER_NO) + PRIMARY KEY (PROD_ID) ); CREATE TABLE tb_contract @@ -384,9 +384,8 @@ CREATE TABLE tb_PRODUCT_OPTION OPT_OTDR_PCKG CHAR(1) NOT NULL DEFAULT '0', OPT_SUN_ROOF CHAR(1) NOT NULL DEFAULT '0', OPT_SOND CHAR(1) NOT NULL DEFAULT '0', - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - PRIMARY KEY (PROD_ID, PROD_SER_NO), - FOREIGN KEY (PROD_ID, PROD_SER_NO) REFERENCES tb_product (PROD_ID, PROD_SER_NO) ON DELETE CASCADE + PRIMARY KEY (PROD_ID), + FOREIGN KEY (PROD_ID) REFERENCES tb_product (PROD_ID) ON DELETE CASCADE ); CREATE TABLE tb_sales_history @@ -498,17 +497,17 @@ VALUES ('CUS_000000001', '홍길동', 45, 'MALE', '010-1111-2222', '010-2222-333 'MEM_000000010'); INSERT INTO tb_product (PROD_ID, PROD_SER_NO, PROD_COST, PROD_NAME, PROD_STCK, CREATED_AT, UPDATED_AT, ACTIVE) -VALUES ('PRO_000000001', 'KNAHAA4AALU1A00001', 25000000, '쏘렌토', 10, '2024-01-10 10:00:00', '2024-01-10 11:00:00', TRUE), - ('PRO_000000002', 'KNAHBA4BALR2Z00002', 22000000, '스포티지', 15, '2024-01-11 11:00:00', '2024-01-11 12:00:00', +VALUES ('PROD_000000001', 'KNAHAA4AALU1A00001', 25000000, '쏘렌토', 10, '2024-01-10 10:00:00', '2024-01-10 11:00:00', TRUE), + ('PROD_000000002', 'KNAHBA4BALR2Z00002', 22000000, '스포티지', 15, '2024-01-11 11:00:00', '2024-01-11 12:00:00', TRUE), - ('PRO_000000003', 'KMBHC64CAMJ5A00003', 28000000, 'K7', 8, '2024-01-12 12:00:00', '2024-01-12 13:00:00', TRUE), - ('PRO_000000004', 'KNJFA42DALU3C00004', 19000000, '셀토스', 12, '2024-01-13 13:00:00', '2024-01-13 14:00:00', TRUE), - ('PRO_000000005', 'KFBGBM5EARP1M00005', 18000000, 'K3', 20, '2024-01-14 14:00:00', '2024-01-14 15:00:00', TRUE), - ('PRO_000000006', 'KNHGA6BALUP7A00006', 34000000, '모하비', 7, '2024-01-15 15:00:00', '2024-01-15 16:00:00', TRUE), - ('PRO_000000007', 'KNJFA34AALU4Z00007', 32000000, 'K8', 5, '2024-01-16 16:00:00', '2024-01-16 17:00:00', TRUE), - ('PRO_000000008', 'KMAHDA2AAMJ3T00008', 27000000, '스팅어', 9, '2024-01-17 17:00:00', '2024-01-17 18:00:00', TRUE), - ('PRO_000000009', 'KNAHCA5BALU5C00009', 23000000, '니로', 14, '2024-01-18 18:00:00', '2024-01-18 19:00:00', TRUE), - ('PRO_000000010', 'KNFHC54CAMR1A00010', 28000000, 'K5', 6, '2024-01-19 19:00:00', '2024-01-19 20:00:00', TRUE); + ('PROD_000000003', 'KMBHC64CAMJ5A00003', 28000000, 'K7', 8, '2024-01-12 12:00:00', '2024-01-12 13:00:00', TRUE), + ('PROD_000000004', 'KNJFA42DALU3C00004', 19000000, '셀토스', 12, '2024-01-13 13:00:00', '2024-01-13 14:00:00', TRUE), + ('PROD_000000005', 'KFBGBM5EARP1M00005', 18000000, 'K3', 20, '2024-01-14 14:00:00', '2024-01-14 15:00:00', TRUE), + ('PROD_000000006', 'KNHGA6BALUP7A00006', 34000000, '모하비', 7, '2024-01-15 15:00:00', '2024-01-15 16:00:00', TRUE), + ('PROD_000000007', 'KNJFA34AALU4Z00007', 32000000, 'K8', 5, '2024-01-16 16:00:00', '2024-01-16 17:00:00', TRUE), + ('PROD_000000008', 'KMAHDA2AAMJ3T00008', 27000000, '스팅어', 9, '2024-01-17 17:00:00', '2024-01-17 18:00:00', TRUE), + ('PROD_000000009', 'KNAHCA5BALU5C00009', 23000000, '니로', 14, '2024-01-18 18:00:00', '2024-01-18 19:00:00', TRUE), + ('PROD_000000010', 'KNFHC54CAMR1A00010', 28000000, 'K5', 6, '2024-01-19 19:00:00', '2024-01-19 20:00:00', TRUE); INSERT INTO tb_contract (CONR_ID, CONR_NAME, CONR_CUST_NAME, CONR_CUST_IDEN_NO, CONR_CUST_ADR, @@ -840,38 +839,38 @@ INSERT INTO tb_product_option (PROD_ID, PROD_SER_NO, OPT_CNTY, OPT_MNFR, OPT_VHC OPT_SFTY_DVCE, OPT_ENGN_CPCT, OPT_SCRT_CODE, OPT_PRDC_YEAR, OPT_PRDC_PLNT, OPT_ENGN, OPT_MSSN, OPT_TRIM, OPT_XTNL_COLR, OPT_ITNL_COLR, OPT_HUD, OPT_NAVI, OPT_DRVE_WISE, OPT_SMRT_CNCT, - OPT_STYL, OPT_MY_CFRT_PCKG, OPT_OTDR_PCKG, OPT_SUN_ROOF, OPT_SOND, ACTIVE) + OPT_STYL, OPT_MY_CFRT_PCKG, OPT_OTDR_PCKG, OPT_SUN_ROOF, OPT_SOND) VALUES -- 데이터 1 -('PRO_000000001', 'KNAHAA4AALU1A00001', 'K', 'N', 'A', 'A', 'L', '4', '2', 'A', 'P', 'A', 'U', '1', '0', '1', 'B', 'W', - '1', '1', '1', '1', '1', '0', '1', '1', '1', TRUE), +('PROD_000000001', 'KNAHAA4AALU1A00001', 'K', 'N', 'A', 'A', 'L', '4', '2', 'A', 'P', 'A', 'U', '1', '0', '1', 'B', 'W', + '1', '1', '1', '1', '1', '0', '1', '1', '1'), -- 데이터 2 -('PRO_000000002', 'KNAHBA4BALR2Z00002', 'K', 'N', 'H', 'B', 'L', '4', '4', 'B', 'R', 'B', 'Z', '1', '1', '0', 'G', 'B', - '0', '1', '0', '1', '0', '1', '0', '0', '1', TRUE), +('PROD_000000002', 'KNAHBA4BALR2Z00002', 'K', 'N', 'H', 'B', 'L', '4', '4', 'B', 'R', 'B', 'Z', '1', '1', '0', 'G', 'B', + '0', '1', '0', '1', '0', '1', '0', '0', '1'), -- 데이터 3 -('PRO_000000003', 'KMBHC64CAMJ5A00003', 'K', 'M', 'H', 'C', 'M', '6', '3', 'C', 'P', 'C', 'A', '0', '1', '1', 'R', 'G', - '1', '1', '1', '0', '1', '1', '0', '1', '0', TRUE), +('PROD_000000003', 'KMBHC64CAMJ5A00003', 'K', 'M', 'H', 'C', 'M', '6', '3', 'C', 'P', 'C', 'A', '0', '1', '1', 'R', 'G', + '1', '1', '1', '0', '1', '1', '0', '1', '0'), -- 데이터 4 -('PRO_000000004', 'KNJFA42DALU3C00004', 'K', 'N', 'J', 'D', 'L', '2', '4', 'A', 'R', 'D', 'C', '1', '0', '0', 'B', 'R', - '0', '0', '0', '1', '0', '0', '1', '0', '1', TRUE), +('PROD_000000004', 'KNJFA42DALU3C00004', 'K', 'N', 'J', 'D', 'L', '2', '4', 'A', 'R', 'D', 'C', '1', '0', '0', 'B', 'R', + '0', '0', '0', '1', '0', '0', '1', '0', '1'), -- 데이터 5 -('PRO_000000005', 'KFBGBM5EARP1M00005', 'K', 'B', 'F', 'B', 'M', '5', '1', 'B', 'P', 'E', 'M', '1', '1', '1', 'W', 'B', - '1', '1', '0', '0', '1', '1', '0', '1', '1', TRUE), +('PROD_000000005', 'KFBGBM5EARP1M00005', 'K', 'B', 'F', 'B', 'M', '5', '1', 'B', 'P', 'E', 'M', '1', '1', '1', 'W', 'B', + '1', '1', '0', '0', '1', '1', '0', '1', '1'), -- 데이터 6 -('PRO_000000006', 'KNHGA6BALUP7A00006', 'K', 'N', 'H', 'G', 'L', '6', '3', 'A', 'R', 'F', 'A', '0', '0', '1', 'B', 'G', - '1', '1', '1', '1', '0', '1', '1', '0', '0', TRUE), +('PROD_000000006', 'KNHGA6BALUP7A00006', 'K', 'N', 'H', 'G', 'L', '6', '3', 'A', 'R', 'F', 'A', '0', '0', '1', 'B', 'G', + '1', '1', '1', '1', '0', '1', '1', '0', '0'), -- 데이터 7 -('PRO_000000007', 'KNJFA34AALU4Z00007', 'K', 'N', 'J', 'A', 'M', '4', '4', 'C', 'P', 'G', 'Z', '1', '1', '0', 'R', 'W', - '0', '0', '1', '0', '1', '0', '1', '1', '1', TRUE), +('PROD_000000007', 'KNJFA34AALU4Z00007', 'K', 'N', 'J', 'A', 'M', '4', '4', 'C', 'P', 'G', 'Z', '1', '1', '0', 'R', 'W', + '0', '0', '1', '0', '1', '0', '1', '1', '1'), -- 데이터 8 -('PRO_000000008', 'KMAHDA2AAMJ3T00008', 'K', 'M', 'H', 'D', 'L', '2', '2', 'A', 'R', 'H', 'T', '0', '0', '1', 'B', 'G', - '1', '1', '0', '1', '0', '1', '1', '0', '0', TRUE), +('PROD_000000008', 'KMAHDA2AAMJ3T00008', 'K', 'M', 'H', 'D', 'L', '2', '2', 'A', 'R', 'H', 'T', '0', '0', '1', 'B', 'G', + '1', '1', '0', '1', '0', '1', '1', '0', '0'), -- 데이터 9 -('PRO_000000009', 'KNAHCA5BALU5C00009', 'K', 'N', 'A', 'C', 'M', '5', '1', 'B', 'P', 'J', 'C', '1', '1', '0', 'R', 'R', - '0', '1', '1', '1', '1', '0', '1', '1', '0', TRUE), +('PROD_000000009', 'KNAHCA5BALU5C00009', 'K', 'N', 'A', 'C', 'M', '5', '1', 'B', 'P', 'J', 'C', '1', '1', '0', 'R', 'R', + '0', '1', '1', '1', '1', '0', '1', '1', '0'), -- 데이터 10 -('PRO_000000010', 'KNFHC54CAMR1A00010', 'K', 'N', 'F', 'F', 'N', '4', '3', 'C', 'R', 'K', 'A', '0', '1', '1', 'W', 'B', - '1', '1', '0', '0', '0', '1', '0', '1', '1', TRUE); +('PROD_000000010', 'KNFHC54CAMR1A00010', 'K', 'N', 'F', 'F', 'N', '4', '3', 'C', 'R', 'K', 'A', '0', '1', '1', 'W', 'B', + '1', '1', '0', '0', '0', '1', '0', '1', '1'); INSERT INTO tb_sales_history (SAL_HIST_ID, CONR_ID) VALUES ('SAL_000000001', 'CON_000000001'), diff --git a/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml b/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml new file mode 100644 index 00000000..3d6901b1 --- /dev/null +++ b/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From b9acf6df5810015ae097678ad2259331f362290e Mon Sep 17 00:00:00 2001 From: giuseog Date: Sat, 16 Nov 2024 20:24:16 +0900 Subject: [PATCH 080/563] =?UTF-8?q?feat:=20=ED=8F=89=EA=B0=80=EC=84=9C=20c?= =?UTF-8?q?ud=20=EA=B5=AC=ED=98=84(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/EvaluationController.java | 93 +++++++++++++++++++ .../application/dto/EvaluationModifyDTO.java | 16 ++++ .../application/dto/EvaluationRegistDTO.java | 17 ++++ .../service/EvaluationCommandService.java | 13 +++ .../domain/aggregate/entity/Evaluation.java | 78 ++++++++++++++++ .../repository/EvaluationRepository.java | 9 ++ .../service/EvaluationCommandServiceImpl.java | 72 ++++++++++++++ .../exception/EvaluationCommonException.java | 17 ++++ .../common/exception/EvaluationErrorCode.java | 53 +++++++++++ .../EvaluationExceptionResponse.java | 23 +++++ .../response/EvaluationResponseMessage.java | 14 +++ .../controller/EvaluationController.java | 75 +++++++++++++++ .../evaluation/query/dto/EvaluationDTO.java | 12 +++ .../query/respository/EvaluationMapper.java | 12 +++ .../query/service/EvaluationQueryService.java | 10 ++ .../service/EvaluationQueryServiceImpl.java | 48 ++++++++++ 16 files changed, 562 insertions(+) create mode 100644 src/main/java/stanl_2/final_backend/domain/evaluation/command/application/controller/EvaluationController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationModifyDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationRegistDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/evaluation/command/application/service/EvaluationCommandService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/aggregate/entity/Evaluation.java create mode 100644 src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/repository/EvaluationRepository.java create mode 100644 src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/service/EvaluationCommandServiceImpl.java create mode 100644 src/main/java/stanl_2/final_backend/domain/evaluation/common/exception/EvaluationCommonException.java create mode 100644 src/main/java/stanl_2/final_backend/domain/evaluation/common/exception/EvaluationErrorCode.java create mode 100644 src/main/java/stanl_2/final_backend/domain/evaluation/common/exception/EvaluationExceptionResponse.java create mode 100644 src/main/java/stanl_2/final_backend/domain/evaluation/common/response/EvaluationResponseMessage.java create mode 100644 src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/evaluation/query/respository/EvaluationMapper.java create mode 100644 src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/controller/EvaluationController.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/controller/EvaluationController.java new file mode 100644 index 00000000..0b24395d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/controller/EvaluationController.java @@ -0,0 +1,93 @@ +package stanl_2.final_backend.domain.evaluation.command.application.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleModifyDTO; +import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleRegistDTO; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; +import stanl_2.final_backend.domain.evaluation.command.application.dto.EvaluationModifyDTO; +import stanl_2.final_backend.domain.evaluation.command.application.dto.EvaluationRegistDTO; +import stanl_2.final_backend.domain.evaluation.command.application.service.EvaluationCommandService; +import stanl_2.final_backend.domain.evaluation.common.response.EvaluationResponseMessage; + +@RestController("commandEvaluationController") +@RequestMapping("/api/v1/evaluation") +public class EvaluationController { + + private final EvaluationCommandService evaluationCommandService; + + @Autowired + public EvaluationController(EvaluationCommandService evaluationCommandService) { + this.evaluationCommandService = evaluationCommandService; + } + + @Operation(summary = "평가서 요청 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = EvaluationResponseMessage.class))}) + }) + @PostMapping("") + public ResponseEntity postTest(@RequestBody EvaluationRegistDTO evaluationRegistRequestDTO) { + + evaluationCommandService.registerEvaluation(evaluationRegistRequestDTO); + + return ResponseEntity.ok(EvaluationResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + } + + /** + * [PUT] http://localhost:7777/api/v1/sample?mem_id=SAM_000001 + * Request + * { + * "name": "abcc" + * } + * */ + @Operation(summary = "평가서 수정 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) + }) + @PutMapping("{id}") + public ResponseEntity putTest(@PathVariable String id, + @RequestBody EvaluationModifyDTO evaluationModifyRequestDTO) { + +// evaluationModifyRequestDTO.setEvaluationId(id); + evaluationCommandService.modifyEvaluation(id,evaluationModifyRequestDTO); + + return ResponseEntity.ok(EvaluationResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + } + + /** + * [DELETE] http://localhost:7777/api/v1/sample?mem_id=SAM_000001 + * */ + @Operation(summary = "평가서 삭제 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = EvaluationResponseMessage.class))}) + }) + @DeleteMapping("{id}") + public ResponseEntity deleteTest(@PathVariable String id) { + + evaluationCommandService.deleteEvaluation(id); + + return ResponseEntity.ok(EvaluationResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationModifyDTO.java new file mode 100644 index 00000000..b2e30198 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationModifyDTO.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.evaluation.command.application.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class EvaluationModifyDTO { + private String title; + private String content; + private String centerId; + private String memberId; + private String writerId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationRegistDTO.java new file mode 100644 index 00000000..fcc29121 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationRegistDTO.java @@ -0,0 +1,17 @@ +package stanl_2.final_backend.domain.evaluation.command.application.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class EvaluationRegistDTO { + private String title; + private String content; + private String memberId; + private String writerId; + /* 설명. 레포지토리에서 불러오게 할지, 서비스 층에 만들지 고민 중 (centerId)*/ + private String centerId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/service/EvaluationCommandService.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/service/EvaluationCommandService.java new file mode 100644 index 00000000..5fd0a3cd --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/service/EvaluationCommandService.java @@ -0,0 +1,13 @@ +package stanl_2.final_backend.domain.evaluation.command.application.service; + +import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleModifyDTO; +import stanl_2.final_backend.domain.evaluation.command.application.dto.EvaluationModifyDTO; +import stanl_2.final_backend.domain.evaluation.command.application.dto.EvaluationRegistDTO; + +public interface EvaluationCommandService { + void registerEvaluation(EvaluationRegistDTO evaluationRegistRequestDTO); + + void modifyEvaluation(String id, EvaluationModifyDTO evaluationModifyRequestDTO); + + void deleteEvaluation(String id); +} diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/aggregate/entity/Evaluation.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/aggregate/entity/Evaluation.java new file mode 100644 index 00000000..a98cbfdd --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/aggregate/entity/Evaluation.java @@ -0,0 +1,78 @@ +package stanl_2.final_backend.domain.evaluation.command.domain.aggregate.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "TB_EVALUATION") +public class Evaluation { + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "EVAL") + ) + @Column(name = "EVAL_ID") + private String id; + + @Column(name = "EVAL_TTL") + private String title; + + @Column(name = "EVAL_CONT") + private String content; + + @Column(name = "CREATED_AT", nullable = false, updatable = false) + private String createdAt; + + @Column(name = "UPDATED_AT", nullable = false) + private String updatedAt; + + @Column(name = "DELETED_AT") + private String deletedAt; + + @Column(name = "ACTIVE") + private Boolean active = true; + + @Column(name = "CENT_ID") + private String centerId; + + @Column(name = "MEM_ID") + private String memberId; + + @Column(name = "WRI_ID") + private String writerId; + + /* 설명. updatedAt 자동화 */ + // Insert 되기 전에 실행 + @PrePersist + private void prePersist() { + this.createdAt = getCurrentTime(); + this.updatedAt = this.createdAt; + } + + // Update 되기 전에 실행 + @PreUpdate + private void preUpdate() { + this.updatedAt = getCurrentTime(); + } + + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } +} + + diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/repository/EvaluationRepository.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/repository/EvaluationRepository.java new file mode 100644 index 00000000..140d06ec --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/repository/EvaluationRepository.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.evaluation.command.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import stanl_2.final_backend.domain.evaluation.command.domain.aggregate.entity.Evaluation; + +@Repository +public interface EvaluationRepository extends JpaRepository { +} diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/service/EvaluationCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/service/EvaluationCommandServiceImpl.java new file mode 100644 index 00000000..ff6bca72 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/service/EvaluationCommandServiceImpl.java @@ -0,0 +1,72 @@ +package stanl_2.final_backend.domain.evaluation.command.domain.service; + +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.evaluation.command.application.dto.EvaluationModifyDTO; +import stanl_2.final_backend.domain.evaluation.command.application.dto.EvaluationRegistDTO; +import stanl_2.final_backend.domain.evaluation.command.application.service.EvaluationCommandService; +import stanl_2.final_backend.domain.evaluation.command.domain.aggregate.entity.Evaluation; +import stanl_2.final_backend.domain.evaluation.command.domain.repository.EvaluationRepository; +import stanl_2.final_backend.domain.evaluation.common.exception.EvaluationCommonException; +import stanl_2.final_backend.domain.evaluation.common.exception.EvaluationErrorCode; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@Service("commandEvaluationService") +public class EvaluationCommandServiceImpl implements EvaluationCommandService { + + private final EvaluationRepository evaluationRepository; + private final ModelMapper modelMapper; + + @Autowired + public EvaluationCommandServiceImpl(EvaluationRepository evaluationRepository, ModelMapper modelMapper) { + this.evaluationRepository = evaluationRepository; + this.modelMapper = modelMapper; + } + + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } + + @Override + @Transactional + public void registerEvaluation(EvaluationRegistDTO evaluationRegistRequestDTO) { + + Evaluation evaluation = modelMapper.map(evaluationRegistRequestDTO, Evaluation.class); + + evaluationRepository.save(evaluation); + } + + @Override + @Transactional + public void modifyEvaluation(String id, EvaluationModifyDTO evaluationModifyRequestDTO) { + Evaluation evaluation = evaluationRepository.findById(id) + .orElseThrow(() -> new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND)); + + Evaluation updateEvaluation = modelMapper.map(evaluationModifyRequestDTO, Evaluation.class); + + updateEvaluation.setId(evaluation.getId()); + updateEvaluation.setCreatedAt(evaluation.getCreatedAt()); + updateEvaluation.setUpdatedAt(getCurrentTime()); + updateEvaluation.setActive(evaluation.getActive()); + + evaluationRepository.save(updateEvaluation); + } + + @Override + @Transactional + public void deleteEvaluation(String id) { + Evaluation evaluation = evaluationRepository.findById(id) + .orElseThrow(() -> new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND)); + + evaluation.setActive(false); + evaluation.setDeletedAt(getCurrentTime()); + + evaluationRepository.save(evaluation); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/common/exception/EvaluationCommonException.java b/src/main/java/stanl_2/final_backend/domain/evaluation/common/exception/EvaluationCommonException.java new file mode 100644 index 00000000..d5ed1115 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/common/exception/EvaluationCommonException.java @@ -0,0 +1,17 @@ +package stanl_2.final_backend.domain.evaluation.common.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import stanl_2.final_backend.domain.A_sample.common.exception.SampleErrorCode; + +@Getter +@RequiredArgsConstructor +public class EvaluationCommonException extends RuntimeException { + private final EvaluationErrorCode evaluationErrorCode; + + // 에러 발생시 ErroCode 별 메시지 + @Override + public String getMessage() { + return this.evaluationErrorCode.getMsg(); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/common/exception/EvaluationErrorCode.java b/src/main/java/stanl_2/final_backend/domain/evaluation/common/exception/EvaluationErrorCode.java new file mode 100644 index 00000000..1df3bab8 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/common/exception/EvaluationErrorCode.java @@ -0,0 +1,53 @@ +package stanl_2.final_backend.domain.evaluation.common.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum EvaluationErrorCode { + + /** + * 400(Bad Request) + * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. + */ + + + + /** + * 401(Unauthorized) + * 비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만, + * 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다. + * 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다. + */ + + + /** + * 403(Forbidden) + * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. + * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다. + */ + + + + /** + * 404(Not Found) + * 서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다. + * 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있습니다. + * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. + * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. + */ + SAMPLE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "sample 데이터를 찾지 못했습니다"), + CENTER_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "center 데이터를 찾지 못했습니다."), + EVALUATION_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "evaluation 데이터를 찾지 못했습니다."), + /** + * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. + */ + INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); + + + private final Integer code; + private final HttpStatus httpStatus; + private final String msg; +} diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/common/exception/EvaluationExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/evaluation/common/exception/EvaluationExceptionResponse.java new file mode 100644 index 00000000..27890ff2 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/common/exception/EvaluationExceptionResponse.java @@ -0,0 +1,23 @@ +package stanl_2.final_backend.domain.evaluation.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; +import stanl_2.final_backend.domain.A_sample.common.exception.SampleErrorCode; + +@Getter +public class EvaluationExceptionResponse { + private final Integer code; + private final String msg; + private final HttpStatus httpStatus; + + public EvaluationExceptionResponse(EvaluationErrorCode evaluationErrorCode) { + this.code = evaluationErrorCode.getCode(); + this.msg = evaluationErrorCode.getMsg(); + this.httpStatus = evaluationErrorCode.getHttpStatus(); + } + + public static EvaluationExceptionResponse of(EvaluationErrorCode evaluationErrorCode) { + return new EvaluationExceptionResponse(evaluationErrorCode); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/common/response/EvaluationResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/evaluation/common/response/EvaluationResponseMessage.java new file mode 100644 index 00000000..3ae1f6d2 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/common/response/EvaluationResponseMessage.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.evaluation.common.response; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class EvaluationResponseMessage { + private int httpStatus; + private String msg; + private Object result; +} diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java new file mode 100644 index 00000000..c0b0e5ff --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java @@ -0,0 +1,75 @@ +package stanl_2.final_backend.domain.evaluation.query.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +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.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; +import stanl_2.final_backend.domain.A_sample.query.dto.SampleDTO; +import stanl_2.final_backend.domain.A_sample.query.service.SampleQueryService; +import stanl_2.final_backend.domain.evaluation.common.response.EvaluationResponseMessage; +import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationDTO; +import stanl_2.final_backend.domain.evaluation.query.service.EvaluationQueryService; + +@RestController(value = "queryEvaluationController") +@RequestMapping("/api/v1/evaluation") +public class EvaluationController { + private final EvaluationQueryService evaluationQueryService; + + @Autowired + public EvaluationController(EvaluationQueryService evaluationQueryService) { + this.evaluationQueryService = evaluationQueryService; + } + + /** + * [GET] http://localhost:7777/api/v1/sample/SAM_000000001 + * */ + @Operation(summary = "평가서 조회 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = EvaluationResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("{id}") + public ResponseEntity getTest(@PathVariable String id){ + + EvaluationDTO evaluationDTO = evaluationQueryService.selectEvaluation(id); + + return ResponseEntity.ok(EvaluationResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(evaluationDTO) + .build()); + } + + /** + * [GET] http://localhost:7777/api/v1/sample/detail/SAM_000000001 + * */ + @Operation(summary = "평가서 상세 조회 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = EvaluationResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/detail/{id}") + public ResponseEntity getDetailTest(@PathVariable String id) { + + String name = evaluationQueryService.selectEvaluationName(id); + + return ResponseEntity.ok(EvaluationResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(name) + .build()); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java new file mode 100644 index 00000000..622f5015 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java @@ -0,0 +1,12 @@ +package stanl_2.final_backend.domain.evaluation.query.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString +public class EvaluationDTO { + private String id; +} diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/respository/EvaluationMapper.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/respository/EvaluationMapper.java new file mode 100644 index 00000000..072682c8 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/respository/EvaluationMapper.java @@ -0,0 +1,12 @@ +package stanl_2.final_backend.domain.evaluation.query.respository; + +import org.apache.ibatis.annotations.Mapper; +import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationDTO; + +@Mapper +public interface EvaluationMapper { + + String selectNameById(String id); + + EvaluationDTO selectById(String id); +} diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryService.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryService.java new file mode 100644 index 00000000..608ab996 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryService.java @@ -0,0 +1,10 @@ +package stanl_2.final_backend.domain.evaluation.query.service; + + +import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationDTO; + +public interface EvaluationQueryService { + EvaluationDTO selectEvaluation(String id); + + String selectEvaluationName(String id); +} diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java new file mode 100644 index 00000000..c925b355 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java @@ -0,0 +1,48 @@ +package stanl_2.final_backend.domain.evaluation.query.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.evaluation.common.exception.EvaluationCommonException; +import stanl_2.final_backend.domain.evaluation.common.exception.EvaluationErrorCode; +import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationDTO; +import stanl_2.final_backend.domain.evaluation.query.respository.EvaluationMapper; + +@Service +public class EvaluationQueryServiceImpl implements EvaluationQueryService { + + private final EvaluationMapper evaluationMapper; + + @Autowired + public EvaluationQueryServiceImpl(EvaluationMapper evaluationMapper) { + this.evaluationMapper = evaluationMapper; + } + + @Override + @Transactional(readOnly = true) + public String selectEvaluationName(String id) { + + String name = evaluationMapper.selectNameById(id); + + if(name == null){ + throw new EvaluationCommonException(EvaluationErrorCode.SAMPLE_NOT_FOUND); + } + + return name; + } + + @Override + @Transactional(readOnly = true) + public EvaluationDTO selectEvaluation(String id) { + + EvaluationDTO evaluationDTO = evaluationMapper.selectById(id); + + if(evaluationDTO == null){ + throw new EvaluationCommonException(EvaluationErrorCode.SAMPLE_NOT_FOUND); + } + + return evaluationDTO; + } + + +} From 946e0b5449da824cfab5193e89b5cd5f0882fc06 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Sat, 16 Nov 2024 21:37:26 +0900 Subject: [PATCH 081/563] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=EA=B5=AC=EC=A1=B0=20=EC=84=B8=ED=8C=85=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/alarm/aggregate/dto/AlarmDTO.java | 15 ++++++ .../domain/alarm/aggregate/entity/Alarm.java | 48 +++++++++++++++++++ .../controller/AlarmController.java | 32 ------------- .../service/AlarmCommandService.java | 4 -- .../service/AlarmCommandServiceImpl.java | 10 ---- .../alarm/controller/AlarmController.java | 34 +++++++++++++ .../alarm/repository/AlarmRepository.java | 9 ++++ .../domain/alarm/service/AlarmService.java | 8 ++++ .../alarm/service/AlarmServiceImpl.java | 28 +++++++++++ src/main/resources/sql/ddl.sql | 26 +++++----- 10 files changed, 157 insertions(+), 57 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/aggregate/dto/AlarmDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/aggregate/entity/Alarm.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/command/application/controller/AlarmController.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/command/application/service/AlarmCommandService.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java create mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/controller/AlarmController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/repository/AlarmRepository.java create mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/dto/AlarmDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/dto/AlarmDTO.java new file mode 100644 index 00000000..f77919dc --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/dto/AlarmDTO.java @@ -0,0 +1,15 @@ +package stanl_2.final_backend.domain.alarm.aggregate.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString +public class AlarmDTO { + + private String memberId; + private String lastEventId; + +} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/entity/Alarm.java b/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/entity/Alarm.java new file mode 100644 index 00000000..270468be --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/entity/Alarm.java @@ -0,0 +1,48 @@ +package stanl_2.final_backend.domain.alarm.aggregate.entity; + +import jakarta.persistence.*; +import lombok.*; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.Parameter; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +@Entity +@Table(name = "TB_ALARM") +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class Alarm { + + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @Parameter(name = "prefix", value = "ALR") + ) + @Column(name = "ALR_ID", nullable = false) + private String alarmId; + + @Column(name = "ALR_MSG", nullable = false) + private String message; + + @Column(name = "ALR_URL", nullable = false) + private String redirectUrl; + + @Column(name = "ALR_TYPE", nullable = false) + private String type = "NOTICE"; + + @Column(name = "ALR_READ_STAT", nullable = false) + private Boolean readStatus; + + @Column(name = "CREATED_AT", nullable = false) + private String createdAt; + + @Column(name = "ALR_SND", nullable = false) + private String sender; + + @Column(name = "MEM_ID", nullable = false) + private String memberId; + + +} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/application/controller/AlarmController.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/controller/AlarmController.java deleted file mode 100644 index 2fae0751..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/application/controller/AlarmController.java +++ /dev/null @@ -1,32 +0,0 @@ -package stanl_2.final_backend.domain.alarm.command.application.controller; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import stanl_2.final_backend.domain.alarm.command.application.service.AlarmCommandService; -import stanl_2.final_backend.domain.alarm.common.response.AlarmResponseMessage; -import stanl_2.final_backend.domain.schedule.common.response.ScheduleResponseMessage; - -@RestController("commandAlarmController") -@RequestMapping("/api/v1/alarm") -public class AlarmController { - - private final AlarmCommandService alarmCommandService; - - @Autowired - public AlarmController(AlarmCommandService alarmCommandService) { - this.alarmCommandService = alarmCommandService; - } - - @PostMapping("") - public ResponseEntity registAlarm(){ - - return ResponseEntity.ok(AlarmResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(" ") - .build()); - } -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/application/service/AlarmCommandService.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/service/AlarmCommandService.java deleted file mode 100644 index 53c1ce4a..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/application/service/AlarmCommandService.java +++ /dev/null @@ -1,4 +0,0 @@ -package stanl_2.final_backend.domain.alarm.command.application.service; - -public interface AlarmCommandService { -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java deleted file mode 100644 index bcb92270..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java +++ /dev/null @@ -1,10 +0,0 @@ -package stanl_2.final_backend.domain.alarm.command.domain.service; - -import org.springframework.stereotype.Service; -import stanl_2.final_backend.domain.alarm.command.application.service.AlarmCommandService; - -@Service -public class AlarmCommandServiceImpl implements AlarmCommandService { - - -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/controller/AlarmController.java b/src/main/java/stanl_2/final_backend/domain/alarm/controller/AlarmController.java new file mode 100644 index 00000000..2f0a8b62 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/alarm/controller/AlarmController.java @@ -0,0 +1,34 @@ +package stanl_2.final_backend.domain.alarm.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; +import stanl_2.final_backend.domain.alarm.aggregate.dto.AlarmDTO; +import stanl_2.final_backend.domain.alarm.service.AlarmService; + +@RestController("queryAlarmController") +@RequestMapping("/api/v1/alarm") +public class AlarmController { + + private final AlarmService alarmService; + + @Autowired + public AlarmController(AlarmService alarmService) { + this.alarmService = alarmService; + } + + @GetMapping(value= "/connect/{memberId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE) + public ResponseEntity subscribe(@PathVariable String memberId, + @RequestHeader(value = "Last-Event-ID", required = false, defaultValue = "") + String lastEventId){ + + AlarmDTO alarmDTO = new AlarmDTO(); + alarmDTO.setMemberId(memberId); + alarmDTO.setLastEventId(lastEventId); + + return ResponseEntity.ok(alarmService.subscribe(alarmDTO)); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/repository/AlarmRepository.java b/src/main/java/stanl_2/final_backend/domain/alarm/repository/AlarmRepository.java new file mode 100644 index 00000000..7392412b --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/alarm/repository/AlarmRepository.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.alarm.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import stanl_2.final_backend.domain.alarm.aggregate.entity.Alarm; + +@Repository +public interface AlarmRepository extends JpaRepository { +} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java new file mode 100644 index 00000000..c3fee2e0 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java @@ -0,0 +1,8 @@ +package stanl_2.final_backend.domain.alarm.service; + +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; +import stanl_2.final_backend.domain.alarm.aggregate.dto.AlarmDTO; + +public interface AlarmService { + SseEmitter subscribe(AlarmDTO alarmDTO); +} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java new file mode 100644 index 00000000..57a9156a --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java @@ -0,0 +1,28 @@ +package stanl_2.final_backend.domain.alarm.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; +import stanl_2.final_backend.domain.alarm.aggregate.dto.AlarmDTO; +import stanl_2.final_backend.domain.alarm.repository.AlarmRepository; + +import java.util.Map; + +@Slf4j +@Service +public class AlarmServiceImpl implements AlarmService { + + private final AlarmRepository alarmRepository; + + @Autowired + public AlarmServiceImpl(AlarmRepository alarmRepository) { + this.alarmRepository = alarmRepository; + } + + @Override + public SseEmitter subscribe(AlarmDTO alarmDTO) { + + return null; + } +} diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index 7f477d32..6b8718b1 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -308,8 +308,10 @@ CREATE TABLE tb_alarm ALR_ID VARCHAR(255) NOT NULL, ALR_MSG VARCHAR(255) NOT NULL, ALR_URL VARCHAR(255) NOT NULL, + ALR_TYPE VARCHAR(255) NOT NULL, ALR_READ_STAT BOOLEAN NOT NULL DEFAULT FALSE, CREATED_AT CHAR(19) NOT NULL, + ALR_SND VARCHAR(255) NOT NULL DEFAULT 'NOTICE', MEM_ID VARCHAR(255) NOT NULL, PRIMARY KEY (ALR_ID), FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) @@ -738,17 +740,19 @@ VALUES '2024-01-29 19:00:00', '2024-01-29 19:30:00', NULL, TRUE, 'MEM_000000010'); -INSERT INTO tb_alarm (ALR_ID, ALR_MSG, ALR_URL, ALR_READ_STAT, CREATED_AT, MEM_ID) -VALUES ('ALR_000000001', '신차 출시 공지 알림', '/notices/1', FALSE, '2024-01-20 12:00:00', 'MEM_000000001'), - ('ALR_000000002', '스포티지 리콜 안내', '/notices/2', FALSE, '2024-01-21 14:00:00', 'MEM_000000002'), - ('ALR_000000003', 'K7 고객 이벤트 초대', '/events/3', TRUE, '2024-01-22 09:00:00', 'MEM_000000003'), - ('ALR_000000004', '셀토스 정비 완료', '/service/4', FALSE, '2024-01-23 10:00:00', 'MEM_000000004'), - ('ALR_000000005', 'K3 테스트 드라이브 일정 변경', '/schedules/5', TRUE, '2024-01-24 11:00:00', 'MEM_000000005'), - ('ALR_000000006', '모하비 부품 입고 알림', '/inventory/6', FALSE, '2024-01-25 12:00:00', 'MEM_000000006'), - ('ALR_000000007', 'K8 디자인 변경 확정', '/design/7', TRUE, '2024-01-26 13:00:00', 'MEM_000000007'), - ('ALR_000000008', '스팅어 성능 테스트 보고서', '/reports/8', FALSE, '2024-01-27 14:00:00', 'MEM_000000008'), - ('ALR_000000009', '니로 전기차 충전소 위치', '/maps/9', TRUE, '2024-01-28 15:00:00', 'MEM_000000009'), - ('ALR_000000010', 'K5 재고 부족 경고', '/inventory/10', FALSE, '2024-01-29 16:00:00', 'MEM_000000010'); +INSERT INTO tb_alarm (ALR_ID, ALR_MSG, ALR_URL, ALR_TYPE, ALR_READ_STAT, CREATED_AT, ALR_SND, MEM_ID) +VALUES +('ALR_000000001', '신규 보안 지침이 업데이트되었습니다.', 'api/v1/notice/1', 'NOTICE', FALSE, '2024-11-16 08:00:00', 'MEM_000000025', 'MEM_000000001'), +('ALR_000000002', '회사 워크샵 일정 공지', 'api/v1/notice/2', 'NOTICE', FALSE, '2024-11-15 09:00:00', 'MEM_000000025', 'MEM_000000002'), +('ALR_000000003', '연말 정산 안내 공지', 'api/v1/notice/3', 'NOTICE', FALSE, '2024-11-14 10:30:00', 'MEM_000000025', 'MEM_000000003'), +('ALR_000000004', '신규 프로젝트 관련 공지사항', 'api/v1/notice/4', 'NOTICE', TRUE, '2024-11-13 11:00:00', 'MEM_000000025', 'MEM_000000004'), +('ALR_000000005', '시스템 점검 일정 안내', 'api/v1/notice/5', 'NOTICE', FALSE, '2024-11-12 14:00:00', 'MEM_000000025', 'MEM_000000005'), +('ALR_000000006', '홍길동 고객님과 미팅 1일 전입니다.', 'api/v1/schedule/6', 'SCHEDULE', FALSE, '2024-11-17 10:00:00', 'SYSTEM', 'MEM_000000006'), +('ALR_000000007', '홍길동 고객님과 1시간 전입니다.', 'api/v1/schedule/7', 'SCHEDULE', FALSE, '2024-11-18 09:00:00', 'SYSTEM', 'MEM_000000006'), +('ALR_000000008', '이순신 고객님과 미팅 1일 전입니다.', 'api/v1/schedule/8', 'SCHEDULE', TRUE, '2024-11-18 09:00:00', 'SYSTEM', 'MEM_000000008'), +('ALR_000000009', '김구고객님과 미팅 1시간 전입니다.', 'api/v1/schedule/9', 'SCHEDULE', FALSE, '2024-11-18 11:00:00', 'SYSTEM', 'MEM_000000009'), +('ALR_000000010', '지리리고객님과 미팅 1시간 전입니다.', 'api/v1/schedule/10', 'SCHEDULE', FALSE, '2024-11-18 15:00:00', 'SYSTEM', 'MEM_000000010'); + INSERT INTO tb_member_detail (MEM_DET_ID, MEM_DET_REL, MEM_DET_NAME, MEM_DET_BIR, MEM_DET_IDEN_NO, MEM_DET_PHO, MEM_DET_SEX, MEM_DET_DIS, MEM_DET_DIE, MEM_DET_NOTE, MEM_DET_ENTD, MEM_DET_GRAD, From 5f3b489b1dab288c9a2ef03350e9ca491d264e9d Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Sat, 16 Nov 2024 21:55:07 +0900 Subject: [PATCH 082/563] =?UTF-8?q?feat:=20schedule=20=EC=B6=95=EC=95=BD?= =?UTF-8?q?=EC=96=B4=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EB=B0=8F=20id=EB=AA=85=20=EB=B3=80=EA=B2=BD=20(#54)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/controller/ScheduleController.java | 2 +- .../command/application/dto/ScheduleModifyDTO.java | 2 +- .../command/domain/aggregate/entity/Schedule.java | 2 +- .../command/domain/repository/ScheduleRepository.java | 4 ++++ .../domain/service/ScheduleCommandServiceImpl.java | 4 ++-- .../schedule/query/controller/ScheduleController.java | 2 +- .../domain/schedule/query/dto/ScheduleDTO.java | 2 +- .../domain/schedule/query/dto/ScheduleDetailDTO.java | 2 +- .../domain/schedule/query/dto/ScheduleYearMonthDTO.java | 2 +- .../domain/schedule/query/repository/ScheduleMapper.java | 4 ++-- .../schedule/query/service/ScheduleQueryServiceImpl.java | 6 +++--- .../domain/schedule/query/repository/ScheduleMapper.xml | 8 ++++---- 12 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java index 4b01ab0e..d926c300 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java @@ -51,7 +51,7 @@ public ResponseEntity registSchedule(@RequestBody Sched public ResponseEntity modifySchedule(@PathVariable String scheduleId, @RequestBody ScheduleModifyDTO scheduleModifyDTO){ - scheduleModifyDTO.setId(scheduleId); + scheduleModifyDTO.setScheduleId(scheduleId); Boolean answer = scheduleCommandService.modifySchedule(scheduleModifyDTO); return ResponseEntity.ok(ScheduleResponseMessage.builder() diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleModifyDTO.java index 21df0c27..c7532bed 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleModifyDTO.java @@ -11,7 +11,7 @@ @Setter public class ScheduleModifyDTO { - private String id; + private String scheduleId; private String name; private String content; private String tag; diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java index 72afca5b..02815597 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/aggregate/entity/Schedule.java @@ -28,7 +28,7 @@ public class Schedule { parameters = @Parameter(name = "prefix", value = "SCH") ) @Column(name = "SCH_ID", nullable = false) - private String id; + private String scheduleId; @Column(name = "SCH_NAME", nullable = false) private String name; diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/repository/ScheduleRepository.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/repository/ScheduleRepository.java index a594f5c5..148cdbff 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/repository/ScheduleRepository.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/repository/ScheduleRepository.java @@ -4,6 +4,10 @@ import org.springframework.stereotype.Repository; import stanl_2.final_backend.domain.schedule.command.domain.aggregate.entity.Schedule; +import java.util.Optional; + @Repository public interface ScheduleRepository extends JpaRepository { + + Optional findByScheduleId(String scheduleId); } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java index b652f1cc..810fa1a7 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java @@ -71,7 +71,7 @@ public Boolean modifySchedule(ScheduleModifyDTO scheduleModifyDTO) { throw new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND); } - Schedule schedule = scheduleRepository.findById(scheduleModifyDTO.getId()) + Schedule schedule = scheduleRepository.findByScheduleId(scheduleModifyDTO.getScheduleId()) .orElseThrow(() -> new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND)); if(!scheduleModifyDTO.getMemberId().equals(schedule.getMemberId())){ @@ -106,7 +106,7 @@ public Boolean modifySchedule(ScheduleModifyDTO scheduleModifyDTO) { @Transactional public Boolean deleteSchedule(String scheduleId) { - Schedule schedule = scheduleRepository.findById(scheduleId) + Schedule schedule = scheduleRepository.findByScheduleId(scheduleId) .orElseThrow(() -> new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND)); schedule.setActive(false); diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java index 17c989ee..aa36aeb3 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java @@ -84,7 +84,7 @@ public ResponseEntity selectDetailSchedule(@PathVariabl ScheduleDetailDTO scheduleDetailDTO = new ScheduleDetailDTO(); scheduleDetailDTO.setMemberId(memberId); - scheduleDetailDTO.setId(scheduleId); + scheduleDetailDTO.setScheduleId(scheduleId); ScheduleDetailDTO responseDetailSchedule = scheduleQueryService.selectDetailSchedule(scheduleDetailDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java index 92908e73..028fffb8 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDTO.java @@ -11,7 +11,7 @@ @ToString public class ScheduleDTO { - private String id; + private String scheduleId; private String name; private String tag; private String startAt; diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDetailDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDetailDTO.java index f6d689a8..4432f2ac 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDetailDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDetailDTO.java @@ -9,7 +9,7 @@ @ToString public class ScheduleDetailDTO { - private String id; + private String scheduleId; private String name; private String content; private String tag; diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleYearMonthDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleYearMonthDTO.java index 35f9fede..d63b8dce 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleYearMonthDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleYearMonthDTO.java @@ -9,7 +9,7 @@ @ToString public class ScheduleYearMonthDTO { - private String id; + private String scheduleId; private String name; private String tag; private String startAt; diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java index ee9490ae..bd68f381 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java @@ -11,9 +11,9 @@ @Mapper public interface ScheduleMapper { - List findSchedulesByMemberIdAndSrtAt(Map arg); - List findSchedulesByMemberIdAndYearMonth(Map arg); ScheduleDetailDTO findScheduleByMemberIdAndScheduleId(Map arg); + + List findSchedulesByMemberIdAndStartAt(Map arg); } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java index b8f797e2..d225da65 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java @@ -49,7 +49,7 @@ public List selectAllSchedule(String memberId) { arg.put("memberId",memberId); arg.put("month",currentMonth); - List scheduleList = scheduleMapper.findSchedulesByMemberIdAndSrtAt(arg); + List scheduleList = scheduleMapper.findSchedulesByMemberIdAndStartAt(arg); // Mapping 오류 체크 고려하기 @@ -88,13 +88,13 @@ public ScheduleDetailDTO selectDetailSchedule(ScheduleDetailDTO scheduleDetailDT throw new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND); } - if(scheduleDetailDTO.getId() == null || scheduleDetailDTO.getId().trim().isEmpty()){ + if(scheduleDetailDTO.getScheduleId() == null || scheduleDetailDTO.getScheduleId().trim().isEmpty()){ throw new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND); } Map arg = new HashMap<>(); arg.put("memberId", scheduleDetailDTO.getMemberId()); - arg.put("scheduleId", scheduleDetailDTO.getId()); + arg.put("scheduleId", scheduleDetailDTO.getScheduleId()); ScheduleDetailDTO responseDetailSchedule = scheduleMapper.findScheduleByMemberIdAndScheduleId(arg); diff --git a/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml b/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml index da3d3079..7207d923 100644 --- a/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml @@ -4,7 +4,7 @@ "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> - + @@ -12,7 +12,7 @@ - SELECT A.SCH_ID, A.SCH_NAME, @@ -27,7 +27,7 @@ - + @@ -50,7 +50,7 @@ - + From 186d27e028ab2cc09a91cdae761f622b5929adc9 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Sun, 17 Nov 2024 02:07:28 +0900 Subject: [PATCH 083/563] =?UTF-8?q?feat:=20=EA=B3=84=EC=95=BD=EC=84=9C=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84(#21)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ontroller.java => ContractController.java} | 29 ++-- .../query/controller/ContractController.java | 49 ++++--- .../contract/query/dto/ContractSearchDTO.java | 20 ++- .../query/repository/ContractMapper.java | 10 +- .../query/service/ContractQueryService.java | 6 +- .../service/ContractQueryServiceImpl.java | 38 +++-- .../query/repository/ContractMapper.xml | 137 ++++++++---------- 7 files changed, 157 insertions(+), 132 deletions(-) rename src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/{ContractCommandController.java => ContractController.java} (90%) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractCommandController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java similarity index 90% rename from src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractCommandController.java rename to src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java index 13d25f4d..db9483da 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractCommandController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java @@ -15,12 +15,12 @@ @RestController("contractController") @RequestMapping("/api/v1/contract") -public class ContractCommandController { +public class ContractController { private final ContractCommandService contractCommandService; @Autowired - public ContractCommandController(ContractCommandService contractCommandService) { + public ContractController(ContractCommandService contractCommandService) { this.contractCommandService = contractCommandService; } @@ -51,20 +51,19 @@ public ContractCommandController(ContractCommandService contractCommandService) * "memId": "MEM_000000001" * } * */ - @Operation(summary = "계약서 등록 테스트") + @Operation(summary = "계약서 등록") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", + @ApiResponse(responseCode = "200", description = "계약서 등록 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) - @PostMapping("{id}") - public ResponseEntity postTest(@PathVariable String id, - @RequestBody ContractRegistDTO contractRegistRequestDTO) { - contractRegistRequestDTO.setMemberId(id); + @PostMapping("") + public ResponseEntity postTest(@RequestBody ContractRegistDTO contractRegistRequestDTO) { + contractCommandService.registerContract(contractRegistRequestDTO); return ResponseEntity.ok(ContractResponseMessage.builder() .httpStatus(200) - .msg("성공") + .msg("계약서가 성공적으로 등록되었습니다.") .result(null) .build()); } @@ -95,9 +94,9 @@ public ResponseEntity postTest(@PathVariable String id, * "memId": "MEM_000000001" * } * */ - @Operation(summary = "계약서 수정 테스트") + @Operation(summary = "계약서 수정") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", + @ApiResponse(responseCode = "200", description = "계약서 수정 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) @PutMapping("{id}") @@ -109,7 +108,7 @@ public ResponseEntity putContract(@PathVariable String return ResponseEntity.ok(ContractResponseMessage.builder() .httpStatus(200) - .msg("계약서가 수정되었습니다.") + .msg("계약서가 성공적으로 수정되었습니다.") .result(contractModifyDTO) .build()); } @@ -117,9 +116,9 @@ public ResponseEntity putContract(@PathVariable String /** * [DELETE] http://localhost:8080/api/v1/contract/CON_000000011 * */ - @Operation(summary = "샘플 삭제 테스트") + @Operation(summary = "샘플 삭제") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", + @ApiResponse(responseCode = "200", description = "샘플 삭제 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) @DeleteMapping("{id}") @@ -130,7 +129,7 @@ public ResponseEntity deleteContract(@PathVariable Stri return ResponseEntity.ok(ContractResponseMessage.builder() .httpStatus(200) - .msg("성공") + .msg("계약서를 성공적으로 삭제하였습니다.") .result(null) .build()); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java index a3678051..2692af46 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java @@ -12,12 +12,11 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.contract.common.response.ContractResponseMessage; +import stanl_2.final_backend.domain.contract.query.dto.ContractSearchDTO; +import stanl_2.final_backend.domain.contract.query.dto.ContractSelectAllDTO; import stanl_2.final_backend.domain.contract.query.dto.ContractSeletIdDTO; import stanl_2.final_backend.domain.contract.query.service.ContractQueryService; -import java.util.HashMap; -import java.util.Map; - @Slf4j @RestController("queryContractController") @RequestMapping("/api/v1/contract") @@ -32,7 +31,7 @@ public ContractController(ContractQueryService contractQueryService) { /** * [GET] http://localhost:8080/api/v1/contract/MEM_000000001?page=0&size=10 * */ - @Operation(summary = "계약서 전체 조회 api") + @Operation(summary = "계약서 전체 조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "계약서 전채 조회 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) @@ -40,7 +39,10 @@ public ContractController(ContractQueryService contractQueryService) { @GetMapping("{memberId}") public ResponseEntity getAllContract(@PathVariable("memberId") String memberId, @PageableDefault(size = 10) Pageable pageable) { - Page> responseContracts = contractQueryService.selectAll(memberId, pageable); + + // 회원 아이디 받아 오는건 나중에 수정할 예정 + + Page responseContracts = contractQueryService.selectAll(memberId, pageable); return ResponseEntity.ok(ContractResponseMessage.builder() .httpStatus(200) @@ -52,7 +54,7 @@ public ResponseEntity getAllContract(@PathVariable("mem /** * [GET] http://localhost:8080/api/v1/contract/CON_000000001/MEM_000000001 * */ - @Operation(summary = "계약서 상세 조회 api") + @Operation(summary = "계약서 상세 조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "계약서 상세 조회 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) @@ -77,29 +79,30 @@ public ResponseEntity getDetailContract(@PathVariable(" /** 수정예정 * [GET] http://localhost:8080/api/v1/contract/search?memId=MEM_000000001 * */ - @Operation(summary = "계약서 검색 조회 api") + @Operation(summary = "계약서 검색 조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "계약서 검색 조회 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) @GetMapping("/search") - public ResponseEntity getContractBySearch(@RequestParam Map params, + public ResponseEntity getContractBySearch(@RequestParam(required = false) String memberId, + @RequestParam(required = false) String memId, + @RequestParam(required = false) String centerId, + @RequestParam(required = false) String name, + @RequestParam(required = false) String startAt, + @RequestParam(required = false) String endAt, + @RequestParam(required = false) String customerName, + @RequestParam(required = false) String customerClassifcation, + @RequestParam(required = false) String productId, + @RequestParam(required = false) String status, + @RequestParam(required = false) String companyName, + @RequestParam(required = false) String customerPurchaseCondition, @PageableDefault(size = 10) Pageable pageable) { - Map paramMap = new HashMap<>(); - paramMap.put("memberId", params.get("memberId")); - paramMap.put("centerId", params.get("centerId")); - paramMap.put("name", params.get("name")); - paramMap.put("startAt", params.get("startAt")); - paramMap.put("endAt", params.get("endAt")); - paramMap.put("customerName", params.get("customerName")); - paramMap.put("customerClassifcation", params.get("customerClassifcation")); - paramMap.put("productId", params.get("productId")); - paramMap.put("status", params.get("status")); - paramMap.put("companyName", params.get("companyName")); - paramMap.put("customerPurchaseCondition", params.get("customerPurchaseCondition")); - paramMap.put("pageable", pageable); - - Page> responseContracts = contractQueryService.selectBySearch(paramMap); + + + ContractSearchDTO contractSearchDTO = new ContractSearchDTO(memberId, memId, centerId, name, startAt, endAt, + customerName, customerClassifcation, productId, status, companyName, customerPurchaseCondition); + Page responseContracts = contractQueryService.selectBySearch(contractSearchDTO, pageable); return ResponseEntity.ok(ContractResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java index 86f9cc1f..3a936f3d 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java @@ -26,9 +26,9 @@ public class ContractSearchDTO { private Integer remainderPayment; private Integer consignmentPayment; private String delveryDate; - private String ddelveryLocationelvLoc; + private String delveryLocationLoc; private String status; - private String NumberOfVehicles; + private String numberOfVehicles; private String createdUrl; private String updatedUrl; private boolean active; @@ -36,10 +36,26 @@ public class ContractSearchDTO { private String updatedAt; private String deletedAt; private String memberId; + private String memId; private String centerId; private String customerId; private String productId; private String startAt; private String endAt; private String productName; + + public ContractSearchDTO(String memberId, String memId, String centerId, String name, String startAt, String endAt, String customerName, String customerClassifcation, String productId, String status, String companyName, String customerPurchaseCondition) { + this.memberId = memberId; + this.memId = memId; + this.centerId = centerId; + this.name = name; + this.startAt = startAt; + this.endAt = endAt; + this.customerName = customerName; + this.customerClassifcation = customerClassifcation; + this.productId = productId; + this.status = status; + this.companyName = companyName; + this.customerPurchaseCondition = customerPurchaseCondition; + } } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java index 89d81ac2..f3857623 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java @@ -1,8 +1,8 @@ package stanl_2.final_backend.domain.contract.query.repository; import org.apache.ibatis.annotations.Mapper; -import org.springframework.data.domain.Pageable; -import stanl_2.final_backend.domain.center.common.util.RequestList; +import stanl_2.final_backend.domain.contract.query.dto.ContractSearchDTO; +import stanl_2.final_backend.domain.contract.query.dto.ContractSelectAllDTO; import stanl_2.final_backend.domain.contract.query.dto.ContractSeletIdDTO; import java.util.List; @@ -12,11 +12,11 @@ public interface ContractMapper { ContractSeletIdDTO findContractByIdAndMemId(ContractSeletIdDTO contractDTO); - List> findContractBySearch(Map paramMap); + List findContractBySearch(Map map); - int findContractBySearchCount(Map paramMap); + int findContractBySearchCount(Map map); - List> findContractAllByMemId(Map params); + List findContractAllByMemId(Map map); int findContractCountByMemId(String memId); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java index 293a146c..ccc7a126 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java @@ -2,6 +2,8 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import stanl_2.final_backend.domain.contract.query.dto.ContractSearchDTO; +import stanl_2.final_backend.domain.contract.query.dto.ContractSelectAllDTO; import stanl_2.final_backend.domain.contract.query.dto.ContractSeletIdDTO; import java.util.Map; @@ -9,7 +11,7 @@ public interface ContractQueryService { ContractSeletIdDTO selectDetailContract(ContractSeletIdDTO contractDTO); - Page> selectBySearch(Map paramMap); + Page selectBySearch(ContractSearchDTO contractSearchDTO, Pageable pageable); - Page> selectAll(String memId, Pageable pageable); + Page selectAll(String memId, Pageable pageable); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java index 2fd66add..ee5055b5 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java @@ -6,9 +6,10 @@ import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; -import stanl_2.final_backend.domain.center.common.util.RequestList; import stanl_2.final_backend.domain.contract.common.exception.ContractCommonException; import stanl_2.final_backend.domain.contract.common.exception.ContractErrorCode; +import stanl_2.final_backend.domain.contract.query.dto.ContractSearchDTO; +import stanl_2.final_backend.domain.contract.query.dto.ContractSelectAllDTO; import stanl_2.final_backend.domain.contract.query.dto.ContractSeletIdDTO; import stanl_2.final_backend.domain.contract.query.repository.ContractMapper; @@ -29,14 +30,14 @@ public ContractQueryServiceImpl(ContractMapper contractMapper) { // 계약서 전체조회 @Override - public Page> selectAll(String memId, Pageable pageable) { + public Page selectAll(String memId, Pageable pageable) { Map params = new HashMap<>(); params.put("memId", memId); params.put("offset", pageable.getOffset()); params.put("pageSize", pageable.getPageSize()); - List> contractList = contractMapper.findContractAllByMemId(params); + List contractList = contractMapper.findContractAllByMemId(params); if (contractList == null || contractList.isEmpty()) { throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); @@ -61,18 +62,33 @@ public ContractSeletIdDTO selectDetailContract(ContractSeletIdDTO contractDTO) { } @Override - public Page> selectBySearch(Map paramMap) { - Pageable pageable = (Pageable) paramMap.get("pageable"); - - List> contractList = contractMapper.findContractBySearch(paramMap); - - if (contractList == null || contractList.isEmpty()) { + public Page selectBySearch(ContractSearchDTO contractSearchDTO, Pageable pageable) { + + Map map = new HashMap<>(); + map.put("memberId", contractSearchDTO.getMemberId()); + map.put("memId", contractSearchDTO.getMemId()); + map.put("centerId", contractSearchDTO.getCenterId()); + map.put("name", contractSearchDTO.getName()); + map.put("startAt", contractSearchDTO.getStartAt()); + map.put("endAt", contractSearchDTO.getEndAt()); + map.put("customerName", contractSearchDTO.getCustomerName()); + map.put("customerClassifcation", contractSearchDTO.getCustomerClassifcation()); + map.put("productId", contractSearchDTO.getProductId()); + map.put("status", contractSearchDTO.getStatus()); + map.put("companyName", contractSearchDTO.getCompanyName()); + map.put("customerPurchaseCondition", contractSearchDTO.getCustomerPurchaseCondition()); + map.put("pageSize", pageable.getPageSize()); + map.put("offset", pageable.getOffset()); + + List contracts = contractMapper.findContractBySearch(map); + + if (contracts == null || contracts.isEmpty()) { throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); } - int total = contractMapper.findContractBySearchCount(paramMap); + int total = contractMapper.findContractBySearchCount(map); - return new PageImpl<>(contractList, pageable, total); + return new PageImpl<>(contracts, pageable, total); } diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index 90e29a7d..ba8c2ebb 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -143,44 +143,39 @@ B.PROD_NAME FROM TB_CONTRACT A JOIN TB_PRODUCT B ON A.PROD_ID = B.PROD_ID - WHERE A.MEM_ID = #{memberId} - AND A.ACTIVE = TRUE - - - AND A.CENT_ID LIKE CONCAT('%', #{centerId}, '%') - - - AND A.CONR_NAME LIKE CONCAT('%', #{name}, '%') - - - AND A.CONR_CUST_NAME LIKE CONCAT('%', #{customerName}, '%') - - - AND A.CONR_CUST_CLA LIKE CONCAT('%', #{customerClassifcation}, '%') - - - AND A.CONR_STATUS = #{status} - - - AND A.CONR_COMP_NAME LIKE CONCAT('%', #{companyName}, '%') - - - AND A.CONR_CUST_PUR_COND LIKE CONCAT('%', #{customerPurchaseCondition}, '%') - - - AND A.CREATED_AT BETWEEN #{startAt} AND #{endAt} - - - AND A.CREATED_AT > #{startAt} - - - AND A.CREATED_AT < #{endAt} - - - AND B.PROD_NAME LIKE CONCAT('%', #{productName}, '%') - + + A.MEM_ID = #{memId} + AND A.ACTIVE = TRUE + + AND A.CENT_ID = #{centerId} + + + AND A.CONR_NAME LIKE CONCAT('%', #{name}, '%') + + + AND A.CONR_CUST_NAME LIKE CONCAT('%', #{customerName}, '%') + + + AND A.CONR_CUST_CLA LIKE CONCAT('%', #{customerClassifcation}, '%') + + + AND A.CONR_STAT = #{status} + + + AND A.CONR_COMP_NAME LIKE CONCAT('%', #{companyName}, '%') + + + AND A.CONR_CUST_PUR_COND LIKE CONCAT('%', #{customerPurchaseCondition}, '%') + + + AND A.CREATED_AT BETWEEN #{startAt} AND #{endAt} + + + AND B.PROD_NAME LIKE CONCAT('%', #{productId}, '%') + + ORDER BY A.CREATED_AT DESC - LIMIT #{pageable.pageSize} OFFSET #{pageable.offset} + LIMIT #{pageSize} OFFSET #{offset} SELECT COUNT(*) FROM TB_CONTRACT A - JOIN TB_PRODUCT B ON A.PROD_ID = B.PROD_ID - WHERE A.MEM_ID = #{memberId} - AND A.ACTIVE = TRUE - - - AND A.CENT_ID LIKE CONCAT('%', #{centerId}, '%') - - - AND A.CONR_NAME LIKE CONCAT('%', #{name}, '%') - - - AND A.CONR_CUST_NAME LIKE CONCAT('%', #{customerName}, '%') - - - AND A.CONR_CUST_CLA LIKE CONCAT('%', #{customerClassifcation}, '%') - - - AND A.CONR_STATUS = #{status} - - - AND A.CONR_COMP_NAME LIKE CONCAT('%', #{companyName}, '%') - - - AND A.CONR_CUST_PUR_COND LIKE CONCAT('%', #{customerPurchaseCondition}, '%') - - - AND A.CREATED_AT BETWEEN #{startAt} AND #{endAt} - - - AND A.CREATED_AT > #{startAt} - - - AND A.CREATED_AT < #{endAt} - - - AND B.PROD_NAME LIKE CONCAT('%', #{productName}, '%') - + + A.MEM_ID = #{memId} + AND A.ACTIVE = TRUE + + AND A.CENT_ID = #{centerId} + + + AND A.CONR_NAME LIKE CONCAT('%', #{name}, '%') + + + AND A.CONR_CUST_NAME LIKE CONCAT('%', #{customerName}, '%') + + + AND A.CONR_CUST_CLA LIKE CONCAT('%', #{customerClassifcation}, '%') + + + AND A.CONR_STAT = #{status} + + + AND A.CONR_COMP_NAME LIKE CONCAT('%', #{companyName}, '%') + + + AND A.CONR_CUST_PUR_COND LIKE CONCAT('%', #{customerPurchaseCondition}, '%') + + + AND A.CREATED_AT BETWEEN #{startAt} AND #{endAt} + + + AND B.PROD_NAME LIKE CONCAT('%', #{productId}, '%') + + \ No newline at end of file From 9350ccfa6f9fb3a1af02a750da371c20769a24a5 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Sun, 17 Nov 2024 07:32:23 +0900 Subject: [PATCH 084/563] =?UTF-8?q?feat:=20spring=20batch=20&=20scheduler?= =?UTF-8?q?=20=EA=B8=B0=EB=B3=B8=20=EC=84=B8=ED=8C=85=20(#57)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 ++ .../FinalBackendApplication.java | 4 + .../domain/batch/BatchConfig.java | 58 ++++++++++++ .../domain/batch/BatchScheduler.java | 38 ++++++++ src/main/resources/application.yml | 7 ++ src/main/resources/sql/ddl.sql | 90 +++++++++++++++++++ 6 files changed, 202 insertions(+) create mode 100644 src/main/java/stanl_2/final_backend/domain/batch/BatchConfig.java create mode 100644 src/main/java/stanl_2/final_backend/domain/batch/BatchScheduler.java diff --git a/build.gradle b/build.gradle index 24a2496f..7bad5c09 100644 --- a/build.gradle +++ b/build.gradle @@ -56,6 +56,11 @@ dependencies { //hibernate core implementation 'org.hibernate.orm:hibernate-core:6.5.2.Final' + // Spring batch + scheduler + implementation 'org.springframework.boot:spring-boot-starter-batch' + implementation 'org.springframework.boot:spring-boot-starter-quartz' + testImplementation 'org.springframework.batch:spring-batch-test' + } tasks.named('test') { diff --git a/src/main/java/stanl_2/final_backend/FinalBackendApplication.java b/src/main/java/stanl_2/final_backend/FinalBackendApplication.java index 5f0cb0ad..8045bf81 100644 --- a/src/main/java/stanl_2/final_backend/FinalBackendApplication.java +++ b/src/main/java/stanl_2/final_backend/FinalBackendApplication.java @@ -1,8 +1,12 @@ package stanl_2.final_backend; +import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; +@EnableScheduling +@EnableBatchProcessing @SpringBootApplication public class FinalBackendApplication { diff --git a/src/main/java/stanl_2/final_backend/domain/batch/BatchConfig.java b/src/main/java/stanl_2/final_backend/domain/batch/BatchConfig.java new file mode 100644 index 00000000..a442a27f --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/batch/BatchConfig.java @@ -0,0 +1,58 @@ +package stanl_2.final_backend.domain.batch; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.Step; +import org.springframework.batch.core.StepContribution; +import org.springframework.batch.core.configuration.annotation.JobScope; +import org.springframework.batch.core.configuration.annotation.StepScope; +import org.springframework.batch.core.job.builder.JobBuilder; +import org.springframework.batch.core.launch.support.RunIdIncrementer; +import org.springframework.batch.core.repository.JobRepository; +import org.springframework.batch.core.scope.context.ChunkContext; +import org.springframework.batch.core.step.builder.StepBuilder; +import org.springframework.batch.core.step.tasklet.Tasklet; +import org.springframework.batch.repeat.RepeatStatus; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.PlatformTransactionManager; + +@Slf4j +@Configuration +@RequiredArgsConstructor +public class BatchConfig { + + private final String JOB_NAME = "checkJob"; + private final String STEP_NAME = "checkStep"; + + + @Bean + public Job checkJob(JobRepository jobRepository, PlatformTransactionManager transactionManager){ + return new JobBuilder(JOB_NAME, jobRepository) + .incrementer(new RunIdIncrementer()) + .start(checkStep(jobRepository, transactionManager)) + .build(); + } + + @Bean + @JobScope + public Step checkStep(JobRepository jobRepository, PlatformTransactionManager transactionManager){ + return new StepBuilder(STEP_NAME, jobRepository) + .tasklet(checkTasklet(), transactionManager) + .build(); + } + + @Bean + @StepScope + public Tasklet checkTasklet(){ + return new Tasklet(){ + @Override + public RepeatStatus execute(StepContribution contribution, ChunkContext context) throws Exception { + log.info("Spring batch check Suceess"); + // 원하는 비지니스 모델 추가 + return RepeatStatus.FINISHED; + } + }; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/batch/BatchScheduler.java b/src/main/java/stanl_2/final_backend/domain/batch/BatchScheduler.java new file mode 100644 index 00000000..7cba370c --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/batch/BatchScheduler.java @@ -0,0 +1,38 @@ +package stanl_2.final_backend.domain.batch; + +import org.springframework.batch.core.*; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; +import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; +import org.springframework.batch.core.repository.JobRestartException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.Collections; + +@Component +public class BatchScheduler { + + private final JobLauncher jobLauncher; + private final Job checkJob; + + @Autowired + public BatchScheduler(JobLauncher jobLauncher, Job checkJob) { + this.jobLauncher = jobLauncher; + this.checkJob = checkJob; + } + + + @Scheduled(cron = "0 03 00 * * ?", zone = "Asia/Seoul") + public void testJobRun() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, + JobParametersInvalidException, JobRestartException { + + JobParameters jobParameters = new JobParameters( + Collections.singletonMap("requestTime", new JobParameter(System.currentTimeMillis(), Long.class)) + ); + + jobLauncher.run(checkJob, jobParameters); + } +} +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a6f2cd66..8fdf6a6b 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -27,6 +27,13 @@ spring: properties: hibernate.format_sql: true + batch: + jdbc: + initialize-schema: always + job: + name: checkJob + enabled: false + jwt: secret-key: ${SECRET_KEY} secret-default-value: ${JWT_SECRET_DEFAULT_VALUE} diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index 7f477d32..f1b89a7a 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -397,6 +397,96 @@ CREATE TABLE tb_sales_history CONR_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고' ); +# spring batch & scheduler 테이블 +CREATE TABLE BATCH_JOB_INSTANCE +( + JOB_INSTANCE_ID BIGINT NOT NULL PRIMARY KEY, + VERSION BIGINT, + JOB_NAME VARCHAR(100) NOT NULL, + JOB_KEY VARCHAR(32) NOT NULL, + constraint JOB_INST_UN unique (JOB_NAME, JOB_KEY) +) ENGINE = InnoDB; + +CREATE TABLE BATCH_JOB_EXECUTION +( + JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY, + VERSION BIGINT, + JOB_INSTANCE_ID BIGINT NOT NULL, + CREATE_TIME DATETIME(6) NOT NULL, + START_TIME DATETIME(6) DEFAULT NULL, + END_TIME DATETIME(6) DEFAULT NULL, + STATUS VARCHAR(10), + EXIT_CODE VARCHAR(2500), + EXIT_MESSAGE VARCHAR(2500), + LAST_UPDATED DATETIME(6), + constraint JOB_INST_EXEC_FK foreign key (JOB_INSTANCE_ID) + references BATCH_JOB_INSTANCE (JOB_INSTANCE_ID) +) ENGINE = InnoDB; + +CREATE TABLE BATCH_JOB_EXECUTION_PARAMS +( + JOB_EXECUTION_ID BIGINT NOT NULL, + PARAMETER_NAME VARCHAR(100) NOT NULL, + PARAMETER_TYPE VARCHAR(100) NOT NULL, + PARAMETER_VALUE VARCHAR(2500), + IDENTIFYING CHAR(1) NOT NULL, + constraint JOB_EXEC_PARAMS_FK foreign key (JOB_EXECUTION_ID) + references BATCH_JOB_EXECUTION (JOB_EXECUTION_ID) +) ENGINE = InnoDB; + +CREATE TABLE BATCH_STEP_EXECUTION +( + STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY, + VERSION BIGINT NOT NULL, + STEP_NAME VARCHAR(100) NOT NULL, + JOB_EXECUTION_ID BIGINT NOT NULL, + CREATE_TIME DATETIME(6) NOT NULL, + START_TIME DATETIME(6) DEFAULT NULL, + END_TIME DATETIME(6) DEFAULT NULL, + STATUS VARCHAR(10), + COMMIT_COUNT BIGINT, + READ_COUNT BIGINT, + FILTER_COUNT BIGINT, + WRITE_COUNT BIGINT, + READ_SKIP_COUNT BIGINT, + WRITE_SKIP_COUNT BIGINT, + PROCESS_SKIP_COUNT BIGINT, + ROLLBACK_COUNT BIGINT, + EXIT_CODE VARCHAR(2500), + EXIT_MESSAGE VARCHAR(2500), + LAST_UPDATED DATETIME(6), + constraint JOB_EXEC_STEP_FK foreign key (JOB_EXECUTION_ID) + references BATCH_JOB_EXECUTION (JOB_EXECUTION_ID) +) ENGINE = InnoDB; + +CREATE TABLE BATCH_STEP_EXECUTION_CONTEXT +( + STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY, + SHORT_CONTEXT VARCHAR(2500) NOT NULL, + SERIALIZED_CONTEXT TEXT, + constraint STEP_EXEC_CTX_FK foreign key (STEP_EXECUTION_ID) + references BATCH_STEP_EXECUTION (STEP_EXECUTION_ID) +) ENGINE = InnoDB; + +CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT +( + JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY, + SHORT_CONTEXT VARCHAR(2500) NOT NULL, + SERIALIZED_CONTEXT TEXT, + constraint JOB_EXEC_CTX_FK foreign key (JOB_EXECUTION_ID) + references BATCH_JOB_EXECUTION (JOB_EXECUTION_ID) +) ENGINE = InnoDB; + +CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ START WITH 1 MINVALUE 1 MAXVALUE 9223372036854775806 + INCREMENT BY 1 NOCACHE NOCYCLE ENGINE =InnoDB; + +CREATE SEQUENCE BATCH_JOB_EXECUTION_SEQ START WITH 1 MINVALUE 1 MAXVALUE 9223372036854775806 + INCREMENT BY 1 NOCACHE NOCYCLE ENGINE =InnoDB; + +CREATE SEQUENCE BATCH_JOB_SEQ START WITH 1 MINVALUE 1 MAXVALUE 9223372036854775806 + INCREMENT BY 1 NOCACHE NOCYCLE ENGINE =InnoDB; + + INSERT INTO tb_organization_chart (ORG_CHA_ID, ORG_CHA_NAME, ORG_CHA_DEPTH) VALUES ('ORG_000000001', '본사', 1), ('ORG_000000002', '서울지사', 2), From 3155d565033deefb15d2c758b54c389054a07507 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Sun, 17 Nov 2024 07:33:05 +0900 Subject: [PATCH 085/563] =?UTF-8?q?feat:=20=EC=88=98=EC=A0=95=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=A0=81=EC=9A=A9=20(#57)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/stanl_2/final_backend/domain/batch/BatchScheduler.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/batch/BatchScheduler.java b/src/main/java/stanl_2/final_backend/domain/batch/BatchScheduler.java index 7cba370c..1f75dd06 100644 --- a/src/main/java/stanl_2/final_backend/domain/batch/BatchScheduler.java +++ b/src/main/java/stanl_2/final_backend/domain/batch/BatchScheduler.java @@ -35,4 +35,3 @@ public void testJobRun() throws JobInstanceAlreadyCompleteException, JobExecutio jobLauncher.run(checkJob, jobParameters); } } -} From 24d977483ad49772389b75d357bd6cd3a58c5bb4 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Sun, 17 Nov 2024 09:33:06 +0900 Subject: [PATCH 086/563] =?UTF-8?q?fix:=20pr=20=EA=B3=A0=EA=B0=9DId=20null?= =?UTF-8?q?able=3Dfalse=20=EC=88=98=EC=A0=95(#21)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../contract/command/domain/aggregate/entity/Contract.java | 2 +- .../command/domain/service/ContractCommandServiceImpl.java | 1 + .../contract/query/service/ContractQueryServiceImpl.java | 5 +++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java index 11965457..450cedad 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java @@ -118,7 +118,7 @@ public class Contract { @Column(name = "CENT_ID", nullable = false) private String centerId; - @Column(name = "CUST_ID") + @Column(name = "CUST_ID", nullable = false) private String customerId; @Column(name = "PROD_ID", nullable = false) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index 1b391999..641b8e34 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -44,6 +44,7 @@ public void registerContract(ContractRegistDTO contractRegistRequestDTO) { contract.setCenterId("CEN_000000001"); // 회원의 매장번호 넣기 contract.setProductId("PRO_000000001"); // 제품 번호 넣기 + contract.setCustomerId("CUS_000000001"); // 제품 번호 넣기 contractRepository.save(contract); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java index ee5055b5..9470dfe9 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java @@ -1,6 +1,7 @@ package stanl_2.final_backend.domain.contract.query.service; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringEscapeUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; @@ -58,6 +59,10 @@ public ContractSeletIdDTO selectDetailContract(ContractSeletIdDTO contractDTO) { throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); } + // 이스케이프된 HTML 제거 + String unescapedHtml = StringEscapeUtils.unescapeJson(responseContract.getCreatedUrl()); + responseContract.setCreatedUrl(unescapedHtml); + return responseContract; } From 5991113286ed84e9ccbb2b89d7ea2d3b70803770 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Sun, 17 Nov 2024 15:05:27 +0900 Subject: [PATCH 087/563] =?UTF-8?q?promotion=20=EC=97=94=ED=8B=B0=ED=8B=B0?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20(#61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/dto/PromotionModifyDTO.java | 19 +++++ .../application/dto/PromotionRegistDTO.java | 19 +++++ .../domain/aggregate/entity/Promotion.java | 70 +++++++++++++++++++ .../repository/PromotionRepository.java | 7 ++ 4 files changed, 115 insertions(+) create mode 100644 src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionRegistDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/entity/Promotion.java create mode 100644 src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/repository/PromotionRepository.java diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java new file mode 100644 index 00000000..6695a736 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java @@ -0,0 +1,19 @@ +package stanl_2.final_backend.domain.promotion.command.application.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class PromotionModifyDTO { + + private String title; + + private String content; + + private String memberId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionRegistDTO.java new file mode 100644 index 00000000..bd487135 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionRegistDTO.java @@ -0,0 +1,19 @@ +package stanl_2.final_backend.domain.promotion.command.application.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class PromotionRegistDTO { + + private String title; + + private String content; + + private String memberId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/entity/Promotion.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/entity/Promotion.java new file mode 100644 index 00000000..8a73a8e4 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/entity/Promotion.java @@ -0,0 +1,70 @@ +package stanl_2.final_backend.domain.promotion.command.domain.aggregate.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@Entity +@Table(name="TB_PROMOTION") +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class Promotion { + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name="prefix", value = "NOT") + ) + @Column(name = "PROM_ID") + private String promotionId; + + @Column(name = "PROM_TTL", nullable = false) + private String title; + + @Column(name = "PROM_CONT", nullable = false) + private String content; + + @Column(name = "CREATED_AT", nullable = false, updatable = false, length=19) + private String createdAt; + + @Column(name = "UPDATED_AT", nullable = false, length=19) + private String updatedAt; + + @Column(name = "DELETED_AT", length=19) + private String deletedAt; + + @Column(name = "ACTIVE", nullable = false) + private Boolean active = true; + + @Column(name = "MEM_ID", nullable = false) + private String memberId; + + @PrePersist + private void prePersist() { + this.createdAt = getCurrentTime(); + this.updatedAt = this.createdAt; + } + + // Update 되기 전에 실행 + @PreUpdate + private void preUpdate() { + this.updatedAt = getCurrentTime(); + } + + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } + + +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/repository/PromotionRepository.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/repository/PromotionRepository.java new file mode 100644 index 00000000..f1256a0a --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/repository/PromotionRepository.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.promotion.command.domain.aggregate.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import stanl_2.final_backend.domain.promotion.command.domain.aggregate.entity.Promotion; + +public interface PromotionRepository extends JpaRepository { +} From 40b6cb207993cb5300b9f47c0843ef2ae5634c77 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Sun, 17 Nov 2024 15:09:16 +0900 Subject: [PATCH 088/563] =?UTF-8?q?fix:=20=ED=98=84=EC=9E=AC=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EB=B6=88=EB=9F=AC=EC=98=A4=EB=8A=94=20?= =?UTF-8?q?=EB=B0=A9=EB=B2=95=20=EC=88=98=EC=A0=95(#41)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/MemberController.java | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java index 4844e35d..d31589e1 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java @@ -11,6 +11,8 @@ import stanl_2.final_backend.domain.member.command.application.service.MemberCommandService; import stanl_2.final_backend.domain.member.common.response.MemberResponseMessage; +import java.security.Principal; + @Slf4j @RestController("commandMemberController") @RequestMapping("/api/v1/member") @@ -23,23 +25,37 @@ public MemberController(MemberCommandService memberCommandService) { this.memberCommandService = memberCommandService; } +// @GetMapping("/authorities") +// public ResponseEntity check(Authentication authentication) { +// if (authentication == null || !authentication.isAuthenticated() || "anonymousUser".equals(authentication.getName())) { +// return ResponseEntity.status(HttpStatus.UNAUTHORIZED) +// .body(MemberResponseMessage.builder() +// .httpStatus(401) +// .msg("Unauthorized") +// .build()); +// } +// +// // 인증된 사용자 정보 출력 +// log.info("인증된 사용자: {}", authentication.getName()); +// +// return ResponseEntity.ok(MemberResponseMessage.builder() +// .httpStatus(200) +// .msg("성공") +// .result("인증된 사용자: " + authentication.getName()) +// .build()); +// } + + @GetMapping("/authorities") - public ResponseEntity check(Authentication authentication) { - if (authentication == null || !authentication.isAuthenticated() || "anonymousUser".equals(authentication.getName())) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(MemberResponseMessage.builder() - .httpStatus(401) - .msg("Unauthorized") - .build()); - } + public ResponseEntity check(Principal principal) { // 인증된 사용자 정보 출력 - log.info("인증된 사용자: {}", authentication.getName()); + log.info("인증된 사용자: {}", principal.getName()); return ResponseEntity.ok(MemberResponseMessage.builder() .httpStatus(200) .msg("성공") - .result("인증된 사용자: " + authentication.getName()) + .result("인증된 사용자: " + principal.getName()) .build()); } From 8686c608f5c409245436f2575cf0dc7d32107eca Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Sun, 17 Nov 2024 16:01:22 +0900 Subject: [PATCH 089/563] =?UTF-8?q?feat=20promotion=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0id=20=EA=B0=92=20=EC=88=98=EC=A0=95=20(#61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../promotion/command/domain/aggregate/entity/Promotion.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/entity/Promotion.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/entity/Promotion.java index 8a73a8e4..2554695c 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/entity/Promotion.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/entity/Promotion.java @@ -23,7 +23,7 @@ public class Promotion { @GeneratedValue(generator = "PrefixGeneratorConfig") @GenericGenerator(name = "PrefixGeneratorConfig", type = PrefixGeneratorConfig.class, - parameters = @org.hibernate.annotations.Parameter(name="prefix", value = "NOT") + parameters = @org.hibernate.annotations.Parameter(name="prefix", value = "PROM") ) @Column(name = "PROM_ID") private String promotionId; From ce1a4fc381871a5f32d5503b752fae056c0a47c9 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Sun, 17 Nov 2024 16:03:40 +0900 Subject: [PATCH 090/563] =?UTF-8?q?feat:=20promotion=20common=ED=8C=A8?= =?UTF-8?q?=ED=82=A4=EC=A7=80=20=EC=84=B8=ED=8C=85=20(#61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/PromotionCommonException.java | 16 ++++++ .../common/exception/PromotionErrorCode.java | 51 +++++++++++++++++++ .../exception/PromotionExceptionResponse.java | 4 ++ .../response/PromotionResponseMessage.java | 14 +++++ .../promotion/common/util/RequestList.java | 14 +++++ 5 files changed, 99 insertions(+) create mode 100644 src/main/java/stanl_2/final_backend/domain/promotion/common/exception/PromotionCommonException.java create mode 100644 src/main/java/stanl_2/final_backend/domain/promotion/common/exception/PromotionErrorCode.java create mode 100644 src/main/java/stanl_2/final_backend/domain/promotion/common/exception/PromotionExceptionResponse.java create mode 100644 src/main/java/stanl_2/final_backend/domain/promotion/common/response/PromotionResponseMessage.java create mode 100644 src/main/java/stanl_2/final_backend/domain/promotion/common/util/RequestList.java diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/common/exception/PromotionCommonException.java b/src/main/java/stanl_2/final_backend/domain/promotion/common/exception/PromotionCommonException.java new file mode 100644 index 00000000..e4f79a35 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/promotion/common/exception/PromotionCommonException.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.promotion.common.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class PromotionCommonException extends RuntimeException { + private final PromotionErrorCode promotionErrorCode; + + // 에러 발생시 ErroCode 별 메시지 + @Override + public String getMessage() { + return this.promotionErrorCode.getMsg(); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/common/exception/PromotionErrorCode.java b/src/main/java/stanl_2/final_backend/domain/promotion/common/exception/PromotionErrorCode.java new file mode 100644 index 00000000..2e72fdf9 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/promotion/common/exception/PromotionErrorCode.java @@ -0,0 +1,51 @@ +package stanl_2.final_backend.domain.promotion.common.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum PromotionErrorCode { + + /** + * 400(Bad Request) + * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. + */ + + + + /** + * 401(Unauthorized) + * 비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만, + * 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다. + * 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다. + */ + + + /** + * 403(Forbidden) + * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. + * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다. + */ + + + + /** + * 404(Not Found) + * 서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다. + * 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있습니다. + * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. + * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. + */ + PROMOTION_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "promotion 데이터를 찾지 못했습니다"), + /** + * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. + */ + INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); + + + private final Integer code; + private final HttpStatus httpStatus; + private final String msg; +} diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/common/exception/PromotionExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/promotion/common/exception/PromotionExceptionResponse.java new file mode 100644 index 00000000..2fc8f780 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/promotion/common/exception/PromotionExceptionResponse.java @@ -0,0 +1,4 @@ +package stanl_2.final_backend.domain.promotion.common.exception; + +public class PromotionExceptionResponse { +} diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/common/response/PromotionResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/promotion/common/response/PromotionResponseMessage.java new file mode 100644 index 00000000..30b3106d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/promotion/common/response/PromotionResponseMessage.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.promotion.common.response; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class PromotionResponseMessage { + private int httpStatus; + private String msg; + private Object result; +} diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/common/util/RequestList.java b/src/main/java/stanl_2/final_backend/domain/promotion/common/util/RequestList.java new file mode 100644 index 00000000..a3d83a2c --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/promotion/common/util/RequestList.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.promotion.common.util; + +import lombok.*; +import org.springframework.data.domain.Pageable; + +@Builder +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class RequestList { + private T data; + private Pageable pageable; +} \ No newline at end of file From 26fbe3c9b2ec68548147ce69b78a4f7c17226b6f Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Sun, 17 Nov 2024 16:04:04 +0900 Subject: [PATCH 091/563] =?UTF-8?q?feat:=20promotion=20CUD=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PromotionController.java | 75 ++++++++++++++++++ .../application/dto/PromotionModifyDTO.java | 1 - .../service/PromotionCommandService.java | 13 ++++ .../service/PromotionServiceImpl.java | 77 +++++++++++++++++++ 4 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/promotion/command/application/service/PromotionCommandService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java new file mode 100644 index 00000000..257e3822 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java @@ -0,0 +1,75 @@ +package stanl_2.final_backend.domain.promotion.command.application.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionModifyDTO; +import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionRegistDTO; +import stanl_2.final_backend.domain.promotion.command.application.service.PromotionCommandService; +import stanl_2.final_backend.domain.promotion.common.response.PromotionResponseMessage; + +@RestController("commandPromotionController") +@RequestMapping("/api/v1/promotion") +public class PromotionController { + private final PromotionCommandService promotionCommandService; + + @Autowired + public PromotionController(PromotionCommandService promotionCommandService) { + this.promotionCommandService = promotionCommandService; + } + + @Operation(summary = "프로모션 작성") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = PromotionResponseMessage.class))}) + }) + @PostMapping("") + public ResponseEntity postNotice(@RequestBody PromotionRegistDTO prmotionRegistDTO){ + promotionCommandService.registerPromotion(prmotionRegistDTO); + return ResponseEntity.ok(PromotionResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + + } + @Operation(summary = "프로모션 수정") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = PromotionResponseMessage.class))}) + }) + @PutMapping("{promotionId}") + public ResponseEntity modifyNotice(@PathVariable String promotionId, + @RequestBody PromotionModifyDTO promotionModifyRequestDTO){ + + PromotionModifyDTO promotionModifyDTO = promotionCommandService.modifyPromotion(promotionId,promotionModifyRequestDTO); + + return ResponseEntity.ok(PromotionResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(promotionModifyDTO) + .build()); + } + + @Operation(summary = "프로모션 삭제") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = PromotionResponseMessage.class))}) + }) + @DeleteMapping("{promotionId}") + public ResponseEntity deleteNotice(@PathVariable String promotionId) { + + promotionCommandService.deletePromotion(promotionId); + + return ResponseEntity.ok(PromotionResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java index 6695a736..a6c5cdd6 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java @@ -15,5 +15,4 @@ public class PromotionModifyDTO { private String content; - private String memberId; } diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/service/PromotionCommandService.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/service/PromotionCommandService.java new file mode 100644 index 00000000..46d6e76b --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/service/PromotionCommandService.java @@ -0,0 +1,13 @@ +package stanl_2.final_backend.domain.promotion.command.application.service; + +import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionModifyDTO; +import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionRegistDTO; + +public interface PromotionCommandService { + + void registerPromotion(PromotionRegistDTO promotionRegistDTO); + + PromotionModifyDTO modifyPromotion(String promotionId, PromotionModifyDTO promotionModifyDTO); + + void deletePromotion(String promotionId); +} diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java new file mode 100644 index 00000000..02e405b5 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java @@ -0,0 +1,77 @@ +package stanl_2.final_backend.domain.promotion.command.domain.aggregate.service; + +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.notices.command.domain.aggragate.entity.Notice; +import stanl_2.final_backend.domain.notices.common.exception.NoticeCommonException; +import stanl_2.final_backend.domain.notices.common.exception.NoticeErrorCode; +import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionModifyDTO; +import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionRegistDTO; +import stanl_2.final_backend.domain.promotion.command.application.service.PromotionCommandService; +import stanl_2.final_backend.domain.promotion.command.domain.aggregate.entity.Promotion; +import stanl_2.final_backend.domain.promotion.command.domain.aggregate.repository.PromotionRepository; +import stanl_2.final_backend.domain.promotion.common.exception.PromotionCommonException; +import stanl_2.final_backend.domain.promotion.common.exception.PromotionErrorCode; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@Service("commandPromotionService") +public class PromotionServiceImpl implements PromotionCommandService { + + private final PromotionRepository promotionRepository; + + private final ModelMapper modelMapper; + + @Autowired + public PromotionServiceImpl(PromotionRepository promotionRepository, ModelMapper modelMapper) { + this.promotionRepository = promotionRepository; + this.modelMapper = modelMapper; + } + private String getCurrentTimestamp() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } + + @Transactional + @Override + public void registerPromotion(PromotionRegistDTO promotionRegistDTO) { + Promotion promotion =modelMapper.map(promotionRegistDTO,Promotion.class); + promotionRepository.save(promotion); + } + + @Transactional + @Override + public PromotionModifyDTO modifyPromotion(String promotionId, PromotionModifyDTO promotionModifyDTO) { + Promotion promotion = promotionRepository.findById(promotionId) + .orElseThrow(() -> new PromotionCommonException(PromotionErrorCode.PROMOTION_NOT_FOUND)); + + Promotion updatePromotion = modelMapper.map(promotionModifyDTO, Promotion.class); + updatePromotion.setPromotionId(promotion.getPromotionId()); + updatePromotion.setMemberId(promotion.getMemberId()); + updatePromotion.setCreatedAt(promotion.getCreatedAt()); + updatePromotion.setActive(promotion.getActive()); + + promotionRepository.save(updatePromotion); + + PromotionModifyDTO promotionModify = modelMapper.map(updatePromotion,PromotionModifyDTO.class); + + return promotionModify; + } + + @Override + public void deletePromotion(String promotionId) { + + Promotion promotion = promotionRepository.findById(promotionId) + .orElseThrow(()-> new PromotionCommonException(PromotionErrorCode.PROMOTION_NOT_FOUND)); + + promotion.setActive(false); + promotion.setDeletedAt(getCurrentTimestamp()); + + promotionRepository.save(promotion); + + } +} From ce30f7f633244dbbae8f3964d0ef29a44378dd83 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Sun, 17 Nov 2024 16:12:41 +0900 Subject: [PATCH 092/563] =?UTF-8?q?fix:=20ResponseMessage=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=88=98=EC=A0=95=EC=82=AC=ED=95=AD=20(#26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/application/controller/NoticeController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java index 63a192d9..a19dc10a 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java @@ -43,7 +43,7 @@ public ResponseEntity postNotice(@RequestBody NoticeRegis @Operation(summary = "공지사항 수정") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = NoticeResponseMessage.class))}) }) @PutMapping("{id}") public ResponseEntity modifyNotice(@PathVariable String id, From 5cdd4ce2fd0228ac2fb6aa69aea3cfc5019bd1e9 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Sun, 17 Nov 2024 16:14:43 +0900 Subject: [PATCH 093/563] =?UTF-8?q?fix:=20=EB=A6=AC=EB=B7=B0=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81(#62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/domain/aggregate/entity/Promotion.java | 6 +++--- .../domain/aggregate/service/PromotionServiceImpl.java | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/entity/Promotion.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/entity/Promotion.java index 2554695c..eb0adbb3 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/entity/Promotion.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/entity/Promotion.java @@ -25,13 +25,13 @@ public class Promotion { type = PrefixGeneratorConfig.class, parameters = @org.hibernate.annotations.Parameter(name="prefix", value = "PROM") ) - @Column(name = "PROM_ID") + @Column(name = "PRM_ID") private String promotionId; - @Column(name = "PROM_TTL", nullable = false) + @Column(name = "PRM_TTL", nullable = false) private String title; - @Column(name = "PROM_CONT", nullable = false) + @Column(name = "PRM_CONT", nullable = false) private String content; @Column(name = "CREATED_AT", nullable = false, updatable = false, length=19) diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java index 02e405b5..d1e192de 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java @@ -62,6 +62,7 @@ public PromotionModifyDTO modifyPromotion(String promotionId, PromotionModifyDTO return promotionModify; } + @Transactional @Override public void deletePromotion(String promotionId) { From e80c333041baaee134914b14c2ed54b90a60a9dc Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Sun, 17 Nov 2024 17:13:51 +0900 Subject: [PATCH 094/563] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=A1=B0=ED=9A=8C(#60)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/application/dto/GrantDTO.java | 1 - .../command/application/dto/RefreshDTO.java | 1 - .../application/dto/SigninRequestDTO.java | 1 - .../application/dto/SigninResponseDTO.java | 1 - .../command/application/dto/SignupDTO.java | 8 +-- .../domain/aggregate/entity/Member.java | 13 +++-- .../service/AuthCommandServiceImpl.java | 4 +- .../query/config/MybatisConfiguration.java | 9 ++++ .../query/controller/MemberController.java | 53 +++++++++++++++++++ .../dto/{SignupDTO.java => MemberDTO.java} | 14 ++--- .../member/query/repository/MemberMapper.java | 10 ++++ .../query/service/MemberQueryService.java | 9 ++++ .../query/service/MemberQueryServiceImpl.java | 48 +++++++++++++++++ .../member/query/repository/MemberMapper.xml | 47 ++++++++++++++++ 14 files changed, 200 insertions(+), 19 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/config/MybatisConfiguration.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java rename src/main/java/stanl_2/final_backend/domain/member/query/dto/{SignupDTO.java => MemberDTO.java} (71%) create mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java create mode 100644 src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/GrantDTO.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/GrantDTO.java index b15b51cb..80f9767d 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/GrantDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/GrantDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Setter @Getter -@ToString public class GrantDTO { private String memberId; private String loginId; diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/RefreshDTO.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/RefreshDTO.java index 1f153a87..b3a55f69 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/RefreshDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/RefreshDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Setter @Getter -@ToString @Builder public class RefreshDTO { private String refreshToken; diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninRequestDTO.java index 230ec928..f83b52f7 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninRequestDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Setter @Getter -@ToString public class SigninRequestDTO { private String loginId; private String password; diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninResponseDTO.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninResponseDTO.java index bbde843d..085cd36c 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninResponseDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninResponseDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Setter @Getter -@ToString public class SigninResponseDTO { private String accessToken; private String refreshToken; diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java index fd2e8078..e31d1aad 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java @@ -1,12 +1,12 @@ package stanl_2.final_backend.domain.member.command.application.dto; +import jakarta.persistence.Column; import lombok.*; @AllArgsConstructor @NoArgsConstructor @Setter @Getter -@ToString public class SignupDTO { private String loginId; private String password; @@ -14,9 +14,9 @@ public class SignupDTO { private String email; private Integer age; private String sex; - private String idenNo; + private String identNo; private String phone; - private String emergPhone; + private String emergePhone; private String address; private String note; private String position; @@ -25,4 +25,6 @@ public class SignupDTO { private String military; private String bankName; private String account; + private String centerId; + private String organizationId; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java index 5b34e930..a4e843f1 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java @@ -50,13 +50,13 @@ public class Member { private String sex; @Column(name = "MEM_IDEN_NO", nullable = false) - private String idenNo; + private String identNo; - @Column(name = "MEM_MEM_PHO", nullable = false) + @Column(name = "MEM_PHO", nullable = false) private String phone; @Column(name = "MEM_EMER_PHO") - private String emergPhone; + private String emergePhone; @Column(name = "MEM_ADR", nullable = false) private String address; @@ -94,10 +94,17 @@ public class Member { @Column(name = "ACTIVE", nullable = false) private Boolean active = true; + @Column(name = "CENTER_ID", nullable = false) + private String centerId; + + @Column(name = "ORG_CHA_ID", nullable = false) + private String organizationId; + @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinColumn(name = "MEM_ID") private List roles = new ArrayList<>(); + /* 설명. updatedAt 자동화 */ // Insert 되기 전에 실행 @PrePersist diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java index 16019cd0..66dbf2c0 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java @@ -80,9 +80,9 @@ public void signup(SignupDTO signupDTO) throws GeneralSecurityException { signupDTO.setPassword(hashPwd); signupDTO.setName(aesUtils.encrypt(signupDTO.getName())); signupDTO.setEmail(aesUtils.encrypt(signupDTO.getEmail())); - signupDTO.setIdenNo(aesUtils.encrypt(signupDTO.getIdenNo())); + signupDTO.setIdentNo(aesUtils.encrypt(signupDTO.getIdentNo())); signupDTO.setPhone(aesUtils.encrypt(signupDTO.getPhone())); - signupDTO.setEmergPhone(aesUtils.encrypt(signupDTO.getEmergPhone())); + signupDTO.setEmergePhone(aesUtils.encrypt(signupDTO.getEmergePhone())); signupDTO.setAddress(aesUtils.encrypt(signupDTO.getAddress())); signupDTO.setBankName(aesUtils.encrypt(signupDTO.getBankName())); signupDTO.setAccount(aesUtils.encrypt(signupDTO.getAccount())); diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/config/MybatisConfiguration.java b/src/main/java/stanl_2/final_backend/domain/member/query/config/MybatisConfiguration.java new file mode 100644 index 00000000..f9a031c9 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/query/config/MybatisConfiguration.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.member.query.config; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Configuration; + +@Configuration("memberMybatisConfiguration") +@MapperScan(basePackages = "stanl_2.final_backend.domain.member.query.repository") +public class MybatisConfiguration { +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java b/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java new file mode 100644 index 00000000..13d4d447 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java @@ -0,0 +1,53 @@ +package stanl_2.final_backend.domain.member.query.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; +import stanl_2.final_backend.domain.member.common.response.MemberResponseMessage; +import stanl_2.final_backend.domain.member.query.dto.MemberDTO; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; + +import java.security.GeneralSecurityException; +import java.security.Principal; + +@RestController(value = "queryMemberController") +@RequestMapping("/api/v1/member") +public class MemberController { + + private final MemberQueryService memberQueryService; + + @Autowired + public MemberController(MemberQueryService memberQueryService) { + this.memberQueryService = memberQueryService; + } + + @Operation(summary = "회원 정보 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("") + public ResponseEntity getMemberInfo(Principal principal) throws GeneralSecurityException { + + MemberDTO memberInfo = memberQueryService.selectMemberInfo(principal.getName()); + + return ResponseEntity.ok(MemberResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(memberInfo) + .build()); + + } + + +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/dto/SignupDTO.java b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java similarity index 71% rename from src/main/java/stanl_2/final_backend/domain/member/query/dto/SignupDTO.java rename to src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java index d918f85b..7fe73930 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/dto/SignupDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java @@ -1,23 +1,23 @@ package stanl_2.final_backend.domain.member.query.dto; -import lombok.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; @AllArgsConstructor @NoArgsConstructor @Setter @Getter -@ToString -public class SignupDTO { - private String id; +public class MemberDTO { private String loginId; - private String password; private String name; private String email; private Integer age; private String sex; - private String idenNo; + private String identNo; private String phone; - private String emergPhone; + private String emergePhone; private String address; private String note; private String position; diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java b/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java new file mode 100644 index 00000000..3cc8a550 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java @@ -0,0 +1,10 @@ +package stanl_2.final_backend.domain.member.query.repository; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import stanl_2.final_backend.domain.member.query.dto.MemberDTO; + +@Mapper +public interface MemberMapper { + MemberDTO findMemberInfoById(@Param("loginId") String loginId); +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java new file mode 100644 index 00000000..3ed51d09 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.member.query.service; + +import stanl_2.final_backend.domain.member.query.dto.MemberDTO; + +import java.security.GeneralSecurityException; + +public interface MemberQueryService { + MemberDTO selectMemberInfo(String name) throws GeneralSecurityException; +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java new file mode 100644 index 00000000..670f9c20 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -0,0 +1,48 @@ +package stanl_2.final_backend.domain.member.query.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.member.common.exception.MemberCommonException; +import stanl_2.final_backend.domain.member.common.exception.MemberErrorCode; +import stanl_2.final_backend.domain.member.query.dto.MemberDTO; +import stanl_2.final_backend.domain.member.query.repository.MemberMapper; +import stanl_2.final_backend.global.utils.AESUtils; + +import java.security.GeneralSecurityException; + +@Service(value = "queryMemberService") +public class MemberQueryServiceImpl implements MemberQueryService { + + private final MemberMapper memberMapper; + private final AESUtils aesUtils; + + @Autowired + public MemberQueryServiceImpl(MemberMapper memberMapper, + AESUtils aesUtils) { + this.memberMapper = memberMapper; + this.aesUtils = aesUtils; + } + + @Override + @Transactional + public MemberDTO selectMemberInfo(String name) throws GeneralSecurityException { + + MemberDTO memberInfo = memberMapper.findMemberInfoById(name); + + if(memberInfo == null){ + throw new MemberCommonException(MemberErrorCode.MEMBER_NOT_FOUND); + } + + memberInfo.setName(aesUtils.decrypt(memberInfo.getName())); + memberInfo.setEmail(aesUtils.decrypt(memberInfo.getEmail())); + memberInfo.setIdentNo(aesUtils.decrypt(memberInfo.getIdentNo())); + memberInfo.setPhone(aesUtils.decrypt(memberInfo.getPhone())); + memberInfo.setEmergePhone(aesUtils.decrypt(memberInfo.getEmergePhone())); + memberInfo.setAddress(aesUtils.decrypt(memberInfo.getAddress())); + memberInfo.setBankName(aesUtils.decrypt(memberInfo.getBankName())); + memberInfo.setAccount(aesUtils.decrypt(memberInfo.getAccount())); + + return memberInfo; + } +} diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml new file mode 100644 index 00000000..8b2632d0 --- /dev/null +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 0da47426ac3201e09e764ed3b94b8deb9f57c184 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Sun, 17 Nov 2024 18:33:16 +0900 Subject: [PATCH 095/563] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EC=A0=95=EB=B3=B4=20=EB=93=B1=EB=A1=9D(#60)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/MemberController.java | 32 ++++- .../dto/MemberDetailRegisterDTO.java | 33 +++++ .../service/MemberCommandService.java | 5 + .../domain/aggregate/entity/MemberDetail.java | 121 ++++++++++++++++++ .../repository/MemberDetailRepository.java | 9 ++ .../service/MemberCommandServiceImpl.java | 36 +++++- .../query/controller/MemberController.java | 20 ++- .../member/query/dto/MemberDetailDTO.java | 34 +++++ .../member/query/repository/MemberMapper.java | 3 + .../query/service/MemberQueryService.java | 3 + .../query/service/MemberQueryServiceImpl.java | 9 ++ .../final_backend/global/utils/AESUtils.java | 10 ++ src/main/resources/sql/ddl.sql | 56 ++++---- 13 files changed, 337 insertions(+), 34 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/application/dto/MemberDetailRegisterDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/MemberDetail.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/domain/repository/MemberDetailRepository.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDetailDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java index d31589e1..b89259d7 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java @@ -1,16 +1,20 @@ package stanl_2.final_backend.domain.member.command.application.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.security.core.Authentication; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; +import stanl_2.final_backend.domain.member.command.application.dto.MemberDetailRegisterDTO; import stanl_2.final_backend.domain.member.command.application.service.MemberCommandService; import stanl_2.final_backend.domain.member.common.response.MemberResponseMessage; +import java.security.GeneralSecurityException; import java.security.Principal; @Slf4j @@ -59,4 +63,22 @@ public ResponseEntity check(Principal principal) { .build()); } + @Operation(summary = "회원 상세정보 등록") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) + }) + @PostMapping("/detail") + public ResponseEntity postMemberDetail(@RequestBody MemberDetailRegisterDTO memberDetailDTO) throws GeneralSecurityException { + + memberCommandService.registerMemberDetail(memberDetailDTO); + + return ResponseEntity.ok(MemberResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + } + + } diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/MemberDetailRegisterDTO.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/MemberDetailRegisterDTO.java new file mode 100644 index 00000000..b15a8a9d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/MemberDetailRegisterDTO.java @@ -0,0 +1,33 @@ +package stanl_2.final_backend.domain.member.command.application.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class MemberDetailRegisterDTO { + private String memberDetailRelation; + private String memberDetailName; + private String memberDetailBirth; + private String memberDetailIdentNo; + private String memberDetailPhone; + private String memberDetailSex; + private Boolean memberDetailDisorder; + private Boolean memberDetailDie; + private String memberDetailNote; + private String memberDetailEntrance; + private String memberDetailGraduate; + private String memberDetailFinalEdu; + private String memberDetailEdu; + private String memberDetailMajor; + private String memberDetailEmpDate; + private String memberDetailRtrDate; + private String memberDetailCareerInfo; + private String memberDetailCertificationDate; + private String memberDetailCertificationInst; + private String memberDetailCertificationName; + private String memberDetailCertificationScore; + private String memberId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/MemberCommandService.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/MemberCommandService.java index e1e93f57..17978c8f 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/MemberCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/MemberCommandService.java @@ -1,4 +1,9 @@ package stanl_2.final_backend.domain.member.command.application.service; +import stanl_2.final_backend.domain.member.command.application.dto.MemberDetailRegisterDTO; + +import java.security.GeneralSecurityException; + public interface MemberCommandService { + void registerMemberDetail(MemberDetailRegisterDTO memberDetailRegisterDTO) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/MemberDetail.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/MemberDetail.java new file mode 100644 index 00000000..39ea0293 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/MemberDetail.java @@ -0,0 +1,121 @@ +package stanl_2.final_backend.domain.member.command.domain.aggregate.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "TB_MEMBER_DETAIL") +public class MemberDetail { + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "DET") + ) + @Column(name = "MEM_DET_ID", nullable = false) + private String memberDetailId; + + @Column(name = "MEM_DET_REL") + private String memberDetailRelation; + + @Column(name = "MEM_DET_NAME") + private String memberDetailName; + + @Column(name = "MEM_DET_BIR") + private String memberDetailBirth; + + @Column(name = "MEM_DET_IDEN_NO") + private String memberDetailIdentNo; + + @Column(name = "MEM_DET_PHO") + private String memberDetailPhone; + + @Column(name = "MEM_DET_SEX") + private String memberDetailSex; + + @Column(name = "MEM_DET_DIS") + private Boolean memberDetailDisorder; + + @Column(name = "MEM_DET_DIE") + private Boolean memberDetailDie; + + @Column(name = "MEM_DET_NOTE") + private String memberDetailNote; + + @Column(name = "MEM_DET_ENTD", nullable = false) //222 + private String memberDetailEntrance; + + @Column(name = "MEM_DET_GRAD", nullable = false) //222 + private String memberDetailGraduate; + + @Column(name = "MEM_DET_FNL_EDC", nullable = false) //222 + private String memberDetailFinalEdu; + + @Column(name = "MEM_DET_EDU") + private String memberDetailEdu; + + @Column(name = "MEM_DET_MJR") + private String memberDetailMajor; + + @Column(name = "MEM_DET_EMP_DATE") + private String memberDetailEmpDate; + + @Column(name = "MEM_DET_RTR_DATE") + private String memberDetailRtrDate; + + @Column(name = "CAR_INFO") + private String memberDetailCareerInfo; + + @Column(name = "CERT_DATE") + private String memberDetailCertificationDate; + + @Column(name = "CERT_INST") + private String memberDetailCertificationInst; + + @Column(name = "CERT_NAME") + private String memberDetailCertificationName; + + @Column(name = "CERT_SCO") + private String memberDetailCertificationScore; + + @Column(name = "CREATED_AT", nullable = false, updatable = false) + private String createdAt; + + @Column(name = "UPDATED_AT", nullable = false) + private String updatedAt; + + @Column(name = "MEM_ID", nullable = false) + private String memberId; + + /* 설명. updatedAt 자동화 */ + // Insert 되기 전에 실행 + @PrePersist + private void prePersist() { + this.createdAt = getCurrentTime(); + this.updatedAt = this.createdAt; + } + + // Update 되기 전에 실행 + @PreUpdate + private void preUpdate() { + this.updatedAt = getCurrentTime(); + } + + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/repository/MemberDetailRepository.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/repository/MemberDetailRepository.java new file mode 100644 index 00000000..14e29f01 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/repository/MemberDetailRepository.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.member.command.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.MemberDetail; + +@Repository +public interface MemberDetailRepository extends JpaRepository { +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/MemberCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/MemberCommandServiceImpl.java index 8ee95370..d3b83a16 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/MemberCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/MemberCommandServiceImpl.java @@ -1,17 +1,51 @@ package stanl_2.final_backend.domain.member.command.domain.service; +import lombok.extern.slf4j.Slf4j; +import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.member.command.application.dto.MemberDetailRegisterDTO; import stanl_2.final_backend.domain.member.command.application.service.MemberCommandService; +import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.MemberDetail; +import stanl_2.final_backend.domain.member.command.domain.repository.MemberDetailRepository; import stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; +import stanl_2.final_backend.global.utils.AESUtils; +import java.security.GeneralSecurityException; + +@Slf4j @Service("commandMemberService") public class MemberCommandServiceImpl implements MemberCommandService { private final MemberRepository memberRepository; + private final ModelMapper modelMapper; + private final AESUtils aesUtils; + private final MemberDetailRepository memberDetailRepository; @Autowired - public MemberCommandServiceImpl(MemberRepository memberRepository) { + public MemberCommandServiceImpl(MemberRepository memberRepository, + ModelMapper modelMapper, + AESUtils aesUtils, MemberDetailRepository memberDetailRepository) { this.memberRepository = memberRepository; + this.modelMapper = modelMapper; + this.aesUtils = aesUtils; + this.memberDetailRepository = memberDetailRepository; + } + + @Override + @Transactional + public void registerMemberDetail(MemberDetailRegisterDTO memberDetailRegisterDTO) throws GeneralSecurityException { + + memberDetailRegisterDTO.setMemberDetailIdentNo(aesUtils.encrypt(memberDetailRegisterDTO.getMemberDetailIdentNo())); + memberDetailRegisterDTO.setMemberDetailPhone(aesUtils.encrypt(memberDetailRegisterDTO.getMemberDetailPhone())); + memberDetailRegisterDTO.setMemberDetailBirth(aesUtils.encrypt(memberDetailRegisterDTO.getMemberDetailBirth())); + memberDetailRegisterDTO.setMemberDetailCertificationScore(aesUtils.encrypt(memberDetailRegisterDTO.getMemberDetailCertificationScore())); + memberDetailRegisterDTO.setMemberDetailCertificationInst(aesUtils.encrypt(memberDetailRegisterDTO.getMemberDetailCertificationName())); + memberDetailRegisterDTO.setMemberDetailCareerInfo(aesUtils.encrypt(memberDetailRegisterDTO.getMemberDetailCareerInfo())); + + MemberDetail memberDetail = modelMapper.map(memberDetailRegisterDTO, MemberDetail.class); + + memberDetailRepository.save(memberDetail); } } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java b/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java index 13d4d447..ed47aab0 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java @@ -13,6 +13,7 @@ import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.member.common.response.MemberResponseMessage; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; +import stanl_2.final_backend.domain.member.query.dto.MemberDetailDTO; import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import java.security.GeneralSecurityException; @@ -46,8 +47,25 @@ public ResponseEntity getMemberInfo(Principal principal) .msg("성공") .result(memberInfo) .build()); - } + @Operation(summary = "회원 상세 정보 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + public ResponseEntity getMemberDetail(Principal principal) throws GeneralSecurityException { + + MemberDetailDTO memberDetail = memberQueryService.selectMemberDetail(principal.getName()); + + return ResponseEntity.ok(MemberResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(memberDetail) + .build()); + } + } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDetailDTO.java b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDetailDTO.java new file mode 100644 index 00000000..2dd0425a --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDetailDTO.java @@ -0,0 +1,34 @@ +package stanl_2.final_backend.domain.member.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class MemberDetailDTO { + private String memberDetailRelation; + private String memberDetailName; + private String memberDetailBirth; + private String memberDetailIdentNo; + private String memberDetailPhone; + private String memberDetailSex; + private Boolean memberDetailDisorder; + private Boolean memberDetailDie; + private String memberDetailNote; + private String memberDetailEntrance; + private String memberDetailGraduate; + private String memberDetailFinalEdu; + private String memberDetailEdu; + private String memberDetailMajor; + private String memberDetailEmpDate; + private String memberDetailRtrDate; + private String memberDetailCareerInfo; + private String memberDetailCertificationDate; + private String memberDetailCertificationInst; + private String memberDetailCertificationName; + private String memberDetailCertificationScore; +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java b/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java index 3cc8a550..10677cb0 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java @@ -3,8 +3,11 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; +import stanl_2.final_backend.domain.member.query.dto.MemberDetailDTO; @Mapper public interface MemberMapper { MemberDTO findMemberInfoById(@Param("loginId") String loginId); + + MemberDetailDTO findMemberDetailById(@Param("loginId") String loginId); } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java index 3ed51d09..f5058ec7 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java @@ -1,9 +1,12 @@ package stanl_2.final_backend.domain.member.query.service; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; +import stanl_2.final_backend.domain.member.query.dto.MemberDetailDTO; import java.security.GeneralSecurityException; public interface MemberQueryService { MemberDTO selectMemberInfo(String name) throws GeneralSecurityException; + + MemberDetailDTO selectMemberDetail(String name) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index 670f9c20..af0dff54 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -6,6 +6,7 @@ import stanl_2.final_backend.domain.member.common.exception.MemberCommonException; import stanl_2.final_backend.domain.member.common.exception.MemberErrorCode; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; +import stanl_2.final_backend.domain.member.query.dto.MemberDetailDTO; import stanl_2.final_backend.domain.member.query.repository.MemberMapper; import stanl_2.final_backend.global.utils.AESUtils; @@ -45,4 +46,12 @@ public MemberDTO selectMemberInfo(String name) throws GeneralSecurityException { return memberInfo; } + + @Override + public MemberDetailDTO selectMemberDetail(String name) throws GeneralSecurityException { + + MemberDetailDTO memberDetail = memberMapper.findMemberDetailById(name); + + return null; + } } diff --git a/src/main/java/stanl_2/final_backend/global/utils/AESUtils.java b/src/main/java/stanl_2/final_backend/global/utils/AESUtils.java index 484d5a38..7b94f296 100644 --- a/src/main/java/stanl_2/final_backend/global/utils/AESUtils.java +++ b/src/main/java/stanl_2/final_backend/global/utils/AESUtils.java @@ -29,6 +29,11 @@ public class AESUtils { * AES 대칭키를 사용하여 문자열을 암호화합니다. */ public String encrypt(String data) throws GeneralSecurityException { + + if(data == null){ + return null; + } + Cipher cipher = Cipher.getInstance(transformation); SecretKey secretKey = new SecretKeySpec(secretKeyValue.getBytes(StandardCharsets.UTF_8), algorithm); cipher.init(Cipher.ENCRYPT_MODE, secretKey); @@ -40,6 +45,11 @@ public String encrypt(String data) throws GeneralSecurityException { * AES 대칭키를 사용하여 암호화된 문자열을 복호화합니다. */ public String decrypt(String encryptedData) throws GeneralSecurityException { + + if(encryptedData == null){ + return null; + } + Cipher cipher = Cipher.getInstance(transformation); SecretKey secretKey = new SecretKeySpec(secretKeyValue.getBytes(StandardCharsets.UTF_8), algorithm); cipher.init(Cipher.DECRYPT_MODE, secretKey); diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index 3f9feb77..a04e3a34 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -318,27 +318,29 @@ CREATE TABLE tb_alarm CREATE TABLE tb_member_detail ( MEM_DET_ID VARCHAR(255) NOT NULL, - MEM_DET_REL VARCHAR(255) NOT NULL, - MEM_DET_NAME VARCHAR(255) NOT NULL, - MEM_DET_BIR VARCHAR(255) NOT NULL, - MEM_DET_IDEN_NO VARCHAR(255) NOT NULL, - MEM_DET_PHO VARCHAR(255) NOT NULL, - MEM_DET_SEX VARCHAR(255) NOT NULL DEFAULT 'FEMALE' COMMENT 'FEMALE/MALE', - MEM_DET_DIS BOOLEAN NOT NULL, - MEM_DET_DIE BOOLEAN NOT NULL, + MEM_DET_REL VARCHAR(255) NULL, + MEM_DET_NAME VARCHAR(255) NULL, + MEM_DET_BIR VARCHAR(255) NULL, + MEM_DET_IDEN_NO VARCHAR(255) NULL, + MEM_DET_PHO VARCHAR(255) NULL, + MEM_DET_SEX VARCHAR(255) NULL COMMENT 'FEMALE/MALE', + MEM_DET_DIS BOOLEAN NULL, + MEM_DET_DIE BOOLEAN NULL, MEM_DET_NOTE VARCHAR(255) NULL, MEM_DET_ENTD VARCHAR(255) NOT NULL, MEM_DET_GRAD VARCHAR(255) NOT NULL, MEM_DET_FNL_EDC VARCHAR(255) NOT NULL, - MEM_DET_EDU VARCHAR(255) NOT NULL, - MEM_DET_MJR VARCHAR(255) NOT NULL, - MEM_DET_EMP_DATE VARCHAR(255) NOT NULL, - MEM_DET_RTR_DATE VARCHAR(255) NOT NULL, - CAR_INFO VARCHAR(255) NOT NULL, - CERT_DATE VARCHAR(255) NOT NULL, - CERT_INST VARCHAR(255) NOT NULL, + MEM_DET_EDU VARCHAR(255) NULL, + MEM_DET_MJR VARCHAR(255) NULL, + MEM_DET_EMP_DATE VARCHAR(255) NULL, + MEM_DET_RTR_DATE VARCHAR(255) NULL, + CAR_INFO VARCHAR(255) NULL, + CERT_DATE VARCHAR(255) NULL, + CERT_INST VARCHAR(255) NULL, CERT_NAME VARCHAR(255) NULL, - CERT_SCO VARCHAR(255) NOT NULL, + CERT_SCO VARCHAR(255) NULL, + CREATED_AT CHAR(19) NOT NULL, + UPDATED_AT CHAR(19) NOT NULL, MEM_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', PRIMARY KEY (MEM_DET_ID), FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) @@ -843,37 +845,37 @@ VALUES ('ALR_000000001', '신차 출시 공지 알림', '/notices/1', FALSE, '20 INSERT INTO tb_member_detail (MEM_DET_ID, MEM_DET_REL, MEM_DET_NAME, MEM_DET_BIR, MEM_DET_IDEN_NO, MEM_DET_PHO, MEM_DET_SEX, MEM_DET_DIS, MEM_DET_DIE, MEM_DET_NOTE, MEM_DET_ENTD, MEM_DET_GRAD, MEM_DET_FNL_EDC, MEM_DET_EDU, MEM_DET_MJR, MEM_DET_EMP_DATE, MEM_DET_RTR_DATE, CAR_INFO, - CERT_DATE, CERT_INST, CERT_NAME, CERT_SCO, MEM_ID) + CERT_DATE, CERT_INST, CERT_NAME, CERT_SCO, CREATED_AT, UPDATED_AT, MEM_ID) VALUES ('DET_000000001', '배우자', '박미숙', '1988-05-12', '880512-1234567', '010-1111-2222', 'FEMALE', FALSE, FALSE, NULL, '2010', 'A', '대졸', '경영학', '기아', '2020-01-01', '2025-01-01', 'K5', '2023-06-01', '기아자동차', '세일즈', '95', - 'MEM_000000001'), + '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000001'), ('DET_000000002', '자녀', '김철수', '2012-08-05', '120805-2345678', '010-2222-3333', 'MALE', FALSE, FALSE, NULL, '2022', 'B+', '고졸', '정보통신', '기아', '2022-01-15', '2027-01-15', 'K3', '2023-07-15', '기아자동차', '서비스', '88', - 'MEM_000000002'), + '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000002'), ('DET_000000003', '배우자', '이영희', '1985-03-09', '850309-3456789', '010-3333-4444', 'FEMALE', FALSE, FALSE, '부부 동반 여행', '2009', 'A-', '대졸', '경제학', '기아', '2019-05-12', '2024-05-12', '스포티지', '2023-04-20', '기아자동차', '기술지원', - '92', 'MEM_000000003'), + '92', '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000003'), ('DET_000000004', '배우자', '정수민', '1990-11-11', '901111-4567890', '010-4444-5555', 'FEMALE', FALSE, FALSE, NULL, '2015', 'A', '대졸', '기계공학', '기아', '2021-09-01', '2026-09-01', 'K7', '2023-02-10', '기아자동차', '품질관리', '87', - 'MEM_000000004'), + '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000004'), ('DET_000000005', '자녀', '한지수', '2016-01-20', '160120-5678901', '010-5555-6666', 'MALE', TRUE, FALSE, '특별 교육 필요', '2021', 'B', '초등학교', '과학', '기아', '2023-03-05', '2028-03-05', '니로', '2023-03-01', '기아자동차', '연구개발', '85', - 'MEM_000000005'), + '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000005'), ('DET_000000006', '자녀', '최지우', '2005-06-15', '050615-6789012', '010-6666-7777', 'FEMALE', FALSE, FALSE, NULL, '2023', 'A+', '대졸', '디자인', '기아', '2020-07-01', '2025-07-01', '스팅어', '2023-08-20', '기아자동차', '디자인', '90', - 'MEM_000000006'), + '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000006'), ('DET_000000007', '배우자', '윤미라', '1987-12-30', '871230-7890123', '010-7777-8888', 'FEMALE', FALSE, FALSE, '해외 출장 동반', '2011', 'B+', '대졸', '마케팅', '기아', '2020-12-01', '2025-12-01', '쏘렌토', '2023-01-25', '기아자동차', '마케팅', - '93', 'MEM_000000007'), + '93', '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000007'), ('DET_000000008', '자녀', '이동희', '2010-09-10', '100910-8901234', '010-8888-9999', 'MALE', FALSE, FALSE, NULL, '2025', 'B-', '중졸', '정보기술', '기아', '2023-05-05', '2028-05-05', '모하비', '2023-12-30', '기아자동차', '엔지니어링', '89', - 'MEM_000000008'), + '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000008'), ('DET_000000009', '배우자', '정수영', '1991-04-17', '910417-9012345', '010-9999-0000', 'FEMALE', FALSE, FALSE, '가족 동반 캠핑', '2018', 'A-', '대졸', '화학', '기아', '2022-06-15', '2027-06-15', '셀토스', '2023-11-22', '기아자동차', '생산', - '91', 'MEM_000000009'), + '91', '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000009'), ('DET_000000010', '자녀', '김준호', '2014-07-22', '140722-0123456', '010-0000-1111', 'MALE', FALSE, FALSE, NULL, '2027', 'B+', '고졸', '경영', '기아', '2023-08-12', '2028-08-12', 'K8', '2023-09-10', '기아자동차', '세일즈', '87', - 'MEM_000000010'); + '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000010'); INSERT INTO tb_update_history (UPD_ID, UPD_IP, UPDATED_AT, UPDATED_URL, MEM_ID, CONR_ID) VALUES ('UPD_000000001', '192.168.1.10', '2024-01-10 12:00:00', '/contracts/1', 'MEM_000000001', 'CON_000000001'), From 47c388d053725320da4a6f41365a66be98de20da Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Sun, 17 Nov 2024 19:33:12 +0900 Subject: [PATCH 096/563] =?UTF-8?q?fix:=20=ED=9A=8C=EC=9B=90=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20ddl=20=EC=88=98=EC=A0=95=ED=95=98=EA=B8=B0?= =?UTF-8?q?=20=EC=9C=84=ED=95=9C=20=EC=85=8B=EC=97=85(#60)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/MemberController.java | 23 ---- .../dto/MemberDetailRegisterDTO.java | 33 ----- .../service/MemberCommandService.java | 5 - .../domain/aggregate/entity/MemberDetail.java | 121 ------------------ .../repository/MemberDetailRepository.java | 9 -- .../service/MemberCommandServiceImpl.java | 24 +--- .../query/controller/MemberController.java | 17 --- .../member/query/dto/MemberDetailDTO.java | 34 ----- .../member/query/repository/MemberMapper.java | 3 - .../query/service/MemberQueryService.java | 3 +- .../query/service/MemberQueryServiceImpl.java | 9 -- 11 files changed, 2 insertions(+), 279 deletions(-) delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/application/dto/MemberDetailRegisterDTO.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/MemberDetail.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/domain/repository/MemberDetailRepository.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDetailDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java index b89259d7..751be6da 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java @@ -1,20 +1,12 @@ package stanl_2.final_backend.domain.member.command.application.controller; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; -import stanl_2.final_backend.domain.member.command.application.dto.MemberDetailRegisterDTO; import stanl_2.final_backend.domain.member.command.application.service.MemberCommandService; import stanl_2.final_backend.domain.member.common.response.MemberResponseMessage; -import java.security.GeneralSecurityException; import java.security.Principal; @Slf4j @@ -63,22 +55,7 @@ public ResponseEntity check(Principal principal) { .build()); } - @Operation(summary = "회원 상세정보 등록") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) - }) - @PostMapping("/detail") - public ResponseEntity postMemberDetail(@RequestBody MemberDetailRegisterDTO memberDetailDTO) throws GeneralSecurityException { - memberCommandService.registerMemberDetail(memberDetailDTO); - - return ResponseEntity.ok(MemberResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(null) - .build()); - } } diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/MemberDetailRegisterDTO.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/MemberDetailRegisterDTO.java deleted file mode 100644 index b15a8a9d..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/MemberDetailRegisterDTO.java +++ /dev/null @@ -1,33 +0,0 @@ -package stanl_2.final_backend.domain.member.command.application.dto; - -import lombok.*; - -@AllArgsConstructor -@NoArgsConstructor -@Setter -@Getter -@ToString -public class MemberDetailRegisterDTO { - private String memberDetailRelation; - private String memberDetailName; - private String memberDetailBirth; - private String memberDetailIdentNo; - private String memberDetailPhone; - private String memberDetailSex; - private Boolean memberDetailDisorder; - private Boolean memberDetailDie; - private String memberDetailNote; - private String memberDetailEntrance; - private String memberDetailGraduate; - private String memberDetailFinalEdu; - private String memberDetailEdu; - private String memberDetailMajor; - private String memberDetailEmpDate; - private String memberDetailRtrDate; - private String memberDetailCareerInfo; - private String memberDetailCertificationDate; - private String memberDetailCertificationInst; - private String memberDetailCertificationName; - private String memberDetailCertificationScore; - private String memberId; -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/MemberCommandService.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/MemberCommandService.java index 17978c8f..e1e93f57 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/MemberCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/MemberCommandService.java @@ -1,9 +1,4 @@ package stanl_2.final_backend.domain.member.command.application.service; -import stanl_2.final_backend.domain.member.command.application.dto.MemberDetailRegisterDTO; - -import java.security.GeneralSecurityException; - public interface MemberCommandService { - void registerMemberDetail(MemberDetailRegisterDTO memberDetailRegisterDTO) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/MemberDetail.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/MemberDetail.java deleted file mode 100644 index 39ea0293..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/MemberDetail.java +++ /dev/null @@ -1,121 +0,0 @@ -package stanl_2.final_backend.domain.member.command.domain.aggregate.entity; - -import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import org.hibernate.annotations.GenericGenerator; -import stanl_2.final_backend.global.config.PrefixGeneratorConfig; - -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; - -@NoArgsConstructor -@AllArgsConstructor -@Getter -@Setter -@Entity -@Table(name = "TB_MEMBER_DETAIL") -public class MemberDetail { - @Id - @GeneratedValue(generator = "PrefixGeneratorConfig") - @GenericGenerator(name = "PrefixGeneratorConfig", - type = PrefixGeneratorConfig.class, - parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "DET") - ) - @Column(name = "MEM_DET_ID", nullable = false) - private String memberDetailId; - - @Column(name = "MEM_DET_REL") - private String memberDetailRelation; - - @Column(name = "MEM_DET_NAME") - private String memberDetailName; - - @Column(name = "MEM_DET_BIR") - private String memberDetailBirth; - - @Column(name = "MEM_DET_IDEN_NO") - private String memberDetailIdentNo; - - @Column(name = "MEM_DET_PHO") - private String memberDetailPhone; - - @Column(name = "MEM_DET_SEX") - private String memberDetailSex; - - @Column(name = "MEM_DET_DIS") - private Boolean memberDetailDisorder; - - @Column(name = "MEM_DET_DIE") - private Boolean memberDetailDie; - - @Column(name = "MEM_DET_NOTE") - private String memberDetailNote; - - @Column(name = "MEM_DET_ENTD", nullable = false) //222 - private String memberDetailEntrance; - - @Column(name = "MEM_DET_GRAD", nullable = false) //222 - private String memberDetailGraduate; - - @Column(name = "MEM_DET_FNL_EDC", nullable = false) //222 - private String memberDetailFinalEdu; - - @Column(name = "MEM_DET_EDU") - private String memberDetailEdu; - - @Column(name = "MEM_DET_MJR") - private String memberDetailMajor; - - @Column(name = "MEM_DET_EMP_DATE") - private String memberDetailEmpDate; - - @Column(name = "MEM_DET_RTR_DATE") - private String memberDetailRtrDate; - - @Column(name = "CAR_INFO") - private String memberDetailCareerInfo; - - @Column(name = "CERT_DATE") - private String memberDetailCertificationDate; - - @Column(name = "CERT_INST") - private String memberDetailCertificationInst; - - @Column(name = "CERT_NAME") - private String memberDetailCertificationName; - - @Column(name = "CERT_SCO") - private String memberDetailCertificationScore; - - @Column(name = "CREATED_AT", nullable = false, updatable = false) - private String createdAt; - - @Column(name = "UPDATED_AT", nullable = false) - private String updatedAt; - - @Column(name = "MEM_ID", nullable = false) - private String memberId; - - /* 설명. updatedAt 자동화 */ - // Insert 되기 전에 실행 - @PrePersist - private void prePersist() { - this.createdAt = getCurrentTime(); - this.updatedAt = this.createdAt; - } - - // Update 되기 전에 실행 - @PreUpdate - private void preUpdate() { - this.updatedAt = getCurrentTime(); - } - - private String getCurrentTime() { - ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); - return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); - } -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/repository/MemberDetailRepository.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/repository/MemberDetailRepository.java deleted file mode 100644 index 14e29f01..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/repository/MemberDetailRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package stanl_2.final_backend.domain.member.command.domain.repository; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; -import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.MemberDetail; - -@Repository -public interface MemberDetailRepository extends JpaRepository { -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/MemberCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/MemberCommandServiceImpl.java index d3b83a16..cd042289 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/MemberCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/MemberCommandServiceImpl.java @@ -4,15 +4,10 @@ import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.member.command.application.dto.MemberDetailRegisterDTO; import stanl_2.final_backend.domain.member.command.application.service.MemberCommandService; -import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.MemberDetail; -import stanl_2.final_backend.domain.member.command.domain.repository.MemberDetailRepository; import stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; import stanl_2.final_backend.global.utils.AESUtils; -import java.security.GeneralSecurityException; @Slf4j @Service("commandMemberService") @@ -21,31 +16,14 @@ public class MemberCommandServiceImpl implements MemberCommandService { private final MemberRepository memberRepository; private final ModelMapper modelMapper; private final AESUtils aesUtils; - private final MemberDetailRepository memberDetailRepository; @Autowired public MemberCommandServiceImpl(MemberRepository memberRepository, ModelMapper modelMapper, - AESUtils aesUtils, MemberDetailRepository memberDetailRepository) { + AESUtils aesUtils) { this.memberRepository = memberRepository; this.modelMapper = modelMapper; this.aesUtils = aesUtils; - this.memberDetailRepository = memberDetailRepository; } - @Override - @Transactional - public void registerMemberDetail(MemberDetailRegisterDTO memberDetailRegisterDTO) throws GeneralSecurityException { - - memberDetailRegisterDTO.setMemberDetailIdentNo(aesUtils.encrypt(memberDetailRegisterDTO.getMemberDetailIdentNo())); - memberDetailRegisterDTO.setMemberDetailPhone(aesUtils.encrypt(memberDetailRegisterDTO.getMemberDetailPhone())); - memberDetailRegisterDTO.setMemberDetailBirth(aesUtils.encrypt(memberDetailRegisterDTO.getMemberDetailBirth())); - memberDetailRegisterDTO.setMemberDetailCertificationScore(aesUtils.encrypt(memberDetailRegisterDTO.getMemberDetailCertificationScore())); - memberDetailRegisterDTO.setMemberDetailCertificationInst(aesUtils.encrypt(memberDetailRegisterDTO.getMemberDetailCertificationName())); - memberDetailRegisterDTO.setMemberDetailCareerInfo(aesUtils.encrypt(memberDetailRegisterDTO.getMemberDetailCareerInfo())); - - MemberDetail memberDetail = modelMapper.map(memberDetailRegisterDTO, MemberDetail.class); - - memberDetailRepository.save(memberDetail); - } } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java b/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java index ed47aab0..06203c0c 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java @@ -13,7 +13,6 @@ import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.member.common.response.MemberResponseMessage; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; -import stanl_2.final_backend.domain.member.query.dto.MemberDetailDTO; import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import java.security.GeneralSecurityException; @@ -50,22 +49,6 @@ public ResponseEntity getMemberInfo(Principal principal) } - @Operation(summary = "회원 상세 정보 조회") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), - @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", - content = @Content(mediaType = "application/json")) - }) - public ResponseEntity getMemberDetail(Principal principal) throws GeneralSecurityException { - MemberDetailDTO memberDetail = memberQueryService.selectMemberDetail(principal.getName()); - - return ResponseEntity.ok(MemberResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(memberDetail) - .build()); - } } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDetailDTO.java b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDetailDTO.java deleted file mode 100644 index 2dd0425a..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDetailDTO.java +++ /dev/null @@ -1,34 +0,0 @@ -package stanl_2.final_backend.domain.member.query.dto; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@AllArgsConstructor -@NoArgsConstructor -@Setter -@Getter -public class MemberDetailDTO { - private String memberDetailRelation; - private String memberDetailName; - private String memberDetailBirth; - private String memberDetailIdentNo; - private String memberDetailPhone; - private String memberDetailSex; - private Boolean memberDetailDisorder; - private Boolean memberDetailDie; - private String memberDetailNote; - private String memberDetailEntrance; - private String memberDetailGraduate; - private String memberDetailFinalEdu; - private String memberDetailEdu; - private String memberDetailMajor; - private String memberDetailEmpDate; - private String memberDetailRtrDate; - private String memberDetailCareerInfo; - private String memberDetailCertificationDate; - private String memberDetailCertificationInst; - private String memberDetailCertificationName; - private String memberDetailCertificationScore; -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java b/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java index 10677cb0..3cc8a550 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java @@ -3,11 +3,8 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; -import stanl_2.final_backend.domain.member.query.dto.MemberDetailDTO; @Mapper public interface MemberMapper { MemberDTO findMemberInfoById(@Param("loginId") String loginId); - - MemberDetailDTO findMemberDetailById(@Param("loginId") String loginId); } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java index f5058ec7..26ddc73f 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java @@ -1,12 +1,11 @@ package stanl_2.final_backend.domain.member.query.service; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; -import stanl_2.final_backend.domain.member.query.dto.MemberDetailDTO; import java.security.GeneralSecurityException; public interface MemberQueryService { MemberDTO selectMemberInfo(String name) throws GeneralSecurityException; - MemberDetailDTO selectMemberDetail(String name) throws GeneralSecurityException; +// MemberDetailDTO selectMemberDetail(String name) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index af0dff54..670f9c20 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -6,7 +6,6 @@ import stanl_2.final_backend.domain.member.common.exception.MemberCommonException; import stanl_2.final_backend.domain.member.common.exception.MemberErrorCode; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; -import stanl_2.final_backend.domain.member.query.dto.MemberDetailDTO; import stanl_2.final_backend.domain.member.query.repository.MemberMapper; import stanl_2.final_backend.global.utils.AESUtils; @@ -46,12 +45,4 @@ public MemberDTO selectMemberInfo(String name) throws GeneralSecurityException { return memberInfo; } - - @Override - public MemberDetailDTO selectMemberDetail(String name) throws GeneralSecurityException { - - MemberDetailDTO memberDetail = memberMapper.findMemberDetailById(name); - - return null; - } } From 388c2eba03aca3dea68fc9d2dd8c1a4334bade2b Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Sun, 17 Nov 2024 19:42:01 +0900 Subject: [PATCH 097/563] =?UTF-8?q?fix:=20ddl=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/sql/ddl.sql | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index 3f9feb77..157d048b 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -22,6 +22,15 @@ DROP TABLE IF EXISTS tb_member; DROP TABLE IF EXISTS tb_center; DROP TABLE IF EXISTS tb_organization_chart; DROP TABLE IF EXISTS tb_sales_history; +DROP TABLE IF EXISTS BATCH_STEP_EXECUTION_SEQ; +DROP TABLE IF EXISTS BATCH_STEP_EXECUTION_CONTEXT; +DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_CONTEXT; +DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_PARAMS; +DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_SEQ; +DROP TABLE IF EXISTS BATCH_JOB_SEQ; +DROP TABLE IF EXISTS BATCH_STEP_EXECUTION; +DROP TABLE IF EXISTS BATCH_JOB_EXECUTION; +DROP TABLE IF EXISTS BATCH_JOB_INSTANCE; -- 조직 관련 테이블 생성 CREATE TABLE tb_organization_chart @@ -522,7 +531,7 @@ VALUES ('CEN_000000001', '서울센터', '서울특별시 중구', '010-1234-567 ('CEN_000000010', '전주센터', '전라북도 전주시', '010-0123-4567', 15, '2024-01-10 12:00:00', '2024-01-10 12:00:00', TRUE, '09:00-18:00'); -INSERT INTO tb_member (MEM_ID, MEM_LOGN_ID, MEM_PWD, MEM_NAME, MEM_EMA, MEM_AGE, MEM_SEX, MEM_IDEN_NO, MEM_PHO, MEM_ADR, +INSERT INTO tb_member (MEM_ID, MEM_LOGIN_ID, MEM_PWD, MEM_NAME, MEM_EMA, MEM_AGE, MEM_SEX, MEM_IDEN_NO, MEM_PHO, MEM_ADR, MEM_POS, MEM_GRD, MEM_JOB_TYPE, CENTER_ID, ORG_CHA_ID, CREATED_AT, UPDATED_AT, ACTIVE) VALUES ('MEM_000000001', 101, 'pwd1234', '김철수', 'chulsoo@example.com', 35, 'MALE', '123456-7890123', '010-9876-5432', '서울특별시', 'Manager', 'Bachelor', 'Full-time', 'CEN_000000001', 'ORG_000000006', '2024-01-01 12:00:00', From ff2a05977ba5e80db6e45cc2b113e4389e82348a Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Sun, 17 Nov 2024 20:29:04 +0900 Subject: [PATCH 098/563] =?UTF-8?q?feat:=20main=20merge=20=EC=A0=84=20comm?= =?UTF-8?q?it=20(#57)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/batch/BatchConfig.java | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/batch/BatchConfig.java b/src/main/java/stanl_2/final_backend/domain/batch/BatchConfig.java index a442a27f..84eef8cb 100644 --- a/src/main/java/stanl_2/final_backend/domain/batch/BatchConfig.java +++ b/src/main/java/stanl_2/final_backend/domain/batch/BatchConfig.java @@ -13,10 +13,13 @@ import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.step.builder.StepBuilder; import org.springframework.batch.core.step.tasklet.Tasklet; +import org.springframework.batch.item.data.RepositoryItemReader; +import org.springframework.batch.item.data.RepositoryItemWriter; import org.springframework.batch.repeat.RepeatStatus; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; +import stanl_2.final_backend.domain.schedule.command.domain.aggregate.entity.Schedule; @Slf4j @Configuration @@ -39,20 +42,30 @@ public Job checkJob(JobRepository jobRepository, PlatformTransactionManager tran @JobScope public Step checkStep(JobRepository jobRepository, PlatformTransactionManager transactionManager){ return new StepBuilder(STEP_NAME, jobRepository) - .tasklet(checkTasklet(), transactionManager) + .<>chunk(5,transactionManager) + .reqder(checkReader()) + .processor(checkProcessor()) + .writer(checkWriter()) .build(); } @Bean @StepScope - public Tasklet checkTasklet(){ - return new Tasklet(){ - @Override - public RepeatStatus execute(StepContribution contribution, ChunkContext context) throws Exception { - log.info("Spring batch check Suceess"); - // 원하는 비지니스 모델 추가 - return RepeatStatus.FINISHED; - } - }; + public RepositoryItemWriter<> checkWtriter(){ + return new RepositoryItemWriter<>() + .repository } + +// @Bean +// @StepScope +// public Tasklet checkTasklet(){ +// return new Tasklet(){ +// @Override +// public RepeatStatus execute(StepContribution contribution, ChunkContext context) throws Exception { +// log.info("Spring batch check Suceess"); +// // 원하는 비지니스 모델 추가 +// return RepeatStatus.FINISHED; +// } +// }; +// } } From 45743cd2b9a9d41ab1d0019337c37c5fef8d4128 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Sun, 17 Nov 2024 20:31:46 +0900 Subject: [PATCH 099/563] =?UTF-8?q?fix:=20=EC=98=88=EC=99=B8=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EB=B0=8F=20=EC=83=98=ED=94=8C=20=EC=88=98=EC=A0=95?= =?UTF-8?q?(#49)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SampleController.java | 22 +++++++-- .../query/controller/SampleController.java | 10 +++- .../global/exception/GlobalErrorCode.java | 5 +- .../exception/GlobalExceptionHandler.java | 47 +++++++++++++------ .../security/config/DevSecurityConfig.java | 3 +- .../security/config/ProdSecurityConfig.java | 3 +- .../filter/JWTTokenValidatorFilter.java | 27 +++++++---- 7 files changed, 84 insertions(+), 33 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java index 8aea5c10..ecd25540 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -13,6 +14,9 @@ import stanl_2.final_backend.domain.A_sample.command.application.service.SampleCommandService; import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; +import java.security.Principal; + +@Slf4j @RestController("commandSampleController") @RequestMapping("/api/v1/sample") public class SampleController { @@ -38,7 +42,11 @@ public SampleController(SampleCommandService sampleCommandService) { content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postTest(@RequestBody SampleRegistDTO sampleRegistRequestDTO) { + public ResponseEntity postTest(@RequestBody SampleRegistDTO sampleRegistRequestDTO, + Principal principal) { + + log.info("현재 접속한 회원정보(MEM_LOGIN_ID)"); + log.info(principal.getName()); sampleCommandService.registerSample(sampleRegistRequestDTO); @@ -63,7 +71,11 @@ public ResponseEntity postTest(@RequestBody SampleRegistD }) @PutMapping("{id}") public ResponseEntity putTest(@PathVariable String id, - @RequestBody SampleModifyDTO sampleModifyRequestDTO) { + @RequestBody SampleModifyDTO sampleModifyRequestDTO, + Principal principal) { + + log.info("현재 접속한 회원정보(MEM_LOGIN_ID)"); + log.info(principal.getName()); sampleModifyRequestDTO.setId(id); SampleModifyDTO sampleModifyDTO = sampleCommandService.modifySample(id, sampleModifyRequestDTO); @@ -84,7 +96,11 @@ public ResponseEntity putTest(@PathVariable String id, content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) }) @DeleteMapping("{id}") - public ResponseEntity deleteTest(@PathVariable String id) { + public ResponseEntity deleteTest(@PathVariable String id, + Principal principal) { + + log.info("현재 접속한 회원정보(MEM_LOGIN_ID)"); + log.info(principal.getName()); sampleCommandService.deleteSample(id); diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java b/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java index a4f0221b..8dfda062 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -12,6 +13,9 @@ import stanl_2.final_backend.domain.A_sample.query.dto.SampleDTO; import stanl_2.final_backend.domain.A_sample.query.service.SampleQueryService; +import java.security.Principal; + +@Slf4j @RestController(value = "querySampleController") @RequestMapping("/api/v1/sample") public class SampleController { @@ -34,7 +38,11 @@ public SampleController(SampleQueryService sampleQueryService) { content = @Content(mediaType = "application/json")) }) @GetMapping("{id}") - public ResponseEntity getTest(@PathVariable String id){ + public ResponseEntity getTest(@PathVariable String id, + Principal principal){ + + log.info("현재 접속한 회원정보(MEM_LOGIN_ID)"); + log.info(principal.getName()); SampleDTO sampleDTO = sampleQueryService.selectSampleInfo(id); diff --git a/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java b/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java index f3ed639a..81e6c27d 100644 --- a/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java +++ b/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java @@ -31,14 +31,15 @@ public enum GlobalErrorCode { */ LOGIN_FAILURE(40100, HttpStatus.UNAUTHORIZED, "로그인에 실패했습니다"), INVALID_TOKEN_ERROR(40101, HttpStatus.UNAUTHORIZED, "유효하지 않은 토큰입니다."), - INVALID_CSRF_TOKEN(40102, HttpStatus.UNAUTHORIZED, "유효하지 않은 CSRF토큰입니다."), + JWT_EXPIRED(40102, HttpStatus.UNAUTHORIZED, "만료된 토큰입니다."), + /** * 403(Forbidden) * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다. */ - UNAUTHORIZED(40300, HttpStatus.FORBIDDEN, "권한이 존재하지 않습니다."), + UNAUTHORIZED(40300, HttpStatus.FORBIDDEN, "접근 권한이 존재하지 않습니다."), /** diff --git a/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java b/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java index cace7cc7..a0a111fd 100644 --- a/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java @@ -1,5 +1,7 @@ package stanl_2.final_backend.global.exception; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.JwtException; import lombok.extern.slf4j.Slf4j; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.ResponseEntity; @@ -19,60 +21,77 @@ public class GlobalExceptionHandler { // 지원되지 않는 HTTP 메소드를 사용할 때 발생하는 예외 @ExceptionHandler(value = {NoHandlerFoundException.class, HttpRequestMethodNotSupportedException.class}) public ResponseEntity handleNoPageFoundException(Exception e) { - log.error("handleNoPageFoundException() in GlobalExceptionHandler throw NoHandlerFoundException : {}" - , e.getMessage()); + log.error("지원되지 않는 HTTP 메소드 요청: {}", e.getMessage()); GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.WRONG_ENTRY_POINT).getErrorCode()); - return new ResponseEntity<>(response, response.getHttpStatus()); + return ResponseEntity.status(response.getHttpStatus()).body(response); } // 메소드의 인자 타입이 일치하지 않을 때 발생하는 예외 @ExceptionHandler(value = {MethodArgumentTypeMismatchException.class}) public ResponseEntity handleArgumentNotValidException(MethodArgumentTypeMismatchException e) { - log.error("handleArgumentNotValidException() in GlobalExceptionHandler throw MethodArgumentTypeMismatchException : {}" + log.error("메소드 인자 타입 불일치: {}" , e.getMessage()); GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.INVALID_PARAMETER_FORMAT).getErrorCode()); - return new ResponseEntity<>(response, response.getHttpStatus()); + return ResponseEntity.status(response.getHttpStatus()).body(response); } // 필수 파라미터가 누락되었을 때 발생하는 예외 @ExceptionHandler(value = {MissingServletRequestParameterException.class}) public ResponseEntity handleArgumentNotValidException(MissingServletRequestParameterException e) { - log.error("handleArgumentNotValidException() in GlobalExceptionHandler throw MethodArgumentNotValidException : {}" + log.error("필수 파라미터 누락: {}" , e.getMessage()); GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.MISSING_REQUEST_PARAMETER).getErrorCode()); - return new ResponseEntity<>(response, response.getHttpStatus()); + return ResponseEntity.status(response.getHttpStatus()).body(response); } // 사용자 정의 예외 처리 @ExceptionHandler(value = {GlobalCommonException.class}) public ResponseEntity handleCustomException(GlobalCommonException e) { - log.error("handleCustomException() in GlobalExceptionHandler: {}", e.getMessage()); + log.error("사용자 예외처리: {}", e.getMessage()); GlobalExceptionResponse response = new GlobalExceptionResponse(e.getErrorCode()); - return new ResponseEntity<>(response, response.getHttpStatus()); + return ResponseEntity.status(response.getHttpStatus()).body(response); } // 서버 내부 오류시 작동 @ExceptionHandler(value = {Exception.class}) public ResponseEntity handleServerException(Exception e) { - log.info("occurred exception in handleServerError = {}", e.getMessage()); + log.info("서버 내부 오류 발생: {}", e.getMessage()); e.printStackTrace(); GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.INTERNAL_SERVER_ERROR).getErrorCode()); - return new ResponseEntity<>(response, response.getHttpStatus()); + return ResponseEntity.status(response.getHttpStatus()).body(response); } // 데이터 무결성 위반 예외 처리기 추가 @ExceptionHandler(value = {DataIntegrityViolationException.class}) public ResponseEntity handleDataIntegrityViolationException(DataIntegrityViolationException e) { - log.error("handleDataIntegrityViolationException() in GlobalExceptionHandler : {}", e.getMessage()); + log.error("데이터 무결성 위반: {}", e.getMessage()); GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.DATA_INTEGRITY_VIOLATION).getErrorCode()); - return new ResponseEntity<>(response, response.getHttpStatus()); + return ResponseEntity.status(response.getHttpStatus()).body(response); } + // 유효성 검사 실패 예외 @ExceptionHandler(value = {MethodArgumentNotValidException.class}) public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { log.error("유효성 검사 실패: {}", e.getMessage()); GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.VALIDATION_FAIL).getErrorCode()); - return new ResponseEntity<>(response, response.getHttpStatus()); + return ResponseEntity.status(response.getHttpStatus()).body(response); + } + + // JWT 토큰 만료 예외 처리 + @ExceptionHandler(ExpiredJwtException.class) + public ResponseEntity handleExpiredJwtException(ExpiredJwtException e) { + log.error("만료된 JWT 토큰: {}", e.getMessage()); + GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.JWT_EXPIRED).getErrorCode()); + return ResponseEntity.status(response.getHttpStatus()).body(response); } + + // JWT 토큰 인증 실패 예외 처리 + @ExceptionHandler(JwtException.class) + public ResponseEntity handleJwtException(JwtException e) { + log.error("유효하지 않은 JWT 토큰: {}", e.getMessage()); + GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.INVALID_TOKEN_ERROR).getErrorCode()); + return ResponseEntity.status(response.getHttpStatus()).body(response); + } + } diff --git a/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java index 0c20bb6e..d7ed8e7d 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java @@ -49,8 +49,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Excepti "/api/v1/auth/signin", "/api/v1/auth/signup", "/api/v1/auth/refresh", - "/api/v1/sample/**", - "/api/v1/auth" + "/api/v1/auth" // 권한 부여때문에(일단 열어둠) ).permitAll() // [Example] member는 ADMIN 권한만 접근 가능 설정 예시 .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasRole("ADMIN") diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index 42414d4b..50935c32 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -48,8 +48,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication "/api/v1/auth/signin", "/api/v1/auth/signup", "/api/v1/auth/refresh", - "/api/v1/sample/**", - "/api/v1/auth" + "/api/v1/auth" // 권한 부여때문에(일단 열어둠) ).permitAll() // [Example] member는 ADMIN 권한만 접근 가능 설정 예시 .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasRole("ADMIN") diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java index af07b72d..30421d07 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java @@ -16,6 +16,8 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.filter.OncePerRequestFilter; +import stanl_2.final_backend.global.exception.GlobalCommonException; +import stanl_2.final_backend.global.exception.GlobalErrorCode; import javax.crypto.SecretKey; import java.io.IOException; @@ -54,12 +56,15 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse String authorities = claims.get("authorities", String.class); // 예외 처리: authorities가 null인 경우 - if (authorities == null || authorities.isEmpty()) { - log.error("Invalid token: authorities are missing"); - SecurityContextHolder.clearContext(); - filterChain.doFilter(request, response); - return; + // 예외 처리: 토큰에 유효한 권한이 없을 경우 + if (username == null || authorities == null || authorities.isEmpty()) { + throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); } +// if (authorities == null || authorities.isEmpty()) { +// SecurityContextHolder.clearContext(); +// filterChain.doFilter(request, response); +// return; +// } List grantedAuthorities = Arrays.stream(authorities.split(",")) .map(SimpleGrantedAuthority::new) @@ -72,9 +77,14 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse SecurityContextHolder.getContext().setAuthentication(authentication); } } catch (JwtException e) { - logger.error("Invalid JWT token", e); - SecurityContextHolder.clearContext(); + log.error("Invalid JWT token: {}", e.getMessage()); + throw new GlobalCommonException(GlobalErrorCode.INVALID_TOKEN_ERROR); +// logger.error("Invalid JWT token", e); +// SecurityContextHolder.clearContext(); } + }else { + // 토큰이 없을 경우 예외 처리 + throw new GlobalCommonException(GlobalErrorCode.LOGIN_FAILURE); } filterChain.doFilter(request, response); @@ -91,7 +101,6 @@ protected boolean shouldNotFilter(HttpServletRequest request) throws ServletExce path.startsWith("/v3/api-docs") || path.startsWith("/swagger-resources") || path.startsWith("/webjars") || - path.startsWith("/api/v1/sample") || - path.equals("/api/v1/auth"); + path.equals("/api/v1/auth"); // 권한 부여때문에(일단 열어둠) } } From 562f0df1daded3fe305ac3dcafbf80839811040b Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Sun, 17 Nov 2024 22:25:02 +0900 Subject: [PATCH 100/563] =?UTF-8?q?feat:=20=EB=B0=9C=EC=A3=BC=EC=84=9C=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EA=B5=AC=ED=98=84(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/repository/OrderRepository.java | 3 + .../common/exception/OrderErrorCode.java | 1 + .../controller/PurchaseOrderController.java | 44 ++++++++++ .../dto/PurchaseOrderRegistDTO.java | 15 ++++ .../service/PurchaseOrderCommandService.java | 7 ++ .../aggregate/entity/PurchaseOrder.java | 80 +++++++++++++++++++ .../repository/PurchaseOrderRepository.java | 7 ++ .../PurchaseOrderCommandServiceImpl.java | 55 +++++++++++++ .../PurchaseOrderCommonException.java | 16 ++++ .../exception/PurchaseOrderErrorCode.java | 51 ++++++++++++ .../PurchaseOrderExceptionResponse.java | 22 +++++ .../PurchaseOrderResponseMessage.java | 14 ++++ src/main/resources/sql/ddl.sql | 38 ++++----- 13 files changed, 334 insertions(+), 19 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderRegistDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/service/PurchaseOrderCommandService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/aggregate/entity/PurchaseOrder.java create mode 100644 src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/repository/PurchaseOrderRepository.java create mode 100644 src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java create mode 100644 src/main/java/stanl_2/final_backend/domain/purchase_order/common/exception/PurchaseOrderCommonException.java create mode 100644 src/main/java/stanl_2/final_backend/domain/purchase_order/common/exception/PurchaseOrderErrorCode.java create mode 100644 src/main/java/stanl_2/final_backend/domain/purchase_order/common/exception/PurchaseOrderExceptionResponse.java create mode 100644 src/main/java/stanl_2/final_backend/domain/purchase_order/common/response/PurchaseOrderResponseMessage.java diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/domain/repository/OrderRepository.java b/src/main/java/stanl_2/final_backend/domain/order/command/domain/repository/OrderRepository.java index 3b8342cc..9bc411e2 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/domain/repository/OrderRepository.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/domain/repository/OrderRepository.java @@ -4,4 +4,7 @@ import stanl_2.final_backend.domain.order.command.domain.aggregate.entity.Order; public interface OrderRepository extends JpaRepository { + Order findByOrderIdAndMemberId(String orderId, String memberId); + + Order findByOrderIdAndStatus(String orderId, String approved); } diff --git a/src/main/java/stanl_2/final_backend/domain/order/common/exception/OrderErrorCode.java b/src/main/java/stanl_2/final_backend/domain/order/common/exception/OrderErrorCode.java index f5e5bb4a..05350a0c 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/common/exception/OrderErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/order/common/exception/OrderErrorCode.java @@ -39,6 +39,7 @@ public enum OrderErrorCode { * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. */ ORDER_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "수주서를 찾지 못했습니다"), + ORDER_STATUS_NOT_APPROVED(404002, HttpStatus.NOT_FOUND, "승인되지 않은 수주서입니다."), /** * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. */ diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java new file mode 100644 index 00000000..324c3f09 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java @@ -0,0 +1,44 @@ +package stanl_2.final_backend.domain.purchase_order.command.application.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +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.RestController; +import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderRegistDTO; +import stanl_2.final_backend.domain.purchase_order.command.application.service.PurchaseOrderCommandService; +import stanl_2.final_backend.domain.purchase_order.common.response.PurchaseOrderResponseMessage; + +@RestController("PurchaseOrderCommandController") +@RequestMapping("/api/v1/purchase-order") +public class PurchaseOrderController { + + private final PurchaseOrderCommandService purchaseOrderCommandService; + + @Autowired + public PurchaseOrderController(PurchaseOrderCommandService purchaseOrderCommandService) { + this.purchaseOrderCommandService = purchaseOrderCommandService; + } + + @Operation(summary = "발주서 등록") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "발주서 등록 성공", + content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) + }) + @PostMapping("") + public ResponseEntity postPurchaseOrder(@RequestBody PurchaseOrderRegistDTO purchaseOrderRegistDTO) { + + purchaseOrderCommandService.registerPurchaseOrder(purchaseOrderRegistDTO); + + return ResponseEntity.ok(PurchaseOrderResponseMessage.builder() + .httpStatus(200) + .msg("발주서가 성공적으로 등록되었습니다.") + .build()); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderRegistDTO.java new file mode 100644 index 00000000..88376c48 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderRegistDTO.java @@ -0,0 +1,15 @@ +package stanl_2.final_backend.domain.purchase_order.command.application.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class PurchaseOrderRegistDTO { + private String title; + private String content; + private String orderId; + private String memberId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/service/PurchaseOrderCommandService.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/service/PurchaseOrderCommandService.java new file mode 100644 index 00000000..67189891 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/service/PurchaseOrderCommandService.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.purchase_order.command.application.service; + +import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderRegistDTO; + +public interface PurchaseOrderCommandService { + void registerPurchaseOrder(PurchaseOrderRegistDTO purchaseOrderRegistDTO); +} diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/aggregate/entity/PurchaseOrder.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/aggregate/entity/PurchaseOrder.java new file mode 100644 index 00000000..64ae1d05 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/aggregate/entity/PurchaseOrder.java @@ -0,0 +1,80 @@ +package stanl_2.final_backend.domain.purchase_order.command.domain.aggregate.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "TB_PURCHASE_ORDER") +public class PurchaseOrder { + + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "PUR") + ) + @Column(name = "PUR_ORD_ID") + private String purchaseOrderId; + + @Column(name = "PUR_ORD_TTL", nullable = false) + private String title; + + @Lob + @Column(name = "PUR_ORD_CONT", nullable = false, columnDefinition = "TEXT") + private String content; + + @Column(name = "ACTIVE", nullable = false) + private Boolean active = true; + + @Column(name = "CREATED_AT", nullable = false, updatable = false) + private String createdAt; + + @Column(name = "UPDATED_AT", nullable = false) + private String updatedAt; + + @Column(name = "DELETED_AT") + private String deletedAt; + + @Column(name = "PUR_ORD_STAT", nullable = false) + private String status; + + @Column(name = "ORD_ID", nullable = false) + private String orderId; + + @Column(name = "ADMIN_ID") + private String adminId; + + @Column(name = "MEM_ID", nullable = false) + private String memberId; + + // Insert 되기 전에 실행 + @PrePersist + private void prePersist() { + this.createdAt = getCurrentTime(); + this.updatedAt = this.createdAt; + } + + // Update 되기 전에 실행 + @PreUpdate + private void preUpdate() { + this.updatedAt = getCurrentTime(); + } + + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/repository/PurchaseOrderRepository.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/repository/PurchaseOrderRepository.java new file mode 100644 index 00000000..e5a1b2e9 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/repository/PurchaseOrderRepository.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.purchase_order.command.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import stanl_2.final_backend.domain.purchase_order.command.domain.aggregate.entity.PurchaseOrder; + +public interface PurchaseOrderRepository extends JpaRepository { +} diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java new file mode 100644 index 00000000..a106cb9d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java @@ -0,0 +1,55 @@ +package stanl_2.final_backend.domain.purchase_order.command.domain.service; + +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.order.command.domain.aggregate.entity.Order; +import stanl_2.final_backend.domain.order.command.domain.repository.OrderRepository; +import stanl_2.final_backend.domain.order.common.exception.OrderCommonException; +import stanl_2.final_backend.domain.order.common.exception.OrderErrorCode; +import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderRegistDTO; +import stanl_2.final_backend.domain.purchase_order.command.application.service.PurchaseOrderCommandService; +import stanl_2.final_backend.domain.purchase_order.command.domain.aggregate.entity.PurchaseOrder; +import stanl_2.final_backend.domain.purchase_order.command.domain.repository.PurchaseOrderRepository; + +@Service +public class PurchaseOrderCommandServiceImpl implements PurchaseOrderCommandService { + + private final PurchaseOrderRepository purchaseOrderRepository; + private final OrderRepository orderRepository; + private final ModelMapper modelMapper; + + @Autowired + public PurchaseOrderCommandServiceImpl(PurchaseOrderRepository purchaseOrderRepository, OrderRepository orderRepository, ModelMapper modelMapper) { + this.purchaseOrderRepository = purchaseOrderRepository; + this.orderRepository = orderRepository; + this.modelMapper = modelMapper; + } + + @Override + @Transactional + public void registerPurchaseOrder(PurchaseOrderRegistDTO purchaseOrderRegistDTO) { + + // 회원인지 확인 + + // 수주서가 맞는지 + Order orderCheck = orderRepository.findByOrderIdAndMemberId(purchaseOrderRegistDTO.getOrderId(), purchaseOrderRegistDTO.getMemberId()); + + if(orderCheck == null) { + throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); + } + + // 수주서의 승인상태가 APPROVED인지 확인 + Order orderStatus = orderRepository.findByOrderIdAndStatus(orderCheck.getOrderId(), "APPROVED"); + + if(orderStatus == null) { + throw new OrderCommonException(OrderErrorCode.ORDER_STATUS_NOT_APPROVED); + } + + PurchaseOrder purchaseOrder = modelMapper.map(purchaseOrderRegistDTO, PurchaseOrder.class); + purchaseOrder.setStatus("WAIT"); + + purchaseOrderRepository.save(purchaseOrder); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/common/exception/PurchaseOrderCommonException.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/common/exception/PurchaseOrderCommonException.java new file mode 100644 index 00000000..97000a72 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/common/exception/PurchaseOrderCommonException.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.purchase_order.common.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class PurchaseOrderCommonException extends RuntimeException { + private final PurchaseOrderErrorCode sampleErrorCode; + + // 에러 발생시 ErroCode 별 메시지 + @Override + public String getMessage() { + return this.sampleErrorCode.getMsg(); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/common/exception/PurchaseOrderErrorCode.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/common/exception/PurchaseOrderErrorCode.java new file mode 100644 index 00000000..f11505ff --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/common/exception/PurchaseOrderErrorCode.java @@ -0,0 +1,51 @@ +package stanl_2.final_backend.domain.purchase_order.common.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum PurchaseOrderErrorCode { + + /** + * 400(Bad Request) + * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. + */ + + + + /** + * 401(Unauthorized) + * 비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만, + * 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다. + * 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다. + */ + + + /** + * 403(Forbidden) + * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. + * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다. + */ + + + + /** + * 404(Not Found) + * 서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다. + * 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있습니다. + * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. + * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. + */ + PURCHASE_ORDER_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "발주서를 찾지 못했습니다"), + /** + * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. + */ + INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); + + + private final Integer code; + private final HttpStatus httpStatus; + private final String msg; +} diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/common/exception/PurchaseOrderExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/common/exception/PurchaseOrderExceptionResponse.java new file mode 100644 index 00000000..6b4c2851 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/common/exception/PurchaseOrderExceptionResponse.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.purchase_order.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class PurchaseOrderExceptionResponse { + private final Integer code; + private final String msg; + private final HttpStatus httpStatus; + + public PurchaseOrderExceptionResponse(PurchaseOrderErrorCode sampleErrorCode) { + this.code = sampleErrorCode.getCode(); + this.msg = sampleErrorCode.getMsg(); + this.httpStatus = sampleErrorCode.getHttpStatus(); + } + + public static PurchaseOrderExceptionResponse of(PurchaseOrderErrorCode sampleErrorCode) { + return new PurchaseOrderExceptionResponse(sampleErrorCode); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/common/response/PurchaseOrderResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/common/response/PurchaseOrderResponseMessage.java new file mode 100644 index 00000000..e844a46a --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/common/response/PurchaseOrderResponseMessage.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.purchase_order.common.response; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class PurchaseOrderResponseMessage { + private int httpStatus; + private String msg; + private Object result; +} \ No newline at end of file diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index f1b89a7a..eb52ea5b 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -213,7 +213,7 @@ CREATE TABLE tb_purchase_order ( PUR_ORD_ID VARCHAR(255) NOT NULL, PUR_ORD_TTL VARCHAR(255) NOT NULL, - PUR_CONT TEXT NOT NULL, + PUR_ORD_CONT TEXT NOT NULL, CREATED_AT CHAR(19) NOT NULL, UPDATED_AT CHAR(19) NOT NULL, DELETED_AT CHAR(19) NULL, @@ -622,7 +622,7 @@ VALUES ('CON_000000002', '스포티지 계약', '김영희', '900123-2345678', '부산광역시 해운대구', 'younghee@example.com', '010-2345-6789', '기아자동차', 'PERSONAL', 'CREDIT', 'SER_002', '기본옵션', 3000000, 200000, 17000000, 25000000, - '2024-03-05', '부산광역시 해운대구 배송센터', 'CONFIRMED', 1, + '2024-03-05', '부산광역시 해운대구 배송센터', 'APPROVED', 1, '/contracts/2', NULL, TRUE, '2024-01-12 14:00:00', '2024-01-13 15:00:00', NULL, 'MEM_000000002', 'CEN_000000002', 'CUS_000000002', 'PRO_000000002'), @@ -638,7 +638,7 @@ VALUES ('CON_000000004', '셀토스 계약', '박민준', '970512-4567890', '인천광역시 미추홀구', 'minjoon@example.com', '010-4567-8901', '기아자동차', 'PERSONAL', 'CASH', 'SER_004', '기본옵션', 4000000, 250000, 12000000, 20000000, - '2024-04-01', '인천광역시 배송센터', 'DELIVERED', 1, + '2024-04-01', '인천광역시 배송센터', 'CANCLED', 1, '/contracts/4', NULL, TRUE, '2024-01-16 11:00:00', '2024-01-17 12:00:00', NULL, 'MEM_000000004', 'CEN_000000004', 'CUS_000000004', 'PRO_000000004'), @@ -654,7 +654,7 @@ VALUES ('CON_000000006', '모하비 계약', '김민주', '850824-6789012', '대전광역시 서구', 'minju@example.com', '010-6789-0123', '기아자동차', 'PERSONAL', 'CASH', 'SER_006', '풀옵션', 6000000, 500000, 34000000, 40000000, - '2024-05-01', '대전광역시 배송센터', 'CONFIRMED', 1, + '2024-05-01', '대전광역시 배송센터', 'APPROVED', 1, '/contracts/6', NULL, TRUE, '2024-01-20 15:00:00', '2024-01-21 16:00:00', NULL, 'MEM_000000006', 'CEN_000000006', 'CUS_000000006', 'PRO_000000006'), @@ -670,7 +670,7 @@ VALUES ('CON_000000008', '스팅어 계약', '이준호', '930910-8901234', '경기도 수원시', 'junho@example.com', '010-8901-2345', '기아자동차', 'PERSONAL', 'CREDIT', 'SER_008', '풀옵션', 5000000, 300000, 22000000, 27000000, - '2024-06-01', '수원시 배송센터', 'DELIVERED', 1, + '2024-06-01', '수원시 배송센터', 'CANCLED', 1, '/contracts/8', NULL, TRUE, '2024-01-24 19:00:00', '2024-01-25 20:00:00', NULL, 'MEM_000000008', 'CEN_000000008', 'CUS_000000008', 'PRO_000000008'), @@ -678,7 +678,7 @@ VALUES ('CON_000000009', '니로 계약', '박소영', '890321-9012345', '충청북도 청주시', 'soyoung@example.com', '010-9012-3456', '기아자동차', 'PERSONAL', 'CASH', 'SER_009', '기본옵션', 3500000, 150000, 17000000, 23000000, - '2024-06-15', '청주시 배송센터', 'CONFIRMED', 1, + '2024-06-15', '청주시 배송센터', 'APPROVED', 1, '/contracts/9', NULL, TRUE, '2024-01-26 21:00:00', '2024-01-27 22:00:00', NULL, 'MEM_000000009', 'CEN_000000009', 'CUS_000000009', 'PRO_000000009'), @@ -694,25 +694,25 @@ INSERT INTO tb_order ( ORD_ID, ORD_TTL, ORD_CONT, ACTIVE, CREATED_AT, UPDATED_AT, DELETED_AT, ORD_STAT, CONR_ID, MEM_ID, ADMIN_ID ) VALUES -- 주문 1 -('ORD_000000001', '쏘렌토 계약 주문', '쏘렌토 차량 계약 완료 후 주문 처리', TRUE, '2024-01-10 10:00:00', '2024-01-10 11:00:00', NULL, 'CONFIRMED', 'CON_000000001', 'MEM_000000001', 'MEM_000000005'), +('ORD_000000001', '쏘렌토 계약 주문', '쏘렌토 차량 계약 완료 후 주문 처리', TRUE, '2024-01-10 10:00:00', '2024-01-10 11:00:00', NULL, 'APPROVED', 'CON_000000001', 'MEM_000000001', 'MEM_000000005'), -- 주문 2 ('ORD_000000002', '스포티지 구매 주문', '스포티지 고객 상담 후 주문 확정', TRUE, '2024-01-11 12:00:00', '2024-01-11 13:00:00', NULL, 'WAIT', 'CON_000000002', 'MEM_000000002', 'MEM_000000005'), -- 주문 3 -('ORD_000000003', 'K7 리스 주문', 'K7 리스 계약 완료 후 주문 생성', TRUE, '2024-01-12 09:00:00', '2024-01-12 10:00:00', NULL, 'CONFIRMED', 'CON_000000003', 'MEM_000000003', 'MEM_000000005'), +('ORD_000000003', 'K7 리스 주문', 'K7 리스 계약 완료 후 주문 생성', TRUE, '2024-01-12 09:00:00', '2024-01-12 10:00:00', NULL, 'APPROVED', 'CON_000000003', 'MEM_000000003', 'MEM_000000005'), -- 주문 4 -('ORD_000000004', '셀토스 추가 주문', '셀토스 고객 추가 옵션 주문', TRUE, '2024-01-13 10:30:00', '2024-01-13 11:30:00', NULL, 'CANCELLED', 'CON_000000004', 'MEM_000000004', 'MEM_000000005'), +('ORD_000000004', '셀토스 추가 주문', '셀토스 고객 추가 옵션 주문', TRUE, '2024-01-13 10:30:00', '2024-01-13 11:30:00', NULL, 'CANCLED', 'CON_000000004', 'MEM_000000004', 'MEM_000000005'), -- 주문 5 ('ORD_000000005', 'K3 계약 주문', 'K3 차량 계약 후 최종 주문', TRUE, '2024-01-14 11:00:00', '2024-01-14 12:00:00', NULL, 'DELIVERED', 'CON_000000005', 'MEM_000000001', 'MEM_000000006'), -- 주문 6 ('ORD_000000006', '모하비 계약 수정 주문', '모하비 계약 변경에 따른 주문 수정', TRUE, '2024-01-15 13:00:00', '2024-01-15 14:00:00', NULL, 'WAIT', 'CON_000000006', 'MEM_000000002', 'MEM_000000006'), -- 주문 7 -('ORD_000000007', 'K8 리스 주문', 'K8 차량 리스 계약 주문', TRUE, '2024-01-16 14:30:00', '2024-01-16 15:30:00', NULL, 'CONFIRMED', 'CON_000000007', 'MEM_000000003', 'MEM_000000006'), +('ORD_000000007', 'K8 리스 주문', 'K8 차량 리스 계약 주문', TRUE, '2024-01-16 14:30:00', '2024-01-16 15:30:00', NULL, 'APPROVED', 'CON_000000007', 'MEM_000000003', 'MEM_000000006'), -- 주문 8 -('ORD_000000008', '스팅어 구매 주문', '스팅어 차량 구매 계약 후 주문 확정', TRUE, '2024-01-17 16:00:00', '2024-01-17 17:00:00', NULL, 'DELIVERED', 'CON_000000008', 'MEM_000000004', 'MEM_000000006'), +('ORD_000000008', '스팅어 구매 주문', '스팅어 차량 구매 계약 후 주문 확정', TRUE, '2024-01-17 16:00:00', '2024-01-17 17:00:00', NULL, 'WAIT', 'CON_000000008', 'MEM_000000004', 'MEM_000000006'), -- 주문 9 ('ORD_000000009', '니로 전기차 주문', '니로 전기차 계약 주문', TRUE, '2024-01-18 17:30:00', '2024-01-18 18:30:00', NULL, 'WAIT', 'CON_000000009', 'MEM_000000001', 'MEM_000000010'), -- 주문 10 -('ORD_000000010', 'K5 재고 주문', 'K5 재고 차량 추가 주문', TRUE, '2024-01-19 09:00:00', '2024-01-19 10:00:00', NULL, 'CONFIRMED', 'CON_000000010', 'MEM_000000001', 'MEM_00000009'); +('ORD_000000010', 'K5 재고 주문', 'K5 재고 차량 추가 주문', TRUE, '2024-01-19 09:00:00', '2024-01-19 10:00:00', NULL, 'APPROVED', 'CON_000000010', 'MEM_000000001', 'MEM_00000009'); INSERT INTO tb_problem (PROB_ID, PROB_TTL, PROB_CONT, CREATED_AT, UPDATED_AT, ACTIVE, CUST_ID, MEM_ID, PROD_ID) @@ -737,26 +737,26 @@ VALUES ('PROB_000000001', '쏘렌토 엔진 문제', '엔진에서 소음 발생 ('PROB_000000010', 'K5 시동 문제', '시동이 걸리지 않음', '2024-01-21 17:00:00', '2024-01-21 18:00:00', TRUE, 'CUS_000000010', 'MEM_000000010', 'PRO_000000010'); -INSERT INTO tb_purchase_order (PUR_ORD_ID, PUR_ORD_TTL, PUR_CONT, CREATED_AT, UPDATED_AT, ACTIVE, PUR_ORD_STAT, ORD_ID, +INSERT INTO tb_purchase_order (PUR_ORD_ID, PUR_ORD_TTL, PUR_ORD_CONT, CREATED_AT, UPDATED_AT, ACTIVE, PUR_ORD_STAT, ORD_ID, MEM_ID) VALUES ('PUR_000000001', '쏘렌토 부품 주문', '엔진 부품 요청', '2024-01-12 10:00:00', '2024-01-12 11:00:00', TRUE, 'WAIT', 'ORD_000000001', 'MEM_000000001'), - ('PUR_000000002', '스포티지 타이어 주문', '타이어 4개 요청', '2024-01-13 12:00:00', '2024-01-13 13:00:00', TRUE, 'CONFIRMED', + ('PUR_000000002', '스포티지 타이어 주문', '타이어 4개 요청', '2024-01-13 12:00:00', '2024-01-13 13:00:00', TRUE, 'APPROVED', 'ORD_000000002', 'MEM_000000002'), - ('PUR_000000003', 'K7 배터리 주문', '배터리 교체 요청', '2024-01-14 13:00:00', '2024-01-14 14:00:00', TRUE, 'DELIVERED', + ('PUR_000000003', 'K7 배터리 주문', '배터리 교체 요청', '2024-01-14 13:00:00', '2024-01-14 14:00:00', TRUE, 'CANCLED', 'ORD_000000003', 'MEM_000000003'), ('PUR_000000004', '셀토스 브레이크 주문', '브레이크 패드 요청', '2024-01-15 14:00:00', '2024-01-15 15:00:00', TRUE, 'WAIT', 'ORD_000000004', 'MEM_000000004'), - ('PUR_000000005', 'K3 내비게이션 주문', '내비게이션 교체', '2024-01-16 15:00:00', '2024-01-16 16:00:00', TRUE, 'CANCELLED', + ('PUR_000000005', 'K3 내비게이션 주문', '내비게이션 교체', '2024-01-16 15:00:00', '2024-01-16 16:00:00', TRUE, 'APPROVED', 'ORD_000000005', 'MEM_000000005'), ('PUR_000000006', '모하비 오일 필터 주문', '오일 필터 10개 요청', '2024-01-17 09:00:00', '2024-01-17 10:00:00', TRUE, - 'CONFIRMED', 'ORD_000000006', 'MEM_000000006'), + 'APPROVED', 'ORD_000000006', 'MEM_000000006'), ('PUR_000000007', 'K8 헤드라이트 주문', 'LED 헤드라이트 2개 요청', '2024-01-18 10:00:00', '2024-01-18 11:00:00', TRUE, 'WAIT', 'ORD_000000007', 'MEM_000000007'), - ('PUR_000000008', '스팅어 도어 핸들 주문', '도어 핸들 4개 요청', '2024-01-19 12:00:00', '2024-01-19 13:00:00', TRUE, 'CONFIRMED', + ('PUR_000000008', '스팅어 도어 핸들 주문', '도어 핸들 4개 요청', '2024-01-19 12:00:00', '2024-01-19 13:00:00', TRUE, 'APPROVED', 'ORD_000000008', 'MEM_000000008'), ('PUR_000000009', '니로 충전 케이블 주문', '전기차 충전 케이블 요청', '2024-01-20 14:00:00', '2024-01-20 15:00:00', TRUE, - 'DELIVERED', 'ORD_000000009', 'MEM_000000009'), + 'CANCLED', 'ORD_000000009', 'MEM_000000009'), ('PUR_000000010', 'K5 엔진오일 주문', '엔진오일 20L 요청', '2024-01-21 15:00:00', '2024-01-21 16:00:00', TRUE, 'WAIT', 'ORD_000000010', 'MEM_000000010'); From 416cc1e769b1c833c5f9a854290f0079eaa3969d Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Sun, 17 Nov 2024 22:52:54 +0900 Subject: [PATCH 101/563] =?UTF-8?q?feat:=20=EB=B0=9C=EC=A3=BC=EC=84=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EA=B5=AC=ED=98=84(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PurchaseOrderController.java | 24 +++++++-- .../dto/PurchaseOrderModifyDTO.java | 16 ++++++ .../service/PurchaseOrderCommandService.java | 3 ++ .../repository/PurchaseOrderRepository.java | 3 ++ .../PurchaseOrderCommandServiceImpl.java | 54 +++++++++++++++---- 5 files changed, 86 insertions(+), 14 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderModifyDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java index 324c3f09..522cb80e 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java @@ -7,10 +7,8 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; -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.RestController; +import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderModifyDTO; import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderRegistDTO; import stanl_2.final_backend.domain.purchase_order.command.application.service.PurchaseOrderCommandService; import stanl_2.final_backend.domain.purchase_order.common.response.PurchaseOrderResponseMessage; @@ -41,4 +39,22 @@ public ResponseEntity postPurchaseOrder(@RequestBo .msg("발주서가 성공적으로 등록되었습니다.") .build()); } + + @Operation(summary = "발주서 수정") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "발주서 수정 성공", + content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) + }) + @PutMapping("{id}") + public ResponseEntity putPurchaseOrder(@PathVariable String id, + @RequestBody PurchaseOrderModifyDTO purchaseOrderModifyDTO) { + purchaseOrderModifyDTO.setPurchaseOrderId(id); + PurchaseOrderModifyDTO purchaseOrderModifyResponse = purchaseOrderCommandService.modifyPurchaseOrder(purchaseOrderModifyDTO); + + return ResponseEntity.ok(PurchaseOrderResponseMessage.builder() + .httpStatus(200) + .msg("발주서가 성공적으로 수정되었습니다.") + .result(purchaseOrderModifyResponse) + .build()); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderModifyDTO.java new file mode 100644 index 00000000..e5db540c --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderModifyDTO.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.purchase_order.command.application.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class PurchaseOrderModifyDTO { + private String purchaseOrderId; + private String title; + private String content; + private String orderId; + private String memberId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/service/PurchaseOrderCommandService.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/service/PurchaseOrderCommandService.java index 67189891..6459b2b9 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/service/PurchaseOrderCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/service/PurchaseOrderCommandService.java @@ -1,7 +1,10 @@ package stanl_2.final_backend.domain.purchase_order.command.application.service; +import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderModifyDTO; import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderRegistDTO; public interface PurchaseOrderCommandService { void registerPurchaseOrder(PurchaseOrderRegistDTO purchaseOrderRegistDTO); + + PurchaseOrderModifyDTO modifyPurchaseOrder(PurchaseOrderModifyDTO purchaseOrderModifyDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/repository/PurchaseOrderRepository.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/repository/PurchaseOrderRepository.java index e5a1b2e9..ad21958e 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/repository/PurchaseOrderRepository.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/repository/PurchaseOrderRepository.java @@ -3,5 +3,8 @@ import org.springframework.data.jpa.repository.JpaRepository; import stanl_2.final_backend.domain.purchase_order.command.domain.aggregate.entity.PurchaseOrder; +import java.util.Optional; + public interface PurchaseOrderRepository extends JpaRepository { + Optional findByPurchaseOrderIdAndMemberId(String purchaseOrderId, String memberId); } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java index a106cb9d..391f7902 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java @@ -8,10 +8,13 @@ import stanl_2.final_backend.domain.order.command.domain.repository.OrderRepository; import stanl_2.final_backend.domain.order.common.exception.OrderCommonException; import stanl_2.final_backend.domain.order.common.exception.OrderErrorCode; +import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderModifyDTO; import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderRegistDTO; import stanl_2.final_backend.domain.purchase_order.command.application.service.PurchaseOrderCommandService; import stanl_2.final_backend.domain.purchase_order.command.domain.aggregate.entity.PurchaseOrder; import stanl_2.final_backend.domain.purchase_order.command.domain.repository.PurchaseOrderRepository; +import stanl_2.final_backend.domain.purchase_order.common.exception.PurchaseOrderCommonException; +import stanl_2.final_backend.domain.purchase_order.common.exception.PurchaseOrderErrorCode; @Service public class PurchaseOrderCommandServiceImpl implements PurchaseOrderCommandService { @@ -31,19 +34,15 @@ public PurchaseOrderCommandServiceImpl(PurchaseOrderRepository purchaseOrderRepo @Transactional public void registerPurchaseOrder(PurchaseOrderRegistDTO purchaseOrderRegistDTO) { - // 회원인지 확인 - - // 수주서가 맞는지 - Order orderCheck = orderRepository.findByOrderIdAndMemberId(purchaseOrderRegistDTO.getOrderId(), purchaseOrderRegistDTO.getMemberId()); - - if(orderCheck == null) { + // 수주서가 존재하는지 확인 + Order order = orderRepository.findByOrderIdAndMemberId( + purchaseOrderRegistDTO.getOrderId(), purchaseOrderRegistDTO.getMemberId()); + if (order == null) { throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); } - // 수주서의 승인상태가 APPROVED인지 확인 - Order orderStatus = orderRepository.findByOrderIdAndStatus(orderCheck.getOrderId(), "APPROVED"); - - if(orderStatus == null) { + // 수주서의 상태가 APPROVED인지 확인 + if (!"APPROVED".equals(order.getStatus())) { throw new OrderCommonException(OrderErrorCode.ORDER_STATUS_NOT_APPROVED); } @@ -52,4 +51,39 @@ public void registerPurchaseOrder(PurchaseOrderRegistDTO purchaseOrderRegistDTO) purchaseOrderRepository.save(purchaseOrder); } + + @Override + @Transactional + public PurchaseOrderModifyDTO modifyPurchaseOrder(PurchaseOrderModifyDTO purchaseOrderModifyDTO) { + + // 회원인지 확인 및 발주서 조회 + PurchaseOrder purchaseOrder = (PurchaseOrder) purchaseOrderRepository.findByPurchaseOrderIdAndMemberId( + purchaseOrderModifyDTO.getPurchaseOrderId(), purchaseOrderModifyDTO.getMemberId()) + .orElseThrow(() -> new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND)); + + // 수주서가 존재하는지 확인 + Order order = orderRepository.findByOrderIdAndMemberId( + purchaseOrderModifyDTO.getOrderId(), purchaseOrderModifyDTO.getMemberId()); + if (order == null) { + throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); + } + + // 수주서의 상태가 APPROVED인지 확인 + if (!"APPROVED".equals(order.getStatus())) { + throw new OrderCommonException(OrderErrorCode.ORDER_STATUS_NOT_APPROVED); + } + + PurchaseOrder updatePurchaseOrder = modelMapper.map(purchaseOrderModifyDTO, PurchaseOrder.class); + + updatePurchaseOrder.setCreatedAt(purchaseOrder.getCreatedAt()); + updatePurchaseOrder.setUpdatedAt(purchaseOrder.getUpdatedAt()); + updatePurchaseOrder.setStatus(purchaseOrder.getStatus()); + updatePurchaseOrder.setActive(purchaseOrder.getActive()); + + purchaseOrderRepository.save(updatePurchaseOrder); + + PurchaseOrderModifyDTO purchaseOrderModifyResponse = modelMapper.map(updatePurchaseOrder, PurchaseOrderModifyDTO.class); + + return purchaseOrderModifyResponse; + } } From 2c80e5c7aa48804825e0987b38ef63bae15cd860 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Sun, 17 Nov 2024 23:03:14 +0900 Subject: [PATCH 102/563] =?UTF-8?q?feat:=20batch=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=B6=94=EA=B0=80=20=EC=84=B8=ED=8C=85=20?= =?UTF-8?q?(#57)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/batch/BatchConfig.java | 45 ++++++++++++++----- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/batch/BatchConfig.java b/src/main/java/stanl_2/final_backend/domain/batch/BatchConfig.java index 84eef8cb..72f8d512 100644 --- a/src/main/java/stanl_2/final_backend/domain/batch/BatchConfig.java +++ b/src/main/java/stanl_2/final_backend/domain/batch/BatchConfig.java @@ -4,22 +4,19 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; -import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.configuration.annotation.JobScope; import org.springframework.batch.core.configuration.annotation.StepScope; import org.springframework.batch.core.job.builder.JobBuilder; import org.springframework.batch.core.launch.support.RunIdIncrementer; import org.springframework.batch.core.repository.JobRepository; -import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.step.builder.StepBuilder; -import org.springframework.batch.core.step.tasklet.Tasklet; -import org.springframework.batch.item.data.RepositoryItemReader; -import org.springframework.batch.item.data.RepositoryItemWriter; -import org.springframework.batch.repeat.RepeatStatus; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.data.domain.Sort; import org.springframework.transaction.PlatformTransactionManager; -import stanl_2.final_backend.domain.schedule.command.domain.aggregate.entity.Schedule; + +import java.util.Collections; +import java.util.List; @Slf4j @Configuration @@ -42,7 +39,7 @@ public Job checkJob(JobRepository jobRepository, PlatformTransactionManager tran @JobScope public Step checkStep(JobRepository jobRepository, PlatformTransactionManager transactionManager){ return new StepBuilder(STEP_NAME, jobRepository) - .<>chunk(5,transactionManager) + .chunk(5,transactionManager) .reqder(checkReader()) .processor(checkProcessor()) .writer(checkWriter()) @@ -51,9 +48,35 @@ public Step checkStep(JobRepository jobRepository, PlatformTransactionManager tr @Bean @StepScope - public RepositoryItemWriter<> checkWtriter(){ - return new RepositoryItemWriter<>() - .repository + public RepositoryItemWriter checkWtriter(){ + return new RepositoryItemWriter() + .repository(SalesStatisticsRepository) + .methodName("save") + .build(); + } + + @Bean + @StepScope + public ItemProcessor checkProcessor(){ + return new ItemProcessor() { + @Override + public SalesStatistics process(SalesHistory item) throws Exception { + return new SalesStatistics(item); + } + }; + } + + @Bean + @StepScope + public RepositoryItemReader checkReader(){ + return new RepositoryItemReaderBuilder() + .name("checkReader") + .repository(SalesStatisticsRepository) + .methodName("findAll") + .pageSize(5) + .arguments(List.of()) + .sorts(Collections.singletonMap("id", Sort.Direction.ASC)) + .build(); } // @Bean From 7e98a76797d59800ca0a96a55af8ae980bb4322d Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Sun, 17 Nov 2024 23:05:32 +0900 Subject: [PATCH 103/563] =?UTF-8?q?feat:=20=EB=B0=9C=EC=A3=BC=EC=84=9C=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EA=B5=AC=ED=98=84(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PurchaseOrderController.java | 17 +++++++++++++ .../service/PurchaseOrderCommandService.java | 2 ++ .../repository/PurchaseOrderRepository.java | 2 ++ .../PurchaseOrderCommandServiceImpl.java | 25 +++++++++++++++++++ 4 files changed, 46 insertions(+) diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java index 522cb80e..aa268032 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java @@ -57,4 +57,21 @@ public ResponseEntity putPurchaseOrder(@PathVariab .result(purchaseOrderModifyResponse) .build()); } + + @Operation(summary = "발주서 삭제") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "발주서 삭제 성공", + content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) + }) + @DeleteMapping("{id}") + public ResponseEntity deletePurchaseOrder(@PathVariable String id) { + + purchaseOrderCommandService.deletePurchaseOrder(id); + + return ResponseEntity.ok(PurchaseOrderResponseMessage.builder() + .httpStatus(200) + .msg("발주서가 성공적으로 삭제되었습니다.") + .result(null) + .build()); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/service/PurchaseOrderCommandService.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/service/PurchaseOrderCommandService.java index 6459b2b9..546e4a47 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/service/PurchaseOrderCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/service/PurchaseOrderCommandService.java @@ -7,4 +7,6 @@ public interface PurchaseOrderCommandService { void registerPurchaseOrder(PurchaseOrderRegistDTO purchaseOrderRegistDTO); PurchaseOrderModifyDTO modifyPurchaseOrder(PurchaseOrderModifyDTO purchaseOrderModifyDTO); + + void deletePurchaseOrder(String id); } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/repository/PurchaseOrderRepository.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/repository/PurchaseOrderRepository.java index ad21958e..a6f40244 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/repository/PurchaseOrderRepository.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/repository/PurchaseOrderRepository.java @@ -7,4 +7,6 @@ public interface PurchaseOrderRepository extends JpaRepository { Optional findByPurchaseOrderIdAndMemberId(String purchaseOrderId, String memberId); + + Optional findByPurchaseOrderId(String id); } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java index 391f7902..18b351f4 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java @@ -16,6 +16,10 @@ import stanl_2.final_backend.domain.purchase_order.common.exception.PurchaseOrderCommonException; import stanl_2.final_backend.domain.purchase_order.common.exception.PurchaseOrderErrorCode; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + @Service public class PurchaseOrderCommandServiceImpl implements PurchaseOrderCommandService { @@ -30,6 +34,11 @@ public PurchaseOrderCommandServiceImpl(PurchaseOrderRepository purchaseOrderRepo this.modelMapper = modelMapper; } + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } + @Override @Transactional public void registerPurchaseOrder(PurchaseOrderRegistDTO purchaseOrderRegistDTO) { @@ -86,4 +95,20 @@ public PurchaseOrderModifyDTO modifyPurchaseOrder(PurchaseOrderModifyDTO purchas return purchaseOrderModifyResponse; } + + @Override + @Transactional + public void deletePurchaseOrder(String id) { + // 발주서가 해당 회원의 것인지 확인 (회원도 받아와서 하는걸로 나중에 수정) +// PurchaseOrder purchaseOrder = purchaseOrderRepository.findByIdAndMemberId(id, memberId) +// .orElseThrow(() -> new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND)); + + PurchaseOrder purchaseOrder = (PurchaseOrder) purchaseOrderRepository.findByPurchaseOrderId(id) + .orElseThrow(() -> new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND)); + + purchaseOrder.setActive(false); + purchaseOrder.setDeletedAt(getCurrentTime()); + + purchaseOrderRepository.save(purchaseOrder); + } } From 8006ee6def6f5d7a4e208dee6f6edf3ee33539f8 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Sun, 17 Nov 2024 23:48:31 +0900 Subject: [PATCH 104/563] =?UTF-8?q?feat:=20=ED=95=84=EC=9A=94=ED=95=9C=20D?= =?UTF-8?q?omain=20=EB=A7=8C=EB=93=A4=EA=B8=B0=20=EC=A0=84=20=EC=84=B8?= =?UTF-8?q?=ED=8C=85=20=EC=99=84=EB=A3=8C=20(#57)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/batch/BatchConfig.java | 6 ++--- .../domain/batch/BatchScheduler.java | 3 +-- src/main/resources/sql/ddl.sql | 23 ++++++++++++------- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/batch/BatchConfig.java b/src/main/java/stanl_2/final_backend/domain/batch/BatchConfig.java index 72f8d512..d68ce444 100644 --- a/src/main/java/stanl_2/final_backend/domain/batch/BatchConfig.java +++ b/src/main/java/stanl_2/final_backend/domain/batch/BatchConfig.java @@ -40,7 +40,7 @@ public Job checkJob(JobRepository jobRepository, PlatformTransactionManager tran public Step checkStep(JobRepository jobRepository, PlatformTransactionManager transactionManager){ return new StepBuilder(STEP_NAME, jobRepository) .chunk(5,transactionManager) - .reqder(checkReader()) + .reader(checkReader()) .processor(checkProcessor()) .writer(checkWriter()) .build(); @@ -48,7 +48,7 @@ public Step checkStep(JobRepository jobRepository, PlatformTransactionManager tr @Bean @StepScope - public RepositoryItemWriter checkWtriter(){ + public RepositoryItemWriter checkWriter(){ return new RepositoryItemWriter() .repository(SalesStatisticsRepository) .methodName("save") @@ -71,7 +71,7 @@ public SalesStatistics process(SalesHistory item) throws Exception { public RepositoryItemReader checkReader(){ return new RepositoryItemReaderBuilder() .name("checkReader") - .repository(SalesStatisticsRepository) + .repository(SalesHistoryRepository) .methodName("findAll") .pageSize(5) .arguments(List.of()) diff --git a/src/main/java/stanl_2/final_backend/domain/batch/BatchScheduler.java b/src/main/java/stanl_2/final_backend/domain/batch/BatchScheduler.java index 1f75dd06..bc4347b1 100644 --- a/src/main/java/stanl_2/final_backend/domain/batch/BatchScheduler.java +++ b/src/main/java/stanl_2/final_backend/domain/batch/BatchScheduler.java @@ -23,9 +23,8 @@ public BatchScheduler(JobLauncher jobLauncher, Job checkJob) { this.checkJob = checkJob; } - @Scheduled(cron = "0 03 00 * * ?", zone = "Asia/Seoul") - public void testJobRun() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, + public void checkJobRun() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException, JobRestartException { JobParameters jobParameters = new JobParameters( diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index b16d9187..13019718 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -22,6 +22,7 @@ DROP TABLE IF EXISTS tb_member; DROP TABLE IF EXISTS tb_center; DROP TABLE IF EXISTS tb_organization_chart; DROP TABLE IF EXISTS tb_sales_history; +DROP TABLE IF EXISTS tb_sales_statistics; DROP TABLE IF EXISTS BATCH_STEP_EXECUTION_SEQ; DROP TABLE IF EXISTS BATCH_STEP_EXECUTION_CONTEXT; DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_CONTEXT; @@ -408,6 +409,12 @@ CREATE TABLE tb_sales_history CONR_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고' ); +CREATE TABLE tb_sales_statistics +( + SAL_STC_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', + SAL_HIST_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고' +); + # spring batch & scheduler 테이블 CREATE TABLE BATCH_JOB_INSTANCE ( @@ -488,14 +495,14 @@ CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT references BATCH_JOB_EXECUTION (JOB_EXECUTION_ID) ) ENGINE = InnoDB; -CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ START WITH 1 MINVALUE 1 MAXVALUE 9223372036854775806 - INCREMENT BY 1 NOCACHE NOCYCLE ENGINE =InnoDB; - -CREATE SEQUENCE BATCH_JOB_EXECUTION_SEQ START WITH 1 MINVALUE 1 MAXVALUE 9223372036854775806 - INCREMENT BY 1 NOCACHE NOCYCLE ENGINE =InnoDB; - -CREATE SEQUENCE BATCH_JOB_SEQ START WITH 1 MINVALUE 1 MAXVALUE 9223372036854775806 - INCREMENT BY 1 NOCACHE NOCYCLE ENGINE =InnoDB; +# CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ START WITH 1 MINVALUE 1 MAXVALUE 9223372036854775806 +# INCREMENT BY 1 NOCACHE NOCYCLE ENGINE =InnoDB; +# +# CREATE SEQUENCE BATCH_JOB_EXECUTION_SEQ START WITH 1 MINVALUE 1 MAXVALUE 9223372036854775806 +# INCREMENT BY 1 NOCACHE NOCYCLE ENGINE =InnoDB; +# +# CREATE SEQUENCE BATCH_JOB_SEQ START WITH 1 MINVALUE 1 MAXVALUE 9223372036854775806 +# INCREMENT BY 1 NOCACHE NOCYCLE ENGINE =InnoDB; INSERT INTO tb_organization_chart (ORG_CHA_ID, ORG_CHA_NAME, ORG_CHA_DEPTH) From 7707a52ea9adac94ca0acfc6ff8dfb1abc0e0812 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Mon, 18 Nov 2024 00:05:42 +0900 Subject: [PATCH 105/563] =?UTF-8?q?feat:=20member=20=EC=98=88=EC=99=B8?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EB=B0=8F=20member=20LoginId=20=EB=B0=9B?= =?UTF-8?q?=EC=95=84=EC=98=A4=EA=B8=B0=20=EC=84=A4=EC=A0=95=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20(#66)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ScheduleController.java | 14 ++++++++++++-- .../service/ScheduleCommandServiceImpl.java | 5 +++-- .../query/controller/ScheduleController.java | 18 ++++++++++++------ .../service/ScheduleQueryServiceImpl.java | 11 +++++------ 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java index d926c300..61dfcd2c 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java @@ -13,6 +13,8 @@ import stanl_2.final_backend.domain.schedule.command.application.service.ScheduleCommandService; import stanl_2.final_backend.domain.schedule.common.response.ScheduleResponseMessage; +import java.security.Principal; + @RestController("commandScheduleController") @RequestMapping("/api/v1/schedule") public class ScheduleController { @@ -30,7 +32,11 @@ public ScheduleController(ScheduleCommandService scheduleCommandService) { content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity registSchedule(@RequestBody ScheduleRegistDTO scheduleRegistDTO){ + public ResponseEntity registSchedule(Principal principal + ,@RequestBody ScheduleRegistDTO scheduleRegistDTO){ + + String memberId = principal.getName(); + scheduleRegistDTO.setMemberId(memberId); Boolean answer = scheduleCommandService.registSchedule(scheduleRegistDTO); @@ -48,10 +54,14 @@ public ResponseEntity registSchedule(@RequestBody Sched content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) }) @PutMapping("{scheduleId}") - public ResponseEntity modifySchedule(@PathVariable String scheduleId, + public ResponseEntity modifySchedule(Principal principal, + @PathVariable String scheduleId, @RequestBody ScheduleModifyDTO scheduleModifyDTO){ + String memberId = principal.getName(); + scheduleModifyDTO.setMemberId(memberId); scheduleModifyDTO.setScheduleId(scheduleId); + Boolean answer = scheduleCommandService.modifySchedule(scheduleModifyDTO); return ResponseEntity.ok(ScheduleResponseMessage.builder() diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java index 810fa1a7..0ca8afc7 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java @@ -7,6 +7,8 @@ import org.springframework.data.mapping.MappingException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.member.common.exception.MemberCommonException; +import stanl_2.final_backend.domain.member.common.exception.MemberErrorCode; import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleModifyDTO; import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleRegistDTO; import stanl_2.final_backend.domain.schedule.command.application.service.ScheduleCommandService; @@ -67,8 +69,7 @@ public Boolean registSchedule(ScheduleRegistDTO scheduleRegistDTO) { public Boolean modifySchedule(ScheduleModifyDTO scheduleModifyDTO) { if(scheduleModifyDTO.getMemberId() == null){ - // 향후 memberException으로 바꿀 예정 - throw new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND); + throw new MemberCommonException(MemberErrorCode.MEMBER_NOT_FOUND); } Schedule schedule = scheduleRepository.findByScheduleId(scheduleModifyDTO.getScheduleId()) diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java index aa36aeb3..809bfcb2 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java @@ -17,6 +17,7 @@ import stanl_2.final_backend.domain.schedule.query.dto.ScheduleYearMonthDTO; import stanl_2.final_backend.domain.schedule.query.service.ScheduleQueryService; +import java.security.Principal; import java.util.List; @RestController("queryScheduleController") @@ -35,9 +36,10 @@ public ScheduleController(ScheduleQueryService scheduleQueryService) { @ApiResponse(responseCode = "200", description = "일정 조회 성공", content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) }) - @GetMapping("{memberId}") - public ResponseEntity selectAllSchedule(@PathVariable("memberId") String memberId){ + @GetMapping("") + public ResponseEntity selectAllSchedule(Principal principal){ + String memberId = principal.getName(); List schedules = scheduleQueryService.selectAllSchedule(memberId); return ResponseEntity.ok(ScheduleResponseMessage.builder() @@ -53,11 +55,13 @@ public ResponseEntity selectAllSchedule(@PathVariable(" @ApiResponse(responseCode = "200", description = "일정 조건별(년&일) 전체 조회 성공", content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) }) - @GetMapping("{memberId}/{year}/{month}") - public ResponseEntity selectMonthSchedule(@PathVariable("memberId") String memberId, + @GetMapping("{year}/{month}") + public ResponseEntity selectMonthSchedule(Principal principal, @PathVariable("year") String year, @PathVariable("month") String month){ + String memberId = principal.getName(); + ScheduleYearMonthDTO scheduleYearMonthDTO = new ScheduleYearMonthDTO(); scheduleYearMonthDTO.setMemberId(memberId); scheduleYearMonthDTO.setYear(year); @@ -78,10 +82,12 @@ public ResponseEntity selectMonthSchedule(@PathVariable @ApiResponse(responseCode = "200", description = "일정 상세 조회 성공", content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) }) - @GetMapping("{memberId}/{scheduleId}") - public ResponseEntity selectDetailSchedule(@PathVariable("memberId") String memberId, + @GetMapping("{scheduleId}") + public ResponseEntity selectDetailSchedule(Principal principal, @PathVariable("scheduleId") String scheduleId){ + String memberId = principal.getName();; + ScheduleDetailDTO scheduleDetailDTO = new ScheduleDetailDTO(); scheduleDetailDTO.setMemberId(memberId); scheduleDetailDTO.setScheduleId(scheduleId); diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java index d225da65..d1afd0f5 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java @@ -5,6 +5,8 @@ import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.member.common.exception.MemberCommonException; +import stanl_2.final_backend.domain.member.common.exception.MemberErrorCode; import stanl_2.final_backend.domain.schedule.common.exception.ScheduleCommonException; import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; @@ -39,8 +41,7 @@ public ScheduleQueryServiceImpl(ScheduleMapper scheduleMapper) { public List selectAllSchedule(String memberId) { if(memberId == null || memberId.trim().isEmpty()){ - // 향후 Member의 ErrorCode로 수정할 예정 - throw new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND); + throw new MemberCommonException(MemberErrorCode.MEMBER_NOT_FOUND); } String currentMonth = getCurrentTime().substring(0,7); @@ -61,8 +62,7 @@ public List selectAllSchedule(String memberId) { public List selectYearMonthSchedule(ScheduleYearMonthDTO scheduleYearMonthDTO) { if(scheduleYearMonthDTO.getMemberId() == null || scheduleYearMonthDTO.getMemberId().trim().isEmpty()){ - // 향후 Member의 ErrorCode로 수정할 예정 - throw new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND); + throw new MemberCommonException(MemberErrorCode.MEMBER_NOT_FOUND); } String yearMonth = scheduleYearMonthDTO.getYear() + "-" + scheduleYearMonthDTO.getMonth(); @@ -84,8 +84,7 @@ public List selectYearMonthSchedule(ScheduleYearMonthDTO s public ScheduleDetailDTO selectDetailSchedule(ScheduleDetailDTO scheduleDetailDTO) { if(scheduleDetailDTO.getMemberId() == null || scheduleDetailDTO.getMemberId().trim().isEmpty()){ - // 향후 Member의 ErrorCode로 수정할 예정 - throw new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND); + throw new MemberCommonException(MemberErrorCode.MEMBER_NOT_FOUND); } if(scheduleDetailDTO.getScheduleId() == null || scheduleDetailDTO.getScheduleId().trim().isEmpty()){ From c4888cbba4c86abdc068daf3ef8427f865c785b4 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 00:10:58 +0900 Subject: [PATCH 106/563] =?UTF-8?q?fix:=20ddl=20=EC=88=98=EC=A0=95(#60)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/sql/ddl.sql | 121 ++++++++++++++++++++++++++++++++- 1 file changed, 120 insertions(+), 1 deletion(-) diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index b16d9187..d4625ab0 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -31,6 +31,11 @@ DROP TABLE IF EXISTS BATCH_JOB_SEQ; DROP TABLE IF EXISTS BATCH_STEP_EXECUTION; DROP TABLE IF EXISTS BATCH_JOB_EXECUTION; DROP TABLE IF EXISTS BATCH_JOB_INSTANCE; +DROP TABLE IF EXISTS tb_family; +DROP TABLE IF EXISTS tb_education; +DROP TABLE IF EXISTS tb_certification; +DROP TABLE IF EXISTS tb_career; + -- 조직 관련 테이블 생성 CREATE TABLE tb_organization_chart @@ -324,6 +329,69 @@ CREATE TABLE tb_alarm FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) ); +CREATE TABLE tb_family +( + FAM_ID VARCHAR(255) NOT NULL, + FAM_NAME VARCHAR(255) NOT NULL, + FAM_REL VARCHAR(255) NOT NULL, + FAM_BIR VARCHAR(255) NOT NULL, + FAM_IDEN_NO VARCHAR(255) NOT NULL, + FAM_PHO VARCHAR(255) NOT NULL, + FAM_SEX VARCHAR(255) NOT NULL, + FAM_DIS BOOLEAN NOT NULL, + FAM_DIE BOOLEAN NOT NULL, + FAM_NOTE VARCHAR(255) NULL, + CREATED_AT CHAR(19) NOT NULL, + UPDATED_AT CHAR(19) NOT NULL, + MEM_ID VARCHAR(255) NOT NULL, + PRIMARY KEY (FAM_ID), + FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) +); + +CREATE TABLE tb_education +( + EDU_ID VARCHAR(255) NOT NULL, + EDU_ENTD VARCHAR(255) NOT NULL, + EDU_GRAD VARCHAR(255) NOT NULL, + EDU_NAME VARCHAR(255) NOT NULL, + EDU_MJR VARCHAR(255) NOT NULL, + EDU_SCO VARCHAR(255) NULL, + EDU_NOTE VARCHAR(255) NULL, + CREATED_AT CHAR(19) NOT NULL, + UPDATED_AT CHAR(19) NOT NULL, + MEM_ID VARCHAR(255) NOT NULL, + PRIMARY KEY (EDU_ID), + FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) +); + +CREATE TABLE tb_certification +( + CER_ID VARCHAR(255) NOT NULL, + CER_DATE VARCHAR(255) NOT NULL, + CER_INST VARCHAR(255) NOT NULL, + CER_NAME VARCHAR(255) NOT NULL, + CER_SCO VARCHAR(255) NOT NULL, + CER_NOTE VARCHAR(255) NULL, + CREATED_AT CHAR(19) NOT NULL, + MEM_ID VARCHAR(255) NOT NULL, + PRIMARY KEY (CER_ID), + FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) +); + +CREATE TABLE tb_career +( + CAR_ID VARCHAR(255) NOT NULL, + CAR_EMP_DATE VARCHAR(255) NOT NULL, + CAR_RTR_DATE VARCHAR(255) NULL, + CAR_NAME VARCHAR(255) NOT NULL, + CAR_NOTE VARCHAR(255) NULL, + CREATED_AT CHAR(19) NOT NULL, + UPDATED_AT CHAR(19) NOT NULL, + MEM_ID VARCHAR(255) NOT NULL, + PRIMARY KEY (CAR_ID), + FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) +); + CREATE TABLE tb_member_detail ( MEM_DET_ID VARCHAR(255) NOT NULL, @@ -838,7 +906,6 @@ VALUES ('SCH_000000010', 'K5 재고 점검', 'K5 재고 관리 및 점검', 'MEETING', '2024-11-28 15:00:00', '2024-11-28 17:00:00', '2024-01-29 19:00:00', '2024-01-29 19:30:00', NULL, TRUE, 'MEM_000000010'); - INSERT INTO tb_alarm (ALR_ID, ALR_MSG, ALR_URL, ALR_READ_STAT, CREATED_AT, MEM_ID) VALUES ('ALR_000000001', '신차 출시 공지 알림', '/notices/1', FALSE, '2024-01-20 12:00:00', 'MEM_000000001'), ('ALR_000000002', '스포티지 리콜 안내', '/notices/2', FALSE, '2024-01-21 14:00:00', 'MEM_000000002'), @@ -851,6 +918,58 @@ VALUES ('ALR_000000001', '신차 출시 공지 알림', '/notices/1', FALSE, '20 ('ALR_000000009', '니로 전기차 충전소 위치', '/maps/9', TRUE, '2024-01-28 15:00:00', 'MEM_000000009'), ('ALR_000000010', 'K5 재고 부족 경고', '/inventory/10', FALSE, '2024-01-29 16:00:00', 'MEM_000000010'); +INSERT INTO tb_family (FAM_ID, FAM_NAME, FAM_REL, FAM_BIR, FAM_IDEN_NO, FAM_PHO, FAM_SEX, FAM_DIS, FAM_DIE, FAM_NOTE, CREATED_AT, UPDATED_AT, MEM_ID) +VALUES + ('FAM_000000001', '김영희', '배우자', '1988-03-25', '880325-1234567', '010-1234-5678', 'FEMALE', FALSE, FALSE, NULL, '2024-01-10 10:00:00', '2024-01-10 11:00:00', 'MEM_000000001'), + ('FAM_000000002', '이수진', '자녀', '2015-05-12', '150512-2345678', '010-2345-6789', 'FEMALE', FALSE, FALSE, '초등학교 3학년', '2024-01-11 12:00:00', '2024-01-11 13:00:00', 'MEM_000000002'), + ('FAM_000000003', '박철수', '배우자', '1985-07-11', '850711-3456789', '010-3456-7890', 'MALE', FALSE, FALSE, NULL, '2024-01-12 14:00:00', '2024-01-12 15:00:00', 'MEM_000000003'), + ('FAM_000000004', '최민수', '자녀', '2012-11-03', '121103-4567890', '010-4567-8901', 'MALE', TRUE, FALSE, '특수 교육 필요', '2024-01-13 09:00:00', '2024-01-13 10:00:00', 'MEM_000000004'), + ('FAM_000000005', '정수정', '배우자', '1990-09-20', '900920-5678901', '010-5678-9012', 'FEMALE', FALSE, FALSE, NULL, '2024-01-14 11:00:00', '2024-01-14 12:00:00', 'MEM_000000005'), + ('FAM_000000006', '김지훈', '자녀', '2010-06-15', '100615-6789012', '010-6789-0123', 'MALE', FALSE, FALSE, '중학교 1학년', '2024-01-15 13:00:00', '2024-01-15 14:00:00', 'MEM_000000006'), + ('FAM_000000007', '한지민', '배우자', '1987-02-28', '870228-7890123', '010-7890-1234', 'FEMALE', FALSE, FALSE, NULL, '2024-01-16 15:00:00', '2024-01-16 16:00:00', 'MEM_000000007'), + ('FAM_000000008', '이동수', '자녀', '2017-12-10', '171210-8901234', '010-8901-2345', 'MALE', FALSE, FALSE, '유치원생', '2024-01-17 17:00:00', '2024-01-17 18:00:00', 'MEM_000000008'), + ('FAM_000000009', '윤소희', '배우자', '1989-04-03', '890403-9012345', '010-9012-3456', 'FEMALE', FALSE, FALSE, NULL, '2024-01-18 19:00:00', '2024-01-18 20:00:00', 'MEM_000000009'), + ('FAM_000000010', '박성준', '자녀', '2014-08-25', '140825-0123456', '010-0123-4567', 'MALE', FALSE, FALSE, '초등학교 5학년', '2024-01-19 09:00:00', '2024-01-19 10:00:00', 'MEM_000000010'); + +INSERT INTO tb_education (EDU_ID, EDU_ENTD, EDU_GRAD, EDU_NAME, EDU_MJR, EDU_SCO, EDU_NOTE, CREATED_AT, UPDATED_AT, MEM_ID) +VALUES + ('EDU_000000001', '2006-03-01', '2010-02-28', '서울대학교', '경영학', '3.8', NULL, '2024-01-10 10:00:00', '2024-01-10 11:00:00', 'MEM_000000001'), + ('EDU_000000002', '2008-03-01', '2012-02-28', '연세대학교', '경제학', '3.5', '졸업 논문 우수상', '2024-01-11 12:00:00', '2024-01-11 13:00:00', 'MEM_000000002'), + ('EDU_000000003', '2005-03-01', '2009-02-28', '고려대학교', '컴퓨터공학', '3.9', NULL, '2024-01-12 14:00:00', '2024-01-12 15:00:00', 'MEM_000000003'), + ('EDU_000000004', '2010-03-01', '2014-02-28', '서강대학교', '심리학', '3.6', '사회봉사 활동 참여', '2024-01-13 09:00:00', '2024-01-13 10:00:00', 'MEM_000000004'), + ('EDU_000000005', '2007-03-01', '2011-02-28', '성균관대학교', '화학공학', '3.7', NULL, '2024-01-14 11:00:00', '2024-01-14 12:00:00', 'MEM_000000005'), + ('EDU_000000006', '2009-03-01', '2013-02-28', '한양대학교', '전자공학', '4.0', '학과 대표', '2024-01-15 13:00:00', '2024-01-15 14:00:00', 'MEM_000000006'), + ('EDU_000000007', '2004-03-01', '2008-02-28', '이화여자대학교', '디자인', '3.8', '졸업 작품전 참여', '2024-01-16 15:00:00', '2024-01-16 16:00:00', 'MEM_000000007'), + ('EDU_000000008', '2011-03-01', '2015-02-28', '중앙대학교', '물리학', '3.4', NULL, '2024-01-17 17:00:00', '2024-01-17 18:00:00', 'MEM_000000008'), + ('EDU_000000009', '2003-03-01', '2007-02-28', '부산대학교', '수학', '3.9', '우수 장학금 수상', '2024-01-18 19:00:00', '2024-01-18 20:00:00', 'MEM_000000009'), + ('EDU_000000010', '2012-03-01', '2016-02-28', '경북대학교', '환경공학', '3.6', NULL, '2024-01-19 09:00:00', '2024-01-19 10:00:00', 'MEM_000000010'); + +INSERT INTO tb_certification (CER_ID, CER_DATE, CER_INST, CER_NAME, CER_SCO, CER_NOTE, CREATED_AT, MEM_ID) +VALUES + ('CER_000000001', '2018-06-20', '한국무역협회', '무역영어 1급', 'PASS', NULL, '2024-01-10 10:00:00', 'MEM_000000001'), + ('CER_000000002', '2019-03-15', '한국산업인력공단', '정보처리기사', 'PASS', '필기 및 실기 합격', '2024-01-11 12:00:00', 'MEM_000000002'), + ('CER_000000003', '2017-11-05', '대한상공회의소', '전산회계 2급', '90', NULL, '2024-01-12 14:00:00', 'MEM_000000003'), + ('CER_000000004', '2020-08-10', '국제공인회계사협회', 'CPA', 'PASS', '국제 회계 자격증', '2024-01-13 09:00:00', 'MEM_000000004'), + ('CER_000000005', '2016-02-22', '대한건설협회', '건축기사', 'PASS', NULL, '2024-01-14 11:00:00', 'MEM_000000005'), + ('CER_000000006', '2021-05-30', '한국능률협회', 'PMP', 'PASS', '프로젝트 관리 자격증', '2024-01-15 13:00:00', 'MEM_000000006'), + ('CER_000000007', '2015-12-12', '한국관광공사', '국내여행안내사', '85', NULL, '2024-01-16 15:00:00', 'MEM_000000007'), + ('CER_000000008', '2018-09-18', '한국소방안전협회', '소방안전관리자 1급', 'PASS', NULL, '2024-01-17 17:00:00', 'MEM_000000008'), + ('CER_000000009', '2022-04-25', '대한적십자사', '응급처치 강사', 'PASS', '응급처치 교육 수료', '2024-01-18 19:00:00', 'MEM_000000009'), + ('CER_000000010', '2019-11-11', '한국정보통신기술협회', '정보보안기사', 'PASS', NULL, '2024-01-19 09:00:00', 'MEM_000000010'); + +INSERT INTO tb_career (CAR_ID, CAR_EMP_DATE, CAR_RTR_DATE, CAR_NAME, CAR_NOTE, CREATED_AT, UPDATED_AT, MEM_ID) +VALUES + ('CAR_000000001', '2012-03-01', '2016-12-31', '삼성전자', '마케팅팀 근무', '2024-01-10 10:00:00', '2024-01-10 11:00:00', 'MEM_000000001'), + ('CAR_000000002', '2015-05-01', '2019-08-31', 'LG화학', '연구개발팀 근무', '2024-01-11 12:00:00', '2024-01-11 13:00:00', 'MEM_000000002'), + ('CAR_000000003', '2010-09-01', '2015-02-28', '포스코', '공정관리팀', '2024-01-12 14:00:00', '2024-01-12 15:00:00', 'MEM_000000003'), + ('CAR_000000004', '2013-01-01', '2018-03-31', '현대자동차', '생산기술팀', '2024-01-13 09:00:00', '2024-01-13 10:00:00', 'MEM_000000004'), + ('CAR_000000005', '2017-06-01', '2021-11-30', 'SK텔레콤', '네트워크 운영팀', '2024-01-14 11:00:00', '2024-01-14 12:00:00', 'MEM_000000005'), + ('CAR_000000006', '2011-02-01', '2016-07-31', '네이버', '프론트엔드 개발자', '2024-01-15 13:00:00', '2024-01-15 14:00:00', 'MEM_000000006'), + ('CAR_000000007', '2014-04-01', '2019-12-31', '카카오', '백엔드 개발자', '2024-01-16 15:00:00', '2024-01-16 16:00:00', 'MEM_000000007'), + ('CAR_000000008', '2018-01-01', '2022-06-30', 'CJ제일제당', '품질관리팀', '2024-01-17 17:00:00', '2024-01-17 18:00:00', 'MEM_000000008'), + ('CAR_000000009', '2010-07-01', '2014-09-30', '롯데케미칼', '안전관리팀', '2024-01-18 19:00:00', '2024-01-18 20:00:00', 'MEM_000000009'), + ('CAR_000000010', '2016-03-01', '2020-10-31', '한화생명', '재무팀', '2024-01-19 09:00:00', '2024-01-19 10:00:00', 'MEM_000000010'); + INSERT INTO tb_member_detail (MEM_DET_ID, MEM_DET_REL, MEM_DET_NAME, MEM_DET_BIR, MEM_DET_IDEN_NO, MEM_DET_PHO, MEM_DET_SEX, MEM_DET_DIS, MEM_DET_DIE, MEM_DET_NOTE, MEM_DET_ENTD, MEM_DET_GRAD, MEM_DET_FNL_EDC, MEM_DET_EDU, MEM_DET_MJR, MEM_DET_EMP_DATE, MEM_DET_RTR_DATE, CAR_INFO, From c78b3ebf3c3628b1799cd6e8d73386fbf83c3163 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 00:12:22 +0900 Subject: [PATCH 107/563] =?UTF-8?q?fix:=20ddl=20=EC=88=98=EC=A0=95(#60)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/sql/ddl.sql | 67 ---------------------------------- 1 file changed, 67 deletions(-) diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index d4625ab0..3b96638f 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -4,7 +4,6 @@ SET FOREIGN_KEY_CHECKS = 0; -- 자식 테이블부터 삭제 DROP TABLE IF EXISTS tb_product_option; DROP TABLE IF EXISTS tb_update_history; -DROP TABLE IF EXISTS tb_member_detail; DROP TABLE IF EXISTS tb_alarm; DROP TABLE IF EXISTS tb_schedule; DROP TABLE IF EXISTS tb_file; @@ -392,37 +391,6 @@ CREATE TABLE tb_career FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) ); -CREATE TABLE tb_member_detail -( - MEM_DET_ID VARCHAR(255) NOT NULL, - MEM_DET_REL VARCHAR(255) NULL, - MEM_DET_NAME VARCHAR(255) NULL, - MEM_DET_BIR VARCHAR(255) NULL, - MEM_DET_IDEN_NO VARCHAR(255) NULL, - MEM_DET_PHO VARCHAR(255) NULL, - MEM_DET_SEX VARCHAR(255) NULL COMMENT 'FEMALE/MALE', - MEM_DET_DIS BOOLEAN NULL, - MEM_DET_DIE BOOLEAN NULL, - MEM_DET_NOTE VARCHAR(255) NULL, - MEM_DET_ENTD VARCHAR(255) NOT NULL, - MEM_DET_GRAD VARCHAR(255) NOT NULL, - MEM_DET_FNL_EDC VARCHAR(255) NOT NULL, - MEM_DET_EDU VARCHAR(255) NULL, - MEM_DET_MJR VARCHAR(255) NULL, - MEM_DET_EMP_DATE VARCHAR(255) NULL, - MEM_DET_RTR_DATE VARCHAR(255) NULL, - CAR_INFO VARCHAR(255) NULL, - CERT_DATE VARCHAR(255) NULL, - CERT_INST VARCHAR(255) NULL, - CERT_NAME VARCHAR(255) NULL, - CERT_SCO VARCHAR(255) NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', - PRIMARY KEY (MEM_DET_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - CREATE TABLE tb_UPDATE_HISTORY ( UPD_ID VARCHAR(255) NOT NULL, @@ -970,41 +938,6 @@ VALUES ('CAR_000000009', '2010-07-01', '2014-09-30', '롯데케미칼', '안전관리팀', '2024-01-18 19:00:00', '2024-01-18 20:00:00', 'MEM_000000009'), ('CAR_000000010', '2016-03-01', '2020-10-31', '한화생명', '재무팀', '2024-01-19 09:00:00', '2024-01-19 10:00:00', 'MEM_000000010'); -INSERT INTO tb_member_detail (MEM_DET_ID, MEM_DET_REL, MEM_DET_NAME, MEM_DET_BIR, MEM_DET_IDEN_NO, MEM_DET_PHO, - MEM_DET_SEX, MEM_DET_DIS, MEM_DET_DIE, MEM_DET_NOTE, MEM_DET_ENTD, MEM_DET_GRAD, - MEM_DET_FNL_EDC, MEM_DET_EDU, MEM_DET_MJR, MEM_DET_EMP_DATE, MEM_DET_RTR_DATE, CAR_INFO, - CERT_DATE, CERT_INST, CERT_NAME, CERT_SCO, CREATED_AT, UPDATED_AT, MEM_ID) -VALUES ('DET_000000001', '배우자', '박미숙', '1988-05-12', '880512-1234567', '010-1111-2222', 'FEMALE', FALSE, FALSE, NULL, - '2010', 'A', '대졸', '경영학', '기아', '2020-01-01', '2025-01-01', 'K5', '2023-06-01', '기아자동차', '세일즈', '95', - '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000001'), - ('DET_000000002', '자녀', '김철수', '2012-08-05', '120805-2345678', '010-2222-3333', 'MALE', FALSE, FALSE, NULL, - '2022', 'B+', '고졸', '정보통신', '기아', '2022-01-15', '2027-01-15', 'K3', '2023-07-15', '기아자동차', '서비스', '88', - '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000002'), - ('DET_000000003', '배우자', '이영희', '1985-03-09', '850309-3456789', '010-3333-4444', 'FEMALE', FALSE, FALSE, - '부부 동반 여행', '2009', 'A-', '대졸', '경제학', '기아', '2019-05-12', '2024-05-12', '스포티지', '2023-04-20', '기아자동차', '기술지원', - '92', '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000003'), - ('DET_000000004', '배우자', '정수민', '1990-11-11', '901111-4567890', '010-4444-5555', 'FEMALE', FALSE, FALSE, NULL, - '2015', 'A', '대졸', '기계공학', '기아', '2021-09-01', '2026-09-01', 'K7', '2023-02-10', '기아자동차', '품질관리', '87', - '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000004'), - ('DET_000000005', '자녀', '한지수', '2016-01-20', '160120-5678901', '010-5555-6666', 'MALE', TRUE, FALSE, '특별 교육 필요', - '2021', 'B', '초등학교', '과학', '기아', '2023-03-05', '2028-03-05', '니로', '2023-03-01', '기아자동차', '연구개발', '85', - '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000005'), - ('DET_000000006', '자녀', '최지우', '2005-06-15', '050615-6789012', '010-6666-7777', 'FEMALE', FALSE, FALSE, NULL, - '2023', 'A+', '대졸', '디자인', '기아', '2020-07-01', '2025-07-01', '스팅어', '2023-08-20', '기아자동차', '디자인', '90', - '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000006'), - ('DET_000000007', '배우자', '윤미라', '1987-12-30', '871230-7890123', '010-7777-8888', 'FEMALE', FALSE, FALSE, - '해외 출장 동반', '2011', 'B+', '대졸', '마케팅', '기아', '2020-12-01', '2025-12-01', '쏘렌토', '2023-01-25', '기아자동차', '마케팅', - '93', '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000007'), - ('DET_000000008', '자녀', '이동희', '2010-09-10', '100910-8901234', '010-8888-9999', 'MALE', FALSE, FALSE, NULL, - '2025', 'B-', '중졸', '정보기술', '기아', '2023-05-05', '2028-05-05', '모하비', '2023-12-30', '기아자동차', '엔지니어링', '89', - '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000008'), - ('DET_000000009', '배우자', '정수영', '1991-04-17', '910417-9012345', '010-9999-0000', 'FEMALE', FALSE, FALSE, - '가족 동반 캠핑', '2018', 'A-', '대졸', '화학', '기아', '2022-06-15', '2027-06-15', '셀토스', '2023-11-22', '기아자동차', '생산', - '91', '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000009'), - ('DET_000000010', '자녀', '김준호', '2014-07-22', '140722-0123456', '010-0000-1111', 'MALE', FALSE, FALSE, NULL, - '2027', 'B+', '고졸', '경영', '기아', '2023-08-12', '2028-08-12', 'K8', '2023-09-10', '기아자동차', '세일즈', '87', - '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000010'); - INSERT INTO tb_update_history (UPD_ID, UPD_IP, UPDATED_AT, UPDATED_URL, MEM_ID, CONR_ID) VALUES ('UPD_000000001', '192.168.1.10', '2024-01-10 12:00:00', '/contracts/1', 'MEM_000000001', 'CON_000000001'), ('UPD_000000002', '192.168.1.20', '2024-01-11 14:00:00', '/contracts/2', 'MEM_000000002', 'CON_000000002'), From 9f851dd4959e92e069a45cd22b4beb2d4a8eef28 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Mon, 18 Nov 2024 00:36:14 +0900 Subject: [PATCH 108/563] =?UTF-8?q?feat:=20=EB=B0=9C=EC=A3=BC=EC=84=9C=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=84=B8=ED=8C=85=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/PurchaseOrderController.java | 9 +++++++++ .../query/repository/PurchaseOrderMapper.java | 7 +++++++ .../query/service/PurchaseOrderQueryService.java | 4 ++++ .../query/service/PurchaseOrderQueryServiceImpl.java | 7 +++++++ 4 files changed, 27 insertions(+) create mode 100644 src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java create mode 100644 src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java new file mode 100644 index 00000000..195d8f49 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.purchase_order.query.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController("PurchaseOrderQueryController") +@RequestMapping("/api/v1/purchase-order") +public class PurchaseOrderController { +} diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java new file mode 100644 index 00000000..a6d2f934 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.purchase_order.query.repository; + +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface PurchaseOrderMapper { +} diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java new file mode 100644 index 00000000..421f6e8d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java @@ -0,0 +1,4 @@ +package stanl_2.final_backend.domain.purchase_order.query.service; + +public interface PurchaseOrderQueryService { +} diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java new file mode 100644 index 00000000..a70e780d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.purchase_order.query.service; + +import org.springframework.stereotype.Service; + +@Service +public class PurchaseOrderQueryServiceImpl implements PurchaseOrderQueryService { +} \ No newline at end of file From 54f024753a9f61b3a7664ea12165486a8141bbbe Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 01:55:54 +0900 Subject: [PATCH 109/563] =?UTF-8?q?feat:=20family,=20career,=20education,?= =?UTF-8?q?=20certification=20CQRS=EC=84=B8=ED=8C=85(#60)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/repository/SampleRepository.java | 2 + ...eImpl.java => SampleQueryServiceImpl.java} | 4 +- .../controller/CareerController.java | 20 +++++ .../application/dto/CareerRegistDTO.java | 18 ++++ .../service/CareerCommandService.java | 4 + .../domain/aggregate/entity/Career.java | 68 +++++++++++++++ .../domain/repository/CareerRepository.java | 10 +++ .../service/CareerCommandServiceImpl.java | 21 +++++ .../exception/CareerCommonException.java | 16 ++++ .../common/exception/CareerErrorCode.java | 52 ++++++++++++ .../exception/CareerExceptionResponse.java | 22 +++++ .../response/CareerResponseMessage.java | 14 ++++ .../query/config/MybatisConfiguration.java | 9 ++ .../query/controller/CareerController.java | 18 ++++ .../domain/career/query/dto/CareerDTO.java | 15 ++++ .../career/query/repository/CareerMapper.java | 7 ++ .../query/service/CareerQueryService.java | 4 + .../query/service/CareerQueryServiceImpl.java | 16 ++++ .../controller/CertificationController.java | 18 ++++ .../dto/CertificationRegisterDTO.java | 15 ++++ .../service/CertificationCommandService.java | 4 + .../aggregate/entity/Certification.java | 57 +++++++++++++ .../repository/CertificationRepository.java | 9 ++ .../CertificationCommandServiceImpl.java | 21 +++++ .../CertificationCommonException.java | 16 ++++ .../exception/CertificationErrorCode.java | 52 ++++++++++++ .../CertificationExceptionResponse.java | 22 +++++ .../CertificationResponseMessage.java | 14 ++++ .../query/config/MybatisConfiguration.java | 9 ++ .../controller/CertificationController.java | 18 ++++ .../query/dto/CertificationDTO.java | 15 ++++ .../query/repository/CertificationMapper.java | 7 ++ .../service/CertificationQueryService.java | 4 + .../CertificationQueryServiceImpl.java | 16 ++++ .../controller/EducationController.java | 18 ++++ .../application/dto/EducationModifyDTO.java | 13 +++ .../application/dto/EducationRegistDTO.java | 17 ++++ .../service/EducationCommandService.java | 4 + .../domain/aggregate/entity/Education.java | 73 +++++++++++++++++ .../repository/EducationRepository.java | 9 ++ .../service/EducationCommandServiceImpl.java | 22 +++++ .../exception/EducationCommonException.java | 16 ++++ .../common/exception/EducationErrorCode.java | 52 ++++++++++++ .../exception/EducationExceptionResponse.java | 22 +++++ .../response/EducationResponseMessage.java | 14 ++++ .../query/config/MybatisConfiguration.java | 9 ++ .../query/controller/EducationController.java | 18 ++++ .../education/query/dto/EducationDTO.java | 18 ++++ .../query/repository/EducationMapper.java | 7 ++ .../query/service/EducationQueryService.java | 4 + .../service/EducationQueryServiceImpl.java | 16 ++++ .../controller/FamilyController.java | 18 ++++ .../application/dto/FamilyModifyDTO.java | 16 ++++ .../application/dto/FamilyRegistDTO.java | 20 +++++ .../service/FamilyCommandService.java | 4 + .../domain/aggregate/entity/Family.java | 82 +++++++++++++++++++ .../domain/repository/FamilyRepository.java | 7 ++ .../service/FamilyCommandServiceImpl.java | 25 ++++++ .../exception/FamilyCommonException.java | 16 ++++ .../common/exception/FamilyErrorCode.java | 52 ++++++++++++ .../exception/FamilyExceptionResponse.java | 22 +++++ .../response/FamilyResponseMessage.java | 14 ++++ .../query/config/MybatisConfiguration.java | 9 ++ .../query/controller/FamilyController.java | 18 ++++ .../domain/family/query/dto/FamilyDTO.java | 20 +++++ .../family/query/repository/FamilyMapper.java | 7 ++ .../query/service/FamilyQueryService.java | 4 + .../query/service/FamilyQueryServiceImpl.java | 20 +++++ .../career/query/repository/CareerMapper.xml | 7 ++ .../query/repository/CertificationMapper.xml | 7 ++ .../query/repository/EducationMapper.xml | 7 ++ .../family/query/repository/FamilyMapper.xml | 8 ++ 72 files changed, 1330 insertions(+), 2 deletions(-) rename src/main/java/stanl_2/final_backend/domain/A_sample/query/service/{SampleQueryQueryServiceImpl.java => SampleQueryServiceImpl.java} (90%) create mode 100644 src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerRegistDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/career/command/application/service/CareerCommandService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/career/command/domain/aggregate/entity/Career.java create mode 100644 src/main/java/stanl_2/final_backend/domain/career/command/domain/repository/CareerRepository.java create mode 100644 src/main/java/stanl_2/final_backend/domain/career/command/domain/service/CareerCommandServiceImpl.java create mode 100644 src/main/java/stanl_2/final_backend/domain/career/common/exception/CareerCommonException.java create mode 100644 src/main/java/stanl_2/final_backend/domain/career/common/exception/CareerErrorCode.java create mode 100644 src/main/java/stanl_2/final_backend/domain/career/common/exception/CareerExceptionResponse.java create mode 100644 src/main/java/stanl_2/final_backend/domain/career/common/response/CareerResponseMessage.java create mode 100644 src/main/java/stanl_2/final_backend/domain/career/query/config/MybatisConfiguration.java create mode 100644 src/main/java/stanl_2/final_backend/domain/career/query/controller/CareerController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/career/query/dto/CareerDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/career/query/repository/CareerMapper.java create mode 100644 src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryServiceImpl.java create mode 100644 src/main/java/stanl_2/final_backend/domain/certification/command/application/controller/CertificationController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/certification/command/application/dto/CertificationRegisterDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/certification/command/application/service/CertificationCommandService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/certification/command/domain/aggregate/entity/Certification.java create mode 100644 src/main/java/stanl_2/final_backend/domain/certification/command/domain/repository/CertificationRepository.java create mode 100644 src/main/java/stanl_2/final_backend/domain/certification/command/domain/service/CertificationCommandServiceImpl.java create mode 100644 src/main/java/stanl_2/final_backend/domain/certification/common/exception/CertificationCommonException.java create mode 100644 src/main/java/stanl_2/final_backend/domain/certification/common/exception/CertificationErrorCode.java create mode 100644 src/main/java/stanl_2/final_backend/domain/certification/common/exception/CertificationExceptionResponse.java create mode 100644 src/main/java/stanl_2/final_backend/domain/certification/common/response/CertificationResponseMessage.java create mode 100644 src/main/java/stanl_2/final_backend/domain/certification/query/config/MybatisConfiguration.java create mode 100644 src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/certification/query/dto/CertificationDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.java create mode 100644 src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryServiceImpl.java create mode 100644 src/main/java/stanl_2/final_backend/domain/education/command/application/controller/EducationController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/education/command/application/dto/EducationModifyDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/education/command/application/dto/EducationRegistDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/education/command/application/service/EducationCommandService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/education/command/domain/aggregate/entity/Education.java create mode 100644 src/main/java/stanl_2/final_backend/domain/education/command/domain/repository/EducationRepository.java create mode 100644 src/main/java/stanl_2/final_backend/domain/education/command/domain/service/EducationCommandServiceImpl.java create mode 100644 src/main/java/stanl_2/final_backend/domain/education/common/exception/EducationCommonException.java create mode 100644 src/main/java/stanl_2/final_backend/domain/education/common/exception/EducationErrorCode.java create mode 100644 src/main/java/stanl_2/final_backend/domain/education/common/exception/EducationExceptionResponse.java create mode 100644 src/main/java/stanl_2/final_backend/domain/education/common/response/EducationResponseMessage.java create mode 100644 src/main/java/stanl_2/final_backend/domain/education/query/config/MybatisConfiguration.java create mode 100644 src/main/java/stanl_2/final_backend/domain/education/query/controller/EducationController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/education/query/dto/EducationDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/education/query/repository/EducationMapper.java create mode 100644 src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryServiceImpl.java create mode 100644 src/main/java/stanl_2/final_backend/domain/family/command/application/controller/FamilyController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/family/command/application/dto/FamilyModifyDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/family/command/application/dto/FamilyRegistDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/family/command/application/service/FamilyCommandService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/family/command/domain/aggregate/entity/Family.java create mode 100644 src/main/java/stanl_2/final_backend/domain/family/command/domain/repository/FamilyRepository.java create mode 100644 src/main/java/stanl_2/final_backend/domain/family/command/domain/service/FamilyCommandServiceImpl.java create mode 100644 src/main/java/stanl_2/final_backend/domain/family/common/exception/FamilyCommonException.java create mode 100644 src/main/java/stanl_2/final_backend/domain/family/common/exception/FamilyErrorCode.java create mode 100644 src/main/java/stanl_2/final_backend/domain/family/common/exception/FamilyExceptionResponse.java create mode 100644 src/main/java/stanl_2/final_backend/domain/family/common/response/FamilyResponseMessage.java create mode 100644 src/main/java/stanl_2/final_backend/domain/family/query/config/MybatisConfiguration.java create mode 100644 src/main/java/stanl_2/final_backend/domain/family/query/controller/FamilyController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/family/query/dto/FamilyDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/family/query/repository/FamilyMapper.java create mode 100644 src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryServiceImpl.java create mode 100644 src/main/resources/stanl_2/final_backend/domain/career/query/repository/CareerMapper.xml create mode 100644 src/main/resources/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.xml create mode 100644 src/main/resources/stanl_2/final_backend/domain/education/query/repository/EducationMapper.xml create mode 100644 src/main/resources/stanl_2/final_backend/domain/family/query/repository/FamilyMapper.xml diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/repository/SampleRepository.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/repository/SampleRepository.java index c6bb2636..5299a8a9 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/repository/SampleRepository.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/repository/SampleRepository.java @@ -1,7 +1,9 @@ package stanl_2.final_backend.domain.A_sample.command.domain.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; import stanl_2.final_backend.domain.A_sample.command.domain.aggregate.entity.Sample; +@Repository public interface SampleRepository extends JpaRepository { } diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryServiceImpl.java similarity index 90% rename from src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryQueryServiceImpl.java rename to src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryServiceImpl.java index f0bcf71c..04874bbf 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryServiceImpl.java @@ -11,12 +11,12 @@ @Slf4j @Service -public class SampleQueryQueryServiceImpl implements SampleQueryService { +public class SampleQueryServiceImpl implements SampleQueryService { private final SampleMapper sampleMapper; @Autowired - public SampleQueryQueryServiceImpl(SampleMapper sampleMapper) { + public SampleQueryServiceImpl(SampleMapper sampleMapper) { this.sampleMapper = sampleMapper; } diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java b/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java new file mode 100644 index 00000000..7a96b740 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java @@ -0,0 +1,20 @@ +package stanl_2.final_backend.domain.career.command.application.controller; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.career.command.application.service.CareerCommandService; + +@Slf4j +@RestController("commandCareerController") +@RequestMapping("/api/v1/career") +public class CareerController { + + private final CareerCommandService careerCommandService; + + @Autowired + public CareerController(CareerCommandService careerCommandService) { + this.careerCommandService = careerCommandService; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerRegistDTO.java new file mode 100644 index 00000000..a9f0bf88 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerRegistDTO.java @@ -0,0 +1,18 @@ +package stanl_2.final_backend.domain.career.command.application.dto; + +import jakarta.persistence.Column; +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class CareerRegistDTO { + private String emplDate; + private String resignDate; + private String name; + private String note; + private String createdAt; + private String updatedAt; +} diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/application/service/CareerCommandService.java b/src/main/java/stanl_2/final_backend/domain/career/command/application/service/CareerCommandService.java new file mode 100644 index 00000000..65df99d8 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/career/command/application/service/CareerCommandService.java @@ -0,0 +1,4 @@ +package stanl_2.final_backend.domain.career.command.application.service; + +public interface CareerCommandService { +} diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/domain/aggregate/entity/Career.java b/src/main/java/stanl_2/final_backend/domain/career/command/domain/aggregate/entity/Career.java new file mode 100644 index 00000000..236792b1 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/career/command/domain/aggregate/entity/Career.java @@ -0,0 +1,68 @@ +package stanl_2.final_backend.domain.career.command.domain.aggregate.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "tb_CAREER") +public class Career { + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "CAR") + ) + @Column(name = "CAR_ID", nullable = false) + private String careerId; + + @Column(name = "CAR_EMP_DATE", nullable = false) + private String emplDate; + + @Column(name = "CAR_RTR_DATE") + private String resignDate; + + @Column(name = "CAR_NAME", nullable = false) + private String name; + + @Column(name = "CAR_NOTE") + private String note; + + @Column(name = "CREATED_AT", nullable = false, updatable = false) + private String createdAt; + + @Column(name = "UPDATED_AT", nullable = false) + private String updatedAt; + + + /* 설명. updatedAt 자동화 */ + // Insert 되기 전에 실행 + @PrePersist + private void prePersist() { + this.createdAt = getCurrentTime(); + this.updatedAt = this.createdAt; + } + + // Update 되기 전에 실행 + @PreUpdate + private void preUpdate() { + this.updatedAt = getCurrentTime(); + } + + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/domain/repository/CareerRepository.java b/src/main/java/stanl_2/final_backend/domain/career/command/domain/repository/CareerRepository.java new file mode 100644 index 00000000..47758e96 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/career/command/domain/repository/CareerRepository.java @@ -0,0 +1,10 @@ +package stanl_2.final_backend.domain.career.command.domain.repository; + + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import stanl_2.final_backend.domain.career.command.domain.aggregate.entity.Career; + +@Repository +public interface CareerRepository extends JpaRepository { +} diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/domain/service/CareerCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/career/command/domain/service/CareerCommandServiceImpl.java new file mode 100644 index 00000000..67b32bb7 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/career/command/domain/service/CareerCommandServiceImpl.java @@ -0,0 +1,21 @@ +package stanl_2.final_backend.domain.career.command.domain.service; + +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.career.command.application.service.CareerCommandService; +import stanl_2.final_backend.domain.career.command.domain.repository.CareerRepository; + +@Service("commandCareerService") +public class CareerCommandServiceImpl implements CareerCommandService { + + private final CareerRepository careerRepository; + private final ModelMapper modelMapper; + + @Autowired + public CareerCommandServiceImpl(CareerRepository careerRepository, + ModelMapper modelMapper) { + this.careerRepository = careerRepository; + this.modelMapper = modelMapper; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/career/common/exception/CareerCommonException.java b/src/main/java/stanl_2/final_backend/domain/career/common/exception/CareerCommonException.java new file mode 100644 index 00000000..d062cd16 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/career/common/exception/CareerCommonException.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.career.common.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class CareerCommonException extends RuntimeException { + private final CareerErrorCode sampleErrorCode; + + // 에러 발생시 ErroCode 별 메시지 + @Override + public String getMessage() { + return this.sampleErrorCode.getMsg(); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/career/common/exception/CareerErrorCode.java b/src/main/java/stanl_2/final_backend/domain/career/common/exception/CareerErrorCode.java new file mode 100644 index 00000000..bb85bbaf --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/career/common/exception/CareerErrorCode.java @@ -0,0 +1,52 @@ +package stanl_2.final_backend.domain.career.common.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum CareerErrorCode { + + /** + * 400(Bad Request) + * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. + */ + + + + /** + * 401(Unauthorized) + * 비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만, + * 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다. + * 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다. + */ + + + /** + * 403(Forbidden) + * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. + * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다. + */ + + + + /** + * 404(Not Found) + * 서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다. + * 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있습니다. + * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. + * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. + */ + SAMPLE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "sample 데이터를 찾지 못했습니다"), + CENTER_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "center 데이터를 찾지 못했습니다."), + /** + * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. + */ + INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); + + + private final Integer code; + private final HttpStatus httpStatus; + private final String msg; +} diff --git a/src/main/java/stanl_2/final_backend/domain/career/common/exception/CareerExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/career/common/exception/CareerExceptionResponse.java new file mode 100644 index 00000000..21f05f1a --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/career/common/exception/CareerExceptionResponse.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.career.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class CareerExceptionResponse { + private final Integer code; + private final String msg; + private final HttpStatus httpStatus; + + public CareerExceptionResponse(CareerErrorCode sampleErrorCode) { + this.code = sampleErrorCode.getCode(); + this.msg = sampleErrorCode.getMsg(); + this.httpStatus = sampleErrorCode.getHttpStatus(); + } + + public static CareerExceptionResponse of(CareerErrorCode sampleErrorCode) { + return new CareerExceptionResponse(sampleErrorCode); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/career/common/response/CareerResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/career/common/response/CareerResponseMessage.java new file mode 100644 index 00000000..906e82cd --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/career/common/response/CareerResponseMessage.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.career.common.response; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class CareerResponseMessage { + private int httpStatus; + private String msg; + private Object result; +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/career/query/config/MybatisConfiguration.java b/src/main/java/stanl_2/final_backend/domain/career/query/config/MybatisConfiguration.java new file mode 100644 index 00000000..3fbc136c --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/career/query/config/MybatisConfiguration.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.career.query.config; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Configuration; + +@Configuration("careerMybatisConfiguration") +@MapperScan(basePackages = "stanl_2.final_backend.domain.career.query.repository") +public class MybatisConfiguration { +} diff --git a/src/main/java/stanl_2/final_backend/domain/career/query/controller/CareerController.java b/src/main/java/stanl_2/final_backend/domain/career/query/controller/CareerController.java new file mode 100644 index 00000000..a9bfc3cc --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/career/query/controller/CareerController.java @@ -0,0 +1,18 @@ +package stanl_2.final_backend.domain.career.query.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.career.query.service.CareerQueryService; + +@RestController(value = "queryCareerController") +@RequestMapping("/api/v1/career") +public class CareerController { + + private final CareerQueryService careerQueryService; + + @Autowired + public CareerController(CareerQueryService careerQueryService) { + this.careerQueryService = careerQueryService; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/career/query/dto/CareerDTO.java b/src/main/java/stanl_2/final_backend/domain/career/query/dto/CareerDTO.java new file mode 100644 index 00000000..69b6ee69 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/career/query/dto/CareerDTO.java @@ -0,0 +1,15 @@ +package stanl_2.final_backend.domain.career.query.dto; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Setter +@Getter +@ToString +public class CareerDTO { + private String emplDate; + private String resignDate; + private String name; + private String note; +} diff --git a/src/main/java/stanl_2/final_backend/domain/career/query/repository/CareerMapper.java b/src/main/java/stanl_2/final_backend/domain/career/query/repository/CareerMapper.java new file mode 100644 index 00000000..9ad07d33 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/career/query/repository/CareerMapper.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.career.query.repository; + +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface CareerMapper { +} diff --git a/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryService.java b/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryService.java new file mode 100644 index 00000000..ecb09662 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryService.java @@ -0,0 +1,4 @@ +package stanl_2.final_backend.domain.career.query.service; + +public interface CareerQueryService { +} diff --git a/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryServiceImpl.java new file mode 100644 index 00000000..24cb6aab --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryServiceImpl.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.career.query.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.career.query.repository.CareerMapper; + +@Service("queryCareerService") +public class CareerQueryServiceImpl implements CareerQueryService { + + private final CareerMapper careerMapper; + + @Autowired + public CareerQueryServiceImpl(CareerMapper careerMapper) { + this.careerMapper = careerMapper; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/certification/command/application/controller/CertificationController.java b/src/main/java/stanl_2/final_backend/domain/certification/command/application/controller/CertificationController.java new file mode 100644 index 00000000..4770fff4 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/certification/command/application/controller/CertificationController.java @@ -0,0 +1,18 @@ +package stanl_2.final_backend.domain.certification.command.application.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.certification.command.application.service.CertificationCommandService; + +@RestController("commandCertificationController") +@RequestMapping("/api/v1/certification") +public class CertificationController { + + private final CertificationCommandService certificationCommandService; + + @Autowired + public CertificationController(CertificationCommandService certificationCommandService) { + this.certificationCommandService = certificationCommandService; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/certification/command/application/dto/CertificationRegisterDTO.java b/src/main/java/stanl_2/final_backend/domain/certification/command/application/dto/CertificationRegisterDTO.java new file mode 100644 index 00000000..db4a559f --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/certification/command/application/dto/CertificationRegisterDTO.java @@ -0,0 +1,15 @@ +package stanl_2.final_backend.domain.certification.command.application.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class CertificationRegisterDTO { + private String name; + private String agency; + private String acquisitionDate; + private String note; +} diff --git a/src/main/java/stanl_2/final_backend/domain/certification/command/application/service/CertificationCommandService.java b/src/main/java/stanl_2/final_backend/domain/certification/command/application/service/CertificationCommandService.java new file mode 100644 index 00000000..4323cfe5 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/certification/command/application/service/CertificationCommandService.java @@ -0,0 +1,4 @@ +package stanl_2.final_backend.domain.certification.command.application.service; + +public interface CertificationCommandService { +} diff --git a/src/main/java/stanl_2/final_backend/domain/certification/command/domain/aggregate/entity/Certification.java b/src/main/java/stanl_2/final_backend/domain/certification/command/domain/aggregate/entity/Certification.java new file mode 100644 index 00000000..2882b4e2 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/certification/command/domain/aggregate/entity/Certification.java @@ -0,0 +1,57 @@ +package stanl_2.final_backend.domain.certification.command.domain.aggregate.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "TB_CERTIFICATION") +public class Certification { + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "CER") + ) + @Column(name = "CER_ID", nullable = false) + private String certificationId; + + @Column(name = "CER_NAME", nullable = false) + private String name; + + @Column(name = "CER_INST", nullable = false) + private String agency; + + @Column(name = "CER_DATE", nullable = false) + private String acquisitionDate; + + @Column(name = "CER_NOTE") + private String note; + + @Column(name = "CREATED_AT", nullable = false, updatable = false) + private String createdAt; + + /* 설명. updatedAt 자동화 */ + // Insert 되기 전에 실행 + @PrePersist + private void prePersist() { + this.createdAt = getCurrentTime(); + } + + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/certification/command/domain/repository/CertificationRepository.java b/src/main/java/stanl_2/final_backend/domain/certification/command/domain/repository/CertificationRepository.java new file mode 100644 index 00000000..a7cebf49 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/certification/command/domain/repository/CertificationRepository.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.certification.command.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import stanl_2.final_backend.domain.certification.command.domain.aggregate.entity.Certification; + +@Repository +public interface CertificationRepository extends JpaRepository { +} diff --git a/src/main/java/stanl_2/final_backend/domain/certification/command/domain/service/CertificationCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/certification/command/domain/service/CertificationCommandServiceImpl.java new file mode 100644 index 00000000..5edf0632 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/certification/command/domain/service/CertificationCommandServiceImpl.java @@ -0,0 +1,21 @@ +package stanl_2.final_backend.domain.certification.command.domain.service; + +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.certification.command.application.service.CertificationCommandService; +import stanl_2.final_backend.domain.certification.command.domain.repository.CertificationRepository; + +@Service("commandCertificationService") +public class CertificationCommandServiceImpl implements CertificationCommandService { + + private final CertificationRepository certificationRepository; + private final ModelMapper modelMapper; + + @Autowired + public CertificationCommandServiceImpl(CertificationRepository certificationRepository, + ModelMapper modelMapper) { + this.certificationRepository = certificationRepository; + this.modelMapper = modelMapper; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/certification/common/exception/CertificationCommonException.java b/src/main/java/stanl_2/final_backend/domain/certification/common/exception/CertificationCommonException.java new file mode 100644 index 00000000..2197fe72 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/certification/common/exception/CertificationCommonException.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.certification.common.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class CertificationCommonException extends RuntimeException { + private final CertificationErrorCode sampleErrorCode; + + // 에러 발생시 ErroCode 별 메시지 + @Override + public String getMessage() { + return this.sampleErrorCode.getMsg(); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/certification/common/exception/CertificationErrorCode.java b/src/main/java/stanl_2/final_backend/domain/certification/common/exception/CertificationErrorCode.java new file mode 100644 index 00000000..66d1d90b --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/certification/common/exception/CertificationErrorCode.java @@ -0,0 +1,52 @@ +package stanl_2.final_backend.domain.certification.common.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum CertificationErrorCode { + + /** + * 400(Bad Request) + * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. + */ + + + + /** + * 401(Unauthorized) + * 비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만, + * 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다. + * 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다. + */ + + + /** + * 403(Forbidden) + * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. + * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다. + */ + + + + /** + * 404(Not Found) + * 서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다. + * 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있습니다. + * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. + * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. + */ + SAMPLE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "sample 데이터를 찾지 못했습니다"), + CENTER_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "center 데이터를 찾지 못했습니다."), + /** + * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. + */ + INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); + + + private final Integer code; + private final HttpStatus httpStatus; + private final String msg; +} diff --git a/src/main/java/stanl_2/final_backend/domain/certification/common/exception/CertificationExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/certification/common/exception/CertificationExceptionResponse.java new file mode 100644 index 00000000..69e7d7ac --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/certification/common/exception/CertificationExceptionResponse.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.certification.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class CertificationExceptionResponse { + private final Integer code; + private final String msg; + private final HttpStatus httpStatus; + + public CertificationExceptionResponse(CertificationErrorCode sampleErrorCode) { + this.code = sampleErrorCode.getCode(); + this.msg = sampleErrorCode.getMsg(); + this.httpStatus = sampleErrorCode.getHttpStatus(); + } + + public static CertificationExceptionResponse of(CertificationErrorCode sampleErrorCode) { + return new CertificationExceptionResponse(sampleErrorCode); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/certification/common/response/CertificationResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/certification/common/response/CertificationResponseMessage.java new file mode 100644 index 00000000..4185ab6d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/certification/common/response/CertificationResponseMessage.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.certification.common.response; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class CertificationResponseMessage { + private int httpStatus; + private String msg; + private Object result; +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/certification/query/config/MybatisConfiguration.java b/src/main/java/stanl_2/final_backend/domain/certification/query/config/MybatisConfiguration.java new file mode 100644 index 00000000..86808188 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/certification/query/config/MybatisConfiguration.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.certification.query.config; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Configuration; + +@Configuration("certificationMybatisConfiguration") +@MapperScan(basePackages = "stanl_2.final_backend.domain.certification.query.repository") +public class MybatisConfiguration { +} diff --git a/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java b/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java new file mode 100644 index 00000000..dfa966f9 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java @@ -0,0 +1,18 @@ +package stanl_2.final_backend.domain.certification.query.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.certification.query.service.CertificationQueryService; + +@RestController(value = "queryCertificationController") +@RequestMapping("/api/v1/certification") +public class CertificationController { + + private final CertificationQueryService certificationQueryService; + + @Autowired + public CertificationController(CertificationQueryService certificationQueryService) { + this.certificationQueryService = certificationQueryService; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/certification/query/dto/CertificationDTO.java b/src/main/java/stanl_2/final_backend/domain/certification/query/dto/CertificationDTO.java new file mode 100644 index 00000000..a3ec393d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/certification/query/dto/CertificationDTO.java @@ -0,0 +1,15 @@ +package stanl_2.final_backend.domain.certification.query.dto; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Setter +@Getter +@ToString +public class CertificationDTO { + private String name; + private String agency; + private String acquisitionDate; + private String note; +} diff --git a/src/main/java/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.java b/src/main/java/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.java new file mode 100644 index 00000000..062a3c26 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.certification.query.repository; + +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface CertificationMapper { +} diff --git a/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryService.java b/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryService.java new file mode 100644 index 00000000..dc7fa747 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryService.java @@ -0,0 +1,4 @@ +package stanl_2.final_backend.domain.certification.query.service; + +public interface CertificationQueryService { +} diff --git a/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryServiceImpl.java new file mode 100644 index 00000000..d7f49899 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryServiceImpl.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.certification.query.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.certification.query.repository.CertificationMapper; + +@Service("queryCertificationService") +public class CertificationQueryServiceImpl implements CertificationQueryService { + + private final CertificationMapper certificationMapper; + + @Autowired + public CertificationQueryServiceImpl(CertificationMapper certificationMapper) { + this.certificationMapper = certificationMapper; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/education/command/application/controller/EducationController.java b/src/main/java/stanl_2/final_backend/domain/education/command/application/controller/EducationController.java new file mode 100644 index 00000000..10981cd6 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/education/command/application/controller/EducationController.java @@ -0,0 +1,18 @@ +package stanl_2.final_backend.domain.education.command.application.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.education.command.application.service.EducationCommandService; + +@RestController("commandEducationController") +@RequestMapping("/api/v1/education") +public class EducationController { + + private final EducationCommandService educationCommandService; + + @Autowired + public EducationController(EducationCommandService educationCommandService) { + this.educationCommandService = educationCommandService; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/education/command/application/dto/EducationModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/education/command/application/dto/EducationModifyDTO.java new file mode 100644 index 00000000..3fb24d85 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/education/command/application/dto/EducationModifyDTO.java @@ -0,0 +1,13 @@ +package stanl_2.final_backend.domain.education.command.application.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class EducationModifyDTO { + private String graduationDate; + private String note; +} diff --git a/src/main/java/stanl_2/final_backend/domain/education/command/application/dto/EducationRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/education/command/application/dto/EducationRegistDTO.java new file mode 100644 index 00000000..68290deb --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/education/command/application/dto/EducationRegistDTO.java @@ -0,0 +1,17 @@ +package stanl_2.final_backend.domain.education.command.application.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class EducationRegistDTO { + private String entranceDate; + private String graduationDate; + private String name; + private String major; + private String score; + private String note; +} diff --git a/src/main/java/stanl_2/final_backend/domain/education/command/application/service/EducationCommandService.java b/src/main/java/stanl_2/final_backend/domain/education/command/application/service/EducationCommandService.java new file mode 100644 index 00000000..4a7f2538 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/education/command/application/service/EducationCommandService.java @@ -0,0 +1,4 @@ +package stanl_2.final_backend.domain.education.command.application.service; + +public interface EducationCommandService { +} diff --git a/src/main/java/stanl_2/final_backend/domain/education/command/domain/aggregate/entity/Education.java b/src/main/java/stanl_2/final_backend/domain/education/command/domain/aggregate/entity/Education.java new file mode 100644 index 00000000..34e7c24d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/education/command/domain/aggregate/entity/Education.java @@ -0,0 +1,73 @@ +package stanl_2.final_backend.domain.education.command.domain.aggregate.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "tb_education") +public class Education { + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "EDU") + ) + @Column(name = "EDU_ID", nullable = false) + private String educationId; + + @Column(name = "EDU_ENTD", nullable = false) + private String entranceDate; + + @Column(name = "EDU_GRAD", nullable = false) + private String graduationDate; + + @Column(name = "EDU_NAME", nullable = false) + private String name; + + @Column(name = "EDU_MJR", nullable = false) + private String major; + + @Column(name = "EDU_SCO") + private String score; + + @Column(name = "EDU_NOTE") + private String note; + + @Column(name = "CREATED_AT", nullable = false, updatable = false) + private String createdAt; + + @Column(name = "UPDATED_AT", nullable = false) + private String updatedAt; + + /* 설명. updatedAt 자동화 */ + // Insert 되기 전에 실행 + @PrePersist + private void prePersist() { + this.createdAt = getCurrentTime(); + this.updatedAt = this.createdAt; + } + + // Update 되기 전에 실행 + @PreUpdate + private void preUpdate() { + this.updatedAt = getCurrentTime(); + } + + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/education/command/domain/repository/EducationRepository.java b/src/main/java/stanl_2/final_backend/domain/education/command/domain/repository/EducationRepository.java new file mode 100644 index 00000000..9ce9f649 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/education/command/domain/repository/EducationRepository.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.education.command.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import stanl_2.final_backend.domain.education.command.domain.aggregate.entity.Education; + +@Repository +public interface EducationRepository extends JpaRepository { +} diff --git a/src/main/java/stanl_2/final_backend/domain/education/command/domain/service/EducationCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/education/command/domain/service/EducationCommandServiceImpl.java new file mode 100644 index 00000000..3a8679f1 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/education/command/domain/service/EducationCommandServiceImpl.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.education.command.domain.service; + + +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.education.command.application.service.EducationCommandService; +import stanl_2.final_backend.domain.education.command.domain.repository.EducationRepository; + +@Service("commandEducationService") +public class EducationCommandServiceImpl implements EducationCommandService { + + private final EducationRepository educationRepository; + private final ModelMapper modelMapper; + + @Autowired + public EducationCommandServiceImpl(EducationRepository educationRepository, + ModelMapper modelMapper) { + this.educationRepository = educationRepository; + this.modelMapper = modelMapper; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/education/common/exception/EducationCommonException.java b/src/main/java/stanl_2/final_backend/domain/education/common/exception/EducationCommonException.java new file mode 100644 index 00000000..c6922d4e --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/education/common/exception/EducationCommonException.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.education.common.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class EducationCommonException extends RuntimeException { + private final EducationErrorCode sampleErrorCode; + + // 에러 발생시 ErroCode 별 메시지 + @Override + public String getMessage() { + return this.sampleErrorCode.getMsg(); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/education/common/exception/EducationErrorCode.java b/src/main/java/stanl_2/final_backend/domain/education/common/exception/EducationErrorCode.java new file mode 100644 index 00000000..04b614c5 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/education/common/exception/EducationErrorCode.java @@ -0,0 +1,52 @@ +package stanl_2.final_backend.domain.education.common.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum EducationErrorCode { + + /** + * 400(Bad Request) + * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. + */ + + + + /** + * 401(Unauthorized) + * 비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만, + * 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다. + * 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다. + */ + + + /** + * 403(Forbidden) + * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. + * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다. + */ + + + + /** + * 404(Not Found) + * 서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다. + * 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있습니다. + * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. + * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. + */ + SAMPLE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "sample 데이터를 찾지 못했습니다"), + CENTER_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "center 데이터를 찾지 못했습니다."), + /** + * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. + */ + INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); + + + private final Integer code; + private final HttpStatus httpStatus; + private final String msg; +} diff --git a/src/main/java/stanl_2/final_backend/domain/education/common/exception/EducationExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/education/common/exception/EducationExceptionResponse.java new file mode 100644 index 00000000..b74b7b22 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/education/common/exception/EducationExceptionResponse.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.education.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class EducationExceptionResponse { + private final Integer code; + private final String msg; + private final HttpStatus httpStatus; + + public EducationExceptionResponse(EducationErrorCode sampleErrorCode) { + this.code = sampleErrorCode.getCode(); + this.msg = sampleErrorCode.getMsg(); + this.httpStatus = sampleErrorCode.getHttpStatus(); + } + + public static EducationExceptionResponse of(EducationErrorCode sampleErrorCode) { + return new EducationExceptionResponse(sampleErrorCode); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/education/common/response/EducationResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/education/common/response/EducationResponseMessage.java new file mode 100644 index 00000000..5610fc21 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/education/common/response/EducationResponseMessage.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.education.common.response; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class EducationResponseMessage { + private int httpStatus; + private String msg; + private Object result; +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/education/query/config/MybatisConfiguration.java b/src/main/java/stanl_2/final_backend/domain/education/query/config/MybatisConfiguration.java new file mode 100644 index 00000000..06b4ade9 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/education/query/config/MybatisConfiguration.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.education.query.config; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Configuration; + +@Configuration("educationMybatisConfiguration") +@MapperScan(basePackages = "stanl_2.final_backend.domain.education.query.repository") +public class MybatisConfiguration { +} diff --git a/src/main/java/stanl_2/final_backend/domain/education/query/controller/EducationController.java b/src/main/java/stanl_2/final_backend/domain/education/query/controller/EducationController.java new file mode 100644 index 00000000..ae20203b --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/education/query/controller/EducationController.java @@ -0,0 +1,18 @@ +package stanl_2.final_backend.domain.education.query.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.education.query.service.EducationQueryService; + +@RestController(value = "queryEducationController") +@RequestMapping("/api/v1/education") +public class EducationController { + + private final EducationQueryService educationQueryService; + + @Autowired + public EducationController(EducationQueryService educationQueryService) { + this.educationQueryService = educationQueryService; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/education/query/dto/EducationDTO.java b/src/main/java/stanl_2/final_backend/domain/education/query/dto/EducationDTO.java new file mode 100644 index 00000000..ee2c7e87 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/education/query/dto/EducationDTO.java @@ -0,0 +1,18 @@ +package stanl_2.final_backend.domain.education.query.dto; + +import jakarta.persistence.Column; +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +public class EducationDTO { + private String entranceDate; + private String graduationDate; + private String name; + private String major; + private String score; + private String note; +} diff --git a/src/main/java/stanl_2/final_backend/domain/education/query/repository/EducationMapper.java b/src/main/java/stanl_2/final_backend/domain/education/query/repository/EducationMapper.java new file mode 100644 index 00000000..07ec606c --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/education/query/repository/EducationMapper.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.education.query.repository; + +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface EducationMapper { +} diff --git a/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryService.java b/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryService.java new file mode 100644 index 00000000..9d9b4b83 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryService.java @@ -0,0 +1,4 @@ +package stanl_2.final_backend.domain.education.query.service; + +public interface EducationQueryService { +} diff --git a/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryServiceImpl.java new file mode 100644 index 00000000..2ed9a56d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryServiceImpl.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.education.query.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.education.query.repository.EducationMapper; + +@Service("queryEducationService") +public class EducationQueryServiceImpl implements EducationQueryService{ + + private final EducationMapper educationMapper; + + @Autowired + public EducationQueryServiceImpl(EducationMapper educationMapper) { + this.educationMapper = educationMapper; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/family/command/application/controller/FamilyController.java b/src/main/java/stanl_2/final_backend/domain/family/command/application/controller/FamilyController.java new file mode 100644 index 00000000..b0b0f2ed --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/family/command/application/controller/FamilyController.java @@ -0,0 +1,18 @@ +package stanl_2.final_backend.domain.family.command.application.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.family.command.application.service.FamilyCommandService; + +@RestController("commandFamilyController") +@RequestMapping("/api/v1/family") +public class FamilyController { + + private final FamilyCommandService familyCommandService; + + @Autowired + public FamilyController(FamilyCommandService familyCommandService) { + this.familyCommandService = familyCommandService; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/family/command/application/dto/FamilyModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/family/command/application/dto/FamilyModifyDTO.java new file mode 100644 index 00000000..6ded2089 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/family/command/application/dto/FamilyModifyDTO.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.family.command.application.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class FamilyModifyDTO { + private String relation; + private String phone; + private Boolean disability; + private Boolean die; + private String note; +} diff --git a/src/main/java/stanl_2/final_backend/domain/family/command/application/dto/FamilyRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/family/command/application/dto/FamilyRegistDTO.java new file mode 100644 index 00000000..1aac1e86 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/family/command/application/dto/FamilyRegistDTO.java @@ -0,0 +1,20 @@ +package stanl_2.final_backend.domain.family.command.application.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class FamilyRegistDTO { + private String name; + private String relation; + private String birth; + private String identNo; + private String phone; + private String sex; + private Boolean disability; + private Boolean die; + private String note; +} diff --git a/src/main/java/stanl_2/final_backend/domain/family/command/application/service/FamilyCommandService.java b/src/main/java/stanl_2/final_backend/domain/family/command/application/service/FamilyCommandService.java new file mode 100644 index 00000000..d1768acd --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/family/command/application/service/FamilyCommandService.java @@ -0,0 +1,4 @@ +package stanl_2.final_backend.domain.family.command.application.service; + +public interface FamilyCommandService { +} diff --git a/src/main/java/stanl_2/final_backend/domain/family/command/domain/aggregate/entity/Family.java b/src/main/java/stanl_2/final_backend/domain/family/command/domain/aggregate/entity/Family.java new file mode 100644 index 00000000..b98e80d4 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/family/command/domain/aggregate/entity/Family.java @@ -0,0 +1,82 @@ +package stanl_2.final_backend.domain.family.command.domain.aggregate.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "tb_family") +public class Family { + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "FAM") + ) + @Column(name = "FAM_ID", nullable = false) + private String familyId; + + @Column(name = "FAM_NAME", nullable = false) + private String name; + + @Column(name = "FAM_REL", nullable = false) + private String relation; + + @Column(name = "FAM_BIR", nullable = false) + private String birth; + + @Column(name = "FAM_IDEN_NO", nullable = false) + private String identNo; + + @Column(name = "FAM_PHO", nullable = false) + private String phone; + + @Column(name = "FAM_SEX", nullable = false) + private String sex; + + @Column(name = "FAM_DIS", nullable = false) + private Boolean disability; + + @Column(name = "FAM_DIE", nullable = false) + private Boolean die; + + @Column(name = "FAM_NOTE") + private String note; + + @Column(name = "CREATED_AT", nullable = false, updatable = false) + private String createdAt; + + @Column(name = "UPDATED_AT", nullable = false) + private String updatedAt; + + /* 설명. updatedAt 자동화 */ + // Insert 되기 전에 실행 + @PrePersist + private void prePersist() { + this.createdAt = getCurrentTime(); + this.updatedAt = this.createdAt; + } + + // Update 되기 전에 실행 + @PreUpdate + private void preUpdate() { + this.updatedAt = getCurrentTime(); + } + + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/family/command/domain/repository/FamilyRepository.java b/src/main/java/stanl_2/final_backend/domain/family/command/domain/repository/FamilyRepository.java new file mode 100644 index 00000000..d9f57ab2 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/family/command/domain/repository/FamilyRepository.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.family.command.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import stanl_2.final_backend.domain.family.command.domain.aggregate.entity.Family; + +public interface FamilyRepository extends JpaRepository { +} diff --git a/src/main/java/stanl_2/final_backend/domain/family/command/domain/service/FamilyCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/family/command/domain/service/FamilyCommandServiceImpl.java new file mode 100644 index 00000000..c5ac5930 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/family/command/domain/service/FamilyCommandServiceImpl.java @@ -0,0 +1,25 @@ +package stanl_2.final_backend.domain.family.command.domain.service; + +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.family.command.application.service.FamilyCommandService; +import stanl_2.final_backend.domain.family.command.domain.repository.FamilyRepository; +import stanl_2.final_backend.global.utils.AESUtils; + +@Service("commandFamilyService") +public class FamilyCommandServiceImpl implements FamilyCommandService { + + private final FamilyRepository familyRepository; + private final ModelMapper modelMapper; + private final AESUtils aesUtils; + + @Autowired + public FamilyCommandServiceImpl(FamilyRepository familyRepository, + ModelMapper modelMapper, + AESUtils aesUtils) { + this.familyRepository = familyRepository; + this.modelMapper = modelMapper; + this.aesUtils = aesUtils; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/family/common/exception/FamilyCommonException.java b/src/main/java/stanl_2/final_backend/domain/family/common/exception/FamilyCommonException.java new file mode 100644 index 00000000..7ab54387 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/family/common/exception/FamilyCommonException.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.family.common.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class FamilyCommonException extends RuntimeException { + private final FamilyErrorCode sampleErrorCode; + + // 에러 발생시 ErroCode 별 메시지 + @Override + public String getMessage() { + return this.sampleErrorCode.getMsg(); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/family/common/exception/FamilyErrorCode.java b/src/main/java/stanl_2/final_backend/domain/family/common/exception/FamilyErrorCode.java new file mode 100644 index 00000000..81d35651 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/family/common/exception/FamilyErrorCode.java @@ -0,0 +1,52 @@ +package stanl_2.final_backend.domain.family.common.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum FamilyErrorCode { + + /** + * 400(Bad Request) + * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. + */ + + + + /** + * 401(Unauthorized) + * 비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만, + * 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다. + * 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다. + */ + + + /** + * 403(Forbidden) + * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. + * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다. + */ + + + + /** + * 404(Not Found) + * 서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다. + * 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있습니다. + * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. + * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. + */ + SAMPLE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "sample 데이터를 찾지 못했습니다"), + CENTER_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "center 데이터를 찾지 못했습니다."), + /** + * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. + */ + INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); + + + private final Integer code; + private final HttpStatus httpStatus; + private final String msg; +} diff --git a/src/main/java/stanl_2/final_backend/domain/family/common/exception/FamilyExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/family/common/exception/FamilyExceptionResponse.java new file mode 100644 index 00000000..b7bf51b2 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/family/common/exception/FamilyExceptionResponse.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.family.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class FamilyExceptionResponse { + private final Integer code; + private final String msg; + private final HttpStatus httpStatus; + + public FamilyExceptionResponse(FamilyErrorCode sampleErrorCode) { + this.code = sampleErrorCode.getCode(); + this.msg = sampleErrorCode.getMsg(); + this.httpStatus = sampleErrorCode.getHttpStatus(); + } + + public static FamilyExceptionResponse of(FamilyErrorCode sampleErrorCode) { + return new FamilyExceptionResponse(sampleErrorCode); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/family/common/response/FamilyResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/family/common/response/FamilyResponseMessage.java new file mode 100644 index 00000000..393076d2 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/family/common/response/FamilyResponseMessage.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.family.common.response; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class FamilyResponseMessage { + private int httpStatus; + private String msg; + private Object result; +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/family/query/config/MybatisConfiguration.java b/src/main/java/stanl_2/final_backend/domain/family/query/config/MybatisConfiguration.java new file mode 100644 index 00000000..238e8a1e --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/family/query/config/MybatisConfiguration.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.family.query.config; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Configuration; + +@Configuration("familyMybatisConfiguration") +@MapperScan(basePackages = "stanl_2.final_backend.domain.family.query.repository") +public class MybatisConfiguration { +} diff --git a/src/main/java/stanl_2/final_backend/domain/family/query/controller/FamilyController.java b/src/main/java/stanl_2/final_backend/domain/family/query/controller/FamilyController.java new file mode 100644 index 00000000..db211482 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/family/query/controller/FamilyController.java @@ -0,0 +1,18 @@ +package stanl_2.final_backend.domain.family.query.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.family.query.service.FamilyQueryService; + +@RestController(value = "queryFamilyController") +@RequestMapping("/api/v1/family") +public class FamilyController { + + private final FamilyQueryService familyQueryService; + + @Autowired + public FamilyController(FamilyQueryService familyQueryService) { + this.familyQueryService = familyQueryService; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/family/query/dto/FamilyDTO.java b/src/main/java/stanl_2/final_backend/domain/family/query/dto/FamilyDTO.java new file mode 100644 index 00000000..b8c9b2e9 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/family/query/dto/FamilyDTO.java @@ -0,0 +1,20 @@ +package stanl_2.final_backend.domain.family.query.dto; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +public class FamilyDTO { + private String name; + private String relation; + private String birth; + private String identNo; + private String phone; + private String sex; + private Boolean disability; + private Boolean die; + private String note; +} diff --git a/src/main/java/stanl_2/final_backend/domain/family/query/repository/FamilyMapper.java b/src/main/java/stanl_2/final_backend/domain/family/query/repository/FamilyMapper.java new file mode 100644 index 00000000..ffdac80f --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/family/query/repository/FamilyMapper.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.family.query.repository; + +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface FamilyMapper { +} diff --git a/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryService.java b/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryService.java new file mode 100644 index 00000000..50712b64 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryService.java @@ -0,0 +1,4 @@ +package stanl_2.final_backend.domain.family.query.service; + +public interface FamilyQueryService { +} diff --git a/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryServiceImpl.java new file mode 100644 index 00000000..2f21b36e --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryServiceImpl.java @@ -0,0 +1,20 @@ +package stanl_2.final_backend.domain.family.query.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.family.query.repository.FamilyMapper; +import stanl_2.final_backend.global.utils.AESUtils; + +@Service("queryFamilyService") +public class FamilyQueryServiceImpl implements FamilyQueryService { + + private final FamilyMapper familyMapper; + private final AESUtils aesUtils; + + @Autowired + public FamilyQueryServiceImpl(FamilyMapper familyMapper, + AESUtils aesUtils) { + this.familyMapper = familyMapper; + this.aesUtils = aesUtils; + } +} diff --git a/src/main/resources/stanl_2/final_backend/domain/career/query/repository/CareerMapper.xml b/src/main/resources/stanl_2/final_backend/domain/career/query/repository/CareerMapper.xml new file mode 100644 index 00000000..44b3fb87 --- /dev/null +++ b/src/main/resources/stanl_2/final_backend/domain/career/query/repository/CareerMapper.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.xml b/src/main/resources/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.xml new file mode 100644 index 00000000..3085aaf2 --- /dev/null +++ b/src/main/resources/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/education/query/repository/EducationMapper.xml b/src/main/resources/stanl_2/final_backend/domain/education/query/repository/EducationMapper.xml new file mode 100644 index 00000000..c11fc15b --- /dev/null +++ b/src/main/resources/stanl_2/final_backend/domain/education/query/repository/EducationMapper.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/family/query/repository/FamilyMapper.xml b/src/main/resources/stanl_2/final_backend/domain/family/query/repository/FamilyMapper.xml new file mode 100644 index 00000000..7bf05da2 --- /dev/null +++ b/src/main/resources/stanl_2/final_backend/domain/family/query/repository/FamilyMapper.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file From 8f95897dd720fa06ecba308b9fd4ccd146227c66 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 08:53:21 +0900 Subject: [PATCH 110/563] =?UTF-8?q?fix:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EA=B6=8C=ED=95=9C?= =?UTF-8?q?=EB=B6=80=EC=97=AC=20responsebody=20=EC=98=88=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AuthController.java | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java index fcece68d..f085c30c 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java @@ -27,6 +27,30 @@ public AuthController(AuthCommandService authCommandService) { this.authCommandService = authCommandService; } + /** + * 회원가입 @ResponseBody + * { + * "loginId": "test", + * "password": "test", + * "name": "이름1", + * "email": "test@test.com", + * "age": 30, + * "sex": "MALE", + * "identNo": "12123", + * "phone": "01012345678", + * "emergePhone": "01088888888", + * "address": "서울", + * "note": "비고1", + * "position": "인턴", + * "grade": "고졸", + * "jobType": "영업", + * "military": "미필", + * "bankName": "국민은행", + * "account": "110-2324-131313-12232", + * "centerId": "CEN_000000001", + * "organizationId": "ORG_000000001" + * } + */ @Operation(summary = "회원가입") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", @@ -44,6 +68,14 @@ public ResponseEntity signup(@RequestBody SignupDTO signu .build()); } + /** + * 권한 부여 @ResponseBody + * { + * "loginId": "test", + * "role": "ADMIN" + * } + */ + @Operation(summary = "권한 부여") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", @@ -62,12 +94,19 @@ public ResponseEntity grantAuthority(@RequestBody GrantDT } - @PostMapping("signin") + /** + * 로그인 @ResponseBody + * { + * "loginId": "test", + * "password": "test" + * } + */ @Operation(summary = "로그인") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = MemberResponseMessage.class))}) }) + @PostMapping("signin") public ResponseEntity signin(@RequestBody SigninRequestDTO signinRequestDTO) { SigninResponseDTO responseDTO = authCommandService.signin(signinRequestDTO); From bce5bbca79468b20497ad9393c13ef15de0df693 Mon Sep 17 00:00:00 2001 From: giuseog Date: Mon, 18 Nov 2024 09:23:41 +0900 Subject: [PATCH 111/563] =?UTF-8?q?feat:=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EC=A4=91(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/EvaluationController.java | 3 - .../common/util/EvaluationRequestList.java | 14 ++ .../controller/EvaluationController.java | 46 +++++-- .../evaluation/query/dto/EvaluationDTO.java | 11 +- .../query/respository/EvaluationMapper.java | 14 +- .../query/service/EvaluationQueryService.java | 10 +- .../service/EvaluationQueryServiceImpl.java | 48 +++++-- .../query/repository/EvaluationMapper.xml | 123 ++++++++++++++++++ 8 files changed, 241 insertions(+), 28 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/evaluation/common/util/EvaluationRequestList.java create mode 100644 src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/controller/EvaluationController.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/controller/EvaluationController.java index 0b24395d..1ce67b7f 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/controller/EvaluationController.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/controller/EvaluationController.java @@ -8,8 +8,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleModifyDTO; -import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleRegistDTO; import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.evaluation.command.application.dto.EvaluationModifyDTO; import stanl_2.final_backend.domain.evaluation.command.application.dto.EvaluationRegistDTO; @@ -60,7 +58,6 @@ public ResponseEntity postTest(@RequestBody Evaluatio public ResponseEntity putTest(@PathVariable String id, @RequestBody EvaluationModifyDTO evaluationModifyRequestDTO) { -// evaluationModifyRequestDTO.setEvaluationId(id); evaluationCommandService.modifyEvaluation(id,evaluationModifyRequestDTO); return ResponseEntity.ok(EvaluationResponseMessage.builder() diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/common/util/EvaluationRequestList.java b/src/main/java/stanl_2/final_backend/domain/evaluation/common/util/EvaluationRequestList.java new file mode 100644 index 00000000..81af7e45 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/common/util/EvaluationRequestList.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.evaluation.common.util; + +import lombok.*; +import org.springframework.data.domain.Pageable; + +@Builder +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class EvaluationRequestList { + private T data; + private Pageable pageable; +} diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java index c0b0e5ff..d4120169 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java @@ -6,18 +6,20 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; 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.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; -import stanl_2.final_backend.domain.A_sample.query.dto.SampleDTO; -import stanl_2.final_backend.domain.A_sample.query.service.SampleQueryService; import stanl_2.final_backend.domain.evaluation.common.response.EvaluationResponseMessage; import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationDTO; import stanl_2.final_backend.domain.evaluation.query.service.EvaluationQueryService; +import java.util.Map; + @RestController(value = "queryEvaluationController") @RequestMapping("/api/v1/evaluation") public class EvaluationController { @@ -31,22 +33,44 @@ public EvaluationController(EvaluationQueryService evaluationQueryService) { /** * [GET] http://localhost:7777/api/v1/sample/SAM_000000001 * */ - @Operation(summary = "평가서 조회 테스트") + @Operation(summary = "평가서 담당자 조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = EvaluationResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("{id}") - public ResponseEntity getTest(@PathVariable String id){ + @GetMapping("") + public ResponseEntity getAllEvaluations(@PageableDefault(size = 20) Pageable pageable){ - EvaluationDTO evaluationDTO = evaluationQueryService.selectEvaluation(id); + Page> responseEvaluations = evaluationQueryService.selectAllEvaluations(pageable); return ResponseEntity.ok(EvaluationResponseMessage.builder() .httpStatus(200) .msg("성공") - .result(evaluationDTO) + .result(responseEvaluations) + .build()); + } + + /** + * [GET] http://localhost:7777/api/v1/sample/SAM_000000001 + * */ + @Operation(summary = "평가서 관리자 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = EvaluationResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("{centerId}") + public ResponseEntity getEvaluationByCenter(@PathVariable String centerId, Pageable pageable){ + + Page> responseEvaluations = evaluationQueryService.selectEvaluationByCenter(centerId, pageable); + + return ResponseEntity.ok(EvaluationResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(responseEvaluations) .build()); } @@ -61,14 +85,14 @@ public ResponseEntity getTest(@PathVariable String id content = @Content(mediaType = "application/json")) }) @GetMapping("/detail/{id}") - public ResponseEntity getDetailTest(@PathVariable String id) { + public ResponseEntity getDetailEvaluation(@PathVariable String id) { - String name = evaluationQueryService.selectEvaluationName(id); + EvaluationDTO evaluationDTO = evaluationQueryService.selectEvaluationById(id); return ResponseEntity.ok(EvaluationResponseMessage.builder() .httpStatus(200) .msg("성공") - .result(name) + .result(evaluationDTO) .build()); } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java index 622f5015..2461b63d 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java @@ -8,5 +8,14 @@ @Setter @ToString public class EvaluationDTO { - private String id; + private String evalId; + private String title; + private String content; + private String createdAt; + private String updatedAt; + private String deletedAt; + private Boolean active; + private String centerId; + private String memberId; + private String writerId; } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/respository/EvaluationMapper.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/respository/EvaluationMapper.java index 072682c8..4050802e 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/respository/EvaluationMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/respository/EvaluationMapper.java @@ -1,12 +1,22 @@ package stanl_2.final_backend.domain.evaluation.query.respository; import org.apache.ibatis.annotations.Mapper; +import stanl_2.final_backend.domain.evaluation.common.util.EvaluationRequestList; import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationDTO; +import java.util.List; +import java.util.Map; + @Mapper public interface EvaluationMapper { - String selectNameById(String id); + List> findByCenterId(String centerId, EvaluationRequestList requestList); + + EvaluationDTO findEvaluationById(String id); + + List> findAllEvaluations(EvaluationRequestList requestList); + + int findEvaluationCount(); - EvaluationDTO selectById(String id); + int findEvaluationCountByCenterId(); } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryService.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryService.java index 608ab996..29998cce 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryService.java @@ -1,10 +1,16 @@ package stanl_2.final_backend.domain.evaluation.query.service; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationDTO; +import java.util.Map; + public interface EvaluationQueryService { - EvaluationDTO selectEvaluation(String id); + Page> selectAllEvaluations(Pageable pageable); + + EvaluationDTO selectEvaluationById(String id); - String selectEvaluationName(String id); + Page> selectEvaluationByCenter(String centerId, Pageable pageable); } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java index c925b355..3ae1f1f8 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java @@ -1,13 +1,20 @@ package stanl_2.final_backend.domain.evaluation.query.service; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.evaluation.common.exception.EvaluationCommonException; import stanl_2.final_backend.domain.evaluation.common.exception.EvaluationErrorCode; +import stanl_2.final_backend.domain.evaluation.common.util.EvaluationRequestList; import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationDTO; import stanl_2.final_backend.domain.evaluation.query.respository.EvaluationMapper; +import java.util.List; +import java.util.Map; + @Service public class EvaluationQueryServiceImpl implements EvaluationQueryService { @@ -20,28 +27,51 @@ public EvaluationQueryServiceImpl(EvaluationMapper evaluationMapper) { @Override @Transactional(readOnly = true) - public String selectEvaluationName(String id) { + public Page> selectAllEvaluations(Pageable pageable) { + + EvaluationRequestList requestList = EvaluationRequestList.builder() + .pageable(pageable) + .build(); - String name = evaluationMapper.selectNameById(id); + List> evaluationList = evaluationMapper.findAllEvaluations(requestList); - if(name == null){ - throw new EvaluationCommonException(EvaluationErrorCode.SAMPLE_NOT_FOUND); + int total = evaluationMapper.findEvaluationCount(); + + if(evaluationList == null || total == 0){ + throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); } + return new PageImpl<>(evaluationList, pageable, total); + } - return name; + + @Override + @Transactional(readOnly = true) + public Page> selectEvaluationByCenter(String centerId, Pageable pageable) { + EvaluationRequestList requestList = EvaluationRequestList.builder() + .pageable(pageable) + .build(); + + List> evaluationList = evaluationMapper.findByCenterId(centerId, requestList); + + int total = evaluationMapper.findEvaluationCountByCenterId(); + + if(evaluationList == null || total == 0){ + throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); + } + return new PageImpl<>(evaluationList, pageable, total); } @Override @Transactional(readOnly = true) - public EvaluationDTO selectEvaluation(String id) { + public EvaluationDTO selectEvaluationById(String id) { - EvaluationDTO evaluationDTO = evaluationMapper.selectById(id); + EvaluationDTO evaluationDTO = evaluationMapper.findEvaluationById(id); if(evaluationDTO == null){ - throw new EvaluationCommonException(EvaluationErrorCode.SAMPLE_NOT_FOUND); + throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); } - return evaluationDTO; + } diff --git a/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml b/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml new file mode 100644 index 00000000..7374c46f --- /dev/null +++ b/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 6c54cf5d64e524c1742be8438f83425a963d7c9d Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 09:33:35 +0900 Subject: [PATCH 112/563] =?UTF-8?q?fix:=20DDL=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/sql/ddl.sql | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index b16d9187..3d154519 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -31,6 +31,11 @@ DROP TABLE IF EXISTS BATCH_JOB_SEQ; DROP TABLE IF EXISTS BATCH_STEP_EXECUTION; DROP TABLE IF EXISTS BATCH_JOB_EXECUTION; DROP TABLE IF EXISTS BATCH_JOB_INSTANCE; +DROP TABLE IF EXISTS sample; +DROP TABLE IF EXISTS tb_career; +DROP TABLE IF EXISTS tb_certification; +DROP TABLE IF EXISTS tb_education; +DROP TABLE IF EXISTS tb_family; -- 조직 관련 테이블 생성 CREATE TABLE tb_organization_chart From 12284a2582a4f87095b4f267e565d4ce639dd007 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 09:52:15 +0900 Subject: [PATCH 113/563] =?UTF-8?q?fix:=20=EC=83=98=ED=94=8C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SampleController.java | 8 ++++++- .../controller/MemberController.java | 21 ------------------- .../common/exception/MemberErrorCode.java | 1 + .../query/service/AuthQueryServiceImpl.java | 2 +- .../security/config/ProdSecurityConfig.java | 3 ++- 5 files changed, 11 insertions(+), 24 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java index ecd25540..53f4589f 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java @@ -8,6 +8,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleRegistDTO; import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleModifyDTO; @@ -43,10 +44,15 @@ public SampleController(SampleCommandService sampleCommandService) { }) @PostMapping("") public ResponseEntity postTest(@RequestBody SampleRegistDTO sampleRegistRequestDTO, - Principal principal) { + Principal principal, + Authentication authentication) { + + log.info("현재 접속한 회원의 권한"); + log.info("{}", authentication.getAuthorities()); log.info("현재 접속한 회원정보(MEM_LOGIN_ID)"); log.info(principal.getName()); + log.info(authentication.getName()); sampleCommandService.registerSample(sampleRegistRequestDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java index 751be6da..c33a4755 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java @@ -21,27 +21,6 @@ public MemberController(MemberCommandService memberCommandService) { this.memberCommandService = memberCommandService; } -// @GetMapping("/authorities") -// public ResponseEntity check(Authentication authentication) { -// if (authentication == null || !authentication.isAuthenticated() || "anonymousUser".equals(authentication.getName())) { -// return ResponseEntity.status(HttpStatus.UNAUTHORIZED) -// .body(MemberResponseMessage.builder() -// .httpStatus(401) -// .msg("Unauthorized") -// .build()); -// } -// -// // 인증된 사용자 정보 출력 -// log.info("인증된 사용자: {}", authentication.getName()); -// -// return ResponseEntity.ok(MemberResponseMessage.builder() -// .httpStatus(200) -// .msg("성공") -// .result("인증된 사용자: " + authentication.getName()) -// .build()); -// } - - @GetMapping("/authorities") public ResponseEntity check(Principal principal) { diff --git a/src/main/java/stanl_2/final_backend/domain/member/common/exception/MemberErrorCode.java b/src/main/java/stanl_2/final_backend/domain/member/common/exception/MemberErrorCode.java index 50e44ee9..bbec0dac 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/common/exception/MemberErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/member/common/exception/MemberErrorCode.java @@ -39,6 +39,7 @@ public enum MemberErrorCode { * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. */ MEMBER_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "회원 데이터를 찾지 못했습니다"), + MEMBER_ID_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "회원 pk값을 찾지 못했습니다."), /** diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java index 19610696..87b334c9 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java @@ -26,7 +26,7 @@ public String selectMemberLoginId(String loginId){ String id = authMapper.selectIdByMemberName(loginId); if(id == null){ - throw new MemberCommonException(MemberErrorCode.MEMBER_NOT_FOUND); + throw new MemberCommonException(MemberErrorCode.MEMBER_ID_NOT_FOUND); } return id; diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index 50935c32..3ba77ab0 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -51,7 +51,8 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication "/api/v1/auth" // 권한 부여때문에(일단 열어둠) ).permitAll() // [Example] member는 ADMIN 권한만 접근 가능 설정 예시 - .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasRole("ADMIN") +// .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasRole("ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasAnyRole("ADMIN", "MEMBER") .anyRequest().authenticated()) // 필터 순서: JWT 검증 -> CSRF .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey), UsernamePasswordAuthenticationFilter.class) From 9855ec5ec4a32318f038992292e7104333a90a22 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 18 Nov 2024 10:15:40 +0900 Subject: [PATCH 114/563] =?UTF-8?q?feat:=20=EB=AC=B8=EC=A0=9C=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=84=B8=ED=8C=85(#6?= =?UTF-8?q?2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/aggregate/entity/Problem.java | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java new file mode 100644 index 00000000..4e82b52c --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java @@ -0,0 +1,74 @@ +package stanl_2.final_backend.domain.problem.command.domain.aggregate.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@Entity +@Table(name="TB_PROMOTION") +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class Problem { + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name="prefix", value = "PROB") + ) + @Column(name = "PROB_ID") + private String promotionId; + + @Column(name = "PROB_TTL", nullable = false) + private String title; + + @Column(name = "PROB_CONT", nullable = false) + private String content; + + @Column(name = "CREATED_AT", nullable = false, updatable = false, length=19) + private String createdAt; + + @Column(name = "UPDATED_AT", nullable = false, length=19) + private String updatedAt; + + @Column(name = "DELETED_AT", length=19) + private String deletedAt; + + @Column(name = "ACTIVE", nullable = false) + private Boolean active = true; + + @Column(name = "CUST_ID", nullable = false) + private String customerId; + + @Column(name = "MEM_ID", nullable = false) + private String memberId; + + @Column(name = "PROD_ID", nullable = false) + private String productId; + + @PrePersist + private void prePersist() { + this.createdAt = getCurrentTime(); + this.updatedAt = this.createdAt; + } + + // Update 되기 전에 실행 + @PreUpdate + private void preUpdate() { + this.updatedAt = getCurrentTime(); + } + + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } +} From 911b1838ca7b20e363e6d72eea408e6d4fd25127 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 10:31:10 +0900 Subject: [PATCH 115/563] =?UTF-8?q?feat:=20=EA=B2=BD=EB=A0=A5=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CareerController.java | 38 +++++++++++++++++-- .../application/dto/CareerRegistDTO.java | 5 +-- .../service/CareerCommandService.java | 3 ++ .../domain/aggregate/entity/Career.java | 3 ++ .../service/CareerCommandServiceImpl.java | 11 ++++++ .../aggregate/entity/Certification.java | 3 ++ .../domain/aggregate/entity/Education.java | 3 ++ .../domain/aggregate/entity/Family.java | 3 ++ .../service/AuthCommandServiceImpl.java | 2 +- .../query/service/AuthQueryService.java | 2 +- .../query/service/AuthQueryServiceImpl.java | 2 +- 11 files changed, 66 insertions(+), 9 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java b/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java index 7a96b740..4c2cf4d9 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java +++ b/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java @@ -1,10 +1,20 @@ package stanl_2.final_backend.domain.career.command.application.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; +import stanl_2.final_backend.domain.career.command.application.dto.CareerRegistDTO; import stanl_2.final_backend.domain.career.command.application.service.CareerCommandService; +import stanl_2.final_backend.domain.career.common.response.CareerResponseMessage; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; @Slf4j @RestController("commandCareerController") @@ -12,9 +22,31 @@ public class CareerController { private final CareerCommandService careerCommandService; + private final AuthQueryService authQueryService; @Autowired - public CareerController(CareerCommandService careerCommandService) { + public CareerController(CareerCommandService careerCommandService, + AuthQueryService authQueryService) { this.careerCommandService = careerCommandService; + this.authQueryService = authQueryService; + } + + @Operation(summary = "경력 등록") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) + }) + @PostMapping("") + public ResponseEntity postCareer(@RequestBody CareerRegistDTO careerRegistDTO){ + + careerRegistDTO.setMemId(authQueryService.selectMemberIdByLoginId(careerRegistDTO.getMemberLoginId())); + + careerCommandService.registCareer(careerRegistDTO); + + return ResponseEntity.ok(CareerResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); } } diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerRegistDTO.java index a9f0bf88..c60f2743 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerRegistDTO.java @@ -1,6 +1,5 @@ package stanl_2.final_backend.domain.career.command.application.dto; -import jakarta.persistence.Column; import lombok.*; @AllArgsConstructor @@ -9,10 +8,10 @@ @Getter @ToString public class CareerRegistDTO { + private String memberLoginId; private String emplDate; private String resignDate; private String name; private String note; - private String createdAt; - private String updatedAt; + private String memId; } diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/application/service/CareerCommandService.java b/src/main/java/stanl_2/final_backend/domain/career/command/application/service/CareerCommandService.java index 65df99d8..bec9d855 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/command/application/service/CareerCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/career/command/application/service/CareerCommandService.java @@ -1,4 +1,7 @@ package stanl_2.final_backend.domain.career.command.application.service; +import stanl_2.final_backend.domain.career.command.application.dto.CareerRegistDTO; + public interface CareerCommandService { + void registCareer(CareerRegistDTO careerRegistDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/domain/aggregate/entity/Career.java b/src/main/java/stanl_2/final_backend/domain/career/command/domain/aggregate/entity/Career.java index 236792b1..649c523b 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/command/domain/aggregate/entity/Career.java +++ b/src/main/java/stanl_2/final_backend/domain/career/command/domain/aggregate/entity/Career.java @@ -46,6 +46,9 @@ public class Career { @Column(name = "UPDATED_AT", nullable = false) private String updatedAt; + @Column(name = "MEM_ID", nullable = false) + private String memId; + /* 설명. updatedAt 자동화 */ // Insert 되기 전에 실행 diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/domain/service/CareerCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/career/command/domain/service/CareerCommandServiceImpl.java index 67b32bb7..57a35447 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/command/domain/service/CareerCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/career/command/domain/service/CareerCommandServiceImpl.java @@ -3,7 +3,10 @@ import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.career.command.application.dto.CareerRegistDTO; import stanl_2.final_backend.domain.career.command.application.service.CareerCommandService; +import stanl_2.final_backend.domain.career.command.domain.aggregate.entity.Career; import stanl_2.final_backend.domain.career.command.domain.repository.CareerRepository; @Service("commandCareerService") @@ -18,4 +21,12 @@ public CareerCommandServiceImpl(CareerRepository careerRepository, this.careerRepository = careerRepository; this.modelMapper = modelMapper; } + + @Override + @Transactional + public void registCareer(CareerRegistDTO careerRegistDTO) { + Career career = modelMapper.map(careerRegistDTO, Career.class); + + careerRepository.save(career); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/certification/command/domain/aggregate/entity/Certification.java b/src/main/java/stanl_2/final_backend/domain/certification/command/domain/aggregate/entity/Certification.java index 2882b4e2..9dcdf9a7 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/command/domain/aggregate/entity/Certification.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/command/domain/aggregate/entity/Certification.java @@ -43,6 +43,9 @@ public class Certification { @Column(name = "CREATED_AT", nullable = false, updatable = false) private String createdAt; + @Column(name = "MEM_ID", nullable = false) + private String memId; + /* 설명. updatedAt 자동화 */ // Insert 되기 전에 실행 @PrePersist diff --git a/src/main/java/stanl_2/final_backend/domain/education/command/domain/aggregate/entity/Education.java b/src/main/java/stanl_2/final_backend/domain/education/command/domain/aggregate/entity/Education.java index 34e7c24d..d936666b 100644 --- a/src/main/java/stanl_2/final_backend/domain/education/command/domain/aggregate/entity/Education.java +++ b/src/main/java/stanl_2/final_backend/domain/education/command/domain/aggregate/entity/Education.java @@ -52,6 +52,9 @@ public class Education { @Column(name = "UPDATED_AT", nullable = false) private String updatedAt; + @Column(name = "MEM_ID", nullable = false) + private String memId; + /* 설명. updatedAt 자동화 */ // Insert 되기 전에 실행 @PrePersist diff --git a/src/main/java/stanl_2/final_backend/domain/family/command/domain/aggregate/entity/Family.java b/src/main/java/stanl_2/final_backend/domain/family/command/domain/aggregate/entity/Family.java index b98e80d4..eb18ba98 100644 --- a/src/main/java/stanl_2/final_backend/domain/family/command/domain/aggregate/entity/Family.java +++ b/src/main/java/stanl_2/final_backend/domain/family/command/domain/aggregate/entity/Family.java @@ -61,6 +61,9 @@ public class Family { @Column(name = "UPDATED_AT", nullable = false) private String updatedAt; + @Column(name = "MEM_ID", nullable = false) + private String memId; + /* 설명. updatedAt 자동화 */ // Insert 되기 전에 실행 @PrePersist diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java index 66dbf2c0..8b409fe2 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java @@ -179,7 +179,7 @@ public RefreshDTO refreshAccessToken(String refreshToken) { @Transactional public void grantAuthority(GrantDTO grantDTO) { - String id = authQueryService.selectMemberLoginId(grantDTO.getLoginId()); + String id = authQueryService.selectMemberIdByLoginId(grantDTO.getLoginId()); MemberRole newMemberRole = modelMapper.map(grantDTO, MemberRole.class); diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryService.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryService.java index 4b9dc976..11810da6 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryService.java @@ -1,5 +1,5 @@ package stanl_2.final_backend.domain.member.query.service; public interface AuthQueryService { - String selectMemberLoginId(String name); + String selectMemberIdByLoginId(String name); } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java index 19610696..8db5ea4f 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java @@ -21,7 +21,7 @@ public AuthQueryServiceImpl(AuthMapper authMapper) { @Override @Transactional - public String selectMemberLoginId(String loginId){ + public String selectMemberIdByLoginId(String loginId){ String id = authMapper.selectIdByMemberName(loginId); From 3354011814d2168a5d46626e3010844480e3b5d2 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 18 Nov 2024 10:34:08 +0900 Subject: [PATCH 116/563] =?UTF-8?q?feat:=20=EB=AC=B8=EC=A0=9C=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=84=B8=ED=8C=85(#6?= =?UTF-8?q?2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/problem/command/domain/aggregate/entity/Problem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java index 4e82b52c..f0536aff 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java @@ -26,7 +26,7 @@ public class Problem { parameters = @org.hibernate.annotations.Parameter(name="prefix", value = "PROB") ) @Column(name = "PROB_ID") - private String promotionId; + private String problemId; @Column(name = "PROB_TTL", nullable = false) private String title; From b309425f0f8f75618fcbb750bf537cb357350c6c Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 18 Nov 2024 10:34:51 +0900 Subject: [PATCH 117/563] =?UTF-8?q?feat:=20=EB=AC=B8=EC=A0=9C=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20=EC=84=B8?= =?UTF-8?q?=ED=8C=85(#62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/ProblemCommonException.java | 16 ++++++ .../common/exception/ProblemErrorCode.java | 51 +++++++++++++++++++ .../exception/ProblemExceptionResponse.java | 4 ++ .../response/ProblemResponseMessage.java | 14 +++++ .../problem/common/util/RequestList.java | 14 +++++ 5 files changed, 99 insertions(+) create mode 100644 src/main/java/stanl_2/final_backend/domain/problem/common/exception/ProblemCommonException.java create mode 100644 src/main/java/stanl_2/final_backend/domain/problem/common/exception/ProblemErrorCode.java create mode 100644 src/main/java/stanl_2/final_backend/domain/problem/common/exception/ProblemExceptionResponse.java create mode 100644 src/main/java/stanl_2/final_backend/domain/problem/common/response/ProblemResponseMessage.java create mode 100644 src/main/java/stanl_2/final_backend/domain/problem/common/util/RequestList.java diff --git a/src/main/java/stanl_2/final_backend/domain/problem/common/exception/ProblemCommonException.java b/src/main/java/stanl_2/final_backend/domain/problem/common/exception/ProblemCommonException.java new file mode 100644 index 00000000..7799e30a --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/problem/common/exception/ProblemCommonException.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.problem.common.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class ProblemCommonException extends RuntimeException { + private final ProblemErrorCode problemErrorCode; + + // 에러 발생시 ErroCode 별 메시지 + @Override + public String getMessage() { + return this.problemErrorCode.getMsg(); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/problem/common/exception/ProblemErrorCode.java b/src/main/java/stanl_2/final_backend/domain/problem/common/exception/ProblemErrorCode.java new file mode 100644 index 00000000..50606191 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/problem/common/exception/ProblemErrorCode.java @@ -0,0 +1,51 @@ +package stanl_2.final_backend.domain.problem.common.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum ProblemErrorCode { + + /** + * 400(Bad Request) + * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. + */ + + + + /** + * 401(Unauthorized) + * 비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만, + * 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다. + * 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다. + */ + + + /** + * 403(Forbidden) + * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. + * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다. + */ + + + + /** + * 404(Not Found) + * 서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다. + * 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있습니다. + * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. + * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. + */ + PROBLEM_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "problem 데이터를 찾지 못했습니다"), + /** + * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. + */ + INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); + + + private final Integer code; + private final HttpStatus httpStatus; + private final String msg; +} diff --git a/src/main/java/stanl_2/final_backend/domain/problem/common/exception/ProblemExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/problem/common/exception/ProblemExceptionResponse.java new file mode 100644 index 00000000..9da737c6 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/problem/common/exception/ProblemExceptionResponse.java @@ -0,0 +1,4 @@ +package stanl_2.final_backend.domain.problem.common.exception; + +public class ProblemExceptionResponse { +} diff --git a/src/main/java/stanl_2/final_backend/domain/problem/common/response/ProblemResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/problem/common/response/ProblemResponseMessage.java new file mode 100644 index 00000000..b6dd5845 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/problem/common/response/ProblemResponseMessage.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.problem.common.response; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class ProblemResponseMessage { + private int httpStatus; + private String msg; + private Object result; +} diff --git a/src/main/java/stanl_2/final_backend/domain/problem/common/util/RequestList.java b/src/main/java/stanl_2/final_backend/domain/problem/common/util/RequestList.java new file mode 100644 index 00000000..7771cb6b --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/problem/common/util/RequestList.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.problem.common.util; + +import lombok.*; +import org.springframework.data.domain.Pageable; + +@Builder +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class RequestList { + private T data; + private Pageable pageable; +} \ No newline at end of file From 4774f5dd5c6e6b4f0dcc998184de6fda538b74ef Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 18 Nov 2024 10:35:36 +0900 Subject: [PATCH 118/563] =?UTF-8?q?feat:=20=EB=AC=B8=EC=A0=9C=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20DTO=20=EC=84=B8=ED=8C=85(#62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/dto/ProblemModifyDTO.java | 18 +++++++++++++++ .../application/dto/ProblemRegistDTO.java | 23 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemRegistDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java new file mode 100644 index 00000000..70f02406 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java @@ -0,0 +1,18 @@ +package stanl_2.final_backend.domain.problem.command.application.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class ProblemModifyDTO { + + private String title; + + private String content; + +} diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemRegistDTO.java new file mode 100644 index 00000000..0172752f --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemRegistDTO.java @@ -0,0 +1,23 @@ +package stanl_2.final_backend.domain.problem.command.application.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class ProblemRegistDTO { + + private String title; + + private String content; + + private String customerId; + + private String memberId; + + private String productId; +} From f1b9a5076aa6f395bf09d480799a46ca6e329d82 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 18 Nov 2024 10:36:11 +0900 Subject: [PATCH 119/563] =?UTF-8?q?feat:=20=EB=AC=B8=EC=A0=9C=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20Service&Repository=EB=8B=A8=20=20=EC=84=B8=ED=8C=85?= =?UTF-8?q?(#62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ProblemCommandService.java | 13 ++++ .../repository/ProblemRepository.java | 7 ++ .../aggregate/service/ProblemServiceImpl.java | 77 +++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 src/main/java/stanl_2/final_backend/domain/problem/command/application/service/ProblemCommandService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/repository/ProblemRepository.java create mode 100644 src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/service/ProblemCommandService.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/service/ProblemCommandService.java new file mode 100644 index 00000000..e9fa685b --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/service/ProblemCommandService.java @@ -0,0 +1,13 @@ +package stanl_2.final_backend.domain.problem.command.application.service; + +import stanl_2.final_backend.domain.problem.command.application.dto.ProblemModifyDTO; +import stanl_2.final_backend.domain.problem.command.application.dto.ProblemRegistDTO; + +public interface ProblemCommandService { + + void registerProblem(ProblemRegistDTO problemRegistDTO); + + ProblemModifyDTO modifyProblem(String problemId, ProblemModifyDTO problemModifyDTO); + + void deleteProblem(String problemId); +} diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/repository/ProblemRepository.java b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/repository/ProblemRepository.java new file mode 100644 index 00000000..f79c6d7b --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/repository/ProblemRepository.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.problem.command.domain.aggregate.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import stanl_2.final_backend.domain.problem.command.domain.aggregate.entity.Problem; + +public interface ProblemRepository extends JpaRepository { +} diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java new file mode 100644 index 00000000..b4ba3233 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java @@ -0,0 +1,77 @@ +package stanl_2.final_backend.domain.problem.command.domain.aggregate.service; + +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.problem.command.application.dto.ProblemModifyDTO; +import stanl_2.final_backend.domain.problem.command.application.dto.ProblemRegistDTO; +import stanl_2.final_backend.domain.problem.command.application.service.ProblemCommandService; +import stanl_2.final_backend.domain.problem.command.domain.aggregate.entity.Problem; +import stanl_2.final_backend.domain.problem.command.domain.aggregate.repository.ProblemRepository; +import stanl_2.final_backend.domain.problem.common.exception.ProblemCommonException; +import stanl_2.final_backend.domain.problem.common.exception.ProblemErrorCode; +import stanl_2.final_backend.domain.promotion.common.exception.PromotionCommonException; +import stanl_2.final_backend.domain.promotion.common.exception.PromotionErrorCode; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@Service("commandProblemService") +public class ProblemServiceImpl implements ProblemCommandService { + + private final ProblemRepository problemRepository; + + private final ModelMapper modelMapper; + + @Autowired + public ProblemServiceImpl(ProblemRepository problemRepository, ModelMapper modelMapper) { + this.problemRepository = problemRepository; + this.modelMapper = modelMapper; + } + private String getCurrentTimestamp() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } + + @Transactional + @Override + public void registerProblem(ProblemRegistDTO problemRegistDTO) { + Problem problem =modelMapper.map(problemRegistDTO,Problem.class); + problemRepository.save(problem); + } + + @Transactional + @Override + public ProblemModifyDTO modifyProblem(String problemId, ProblemModifyDTO problemModifyDTO) { + Problem problem = problemRepository.findById(problemId) + .orElseThrow(() -> new ProblemCommonException(ProblemErrorCode.PROBLEM_NOT_FOUND)); + + Problem updateProblem = modelMapper.map(problemModifyDTO, Problem.class); + updateProblem.setProblemId(problem.getProblemId()); + updateProblem.setMemberId(problem.getMemberId()); + updateProblem.setCreatedAt(problem.getCreatedAt()); + updateProblem.setActive(problem.getActive()); + updateProblem.setCustomerId(problem.getCustomerId()); + updateProblem.setProductId(problem.getProductId()); + + problemRepository.save(updateProblem); + + ProblemModifyDTO problemModify = modelMapper.map(updateProblem,ProblemModifyDTO.class); + + return problemModify; + } + + @Transactional + @Override + public void deleteProblem(String problemId) { + Problem problem = problemRepository.findById(problemId) + .orElseThrow(()-> new PromotionCommonException(PromotionErrorCode.PROMOTION_NOT_FOUND)); + + problem.setActive(false); + problem.setDeletedAt(getCurrentTimestamp()); + + problemRepository.save(problem); + } +} From dfd21b82fa5d7b155fd9fc7dd8259a3380a8c95b Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 18 Nov 2024 10:36:34 +0900 Subject: [PATCH 120/563] =?UTF-8?q?feat:=20=EB=AC=B8=EC=A0=9C=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20Controller=20=20=EC=84=B8=ED=8C=85(#62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ProblemController.java | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java new file mode 100644 index 00000000..61e363f5 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java @@ -0,0 +1,75 @@ +package stanl_2.final_backend.domain.problem.command.application.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.problem.command.application.dto.ProblemModifyDTO; +import stanl_2.final_backend.domain.problem.command.application.dto.ProblemRegistDTO; +import stanl_2.final_backend.domain.problem.command.application.service.ProblemCommandService; +import stanl_2.final_backend.domain.problem.common.response.ProblemResponseMessage; + +@RestController("commandProblemController") +@RequestMapping("/api/v1/problem") +public class ProblemController { + private final ProblemCommandService problemCommandService; + + @Autowired + public ProblemController(ProblemCommandService problemCommandService) { + this.problemCommandService = problemCommandService; + } + + @Operation(summary = "문제사항 작성") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = ProblemResponseMessage.class))}) + }) + @PostMapping("") + public ResponseEntity postNotice(@RequestBody ProblemRegistDTO problemRegistDTO){ + problemCommandService.registerProblem(problemRegistDTO); + return ResponseEntity.ok(ProblemResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + + } + @Operation(summary = "문제사항 수정") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = ProblemResponseMessage.class))}) + }) + @PutMapping("{problemId}") + public ResponseEntity modifyProblem(@PathVariable String problemId, + @RequestBody ProblemModifyDTO problemModifyRequestDTO){ + + ProblemModifyDTO problemModifyDTO = problemCommandService.modifyProblem(problemId,problemModifyRequestDTO); + + return ResponseEntity.ok(ProblemResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(problemModifyDTO) + .build()); + } + + @Operation(summary = "문제사항 삭제") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = ProblemResponseMessage.class))}) + }) + @DeleteMapping("{problemId}") + public ResponseEntity deleteProblem(@PathVariable String problemId) { + + problemCommandService.deleteProblem(problemId); + + return ResponseEntity.ok(ProblemResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + } +} From 9ccfbd8965f048ae26db20401c49c5ba22e0e41a Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 18 Nov 2024 10:37:09 +0900 Subject: [PATCH 121/563] =?UTF-8?q?fix:=20=ED=94=84=EB=A1=9C=EB=AA=A8?= =?UTF-8?q?=EC=85=98=20Controller=20=EC=88=98=EC=A0=95=EC=82=AC=ED=95=AD(#?= =?UTF-8?q?61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/domain/aggregate/service/PromotionServiceImpl.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java index d1e192de..396b6882 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java @@ -4,9 +4,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.notices.command.domain.aggragate.entity.Notice; -import stanl_2.final_backend.domain.notices.common.exception.NoticeCommonException; -import stanl_2.final_backend.domain.notices.common.exception.NoticeErrorCode; import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionModifyDTO; import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionRegistDTO; import stanl_2.final_backend.domain.promotion.command.application.service.PromotionCommandService; From 9037b32cc98bac54b9c6ba2dadf097f5ba2b4e99 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 18 Nov 2024 10:53:05 +0900 Subject: [PATCH 122/563] =?UTF-8?q?fix:=20=EB=AC=B8=EC=A0=9C=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20Entity=EB=B0=8F=20Repository=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=82=AC=ED=95=AD(#62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/problem/command/domain/aggregate/entity/Problem.java | 2 +- .../command/domain/aggregate/repository/ProblemRepository.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java index f0536aff..5d3dbda6 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java @@ -13,7 +13,7 @@ import java.time.format.DateTimeFormatter; @Entity -@Table(name="TB_PROMOTION") +@Table(name="TB_PROBLEM") @AllArgsConstructor @NoArgsConstructor @Setter diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/repository/ProblemRepository.java b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/repository/ProblemRepository.java index f79c6d7b..db232407 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/repository/ProblemRepository.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/repository/ProblemRepository.java @@ -1,7 +1,9 @@ package stanl_2.final_backend.domain.problem.command.domain.aggregate.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; import stanl_2.final_backend.domain.problem.command.domain.aggregate.entity.Problem; +@Repository public interface ProblemRepository extends JpaRepository { } From 6e0337bf493f43232b133747a7ca3bfa08f2cd4d Mon Sep 17 00:00:00 2001 From: giuseog Date: Mon, 18 Nov 2024 10:55:53 +0900 Subject: [PATCH 123/563] =?UTF-8?q?feat:=20=ED=8C=90=EB=A7=A4=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84=20=EC=A4=91?= =?UTF-8?q?(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SalesHistoryCommonException.java | 16 ++++++ .../exception/SalesHistoryErrorCode.java | 52 +++++++++++++++++++ .../SalesHistoryExceptionResponse.java | 22 ++++++++ .../response/SalesHistoryResponseMessage.java | 14 +++++ .../controller/SalesHistoryController.java | 47 +++++++++++++++++ .../query/dto/SalesHistorySelectAllDTO.java | 13 +++++ .../query/dto/SalesHistorySelectIdDTO.java | 13 +++++ .../query/repository/SalesHistoryMapper.java | 7 +++ .../service/SalesHistoryQueryService.java | 7 +++ .../service/SalesHistoryQueryServiceImpl.java | 12 +++++ 10 files changed, 203 insertions(+) create mode 100644 src/main/java/stanl_2/final_backend/domain/sales_history/common/exception/SalesHistoryCommonException.java create mode 100644 src/main/java/stanl_2/final_backend/domain/sales_history/common/exception/SalesHistoryErrorCode.java create mode 100644 src/main/java/stanl_2/final_backend/domain/sales_history/common/exception/SalesHistoryExceptionResponse.java create mode 100644 src/main/java/stanl_2/final_backend/domain/sales_history/common/response/SalesHistoryResponseMessage.java create mode 100644 src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectAllDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectIdDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java create mode 100644 src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/common/exception/SalesHistoryCommonException.java b/src/main/java/stanl_2/final_backend/domain/sales_history/common/exception/SalesHistoryCommonException.java new file mode 100644 index 00000000..4329600d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/common/exception/SalesHistoryCommonException.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.sales_history.common.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class SalesHistoryCommonException extends RuntimeException { + private final SalesHistoryErrorCode salesHistoryErrorCode; + + // 에러 발생시 ErroCode 별 메시지 + @Override + public String getMessage() { + return this.salesHistoryErrorCode.getMsg(); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/common/exception/SalesHistoryErrorCode.java b/src/main/java/stanl_2/final_backend/domain/sales_history/common/exception/SalesHistoryErrorCode.java new file mode 100644 index 00000000..657336ea --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/common/exception/SalesHistoryErrorCode.java @@ -0,0 +1,52 @@ +package stanl_2.final_backend.domain.sales_history.common.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum SalesHistoryErrorCode { + + /** + * 400(Bad Request) + * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. + */ + + + + /** + * 401(Unauthorized) + * 비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만, + * 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다. + * 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다. + */ + + + /** + * 403(Forbidden) + * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. + * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다. + */ + + + + /** + * 404(Not Found) + * 서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다. + * 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있습니다. + * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. + * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. + */ + SAMPLE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "sample 데이터를 찾지 못했습니다"), + CENTER_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "center 데이터를 찾지 못했습니다."), + /** + * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. + */ + INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); + + + private final Integer code; + private final HttpStatus httpStatus; + private final String msg; +} diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/common/exception/SalesHistoryExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/sales_history/common/exception/SalesHistoryExceptionResponse.java new file mode 100644 index 00000000..286f7742 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/common/exception/SalesHistoryExceptionResponse.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.sales_history.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class SalesHistoryExceptionResponse { + private final Integer code; + private final String msg; + private final HttpStatus httpStatus; + + public SalesHistoryExceptionResponse(SalesHistoryErrorCode salesHistoryErrorCode) { + this.code = salesHistoryErrorCode.getCode(); + this.msg = salesHistoryErrorCode.getMsg(); + this.httpStatus = salesHistoryErrorCode.getHttpStatus(); + } + + public static SalesHistoryExceptionResponse of(SalesHistoryErrorCode salesHistoryErrorCode) { + return new SalesHistoryExceptionResponse(salesHistoryErrorCode); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/common/response/SalesHistoryResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/sales_history/common/response/SalesHistoryResponseMessage.java new file mode 100644 index 00000000..4c5a92ce --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/common/response/SalesHistoryResponseMessage.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.sales_history.common.response; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class SalesHistoryResponseMessage { + private int httpStatus; + private String msg; + private Object result; +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java new file mode 100644 index 00000000..8cf96941 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java @@ -0,0 +1,47 @@ +package stanl_2.final_backend.domain.sales_history.query.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.sales_history.common.response.SalesHistoryResponseMessage; +import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectAllDTO; +import stanl_2.final_backend.domain.sales_history.query.service.SalesHistoryQueryService; + +@RestController(value = "querySalesHistoryController") +@RequestMapping("/api/v1/salesHistory") +public class SalesHistoryController { + private final SalesHistoryQueryService salesHistoryQueryService; + + @Autowired + public SalesHistoryController(SalesHistoryQueryService salesHistoryQueryService) { + this.salesHistoryQueryService = salesHistoryQueryService; + } + + @Operation(summary = "판매내역 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("") + public ResponseEntity getAllSalesHistory(){ + + SalesHistorySelectAllDTO salesHistorySelectAllDTO = salesHistoryQueryService.selectAllSalesHistory(); + + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(salesHistorySelectAllDTO) + .build()); + } + + +} diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectAllDTO.java new file mode 100644 index 00000000..1861b964 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectAllDTO.java @@ -0,0 +1,13 @@ +package stanl_2.final_backend.domain.sales_history.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class SalesHistorySelectAllDTO { +} diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectIdDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectIdDTO.java new file mode 100644 index 00000000..18d2408e --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectIdDTO.java @@ -0,0 +1,13 @@ +package stanl_2.final_backend.domain.sales_history.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class SalesHistorySelectIdDTO { +} diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java new file mode 100644 index 00000000..5fb7e575 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.sales_history.query.repository; + +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface SalesHistoryMapper { +} diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java new file mode 100644 index 00000000..e5ff25f5 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.sales_history.query.service; + +import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectAllDTO; + +public interface SalesHistoryQueryService { + SalesHistorySelectAllDTO selectAllSalesHistory(); +} diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java new file mode 100644 index 00000000..044ce47b --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java @@ -0,0 +1,12 @@ +package stanl_2.final_backend.domain.sales_history.query.service; + +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectAllDTO; + +@Service +public class SalesHistoryQueryServiceImpl implements SalesHistoryQueryService { + @Override + public SalesHistorySelectAllDTO selectAllSalesHistory() { + return null; + } +} From a319db79cf4a4ba73e27ecc34d537eaa05e50510 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 11:22:39 +0900 Subject: [PATCH 124/563] =?UTF-8?q?feat:=20=EA=B2=BD=EB=A0=A5=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20with=20=EC=82=AC=EB=B2=88(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CareerController.java | 21 +++++++++++- .../application/dto/CareerModifyDTO.java | 15 ++++++++ .../service/CareerCommandService.java | 3 ++ .../service/CareerCommandServiceImpl.java | 8 +++++ .../query/controller/CareerController.java | 34 +++++++++++++++++++ .../career/query/repository/CareerMapper.java | 5 +++ .../query/service/CareerQueryService.java | 5 +++ .../query/service/CareerQueryServiceImpl.java | 26 +++++++++++++- .../career/query/repository/CareerMapper.xml | 16 +++++++++ 9 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerModifyDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java b/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java index 4c2cf4d9..ac1bf5f8 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java +++ b/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java @@ -10,11 +10,11 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; +import stanl_2.final_backend.domain.career.command.application.dto.CareerModifyDTO; import stanl_2.final_backend.domain.career.command.application.dto.CareerRegistDTO; import stanl_2.final_backend.domain.career.command.application.service.CareerCommandService; import stanl_2.final_backend.domain.career.common.response.CareerResponseMessage; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; -import stanl_2.final_backend.domain.member.query.service.MemberQueryService; @Slf4j @RestController("commandCareerController") @@ -49,4 +49,23 @@ public ResponseEntity postCareer(@RequestBody CareerRegis .result(null) .build()); } + + @Operation(summary = "경력 수정") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) + }) + @PutMapping("") + public ResponseEntity putCareer(@RequestBody CareerModifyDTO careerModifyDTO){ + + careerModifyDTO.setMemId(authQueryService.selectMemberIdByLoginId(careerModifyDTO.getMemberLoginId())); + + careerCommandService.modifyCareer(careerModifyDTO); + + return ResponseEntity.ok(CareerResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerModifyDTO.java new file mode 100644 index 00000000..8f83a381 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerModifyDTO.java @@ -0,0 +1,15 @@ +package stanl_2.final_backend.domain.career.command.application.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class CareerModifyDTO { + private String memberLoginId; + private String resignDate; + private String note; + private String memId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/application/service/CareerCommandService.java b/src/main/java/stanl_2/final_backend/domain/career/command/application/service/CareerCommandService.java index bec9d855..c556b4a4 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/command/application/service/CareerCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/career/command/application/service/CareerCommandService.java @@ -1,7 +1,10 @@ package stanl_2.final_backend.domain.career.command.application.service; +import stanl_2.final_backend.domain.career.command.application.dto.CareerModifyDTO; import stanl_2.final_backend.domain.career.command.application.dto.CareerRegistDTO; public interface CareerCommandService { void registCareer(CareerRegistDTO careerRegistDTO); + + void modifyCareer(CareerModifyDTO careerModifyDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/domain/service/CareerCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/career/command/domain/service/CareerCommandServiceImpl.java index 57a35447..952bb423 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/command/domain/service/CareerCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/career/command/domain/service/CareerCommandServiceImpl.java @@ -4,6 +4,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.career.command.application.dto.CareerModifyDTO; import stanl_2.final_backend.domain.career.command.application.dto.CareerRegistDTO; import stanl_2.final_backend.domain.career.command.application.service.CareerCommandService; import stanl_2.final_backend.domain.career.command.domain.aggregate.entity.Career; @@ -29,4 +30,11 @@ public void registCareer(CareerRegistDTO careerRegistDTO) { careerRepository.save(career); } + + @Override + @Transactional + public void modifyCareer(CareerModifyDTO careerModifyDTO) { + + + } } diff --git a/src/main/java/stanl_2/final_backend/domain/career/query/controller/CareerController.java b/src/main/java/stanl_2/final_backend/domain/career/query/controller/CareerController.java index a9bfc3cc..3f105684 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/query/controller/CareerController.java +++ b/src/main/java/stanl_2/final_backend/domain/career/query/controller/CareerController.java @@ -1,10 +1,25 @@ package stanl_2.final_backend.domain.career.query.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +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.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; +import stanl_2.final_backend.domain.career.common.response.CareerResponseMessage; +import stanl_2.final_backend.domain.career.query.dto.CareerDTO; import stanl_2.final_backend.domain.career.query.service.CareerQueryService; +import java.util.List; + +@Slf4j @RestController(value = "queryCareerController") @RequestMapping("/api/v1/career") public class CareerController { @@ -15,4 +30,23 @@ public class CareerController { public CareerController(CareerQueryService careerQueryService) { this.careerQueryService = careerQueryService; } + + @Operation(summary = "사번으로 경력 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/other/{id}") + public ResponseEntity getCareer(@PathVariable String id){ + + List careerList = careerQueryService.selectCareerList(id); + + return ResponseEntity.ok(CareerResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(careerList) + .build()); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/career/query/repository/CareerMapper.java b/src/main/java/stanl_2/final_backend/domain/career/query/repository/CareerMapper.java index 9ad07d33..20ed2158 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/query/repository/CareerMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/career/query/repository/CareerMapper.java @@ -1,7 +1,12 @@ package stanl_2.final_backend.domain.career.query.repository; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import stanl_2.final_backend.domain.career.query.dto.CareerDTO; + +import java.util.List; @Mapper public interface CareerMapper { + List selectCareerInfo(@Param("loginId") String loginId); } diff --git a/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryService.java b/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryService.java index ecb09662..5b8412b3 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryService.java @@ -1,4 +1,9 @@ package stanl_2.final_backend.domain.career.query.service; +import stanl_2.final_backend.domain.career.query.dto.CareerDTO; + +import java.util.List; + public interface CareerQueryService { + List selectCareerList(String id); } diff --git a/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryServiceImpl.java index 24cb6aab..f5573868 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryServiceImpl.java @@ -1,16 +1,40 @@ package stanl_2.final_backend.domain.career.query.service; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.career.query.dto.CareerDTO; import stanl_2.final_backend.domain.career.query.repository.CareerMapper; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import java.util.List; + +@Slf4j @Service("queryCareerService") public class CareerQueryServiceImpl implements CareerQueryService { private final CareerMapper careerMapper; + private final AuthQueryService authQueryService; @Autowired - public CareerQueryServiceImpl(CareerMapper careerMapper) { + public CareerQueryServiceImpl(CareerMapper careerMapper, + AuthQueryService authQueryService) { this.careerMapper = careerMapper; + this.authQueryService = authQueryService; + } + + @Override + @Transactional + public List selectCareerList(String id) { + + String loginId = authQueryService.selectMemberIdByLoginId(id); + + log.info("#####"); + log.info(loginId); + + List careerList = careerMapper.selectCareerInfo(loginId); + + return careerList; } } diff --git a/src/main/resources/stanl_2/final_backend/domain/career/query/repository/CareerMapper.xml b/src/main/resources/stanl_2/final_backend/domain/career/query/repository/CareerMapper.xml index 44b3fb87..2acfdef9 100644 --- a/src/main/resources/stanl_2/final_backend/domain/career/query/repository/CareerMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/career/query/repository/CareerMapper.xml @@ -3,5 +3,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> + + + + + + + \ No newline at end of file From bf545deb1601080016af032c6c78329acd100e49 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 11:26:15 +0900 Subject: [PATCH 125/563] =?UTF-8?q?feat:=20=EA=B2=BD=EB=A0=A5=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20(=EC=A0=91=EC=86=8D=EC=A4=91=EC=9D=B8=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90)(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/CareerController.java | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/career/query/controller/CareerController.java b/src/main/java/stanl_2/final_backend/domain/career/query/controller/CareerController.java index 3f105684..03e0e015 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/query/controller/CareerController.java +++ b/src/main/java/stanl_2/final_backend/domain/career/query/controller/CareerController.java @@ -17,6 +17,7 @@ import stanl_2.final_backend.domain.career.query.dto.CareerDTO; import stanl_2.final_backend.domain.career.query.service.CareerQueryService; +import java.security.Principal; import java.util.List; @Slf4j @@ -31,7 +32,7 @@ public CareerController(CareerQueryService careerQueryService) { this.careerQueryService = careerQueryService; } - @Operation(summary = "사번으로 경력 조회") + @Operation(summary = "경력 조회(with 사번)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), @@ -39,7 +40,7 @@ public CareerController(CareerQueryService careerQueryService) { content = @Content(mediaType = "application/json")) }) @GetMapping("/other/{id}") - public ResponseEntity getCareer(@PathVariable String id){ + public ResponseEntity getCareerByOther(@PathVariable String id){ List careerList = careerQueryService.selectCareerList(id); @@ -49,4 +50,23 @@ public ResponseEntity getCareer(@PathVariable String id){ .result(careerList) .build()); } + + @Operation(summary = "경력 조회(접속중인 사용자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("") + public ResponseEntity getCareer(Principal principal){ + + List careerList = careerQueryService.selectCareerList(principal.getName()); + + return ResponseEntity.ok(CareerResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(careerList) + .build()); + } } From a45ed39c79591d204d5c8b550f2a1ffb625fef81 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 11:33:30 +0900 Subject: [PATCH 126/563] =?UTF-8?q?fix:=20=EA=B2=BD=EB=A0=A5=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EC=A0=95=EB=A6=AC(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CareerController.java | 20 ---------------- .../application/dto/CareerModifyDTO.java | 15 ------------ .../service/CareerCommandService.java | 2 -- .../domain/aggregate/entity/Career.java | 12 ---------- .../service/CareerCommandServiceImpl.java | 8 ------- .../aggregate/entity/Certification.java | 1 - .../domain/aggregate/entity/Family.java | 3 --- .../domain/aggregate/entity/Member.java | 3 --- src/main/resources/sql/ddl.sql | 23 +++++++++---------- 9 files changed, 11 insertions(+), 76 deletions(-) delete mode 100644 src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerModifyDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java b/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java index ac1bf5f8..3dd868a6 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java +++ b/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java @@ -10,7 +10,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; -import stanl_2.final_backend.domain.career.command.application.dto.CareerModifyDTO; import stanl_2.final_backend.domain.career.command.application.dto.CareerRegistDTO; import stanl_2.final_backend.domain.career.command.application.service.CareerCommandService; import stanl_2.final_backend.domain.career.common.response.CareerResponseMessage; @@ -49,23 +48,4 @@ public ResponseEntity postCareer(@RequestBody CareerRegis .result(null) .build()); } - - @Operation(summary = "경력 수정") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) - }) - @PutMapping("") - public ResponseEntity putCareer(@RequestBody CareerModifyDTO careerModifyDTO){ - - careerModifyDTO.setMemId(authQueryService.selectMemberIdByLoginId(careerModifyDTO.getMemberLoginId())); - - careerCommandService.modifyCareer(careerModifyDTO); - - return ResponseEntity.ok(CareerResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(null) - .build()); - } } diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerModifyDTO.java deleted file mode 100644 index 8f83a381..00000000 --- a/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerModifyDTO.java +++ /dev/null @@ -1,15 +0,0 @@ -package stanl_2.final_backend.domain.career.command.application.dto; - -import lombok.*; - -@AllArgsConstructor -@NoArgsConstructor -@Setter -@Getter -@ToString -public class CareerModifyDTO { - private String memberLoginId; - private String resignDate; - private String note; - private String memId; -} diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/application/service/CareerCommandService.java b/src/main/java/stanl_2/final_backend/domain/career/command/application/service/CareerCommandService.java index c556b4a4..ca136437 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/command/application/service/CareerCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/career/command/application/service/CareerCommandService.java @@ -1,10 +1,8 @@ package stanl_2.final_backend.domain.career.command.application.service; -import stanl_2.final_backend.domain.career.command.application.dto.CareerModifyDTO; import stanl_2.final_backend.domain.career.command.application.dto.CareerRegistDTO; public interface CareerCommandService { void registCareer(CareerRegistDTO careerRegistDTO); - void modifyCareer(CareerModifyDTO careerModifyDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/domain/aggregate/entity/Career.java b/src/main/java/stanl_2/final_backend/domain/career/command/domain/aggregate/entity/Career.java index 649c523b..a95b5737 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/command/domain/aggregate/entity/Career.java +++ b/src/main/java/stanl_2/final_backend/domain/career/command/domain/aggregate/entity/Career.java @@ -43,25 +43,13 @@ public class Career { @Column(name = "CREATED_AT", nullable = false, updatable = false) private String createdAt; - @Column(name = "UPDATED_AT", nullable = false) - private String updatedAt; - @Column(name = "MEM_ID", nullable = false) private String memId; - /* 설명. updatedAt 자동화 */ - // Insert 되기 전에 실행 @PrePersist private void prePersist() { this.createdAt = getCurrentTime(); - this.updatedAt = this.createdAt; - } - - // Update 되기 전에 실행 - @PreUpdate - private void preUpdate() { - this.updatedAt = getCurrentTime(); } private String getCurrentTime() { diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/domain/service/CareerCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/career/command/domain/service/CareerCommandServiceImpl.java index 952bb423..57a35447 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/command/domain/service/CareerCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/career/command/domain/service/CareerCommandServiceImpl.java @@ -4,7 +4,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.career.command.application.dto.CareerModifyDTO; import stanl_2.final_backend.domain.career.command.application.dto.CareerRegistDTO; import stanl_2.final_backend.domain.career.command.application.service.CareerCommandService; import stanl_2.final_backend.domain.career.command.domain.aggregate.entity.Career; @@ -30,11 +29,4 @@ public void registCareer(CareerRegistDTO careerRegistDTO) { careerRepository.save(career); } - - @Override - @Transactional - public void modifyCareer(CareerModifyDTO careerModifyDTO) { - - - } } diff --git a/src/main/java/stanl_2/final_backend/domain/certification/command/domain/aggregate/entity/Certification.java b/src/main/java/stanl_2/final_backend/domain/certification/command/domain/aggregate/entity/Certification.java index 9dcdf9a7..162b8e0c 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/command/domain/aggregate/entity/Certification.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/command/domain/aggregate/entity/Certification.java @@ -46,7 +46,6 @@ public class Certification { @Column(name = "MEM_ID", nullable = false) private String memId; - /* 설명. updatedAt 자동화 */ // Insert 되기 전에 실행 @PrePersist private void prePersist() { diff --git a/src/main/java/stanl_2/final_backend/domain/family/command/domain/aggregate/entity/Family.java b/src/main/java/stanl_2/final_backend/domain/family/command/domain/aggregate/entity/Family.java index eb18ba98..93d8f85b 100644 --- a/src/main/java/stanl_2/final_backend/domain/family/command/domain/aggregate/entity/Family.java +++ b/src/main/java/stanl_2/final_backend/domain/family/command/domain/aggregate/entity/Family.java @@ -64,15 +64,12 @@ public class Family { @Column(name = "MEM_ID", nullable = false) private String memId; - /* 설명. updatedAt 자동화 */ - // Insert 되기 전에 실행 @PrePersist private void prePersist() { this.createdAt = getCurrentTime(); this.updatedAt = this.createdAt; } - // Update 되기 전에 실행 @PreUpdate private void preUpdate() { this.updatedAt = getCurrentTime(); diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java index a4e843f1..b6ca6889 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java @@ -105,15 +105,12 @@ public class Member { private List roles = new ArrayList<>(); - /* 설명. updatedAt 자동화 */ - // Insert 되기 전에 실행 @PrePersist private void prePersist() { this.createdAt = getCurrentTime(); this.updatedAt = this.createdAt; } - // Update 되기 전에 실행 @PreUpdate private void preUpdate() { this.updatedAt = getCurrentTime(); diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index 3b96638f..75a0ef52 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -385,7 +385,6 @@ CREATE TABLE tb_career CAR_NAME VARCHAR(255) NOT NULL, CAR_NOTE VARCHAR(255) NULL, CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, MEM_ID VARCHAR(255) NOT NULL, PRIMARY KEY (CAR_ID), FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) @@ -925,18 +924,18 @@ VALUES ('CER_000000009', '2022-04-25', '대한적십자사', '응급처치 강사', 'PASS', '응급처치 교육 수료', '2024-01-18 19:00:00', 'MEM_000000009'), ('CER_000000010', '2019-11-11', '한국정보통신기술협회', '정보보안기사', 'PASS', NULL, '2024-01-19 09:00:00', 'MEM_000000010'); -INSERT INTO tb_career (CAR_ID, CAR_EMP_DATE, CAR_RTR_DATE, CAR_NAME, CAR_NOTE, CREATED_AT, UPDATED_AT, MEM_ID) +INSERT INTO tb_career (CAR_ID, CAR_EMP_DATE, CAR_RTR_DATE, CAR_NAME, CAR_NOTE, CREATED_AT, MEM_ID) VALUES - ('CAR_000000001', '2012-03-01', '2016-12-31', '삼성전자', '마케팅팀 근무', '2024-01-10 10:00:00', '2024-01-10 11:00:00', 'MEM_000000001'), - ('CAR_000000002', '2015-05-01', '2019-08-31', 'LG화학', '연구개발팀 근무', '2024-01-11 12:00:00', '2024-01-11 13:00:00', 'MEM_000000002'), - ('CAR_000000003', '2010-09-01', '2015-02-28', '포스코', '공정관리팀', '2024-01-12 14:00:00', '2024-01-12 15:00:00', 'MEM_000000003'), - ('CAR_000000004', '2013-01-01', '2018-03-31', '현대자동차', '생산기술팀', '2024-01-13 09:00:00', '2024-01-13 10:00:00', 'MEM_000000004'), - ('CAR_000000005', '2017-06-01', '2021-11-30', 'SK텔레콤', '네트워크 운영팀', '2024-01-14 11:00:00', '2024-01-14 12:00:00', 'MEM_000000005'), - ('CAR_000000006', '2011-02-01', '2016-07-31', '네이버', '프론트엔드 개발자', '2024-01-15 13:00:00', '2024-01-15 14:00:00', 'MEM_000000006'), - ('CAR_000000007', '2014-04-01', '2019-12-31', '카카오', '백엔드 개발자', '2024-01-16 15:00:00', '2024-01-16 16:00:00', 'MEM_000000007'), - ('CAR_000000008', '2018-01-01', '2022-06-30', 'CJ제일제당', '품질관리팀', '2024-01-17 17:00:00', '2024-01-17 18:00:00', 'MEM_000000008'), - ('CAR_000000009', '2010-07-01', '2014-09-30', '롯데케미칼', '안전관리팀', '2024-01-18 19:00:00', '2024-01-18 20:00:00', 'MEM_000000009'), - ('CAR_000000010', '2016-03-01', '2020-10-31', '한화생명', '재무팀', '2024-01-19 09:00:00', '2024-01-19 10:00:00', 'MEM_000000010'); + ('CAR_000000001', '2012-03-01', '2016-12-31', '삼성전자', '마케팅팀 근무', '2024-01-10 10:00:00', 'MEM_000000001'), + ('CAR_000000002', '2015-05-01', '2019-08-31', 'LG화학', '연구개발팀 근무', '2024-01-11 12:00:00', 'MEM_000000002'), + ('CAR_000000003', '2010-09-01', '2015-02-28', '포스코', '공정관리팀', '2024-01-12 14:00:00', 'MEM_000000003'), + ('CAR_000000004', '2013-01-01', '2018-03-31', '현대자동차', '생산기술팀', '2024-01-13 09:00:00', 'MEM_000000004'), + ('CAR_000000005', '2017-06-01', '2021-11-30', 'SK텔레콤', '네트워크 운영팀', '2024-01-14 11:00:00', 'MEM_000000005'), + ('CAR_000000006', '2011-02-01', '2016-07-31', '네이버', '프론트엔드 개발자', '2024-01-15 13:00:00', 'MEM_000000006'), + ('CAR_000000007', '2014-04-01', '2019-12-31', '카카오', '백엔드 개발자', '2024-01-16 15:00:00', 'MEM_000000007'), + ('CAR_000000008', '2018-01-01', '2022-06-30', 'CJ제일제당', '품질관리팀', '2024-01-17 17:00:00', 'MEM_000000008'), + ('CAR_000000009', '2010-07-01', '2014-09-30', '롯데케미칼', '안전관리팀', '2024-01-18 19:00:00', 'MEM_000000009'), + ('CAR_000000010', '2016-03-01', '2020-10-31', '한화생명', '재무팀', '2024-01-19 09:00:00', 'MEM_000000010'); INSERT INTO tb_update_history (UPD_ID, UPD_IP, UPDATED_AT, UPDATED_URL, MEM_ID, CONR_ID) VALUES ('UPD_000000001', '192.168.1.10', '2024-01-10 12:00:00', '/contracts/1', 'MEM_000000001', 'CON_000000001'), From 50db76be4645aba590a821b6f64d94690bac824d Mon Sep 17 00:00:00 2001 From: giuseog Date: Mon, 18 Nov 2024 11:35:44 +0900 Subject: [PATCH 127/563] =?UTF-8?q?fix:=20dev=20merge=20=EC=A4=91=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=ED=95=B4=EA=B2=B0=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?commit(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/EvaluationController.java | 32 ++++----- .../evaluation/query/dto/EvaluationDTO.java | 2 - .../query/dto/EvaluationSearchDTO.java | 10 +++ .../query/respository/EvaluationMapper.java | 2 +- .../service/EvaluationQueryServiceImpl.java | 2 +- .../query/repository/EvaluationMapper.xml | 65 +++---------------- 6 files changed, 38 insertions(+), 75 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationSearchDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java index d4120169..f5b69b4b 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java @@ -33,23 +33,23 @@ public EvaluationController(EvaluationQueryService evaluationQueryService) { /** * [GET] http://localhost:7777/api/v1/sample/SAM_000000001 * */ - @Operation(summary = "평가서 담당자 조회") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = EvaluationResponseMessage.class))}), - @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", - content = @Content(mediaType = "application/json")) - }) - @GetMapping("") - public ResponseEntity getAllEvaluations(@PageableDefault(size = 20) Pageable pageable){ + @Operation(summary = "평가서 담당자 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = EvaluationResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("") + public ResponseEntity getAllEvaluations(@PageableDefault(size = 20) Pageable pageable){ - Page> responseEvaluations = evaluationQueryService.selectAllEvaluations(pageable); + Page> responseEvaluations = evaluationQueryService.selectAllEvaluations(pageable); - return ResponseEntity.ok(EvaluationResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(responseEvaluations) - .build()); + return ResponseEntity.ok(EvaluationResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(responseEvaluations) + .build()); } /** @@ -85,7 +85,7 @@ public ResponseEntity getEvaluationByCenter(@PathVari content = @Content(mediaType = "application/json")) }) @GetMapping("/detail/{id}") - public ResponseEntity getDetailEvaluation(@PathVariable String id) { + public ResponseEntity getEvaluationDetail(@PathVariable String id) { EvaluationDTO evaluationDTO = evaluationQueryService.selectEvaluationById(id); diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java index 2461b63d..997e2dc4 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java @@ -13,8 +13,6 @@ public class EvaluationDTO { private String content; private String createdAt; private String updatedAt; - private String deletedAt; - private Boolean active; private String centerId; private String memberId; private String writerId; diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationSearchDTO.java new file mode 100644 index 00000000..95326991 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationSearchDTO.java @@ -0,0 +1,10 @@ +package stanl_2.final_backend.domain.evaluation.query.dto; + +public class EvaluationSearchDTO { + private String evalId; + private String title; + private String writerId; + private String memberId; + private String centerId; + private String createdAt; +} diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/respository/EvaluationMapper.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/respository/EvaluationMapper.java index 4050802e..6b054d16 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/respository/EvaluationMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/respository/EvaluationMapper.java @@ -10,7 +10,7 @@ @Mapper public interface EvaluationMapper { - List> findByCenterId(String centerId, EvaluationRequestList requestList); + List> findEvaluationByCenterId(String centerId, EvaluationRequestList requestList); EvaluationDTO findEvaluationById(String id); diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java index 3ae1f1f8..a8f95ced 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java @@ -51,7 +51,7 @@ public Page> selectEvaluationByCenter(String centerId, Pagea .pageable(pageable) .build(); - List> evaluationList = evaluationMapper.findByCenterId(centerId, requestList); + List> evaluationList = evaluationMapper.findEvaluationByCenterId(centerId, requestList); int total = evaluationMapper.findEvaluationCountByCenterId(); diff --git a/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml b/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml index 7374c46f..6e2c5f80 100644 --- a/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml @@ -10,14 +10,12 @@ - - - SELECT A.EVAL_ID , A.EVAL_TTL @@ -27,30 +25,12 @@ , A.CREATED_AT , A.UPDATED_AT FROM TB_EVALUATION A - WHERE A.CENT_ID = #{id} AND A.ACTIVE = TRUE - ORDER BY CREATED_AT DESC - OFFSET #{pageable.offset} ROWS FETCH NEXT #{pageable.pageSize} ROWS ONLY; - - - - SELECT A.EVAL_ID , A.EVAL_TTL @@ -60,15 +40,16 @@ , A.CREATED_AT , A.UPDATED_AT FROM TB_EVALUATION A - WHERE A.ACTIVE = TRUE - ORDER BY A.CREATED_AT DESC + WHERE A.CENT_ID = #{id} AND A.ACTIVE = TRUE + ORDER BY CREATED_AT DESC OFFSET #{pageable.offset} ROWS FETCH NEXT #{pageable.pageSize} ROWS ONLY; - SELECT A.EVAL_ID , A.EVAL_TTL + , A.EVAL_CONT , A.CENT_ID , A.MEM_ID , A.WRI_ID @@ -76,48 +57,22 @@ , A.UPDATED_AT FROM TB_EVALUATION A WHERE A.ACTIVE = TRUE - ORDER BY CREATED_AT DESC - - AND A.CENT_ID LIKE #{centerSearchRequestDTO.id} - - - AND A.CENT_NAME LIKE #{centerSearchRequestDTO.name} - - - AND A.CENT_ADR LIKE #{centerSearchRequestDTO.address} - - ORDER BY A.CREATED_AT DESC - OFFSET #{pageable.offset} ROWS FETCH NEXT #{pageable.pageSize} ROWS ONLY; + - \ No newline at end of file From 360ac2968a09b506801e01803508b095fc7041d2 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 11:46:32 +0900 Subject: [PATCH 128/563] =?UTF-8?q?feat:=20=EC=9E=90=EA=B2=A9=EC=A6=9D/?= =?UTF-8?q?=EC=99=B8=EA=B5=AD=EC=96=B4=20=EB=93=B1=EB=A1=9D(#69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CertificationController.java | 37 ++++++++++++++++++- .../dto/CertificationRegisterDTO.java | 2 + .../service/CertificationCommandService.java | 3 ++ .../CertificationCommandServiceImpl.java | 12 ++++++ .../service/AuthCommandServiceImpl.java | 4 +- 5 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/certification/command/application/controller/CertificationController.java b/src/main/java/stanl_2/final_backend/domain/certification/command/application/controller/CertificationController.java index 4770fff4..86dcae13 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/command/application/controller/CertificationController.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/command/application/controller/CertificationController.java @@ -1,18 +1,53 @@ package stanl_2.final_backend.domain.certification.command.application.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +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.RestController; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; +import stanl_2.final_backend.domain.career.command.application.dto.CareerRegistDTO; +import stanl_2.final_backend.domain.career.common.response.CareerResponseMessage; +import stanl_2.final_backend.domain.certification.command.application.dto.CertificationRegisterDTO; import stanl_2.final_backend.domain.certification.command.application.service.CertificationCommandService; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; @RestController("commandCertificationController") @RequestMapping("/api/v1/certification") public class CertificationController { private final CertificationCommandService certificationCommandService; + private final AuthQueryService authQueryService; @Autowired - public CertificationController(CertificationCommandService certificationCommandService) { + public CertificationController(CertificationCommandService certificationCommandService, + AuthQueryService authQueryService) { this.certificationCommandService = certificationCommandService; + this.authQueryService = authQueryService; + } + + @Operation(summary = "자격증/외국어 등록") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) + }) + @PostMapping("") + public ResponseEntity postCertification(@RequestBody CertificationRegisterDTO certificationRegisterDTO){ + + certificationRegisterDTO.setMemId(authQueryService.selectMemberIdByLoginId(certificationRegisterDTO.getMemberLoginId())); + + certificationCommandService.registCertification(certificationRegisterDTO); + + return ResponseEntity.ok(CareerResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); } } diff --git a/src/main/java/stanl_2/final_backend/domain/certification/command/application/dto/CertificationRegisterDTO.java b/src/main/java/stanl_2/final_backend/domain/certification/command/application/dto/CertificationRegisterDTO.java index db4a559f..e48c9727 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/command/application/dto/CertificationRegisterDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/command/application/dto/CertificationRegisterDTO.java @@ -8,8 +8,10 @@ @Getter @ToString public class CertificationRegisterDTO { + private String memberLoginId; private String name; private String agency; private String acquisitionDate; private String note; + private String memId; } diff --git a/src/main/java/stanl_2/final_backend/domain/certification/command/application/service/CertificationCommandService.java b/src/main/java/stanl_2/final_backend/domain/certification/command/application/service/CertificationCommandService.java index 4323cfe5..980f18d3 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/command/application/service/CertificationCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/command/application/service/CertificationCommandService.java @@ -1,4 +1,7 @@ package stanl_2.final_backend.domain.certification.command.application.service; +import stanl_2.final_backend.domain.certification.command.application.dto.CertificationRegisterDTO; + public interface CertificationCommandService { + void registCertification(CertificationRegisterDTO certificationRegisterDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/certification/command/domain/service/CertificationCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/certification/command/domain/service/CertificationCommandServiceImpl.java index 5edf0632..a2cf52f1 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/command/domain/service/CertificationCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/command/domain/service/CertificationCommandServiceImpl.java @@ -3,7 +3,10 @@ import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.certification.command.application.dto.CertificationRegisterDTO; import stanl_2.final_backend.domain.certification.command.application.service.CertificationCommandService; +import stanl_2.final_backend.domain.certification.command.domain.aggregate.entity.Certification; import stanl_2.final_backend.domain.certification.command.domain.repository.CertificationRepository; @Service("commandCertificationService") @@ -18,4 +21,13 @@ public CertificationCommandServiceImpl(CertificationRepository certificationRepo this.certificationRepository = certificationRepository; this.modelMapper = modelMapper; } + + @Override + @Transactional + public void registCertification(CertificationRegisterDTO certificationRegisterDTO) { + + Certification certification = modelMapper.map(certificationRegisterDTO, Certification.class); + + certificationRepository.save(certification); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java index 8b409fe2..81d98abb 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java @@ -179,12 +179,12 @@ public RefreshDTO refreshAccessToken(String refreshToken) { @Transactional public void grantAuthority(GrantDTO grantDTO) { - String id = authQueryService.selectMemberIdByLoginId(grantDTO.getLoginId()); + String loginId = authQueryService.selectMemberIdByLoginId(grantDTO.getLoginId()); MemberRole newMemberRole = modelMapper.map(grantDTO, MemberRole.class); // fk 값 설정 - newMemberRole.setMemberId(id); + newMemberRole.setMemberId(loginId); memberRoleRepository.save(newMemberRole); } From 021d67aaf4b854bc65776e59596a4441f9d013b9 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Mon, 18 Nov 2024 12:07:11 +0900 Subject: [PATCH 129/563] =?UTF-8?q?feat:=20=EC=9D=BC=EC=A0=95=ED=91=9C=20m?= =?UTF-8?q?emberLoginId=20=EC=A0=81=EC=9A=A9=20(#66)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ScheduleController.java | 23 ++- .../application/dto/ScheduleDeleteDTO.java | 16 ++ .../application/dto/ScheduleModifyDTO.java | 1 + .../application/dto/ScheduleRegistDTO.java | 1 + .../service/ScheduleCommandService.java | 3 +- .../service/ScheduleCommandServiceImpl.java | 36 ++-- .../query/controller/ScheduleController.java | 12 +- .../schedule/query/dto/ScheduleDetailDTO.java | 1 + .../query/dto/ScheduleYearMonthDTO.java | 1 + .../query/service/ScheduleQueryService.java | 2 +- .../service/ScheduleQueryServiceImpl.java | 21 +- src/main/resources/sql/ddl.sql | 192 +++++++++--------- 12 files changed, 170 insertions(+), 139 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleDeleteDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java index 61dfcd2c..d0f9088c 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java @@ -8,6 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleDeleteDTO; import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleModifyDTO; import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleRegistDTO; import stanl_2.final_backend.domain.schedule.command.application.service.ScheduleCommandService; @@ -32,11 +33,11 @@ public ScheduleController(ScheduleCommandService scheduleCommandService) { content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity registSchedule(Principal principal - ,@RequestBody ScheduleRegistDTO scheduleRegistDTO){ + public ResponseEntity registSchedule(Principal principal, + @RequestBody ScheduleRegistDTO scheduleRegistDTO){ - String memberId = principal.getName(); - scheduleRegistDTO.setMemberId(memberId); + String memberLoginId = principal.getName(); + scheduleRegistDTO.setMemberLoginId(memberLoginId); Boolean answer = scheduleCommandService.registSchedule(scheduleRegistDTO); @@ -58,8 +59,8 @@ public ResponseEntity modifySchedule(Principal principa @PathVariable String scheduleId, @RequestBody ScheduleModifyDTO scheduleModifyDTO){ - String memberId = principal.getName(); - scheduleModifyDTO.setMemberId(memberId); + String memberLoginId = principal.getName(); + scheduleModifyDTO.setMemberLoginId(memberLoginId); scheduleModifyDTO.setScheduleId(scheduleId); Boolean answer = scheduleCommandService.modifySchedule(scheduleModifyDTO); @@ -77,9 +78,15 @@ public ResponseEntity modifySchedule(Principal principa content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) }) @DeleteMapping("{scheduleId}") - public ResponseEntity deleteSchedule(@PathVariable String scheduleId){ + public ResponseEntity deleteSchedule(Principal principal, + @PathVariable String scheduleId){ - Boolean answer = scheduleCommandService.deleteSchedule(scheduleId); + String memberLoginId = principal.getName(); + ScheduleDeleteDTO scheduleDeleteDTO = new ScheduleDeleteDTO(); + scheduleDeleteDTO.setMemberLoginId(memberLoginId); + scheduleDeleteDTO.setScheduleId(scheduleId); + + Boolean answer = scheduleCommandService.deleteSchedule(scheduleDeleteDTO); return ResponseEntity.ok(ScheduleResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleDeleteDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleDeleteDTO.java new file mode 100644 index 00000000..ed4f6a6b --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleDeleteDTO.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.schedule.command.application.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class ScheduleDeleteDTO { + + private String scheduleId; + private String memberLoginId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleModifyDTO.java index c7532bed..6d040a03 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleModifyDTO.java @@ -17,5 +17,6 @@ public class ScheduleModifyDTO { private String tag; private String startAt; private String endAt; + private String memberLoginId; private String memberId; } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleRegistDTO.java index 989aa839..064a99de 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/dto/ScheduleRegistDTO.java @@ -16,5 +16,6 @@ public class ScheduleRegistDTO { private String tag; private String startAt; private String endAt; + private String memberLoginId; private String memberId; } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/service/ScheduleCommandService.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/service/ScheduleCommandService.java index d45e319c..7ee44685 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/service/ScheduleCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/service/ScheduleCommandService.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.schedule.command.application.service; +import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleDeleteDTO; import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleModifyDTO; import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleRegistDTO; @@ -9,5 +10,5 @@ public interface ScheduleCommandService { Boolean modifySchedule(ScheduleModifyDTO scheduleModifyDTO); - Boolean deleteSchedule(String scheduleId); + Boolean deleteSchedule(ScheduleDeleteDTO scheduleDeleteDTO); } \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java index 0ca8afc7..1d3e9e94 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java @@ -9,6 +9,8 @@ import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.member.common.exception.MemberCommonException; import stanl_2.final_backend.domain.member.common.exception.MemberErrorCode; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleDeleteDTO; import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleModifyDTO; import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleRegistDTO; import stanl_2.final_backend.domain.schedule.command.application.service.ScheduleCommandService; @@ -26,12 +28,15 @@ public class ScheduleCommandServiceImpl implements ScheduleCommandService { private final ScheduleRepository scheduleRepository; + private final AuthQueryService authQueryService; private final ModelMapper modelMapper; @Autowired - public ScheduleCommandServiceImpl(ScheduleRepository scheduleRepository, ModelMapper modelMapper) { + public ScheduleCommandServiceImpl(ScheduleRepository scheduleRepository, ModelMapper modelMapper, + AuthQueryService authQueryService) { this.scheduleRepository = scheduleRepository; this.modelMapper = modelMapper; + this.authQueryService = authQueryService; } private String getCurrentTime() { @@ -44,14 +49,12 @@ private String getCurrentTime() { @Transactional public Boolean registSchedule(ScheduleRegistDTO scheduleRegistDTO) { + String memberId = authQueryService.selectMemberLoginId(scheduleRegistDTO.getMemberLoginId()); + scheduleRegistDTO.setMemberId(memberId); + try { Schedule schedule = modelMapper.map(scheduleRegistDTO, Schedule.class); - if(schedule.getMemberId() == null){ - // 매핑 오류 - throw new ScheduleCommonException(ScheduleErrorCode.MAPPING_ERROR); - } - scheduleRepository.save(schedule); return true; @@ -68,9 +71,8 @@ public Boolean registSchedule(ScheduleRegistDTO scheduleRegistDTO) { @Transactional public Boolean modifySchedule(ScheduleModifyDTO scheduleModifyDTO) { - if(scheduleModifyDTO.getMemberId() == null){ - throw new MemberCommonException(MemberErrorCode.MEMBER_NOT_FOUND); - } + String memberId = authQueryService.selectMemberLoginId(scheduleModifyDTO.getMemberLoginId()); + scheduleModifyDTO.setMemberId(memberId); Schedule schedule = scheduleRepository.findByScheduleId(scheduleModifyDTO.getScheduleId()) .orElseThrow(() -> new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND)); @@ -83,11 +85,6 @@ public Boolean modifySchedule(ScheduleModifyDTO scheduleModifyDTO) { try { Schedule updateSchedule = modelMapper.map(scheduleModifyDTO, Schedule.class); - if(updateSchedule.getMemberId() == null){ - // 매핑 오류 - throw new ScheduleCommonException(ScheduleErrorCode.MAPPING_ERROR); - } - updateSchedule.setCreatedAt(schedule.getCreatedAt()); updateSchedule.setActive(schedule.getActive()); @@ -105,11 +102,18 @@ public Boolean modifySchedule(ScheduleModifyDTO scheduleModifyDTO) { @Override @Transactional - public Boolean deleteSchedule(String scheduleId) { + public Boolean deleteSchedule(ScheduleDeleteDTO scheduleDeleteDTO) { + + String memberId = authQueryService.selectMemberLoginId(scheduleDeleteDTO.getMemberLoginId()); - Schedule schedule = scheduleRepository.findByScheduleId(scheduleId) + Schedule schedule = scheduleRepository.findByScheduleId(scheduleDeleteDTO.getScheduleId()) .orElseThrow(() -> new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND)); + if(!memberId.equals(schedule.getMemberId())){ + // 권한 오류 + throw new ScheduleCommonException(ScheduleErrorCode.AUTHORIZATION_VIOLATION); + } + schedule.setActive(false); schedule.setDeletedAt(getCurrentTime()); diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java index 809bfcb2..987ba48d 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/controller/ScheduleController.java @@ -39,8 +39,8 @@ public ScheduleController(ScheduleQueryService scheduleQueryService) { @GetMapping("") public ResponseEntity selectAllSchedule(Principal principal){ - String memberId = principal.getName(); - List schedules = scheduleQueryService.selectAllSchedule(memberId); + String memberLogindId = principal.getName(); + List schedules = scheduleQueryService.selectAllSchedule(memberLogindId); return ResponseEntity.ok(ScheduleResponseMessage.builder() .httpStatus(200) @@ -60,10 +60,10 @@ public ResponseEntity selectMonthSchedule(Principal pri @PathVariable("year") String year, @PathVariable("month") String month){ - String memberId = principal.getName(); + String memberLoginId = principal.getName(); ScheduleYearMonthDTO scheduleYearMonthDTO = new ScheduleYearMonthDTO(); - scheduleYearMonthDTO.setMemberId(memberId); + scheduleYearMonthDTO.setMemberLoginId(memberLoginId); scheduleYearMonthDTO.setYear(year); scheduleYearMonthDTO.setMonth(month); @@ -86,10 +86,10 @@ public ResponseEntity selectMonthSchedule(Principal pri public ResponseEntity selectDetailSchedule(Principal principal, @PathVariable("scheduleId") String scheduleId){ - String memberId = principal.getName();; + String memberLoginId = principal.getName();; ScheduleDetailDTO scheduleDetailDTO = new ScheduleDetailDTO(); - scheduleDetailDTO.setMemberId(memberId); + scheduleDetailDTO.setMemberLoginId(memberLoginId); scheduleDetailDTO.setScheduleId(scheduleId); ScheduleDetailDTO responseDetailSchedule = scheduleQueryService.selectDetailSchedule(scheduleDetailDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDetailDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDetailDTO.java index 4432f2ac..257bc6e2 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDetailDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDetailDTO.java @@ -16,4 +16,5 @@ public class ScheduleDetailDTO { private String startAt; private String endAt; private String memberId; + private String memberLoginId; } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleYearMonthDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleYearMonthDTO.java index d63b8dce..6b9c5ec5 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleYearMonthDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleYearMonthDTO.java @@ -15,6 +15,7 @@ public class ScheduleYearMonthDTO { private String startAt; private String endAt; private String memberId; + private String memberLoginId; private String year; private String month; diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryService.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryService.java index d579db05..597a0d70 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryService.java @@ -7,7 +7,7 @@ import java.util.List; public interface ScheduleQueryService { - List selectAllSchedule(String memberId); + List selectAllSchedule(String memberLogindId); List selectYearMonthSchedule(ScheduleYearMonthDTO scheduleYearMonthDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java index d1afd0f5..9e7b4a8d 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java @@ -7,6 +7,7 @@ import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.member.common.exception.MemberCommonException; import stanl_2.final_backend.domain.member.common.exception.MemberErrorCode; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.schedule.common.exception.ScheduleCommonException; import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; @@ -26,23 +27,23 @@ public class ScheduleQueryServiceImpl implements ScheduleQueryService { private final ScheduleMapper scheduleMapper; + private final AuthQueryService authQueryService; private String getCurrentTime() { ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } @Autowired - public ScheduleQueryServiceImpl(ScheduleMapper scheduleMapper) { + public ScheduleQueryServiceImpl(ScheduleMapper scheduleMapper, AuthQueryService authQueryService) { this.scheduleMapper = scheduleMapper; + this.authQueryService = authQueryService; } @Override @Transactional(readOnly = true) - public List selectAllSchedule(String memberId) { + public List selectAllSchedule(String memberLogindId) { - if(memberId == null || memberId.trim().isEmpty()){ - throw new MemberCommonException(MemberErrorCode.MEMBER_NOT_FOUND); - } + String memberId = authQueryService.selectMemberLoginId(memberLogindId); String currentMonth = getCurrentTime().substring(0,7); @@ -61,9 +62,8 @@ public List selectAllSchedule(String memberId) { @Transactional(readOnly = true) public List selectYearMonthSchedule(ScheduleYearMonthDTO scheduleYearMonthDTO) { - if(scheduleYearMonthDTO.getMemberId() == null || scheduleYearMonthDTO.getMemberId().trim().isEmpty()){ - throw new MemberCommonException(MemberErrorCode.MEMBER_NOT_FOUND); - } + String memberId = authQueryService.selectMemberLoginId(scheduleYearMonthDTO.getMemberLoginId()); + scheduleYearMonthDTO.setMemberId(memberId); String yearMonth = scheduleYearMonthDTO.getYear() + "-" + scheduleYearMonthDTO.getMonth(); @@ -83,9 +83,8 @@ public List selectYearMonthSchedule(ScheduleYearMonthDTO s @Transactional(readOnly = true) public ScheduleDetailDTO selectDetailSchedule(ScheduleDetailDTO scheduleDetailDTO) { - if(scheduleDetailDTO.getMemberId() == null || scheduleDetailDTO.getMemberId().trim().isEmpty()){ - throw new MemberCommonException(MemberErrorCode.MEMBER_NOT_FOUND); - } + String memberId = authQueryService.selectMemberLoginId(scheduleDetailDTO.getMemberLoginId()); + scheduleDetailDTO.setMemberId(memberId); if(scheduleDetailDTO.getScheduleId() == null || scheduleDetailDTO.getScheduleId().trim().isEmpty()){ throw new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND); diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index b16d9187..c5d41c26 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -22,15 +22,15 @@ DROP TABLE IF EXISTS tb_member; DROP TABLE IF EXISTS tb_center; DROP TABLE IF EXISTS tb_organization_chart; DROP TABLE IF EXISTS tb_sales_history; -DROP TABLE IF EXISTS BATCH_STEP_EXECUTION_SEQ; -DROP TABLE IF EXISTS BATCH_STEP_EXECUTION_CONTEXT; -DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_CONTEXT; -DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_PARAMS; -DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_SEQ; -DROP TABLE IF EXISTS BATCH_JOB_SEQ; -DROP TABLE IF EXISTS BATCH_STEP_EXECUTION; -DROP TABLE IF EXISTS BATCH_JOB_EXECUTION; -DROP TABLE IF EXISTS BATCH_JOB_INSTANCE; +# DROP TABLE IF EXISTS BATCH_STEP_EXECUTION_SEQ; +# DROP TABLE IF EXISTS BATCH_STEP_EXECUTION_CONTEXT; +# DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_CONTEXT; +# DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_PARAMS; +# DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_SEQ; +# DROP TABLE IF EXISTS BATCH_JOB_SEQ; +# DROP TABLE IF EXISTS BATCH_STEP_EXECUTION; +# DROP TABLE IF EXISTS BATCH_JOB_EXECUTION; +# DROP TABLE IF EXISTS BATCH_JOB_INSTANCE; -- 조직 관련 테이블 생성 CREATE TABLE tb_organization_chart @@ -409,93 +409,93 @@ CREATE TABLE tb_sales_history ); # spring batch & scheduler 테이블 -CREATE TABLE BATCH_JOB_INSTANCE -( - JOB_INSTANCE_ID BIGINT NOT NULL PRIMARY KEY, - VERSION BIGINT, - JOB_NAME VARCHAR(100) NOT NULL, - JOB_KEY VARCHAR(32) NOT NULL, - constraint JOB_INST_UN unique (JOB_NAME, JOB_KEY) -) ENGINE = InnoDB; - -CREATE TABLE BATCH_JOB_EXECUTION -( - JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY, - VERSION BIGINT, - JOB_INSTANCE_ID BIGINT NOT NULL, - CREATE_TIME DATETIME(6) NOT NULL, - START_TIME DATETIME(6) DEFAULT NULL, - END_TIME DATETIME(6) DEFAULT NULL, - STATUS VARCHAR(10), - EXIT_CODE VARCHAR(2500), - EXIT_MESSAGE VARCHAR(2500), - LAST_UPDATED DATETIME(6), - constraint JOB_INST_EXEC_FK foreign key (JOB_INSTANCE_ID) - references BATCH_JOB_INSTANCE (JOB_INSTANCE_ID) -) ENGINE = InnoDB; - -CREATE TABLE BATCH_JOB_EXECUTION_PARAMS -( - JOB_EXECUTION_ID BIGINT NOT NULL, - PARAMETER_NAME VARCHAR(100) NOT NULL, - PARAMETER_TYPE VARCHAR(100) NOT NULL, - PARAMETER_VALUE VARCHAR(2500), - IDENTIFYING CHAR(1) NOT NULL, - constraint JOB_EXEC_PARAMS_FK foreign key (JOB_EXECUTION_ID) - references BATCH_JOB_EXECUTION (JOB_EXECUTION_ID) -) ENGINE = InnoDB; - -CREATE TABLE BATCH_STEP_EXECUTION -( - STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY, - VERSION BIGINT NOT NULL, - STEP_NAME VARCHAR(100) NOT NULL, - JOB_EXECUTION_ID BIGINT NOT NULL, - CREATE_TIME DATETIME(6) NOT NULL, - START_TIME DATETIME(6) DEFAULT NULL, - END_TIME DATETIME(6) DEFAULT NULL, - STATUS VARCHAR(10), - COMMIT_COUNT BIGINT, - READ_COUNT BIGINT, - FILTER_COUNT BIGINT, - WRITE_COUNT BIGINT, - READ_SKIP_COUNT BIGINT, - WRITE_SKIP_COUNT BIGINT, - PROCESS_SKIP_COUNT BIGINT, - ROLLBACK_COUNT BIGINT, - EXIT_CODE VARCHAR(2500), - EXIT_MESSAGE VARCHAR(2500), - LAST_UPDATED DATETIME(6), - constraint JOB_EXEC_STEP_FK foreign key (JOB_EXECUTION_ID) - references BATCH_JOB_EXECUTION (JOB_EXECUTION_ID) -) ENGINE = InnoDB; - -CREATE TABLE BATCH_STEP_EXECUTION_CONTEXT -( - STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY, - SHORT_CONTEXT VARCHAR(2500) NOT NULL, - SERIALIZED_CONTEXT TEXT, - constraint STEP_EXEC_CTX_FK foreign key (STEP_EXECUTION_ID) - references BATCH_STEP_EXECUTION (STEP_EXECUTION_ID) -) ENGINE = InnoDB; - -CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT -( - JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY, - SHORT_CONTEXT VARCHAR(2500) NOT NULL, - SERIALIZED_CONTEXT TEXT, - constraint JOB_EXEC_CTX_FK foreign key (JOB_EXECUTION_ID) - references BATCH_JOB_EXECUTION (JOB_EXECUTION_ID) -) ENGINE = InnoDB; - -CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ START WITH 1 MINVALUE 1 MAXVALUE 9223372036854775806 - INCREMENT BY 1 NOCACHE NOCYCLE ENGINE =InnoDB; - -CREATE SEQUENCE BATCH_JOB_EXECUTION_SEQ START WITH 1 MINVALUE 1 MAXVALUE 9223372036854775806 - INCREMENT BY 1 NOCACHE NOCYCLE ENGINE =InnoDB; - -CREATE SEQUENCE BATCH_JOB_SEQ START WITH 1 MINVALUE 1 MAXVALUE 9223372036854775806 - INCREMENT BY 1 NOCACHE NOCYCLE ENGINE =InnoDB; +# CREATE TABLE BATCH_JOB_INSTANCE +# ( +# JOB_INSTANCE_ID BIGINT NOT NULL PRIMARY KEY, +# VERSION BIGINT, +# JOB_NAME VARCHAR(100) NOT NULL, +# JOB_KEY VARCHAR(32) NOT NULL, +# constraint JOB_INST_UN unique (JOB_NAME, JOB_KEY) +# ) ENGINE = InnoDB; +# +# CREATE TABLE BATCH_JOB_EXECUTION +# ( +# JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY, +# VERSION BIGINT, +# JOB_INSTANCE_ID BIGINT NOT NULL, +# CREATE_TIME DATETIME(6) NOT NULL, +# START_TIME DATETIME(6) DEFAULT NULL, +# END_TIME DATETIME(6) DEFAULT NULL, +# STATUS VARCHAR(10), +# EXIT_CODE VARCHAR(2500), +# EXIT_MESSAGE VARCHAR(2500), +# LAST_UPDATED DATETIME(6), +# constraint JOB_INST_EXEC_FK foreign key (JOB_INSTANCE_ID) +# references BATCH_JOB_INSTANCE (JOB_INSTANCE_ID) +# ) ENGINE = InnoDB; +# +# CREATE TABLE BATCH_JOB_EXECUTION_PARAMS +# ( +# JOB_EXECUTION_ID BIGINT NOT NULL, +# PARAMETER_NAME VARCHAR(100) NOT NULL, +# PARAMETER_TYPE VARCHAR(100) NOT NULL, +# PARAMETER_VALUE VARCHAR(2500), +# IDENTIFYING CHAR(1) NOT NULL, +# constraint JOB_EXEC_PARAMS_FK foreign key (JOB_EXECUTION_ID) +# references BATCH_JOB_EXECUTION (JOB_EXECUTION_ID) +# ) ENGINE = InnoDB; +# +# CREATE TABLE BATCH_STEP_EXECUTION +# ( +# STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY, +# VERSION BIGINT NOT NULL, +# STEP_NAME VARCHAR(100) NOT NULL, +# JOB_EXECUTION_ID BIGINT NOT NULL, +# CREATE_TIME DATETIME(6) NOT NULL, +# START_TIME DATETIME(6) DEFAULT NULL, +# END_TIME DATETIME(6) DEFAULT NULL, +# STATUS VARCHAR(10), +# COMMIT_COUNT BIGINT, +# READ_COUNT BIGINT, +# FILTER_COUNT BIGINT, +# WRITE_COUNT BIGINT, +# READ_SKIP_COUNT BIGINT, +# WRITE_SKIP_COUNT BIGINT, +# PROCESS_SKIP_COUNT BIGINT, +# ROLLBACK_COUNT BIGINT, +# EXIT_CODE VARCHAR(2500), +# EXIT_MESSAGE VARCHAR(2500), +# LAST_UPDATED DATETIME(6), +# constraint JOB_EXEC_STEP_FK foreign key (JOB_EXECUTION_ID) +# references BATCH_JOB_EXECUTION (JOB_EXECUTION_ID) +# ) ENGINE = InnoDB; +# +# CREATE TABLE BATCH_STEP_EXECUTION_CONTEXT +# ( +# STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY, +# SHORT_CONTEXT VARCHAR(2500) NOT NULL, +# SERIALIZED_CONTEXT TEXT, +# constraint STEP_EXEC_CTX_FK foreign key (STEP_EXECUTION_ID) +# references BATCH_STEP_EXECUTION (STEP_EXECUTION_ID) +# ) ENGINE = InnoDB; +# +# CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT +# ( +# JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY, +# SHORT_CONTEXT VARCHAR(2500) NOT NULL, +# SERIALIZED_CONTEXT TEXT, +# constraint JOB_EXEC_CTX_FK foreign key (JOB_EXECUTION_ID) +# references BATCH_JOB_EXECUTION (JOB_EXECUTION_ID) +# ) ENGINE = InnoDB; +# +# CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ START WITH 1 MINVALUE 1 MAXVALUE 9223372036854775806 +# INCREMENT BY 1 NOCACHE NOCYCLE ENGINE =InnoDB; +# +# CREATE SEQUENCE BATCH_JOB_EXECUTION_SEQ START WITH 1 MINVALUE 1 MAXVALUE 9223372036854775806 +# INCREMENT BY 1 NOCACHE NOCYCLE ENGINE =InnoDB; +# +# CREATE SEQUENCE BATCH_JOB_SEQ START WITH 1 MINVALUE 1 MAXVALUE 9223372036854775806 +# INCREMENT BY 1 NOCACHE NOCYCLE ENGINE =InnoDB; INSERT INTO tb_organization_chart (ORG_CHA_ID, ORG_CHA_NAME, ORG_CHA_DEPTH) From caf62effde833f2475c89a1e9fad59a174636b89 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 12:35:44 +0900 Subject: [PATCH 130/563] =?UTF-8?q?fix:=20DDL=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/service/CareerQueryServiceImpl.java | 3 -- .../controller/CertificationController.java | 53 +++++++++++++++++++ .../query/dto/CertificationDTO.java | 1 + .../query/repository/CertificationMapper.java | 5 ++ .../service/CertificationQueryService.java | 5 ++ .../CertificationQueryServiceImpl.java | 21 +++++++- src/main/resources/application-prod.yml | 2 +- src/main/resources/sql/ddl.sql | 42 +++++++-------- .../query/repository/CertificationMapper.xml | 18 +++++++ 9 files changed, 124 insertions(+), 26 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryServiceImpl.java index f5573868..db424ec7 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryServiceImpl.java @@ -30,9 +30,6 @@ public List selectCareerList(String id) { String loginId = authQueryService.selectMemberIdByLoginId(id); - log.info("#####"); - log.info(loginId); - List careerList = careerMapper.selectCareerInfo(loginId); return careerList; diff --git a/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java b/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java index dfa966f9..f9219f3b 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java @@ -1,10 +1,25 @@ package stanl_2.final_backend.domain.certification.query.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; +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.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; +import stanl_2.final_backend.domain.career.common.response.CareerResponseMessage; +import stanl_2.final_backend.domain.career.query.dto.CareerDTO; +import stanl_2.final_backend.domain.certification.query.dto.CertificationDTO; import stanl_2.final_backend.domain.certification.query.service.CertificationQueryService; +import java.security.Principal; +import java.util.List; + @RestController(value = "queryCertificationController") @RequestMapping("/api/v1/certification") public class CertificationController { @@ -15,4 +30,42 @@ public class CertificationController { public CertificationController(CertificationQueryService certificationQueryService) { this.certificationQueryService = certificationQueryService; } + + @Operation(summary = "자격증/외국어 조회(with 사번)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/other/{id}") + public ResponseEntity getCertificationByOther(@PathVariable String id){ + + List certificationList = certificationQueryService.selectCertificationList(id); + + return ResponseEntity.ok(CareerResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(certificationList) + .build()); + } + +// @Operation(summary = "경력 조회(접속중인 사용자)") +// @ApiResponses(value = { +// @ApiResponse(responseCode = "200", description = "성공", +// content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), +// @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", +// content = @Content(mediaType = "application/json")) +// }) +// @GetMapping("") +// public ResponseEntity getCareer(Principal principal){ +// +// List careerList = careerQueryService.selectCareerList(principal.getName()); +// +// return ResponseEntity.ok(CareerResponseMessage.builder() +// .httpStatus(200) +// .msg("성공") +// .result(careerList) +// .build()); +// } } diff --git a/src/main/java/stanl_2/final_backend/domain/certification/query/dto/CertificationDTO.java b/src/main/java/stanl_2/final_backend/domain/certification/query/dto/CertificationDTO.java index a3ec393d..349b2493 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/query/dto/CertificationDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/query/dto/CertificationDTO.java @@ -11,5 +11,6 @@ public class CertificationDTO { private String name; private String agency; private String acquisitionDate; + private String score; private String note; } diff --git a/src/main/java/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.java b/src/main/java/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.java index 062a3c26..b98a6e1a 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.java @@ -1,7 +1,12 @@ package stanl_2.final_backend.domain.certification.query.repository; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import stanl_2.final_backend.domain.certification.query.dto.CertificationDTO; + +import java.util.List; @Mapper public interface CertificationMapper { + List selectCertificationInfo(@Param("loginId") String loginId); } diff --git a/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryService.java b/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryService.java index dc7fa747..86256090 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryService.java @@ -1,4 +1,9 @@ package stanl_2.final_backend.domain.certification.query.service; +import stanl_2.final_backend.domain.certification.query.dto.CertificationDTO; + +import java.util.List; + public interface CertificationQueryService { + List selectCertificationList(String id); } diff --git a/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryServiceImpl.java index d7f49899..b93e1dc8 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryServiceImpl.java @@ -2,15 +2,34 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.certification.query.dto.CertificationDTO; import stanl_2.final_backend.domain.certification.query.repository.CertificationMapper; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; + +import java.util.List; @Service("queryCertificationService") public class CertificationQueryServiceImpl implements CertificationQueryService { private final CertificationMapper certificationMapper; + private final AuthQueryService authQueryService; @Autowired - public CertificationQueryServiceImpl(CertificationMapper certificationMapper) { + public CertificationQueryServiceImpl(CertificationMapper certificationMapper, + AuthQueryService authQueryService) { this.certificationMapper = certificationMapper; + this.authQueryService = authQueryService; + } + + @Override + @Transactional + public List selectCertificationList(String id) { + + String loginId = authQueryService.selectMemberIdByLoginId(id); + + List certificationList = certificationMapper.selectCertificationInfo(loginId); + + return certificationList; } } diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 23faa6a8..60bf5a48 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: create + ddl-auto: update logging: level: diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index 75a0ef52..032d74e1 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -2,8 +2,9 @@ SET FOREIGN_KEY_CHECKS = 0; -- 자식 테이블부터 삭제 -DROP TABLE IF EXISTS tb_product_option; +DROP TABLE IF EXISTS tb_sales_history; DROP TABLE IF EXISTS tb_update_history; +DROP TABLE IF EXISTS tb_product_option; DROP TABLE IF EXISTS tb_alarm; DROP TABLE IF EXISTS tb_schedule; DROP TABLE IF EXISTS tb_file; @@ -14,26 +15,25 @@ DROP TABLE IF EXISTS tb_problem; DROP TABLE IF EXISTS tb_order; DROP TABLE IF EXISTS tb_notice; DROP TABLE IF EXISTS tb_contract; -DROP TABLE IF EXISTS tb_product; DROP TABLE IF EXISTS tb_customer_info; DROP TABLE IF EXISTS tb_member_role; DROP TABLE IF EXISTS tb_member; DROP TABLE IF EXISTS tb_center; +DROP TABLE IF EXISTS tb_family; +DROP TABLE IF EXISTS tb_education; +DROP TABLE IF EXISTS tb_certification; +DROP TABLE IF EXISTS tb_career; +DROP TABLE IF EXISTS tb_product; DROP TABLE IF EXISTS tb_organization_chart; -DROP TABLE IF EXISTS tb_sales_history; -DROP TABLE IF EXISTS BATCH_STEP_EXECUTION_SEQ; DROP TABLE IF EXISTS BATCH_STEP_EXECUTION_CONTEXT; +DROP TABLE IF EXISTS BATCH_STEP_EXECUTION; DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_CONTEXT; DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_PARAMS; -DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_SEQ; -DROP TABLE IF EXISTS BATCH_JOB_SEQ; -DROP TABLE IF EXISTS BATCH_STEP_EXECUTION; DROP TABLE IF EXISTS BATCH_JOB_EXECUTION; DROP TABLE IF EXISTS BATCH_JOB_INSTANCE; -DROP TABLE IF EXISTS tb_family; -DROP TABLE IF EXISTS tb_education; -DROP TABLE IF EXISTS tb_certification; -DROP TABLE IF EXISTS tb_career; +DROP TABLE IF EXISTS BATCH_STEP_EXECUTION_SEQ; +DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_SEQ; +DROP TABLE IF EXISTS BATCH_JOB_SEQ; -- 조직 관련 테이블 생성 @@ -602,16 +602,16 @@ VALUES ('MEM_000000001', 101, 'pwd1234', '김철수', 'chulsoo@example.com', 35, '2024-01-10 12:00:00', TRUE); INSERT INTO tb_member_role (MEM_ROL_ID, MEM_ROL_NAME, MEM_ID) -VALUES ('ROL_000000001', '영업 사원', 'MEM_000000001'), - ('ROL_000000002', '영업 사원', 'MEM_000000002'), - ('ROL_000000003', '영업 사원', 'MEM_000000003'), - ('ROL_000000004', '영업 사원', 'MEM_000000004'), - ('ROL_000000005', '영업 관리자', 'MEM_000000005'), - ('ROL_000000006', '영업 관리자', 'MEM_000000009'), - ('ROL_000000007', '영업 관리자', 'MEM_000000007'), - ('ROL_000000008', '영업 담당자', 'MEM_000000008'), - ('ROL_000000009', '영업 담당자', 'MEM_000000010'), - ('ROL_000000010', '영업 담당자', 'MEM_000000006'); +VALUES ('MEM_ROL_000000001', '영업 사원', 'MEM_000000001'), + ('MEM_ROL_000000002', '영업 사원', 'MEM_000000002'), + ('MEM_ROL_000000003', '영업 사원', 'MEM_000000003'), + ('MEM_ROL_000000004', '영업 사원', 'MEM_000000004'), + ('MEM_ROL_000000005', '영업 관리자', 'MEM_000000005'), + ('MEM_ROL_000000006', '영업 관리자', 'MEM_000000009'), + ('MEM_ROL_000000007', '영업 관리자', 'MEM_000000007'), + ('MEM_ROL_000000008', '영업 담당자', 'MEM_000000008'), + ('MEM_ROL_000000009', '영업 담당자', 'MEM_000000010'), + ('MEM_ROL_000000010', '영업 담당자', 'MEM_000000006'); INSERT INTO tb_customer_info (CUST_ID, CUST_NAME, CUST_AGE, CUST_SEX, CUST_PHO, CUST_EMER_PHO, CUST_EMA, ACTIVE, MEM_ID) VALUES ('CUS_000000001', '홍길동', 45, 'MALE', '010-1111-2222', '010-2222-3333', 'gildong@example.com', TRUE, diff --git a/src/main/resources/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.xml b/src/main/resources/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.xml index 3085aaf2..14ab71e0 100644 --- a/src/main/resources/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.xml @@ -3,5 +3,23 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> + + + + + + + + \ No newline at end of file From 933dc8313bbd11f93040c738079d86d7db93ccd5 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Mon, 18 Nov 2024 12:40:48 +0900 Subject: [PATCH 131/563] =?UTF-8?q?feat:=20=EC=9D=BC=EC=A0=95=ED=91=9C=20m?= =?UTF-8?q?emberLoginId=20=EC=A0=81=EC=9A=A9=20&=20=EC=98=88=EC=99=B8?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=A0=81=EC=9A=A9=20=EC=99=84=EB=A3=8C=20?= =?UTF-8?q?(#66)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-prod.yml | 2 +- src/main/resources/sql/ddl.sql | 74 ++++++++++++------------- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 23faa6a8..60bf5a48 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: create + ddl-auto: update logging: level: diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index c4758b76..b16d9187 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -31,11 +31,6 @@ DROP TABLE IF EXISTS BATCH_JOB_SEQ; DROP TABLE IF EXISTS BATCH_STEP_EXECUTION; DROP TABLE IF EXISTS BATCH_JOB_EXECUTION; DROP TABLE IF EXISTS BATCH_JOB_INSTANCE; -DROP TABLE IF EXISTS sample; -DROP TABLE IF EXISTS tb_career; -DROP TABLE IF EXISTS tb_certification; -DROP TABLE IF EXISTS tb_education; -DROP TABLE IF EXISTS tb_family; -- 조직 관련 테이블 생성 CREATE TABLE tb_organization_chart @@ -128,7 +123,7 @@ CREATE TABLE tb_product UPDATED_AT CHAR(19) NOT NULL DEFAULT 'CURRENT_TIMESTAMP', DELETED_AT CHAR(19) NULL, ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - PRIMARY KEY (PROD_ID) + PRIMARY KEY (PROD_ID, PROD_SER_NO) ); CREATE TABLE tb_contract @@ -402,8 +397,9 @@ CREATE TABLE tb_PRODUCT_OPTION OPT_OTDR_PCKG CHAR(1) NOT NULL DEFAULT '0', OPT_SUN_ROOF CHAR(1) NOT NULL DEFAULT '0', OPT_SOND CHAR(1) NOT NULL DEFAULT '0', - PRIMARY KEY (PROD_ID), - FOREIGN KEY (PROD_ID) REFERENCES tb_product (PROD_ID) ON DELETE CASCADE + ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, + PRIMARY KEY (PROD_ID, PROD_SER_NO), + FOREIGN KEY (PROD_ID, PROD_SER_NO) REFERENCES tb_product (PROD_ID, PROD_SER_NO) ON DELETE CASCADE ); CREATE TABLE tb_sales_history @@ -605,17 +601,17 @@ VALUES ('CUS_000000001', '홍길동', 45, 'MALE', '010-1111-2222', '010-2222-333 'MEM_000000010'); INSERT INTO tb_product (PROD_ID, PROD_SER_NO, PROD_COST, PROD_NAME, PROD_STCK, CREATED_AT, UPDATED_AT, ACTIVE) -VALUES ('PROD_000000001', 'KNAHAA4AALU1A00001', 25000000, '쏘렌토', 10, '2024-01-10 10:00:00', '2024-01-10 11:00:00', TRUE), - ('PROD_000000002', 'KNAHBA4BALR2Z00002', 22000000, '스포티지', 15, '2024-01-11 11:00:00', '2024-01-11 12:00:00', +VALUES ('PRO_000000001', 'KNAHAA4AALU1A00001', 25000000, '쏘렌토', 10, '2024-01-10 10:00:00', '2024-01-10 11:00:00', TRUE), + ('PRO_000000002', 'KNAHBA4BALR2Z00002', 22000000, '스포티지', 15, '2024-01-11 11:00:00', '2024-01-11 12:00:00', TRUE), - ('PROD_000000003', 'KMBHC64CAMJ5A00003', 28000000, 'K7', 8, '2024-01-12 12:00:00', '2024-01-12 13:00:00', TRUE), - ('PROD_000000004', 'KNJFA42DALU3C00004', 19000000, '셀토스', 12, '2024-01-13 13:00:00', '2024-01-13 14:00:00', TRUE), - ('PROD_000000005', 'KFBGBM5EARP1M00005', 18000000, 'K3', 20, '2024-01-14 14:00:00', '2024-01-14 15:00:00', TRUE), - ('PROD_000000006', 'KNHGA6BALUP7A00006', 34000000, '모하비', 7, '2024-01-15 15:00:00', '2024-01-15 16:00:00', TRUE), - ('PROD_000000007', 'KNJFA34AALU4Z00007', 32000000, 'K8', 5, '2024-01-16 16:00:00', '2024-01-16 17:00:00', TRUE), - ('PROD_000000008', 'KMAHDA2AAMJ3T00008', 27000000, '스팅어', 9, '2024-01-17 17:00:00', '2024-01-17 18:00:00', TRUE), - ('PROD_000000009', 'KNAHCA5BALU5C00009', 23000000, '니로', 14, '2024-01-18 18:00:00', '2024-01-18 19:00:00', TRUE), - ('PROD_000000010', 'KNFHC54CAMR1A00010', 28000000, 'K5', 6, '2024-01-19 19:00:00', '2024-01-19 20:00:00', TRUE); + ('PRO_000000003', 'KMBHC64CAMJ5A00003', 28000000, 'K7', 8, '2024-01-12 12:00:00', '2024-01-12 13:00:00', TRUE), + ('PRO_000000004', 'KNJFA42DALU3C00004', 19000000, '셀토스', 12, '2024-01-13 13:00:00', '2024-01-13 14:00:00', TRUE), + ('PRO_000000005', 'KFBGBM5EARP1M00005', 18000000, 'K3', 20, '2024-01-14 14:00:00', '2024-01-14 15:00:00', TRUE), + ('PRO_000000006', 'KNHGA6BALUP7A00006', 34000000, '모하비', 7, '2024-01-15 15:00:00', '2024-01-15 16:00:00', TRUE), + ('PRO_000000007', 'KNJFA34AALU4Z00007', 32000000, 'K8', 5, '2024-01-16 16:00:00', '2024-01-16 17:00:00', TRUE), + ('PRO_000000008', 'KMAHDA2AAMJ3T00008', 27000000, '스팅어', 9, '2024-01-17 17:00:00', '2024-01-17 18:00:00', TRUE), + ('PRO_000000009', 'KNAHCA5BALU5C00009', 23000000, '니로', 14, '2024-01-18 18:00:00', '2024-01-18 19:00:00', TRUE), + ('PRO_000000010', 'KNFHC54CAMR1A00010', 28000000, 'K5', 6, '2024-01-19 19:00:00', '2024-01-19 20:00:00', TRUE); INSERT INTO tb_contract (CONR_ID, CONR_NAME, CONR_CUST_NAME, CONR_CUST_IDEN_NO, CONR_CUST_ADR, @@ -951,38 +947,38 @@ INSERT INTO tb_product_option (PROD_ID, PROD_SER_NO, OPT_CNTY, OPT_MNFR, OPT_VHC OPT_SFTY_DVCE, OPT_ENGN_CPCT, OPT_SCRT_CODE, OPT_PRDC_YEAR, OPT_PRDC_PLNT, OPT_ENGN, OPT_MSSN, OPT_TRIM, OPT_XTNL_COLR, OPT_ITNL_COLR, OPT_HUD, OPT_NAVI, OPT_DRVE_WISE, OPT_SMRT_CNCT, - OPT_STYL, OPT_MY_CFRT_PCKG, OPT_OTDR_PCKG, OPT_SUN_ROOF, OPT_SOND) + OPT_STYL, OPT_MY_CFRT_PCKG, OPT_OTDR_PCKG, OPT_SUN_ROOF, OPT_SOND, ACTIVE) VALUES -- 데이터 1 -('PROD_000000001', 'KNAHAA4AALU1A00001', 'K', 'N', 'A', 'A', 'L', '4', '2', 'A', 'P', 'A', 'U', '1', '0', '1', 'B', 'W', - '1', '1', '1', '1', '1', '0', '1', '1', '1'), +('PRO_000000001', 'KNAHAA4AALU1A00001', 'K', 'N', 'A', 'A', 'L', '4', '2', 'A', 'P', 'A', 'U', '1', '0', '1', 'B', 'W', + '1', '1', '1', '1', '1', '0', '1', '1', '1', TRUE), -- 데이터 2 -('PROD_000000002', 'KNAHBA4BALR2Z00002', 'K', 'N', 'H', 'B', 'L', '4', '4', 'B', 'R', 'B', 'Z', '1', '1', '0', 'G', 'B', - '0', '1', '0', '1', '0', '1', '0', '0', '1'), +('PRO_000000002', 'KNAHBA4BALR2Z00002', 'K', 'N', 'H', 'B', 'L', '4', '4', 'B', 'R', 'B', 'Z', '1', '1', '0', 'G', 'B', + '0', '1', '0', '1', '0', '1', '0', '0', '1', TRUE), -- 데이터 3 -('PROD_000000003', 'KMBHC64CAMJ5A00003', 'K', 'M', 'H', 'C', 'M', '6', '3', 'C', 'P', 'C', 'A', '0', '1', '1', 'R', 'G', - '1', '1', '1', '0', '1', '1', '0', '1', '0'), +('PRO_000000003', 'KMBHC64CAMJ5A00003', 'K', 'M', 'H', 'C', 'M', '6', '3', 'C', 'P', 'C', 'A', '0', '1', '1', 'R', 'G', + '1', '1', '1', '0', '1', '1', '0', '1', '0', TRUE), -- 데이터 4 -('PROD_000000004', 'KNJFA42DALU3C00004', 'K', 'N', 'J', 'D', 'L', '2', '4', 'A', 'R', 'D', 'C', '1', '0', '0', 'B', 'R', - '0', '0', '0', '1', '0', '0', '1', '0', '1'), +('PRO_000000004', 'KNJFA42DALU3C00004', 'K', 'N', 'J', 'D', 'L', '2', '4', 'A', 'R', 'D', 'C', '1', '0', '0', 'B', 'R', + '0', '0', '0', '1', '0', '0', '1', '0', '1', TRUE), -- 데이터 5 -('PROD_000000005', 'KFBGBM5EARP1M00005', 'K', 'B', 'F', 'B', 'M', '5', '1', 'B', 'P', 'E', 'M', '1', '1', '1', 'W', 'B', - '1', '1', '0', '0', '1', '1', '0', '1', '1'), +('PRO_000000005', 'KFBGBM5EARP1M00005', 'K', 'B', 'F', 'B', 'M', '5', '1', 'B', 'P', 'E', 'M', '1', '1', '1', 'W', 'B', + '1', '1', '0', '0', '1', '1', '0', '1', '1', TRUE), -- 데이터 6 -('PROD_000000006', 'KNHGA6BALUP7A00006', 'K', 'N', 'H', 'G', 'L', '6', '3', 'A', 'R', 'F', 'A', '0', '0', '1', 'B', 'G', - '1', '1', '1', '1', '0', '1', '1', '0', '0'), +('PRO_000000006', 'KNHGA6BALUP7A00006', 'K', 'N', 'H', 'G', 'L', '6', '3', 'A', 'R', 'F', 'A', '0', '0', '1', 'B', 'G', + '1', '1', '1', '1', '0', '1', '1', '0', '0', TRUE), -- 데이터 7 -('PROD_000000007', 'KNJFA34AALU4Z00007', 'K', 'N', 'J', 'A', 'M', '4', '4', 'C', 'P', 'G', 'Z', '1', '1', '0', 'R', 'W', - '0', '0', '1', '0', '1', '0', '1', '1', '1'), +('PRO_000000007', 'KNJFA34AALU4Z00007', 'K', 'N', 'J', 'A', 'M', '4', '4', 'C', 'P', 'G', 'Z', '1', '1', '0', 'R', 'W', + '0', '0', '1', '0', '1', '0', '1', '1', '1', TRUE), -- 데이터 8 -('PROD_000000008', 'KMAHDA2AAMJ3T00008', 'K', 'M', 'H', 'D', 'L', '2', '2', 'A', 'R', 'H', 'T', '0', '0', '1', 'B', 'G', - '1', '1', '0', '1', '0', '1', '1', '0', '0'), +('PRO_000000008', 'KMAHDA2AAMJ3T00008', 'K', 'M', 'H', 'D', 'L', '2', '2', 'A', 'R', 'H', 'T', '0', '0', '1', 'B', 'G', + '1', '1', '0', '1', '0', '1', '1', '0', '0', TRUE), -- 데이터 9 -('PROD_000000009', 'KNAHCA5BALU5C00009', 'K', 'N', 'A', 'C', 'M', '5', '1', 'B', 'P', 'J', 'C', '1', '1', '0', 'R', 'R', - '0', '1', '1', '1', '1', '0', '1', '1', '0'), +('PRO_000000009', 'KNAHCA5BALU5C00009', 'K', 'N', 'A', 'C', 'M', '5', '1', 'B', 'P', 'J', 'C', '1', '1', '0', 'R', 'R', + '0', '1', '1', '1', '1', '0', '1', '1', '0', TRUE), -- 데이터 10 -('PROD_000000010', 'KNFHC54CAMR1A00010', 'K', 'N', 'F', 'F', 'N', '4', '3', 'C', 'R', 'K', 'A', '0', '1', '1', 'W', 'B', - '1', '1', '0', '0', '0', '1', '0', '1', '1'); +('PRO_000000010', 'KNFHC54CAMR1A00010', 'K', 'N', 'F', 'F', 'N', '4', '3', 'C', 'R', 'K', 'A', '0', '1', '1', 'W', 'B', + '1', '1', '0', '0', '0', '1', '0', '1', '1', TRUE); INSERT INTO tb_sales_history (SAL_HIST_ID, CONR_ID) VALUES ('SAL_000000001', 'CON_000000001'), From d880c71a2b39005724555d0dcb34cd05b9292472 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Mon, 18 Nov 2024 14:06:26 +0900 Subject: [PATCH 132/563] =?UTF-8?q?feat:=20=EB=B0=9C=EC=A3=BC=EC=84=9C=20?= =?UTF-8?q?=EC=8A=B9=EC=9D=B8=20=EC=83=81=ED=83=9C=20=EB=B3=80=EA=B2=BD(#5?= =?UTF-8?q?9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PurchaseOrderController.java | 46 ++++++++- .../dto/PurchaseOrderStatusModifyDTO.java | 15 +++ .../service/PurchaseOrderCommandService.java | 8 +- .../aggregate/entity/PurchaseOrder.java | 2 +- .../repository/PurchaseOrderRepository.java | 2 +- .../PurchaseOrderCommandServiceImpl.java | 50 ++++++++-- src/main/resources/sql/ddl.sql | 94 +++++++++---------- 7 files changed, 154 insertions(+), 63 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderStatusModifyDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java index aa268032..25ab672d 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java @@ -5,14 +5,20 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderModifyDTO; import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderRegistDTO; +import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderStatusModifyDTO; import stanl_2.final_backend.domain.purchase_order.command.application.service.PurchaseOrderCommandService; import stanl_2.final_backend.domain.purchase_order.common.response.PurchaseOrderResponseMessage; +import java.security.Principal; + +@Slf4j @RestController("PurchaseOrderCommandController") @RequestMapping("/api/v1/purchase-order") public class PurchaseOrderController { @@ -30,7 +36,10 @@ public PurchaseOrderController(PurchaseOrderCommandService purchaseOrderCommandS content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postPurchaseOrder(@RequestBody PurchaseOrderRegistDTO purchaseOrderRegistDTO) { + public ResponseEntity postPurchaseOrder(@RequestBody PurchaseOrderRegistDTO purchaseOrderRegistDTO, + Principal principal) { + + purchaseOrderRegistDTO.setMemberId(principal.getName()); purchaseOrderCommandService.registerPurchaseOrder(purchaseOrderRegistDTO); @@ -47,8 +56,10 @@ public ResponseEntity postPurchaseOrder(@RequestBo }) @PutMapping("{id}") public ResponseEntity putPurchaseOrder(@PathVariable String id, - @RequestBody PurchaseOrderModifyDTO purchaseOrderModifyDTO) { + @RequestBody PurchaseOrderModifyDTO purchaseOrderModifyDTO, + Principal principal) { purchaseOrderModifyDTO.setPurchaseOrderId(id); + purchaseOrderModifyDTO.setMemberId(principal.getName()); PurchaseOrderModifyDTO purchaseOrderModifyResponse = purchaseOrderCommandService.modifyPurchaseOrder(purchaseOrderModifyDTO); return ResponseEntity.ok(PurchaseOrderResponseMessage.builder() @@ -64,9 +75,10 @@ public ResponseEntity putPurchaseOrder(@PathVariab content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) }) @DeleteMapping("{id}") - public ResponseEntity deletePurchaseOrder(@PathVariable String id) { + public ResponseEntity deletePurchaseOrder(@PathVariable String id, + Principal principal) { - purchaseOrderCommandService.deletePurchaseOrder(id); + purchaseOrderCommandService.deletePurchaseOrder(id, principal.getName()); return ResponseEntity.ok(PurchaseOrderResponseMessage.builder() .httpStatus(200) @@ -74,4 +86,30 @@ public ResponseEntity deletePurchaseOrder(@PathVar .result(null) .build()); } + + @Operation(summary = "발주서 승인 상태 수정(담당자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "발주서 승인 상태 수정 성공", + content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) + }) + @PutMapping("/stauts/{id}") + public ResponseEntity putPurchaseOrderStatus(@PathVariable String id, + PurchaseOrderStatusModifyDTO purchaseOrderStatusModifyDTO, + Authentication authentication) { + String role = authentication.getAuthorities().toString(); + String adminId = authentication.getName(); + + purchaseOrderStatusModifyDTO.setPurchaseOrderId(id); + purchaseOrderStatusModifyDTO.setRole(role); + purchaseOrderStatusModifyDTO.setAdminId(adminId); + + purchaseOrderCommandService.modifyPurchaseOrderStatus(purchaseOrderStatusModifyDTO); + + return ResponseEntity.ok(PurchaseOrderResponseMessage.builder() + .httpStatus(200) + .msg("발주서 승인 상태가 성공적으로 변경되었습니다.") + .result(null) + .build()); + } + } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderStatusModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderStatusModifyDTO.java new file mode 100644 index 00000000..9d34c7d0 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderStatusModifyDTO.java @@ -0,0 +1,15 @@ +package stanl_2.final_backend.domain.purchase_order.command.application.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class PurchaseOrderStatusModifyDTO { + private String purchaseOrderId; + private String status; + private String adminId; + private String role; +} diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/service/PurchaseOrderCommandService.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/service/PurchaseOrderCommandService.java index 546e4a47..fbca5bb0 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/service/PurchaseOrderCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/service/PurchaseOrderCommandService.java @@ -1,12 +1,18 @@ package stanl_2.final_backend.domain.purchase_order.command.application.service; +import org.springframework.security.core.GrantedAuthority; import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderModifyDTO; import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderRegistDTO; +import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderStatusModifyDTO; + +import java.util.Collection; public interface PurchaseOrderCommandService { void registerPurchaseOrder(PurchaseOrderRegistDTO purchaseOrderRegistDTO); PurchaseOrderModifyDTO modifyPurchaseOrder(PurchaseOrderModifyDTO purchaseOrderModifyDTO); - void deletePurchaseOrder(String id); + void deletePurchaseOrder(String id, String loginId); + + void modifyPurchaseOrderStatus(PurchaseOrderStatusModifyDTO purchaseOrderStatusModifyDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/aggregate/entity/PurchaseOrder.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/aggregate/entity/PurchaseOrder.java index 64ae1d05..8009bb94 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/aggregate/entity/PurchaseOrder.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/aggregate/entity/PurchaseOrder.java @@ -24,7 +24,7 @@ public class PurchaseOrder { @GeneratedValue(generator = "PrefixGeneratorConfig") @GenericGenerator(name = "PrefixGeneratorConfig", type = PrefixGeneratorConfig.class, - parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "PUR") + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "PUR_ORD") ) @Column(name = "PUR_ORD_ID") private String purchaseOrderId; diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/repository/PurchaseOrderRepository.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/repository/PurchaseOrderRepository.java index a6f40244..b12b846c 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/repository/PurchaseOrderRepository.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/repository/PurchaseOrderRepository.java @@ -8,5 +8,5 @@ public interface PurchaseOrderRepository extends JpaRepository { Optional findByPurchaseOrderIdAndMemberId(String purchaseOrderId, String memberId); - Optional findByPurchaseOrderId(String id); + Optional findByPurchaseOrderId(String id); } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java index 18b351f4..7f0c5ff4 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java @@ -2,35 +2,45 @@ import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.GrantedAuthority; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.member.query.service.MemberQueryServiceImpl; import stanl_2.final_backend.domain.order.command.domain.aggregate.entity.Order; import stanl_2.final_backend.domain.order.command.domain.repository.OrderRepository; import stanl_2.final_backend.domain.order.common.exception.OrderCommonException; import stanl_2.final_backend.domain.order.common.exception.OrderErrorCode; import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderModifyDTO; import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderRegistDTO; +import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderStatusModifyDTO; import stanl_2.final_backend.domain.purchase_order.command.application.service.PurchaseOrderCommandService; import stanl_2.final_backend.domain.purchase_order.command.domain.aggregate.entity.PurchaseOrder; import stanl_2.final_backend.domain.purchase_order.command.domain.repository.PurchaseOrderRepository; import stanl_2.final_backend.domain.purchase_order.common.exception.PurchaseOrderCommonException; import stanl_2.final_backend.domain.purchase_order.common.exception.PurchaseOrderErrorCode; +import stanl_2.final_backend.global.exception.GlobalCommonException; +import stanl_2.final_backend.global.exception.GlobalErrorCode; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.util.Collection; +import java.util.Optional; @Service public class PurchaseOrderCommandServiceImpl implements PurchaseOrderCommandService { private final PurchaseOrderRepository purchaseOrderRepository; private final OrderRepository orderRepository; + private final AuthQueryService authQueryService; private final ModelMapper modelMapper; @Autowired - public PurchaseOrderCommandServiceImpl(PurchaseOrderRepository purchaseOrderRepository, OrderRepository orderRepository, ModelMapper modelMapper) { + public PurchaseOrderCommandServiceImpl(PurchaseOrderRepository purchaseOrderRepository, OrderRepository orderRepository, AuthQueryService authQueryService, ModelMapper modelMapper) { this.purchaseOrderRepository = purchaseOrderRepository; this.orderRepository = orderRepository; + this.authQueryService = authQueryService; this.modelMapper = modelMapper; } @@ -43,9 +53,10 @@ private String getCurrentTime() { @Transactional public void registerPurchaseOrder(PurchaseOrderRegistDTO purchaseOrderRegistDTO) { + String memberId = authQueryService.selectMemberLoginId(purchaseOrderRegistDTO.getMemberId()); + // 수주서가 존재하는지 확인 - Order order = orderRepository.findByOrderIdAndMemberId( - purchaseOrderRegistDTO.getOrderId(), purchaseOrderRegistDTO.getMemberId()); + Order order = orderRepository.findByOrderIdAndMemberId(purchaseOrderRegistDTO.getOrderId(), memberId); if (order == null) { throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); } @@ -65,9 +76,11 @@ public void registerPurchaseOrder(PurchaseOrderRegistDTO purchaseOrderRegistDTO) @Transactional public PurchaseOrderModifyDTO modifyPurchaseOrder(PurchaseOrderModifyDTO purchaseOrderModifyDTO) { + String memberId = authQueryService.selectMemberLoginId(purchaseOrderModifyDTO.getMemberId()); + // 회원인지 확인 및 발주서 조회 PurchaseOrder purchaseOrder = (PurchaseOrder) purchaseOrderRepository.findByPurchaseOrderIdAndMemberId( - purchaseOrderModifyDTO.getPurchaseOrderId(), purchaseOrderModifyDTO.getMemberId()) + purchaseOrderModifyDTO.getPurchaseOrderId(), memberId) .orElseThrow(() -> new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND)); // 수주서가 존재하는지 확인 @@ -98,12 +111,12 @@ public PurchaseOrderModifyDTO modifyPurchaseOrder(PurchaseOrderModifyDTO purchas @Override @Transactional - public void deletePurchaseOrder(String id) { - // 발주서가 해당 회원의 것인지 확인 (회원도 받아와서 하는걸로 나중에 수정) -// PurchaseOrder purchaseOrder = purchaseOrderRepository.findByIdAndMemberId(id, memberId) -// .orElseThrow(() -> new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND)); + public void deletePurchaseOrder(String id, String loginId) { + + String memberId = authQueryService.selectMemberLoginId(loginId); - PurchaseOrder purchaseOrder = (PurchaseOrder) purchaseOrderRepository.findByPurchaseOrderId(id) + // 발주서가 해당 회원의 것인지 확인 + PurchaseOrder purchaseOrder = (PurchaseOrder) purchaseOrderRepository.findByPurchaseOrderIdAndMemberId(id, memberId) .orElseThrow(() -> new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND)); purchaseOrder.setActive(false); @@ -111,4 +124,23 @@ public void deletePurchaseOrder(String id) { purchaseOrderRepository.save(purchaseOrder); } + + @Override + @Transactional + public void modifyPurchaseOrderStatus(PurchaseOrderStatusModifyDTO purchaseOrderStatusModifyDTO) { + + String adminId = authQueryService.selectMemberLoginId(purchaseOrderStatusModifyDTO.getAdminId()); + + if ("[ROLE_REPRESENTATIVE]".equals(purchaseOrderStatusModifyDTO.getRole())) { + PurchaseOrder purchaseOrder = purchaseOrderRepository.findByPurchaseOrderId(purchaseOrderStatusModifyDTO.getPurchaseOrderId()) + .orElseThrow(() -> new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND)); + + purchaseOrder.setStatus(purchaseOrderStatusModifyDTO.getStatus()); + purchaseOrder.setAdminId(adminId); + + purchaseOrderRepository.save(purchaseOrder); + } else { + throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + } + } } diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index ee47bd4e..75ffb484 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -235,9 +235,11 @@ CREATE TABLE tb_purchase_order PUR_ORD_STAT VARCHAR(255) NOT NULL DEFAULT 'WAIT', ORD_ID VARCHAR(255) NOT NULL, MEM_ID VARCHAR(255) NOT NULL, + ADMIN_ID VARCHAR(255) NULL, PRIMARY KEY (PUR_ORD_ID), FOREIGN KEY (ORD_ID) REFERENCES tb_order (ORD_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) + FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), + FOREIGN KEY (ADMIN_ID) REFERENCES tb_member (MEM_ID) ); CREATE TABLE tb_EVALUATION @@ -571,16 +573,16 @@ VALUES ('MEM_000000001', 101, 'pwd1234', '김철수', 'chulsoo@example.com', 35, '2024-01-10 12:00:00', TRUE); INSERT INTO tb_member_role (MEM_ROL_ID, MEM_ROL_NAME, MEM_ID) -VALUES ('ROL_000000001', '영업 사원', 'MEM_000000001'), - ('ROL_000000002', '영업 사원', 'MEM_000000002'), - ('ROL_000000003', '영업 사원', 'MEM_000000003'), - ('ROL_000000004', '영업 사원', 'MEM_000000004'), - ('ROL_000000005', '영업 관리자', 'MEM_000000005'), - ('ROL_000000006', '영업 관리자', 'MEM_000000009'), - ('ROL_000000007', '영업 관리자', 'MEM_000000007'), - ('ROL_000000008', '영업 담당자', 'MEM_000000008'), - ('ROL_000000009', '영업 담당자', 'MEM_000000010'), - ('ROL_000000010', '영업 담당자', 'MEM_000000006'); +VALUES ('MEM_ROL_000000001', '영업 사원', 'MEM_000000001'), + ('MEM_ROL_000000002', '영업 사원', 'MEM_000000002'), + ('MEM_ROL_000000003', '영업 사원', 'MEM_000000003'), + ('MEM_ROL_000000004', '영업 사원', 'MEM_000000004'), + ('MEM_ROL_000000005', '영업 관리자', 'MEM_000000005'), + ('MEM_ROL_000000006', '영업 관리자', 'MEM_000000009'), + ('MEM_ROL_000000007', '영업 관리자', 'MEM_000000007'), + ('MEM_ROL_000000008', '영업 담당자', 'MEM_000000008'), + ('MEM_ROL_000000009', '영업 담당자', 'MEM_000000010'), + ('MEM_ROL_000000010', '영업 담당자', 'MEM_000000006'); INSERT INTO tb_customer_info (CUST_ID, CUST_NAME, CUST_AGE, CUST_SEX, CUST_PHO, CUST_EMER_PHO, CUST_EMA, ACTIVE, MEM_ID) VALUES ('CUS_000000001', '홍길동', 45, 'MALE', '010-1111-2222', '010-2222-3333', 'gildong@example.com', TRUE, @@ -637,7 +639,7 @@ VALUES ('CON_000000002', '스포티지 계약', '김영희', '900123-2345678', '부산광역시 해운대구', 'younghee@example.com', '010-2345-6789', '기아자동차', 'PERSONAL', 'CREDIT', 'SER_002', '기본옵션', 3000000, 200000, 17000000, 25000000, - '2024-03-05', '부산광역시 해운대구 배송센터', 'APPROVED', 1, + '2024-03-05', '부산광역시 해운대구 배송센터', 'CONFIRMED', 1, '/contracts/2', NULL, TRUE, '2024-01-12 14:00:00', '2024-01-13 15:00:00', NULL, 'MEM_000000002', 'CEN_000000002', 'CUS_000000002', 'PRO_000000002'), @@ -653,7 +655,7 @@ VALUES ('CON_000000004', '셀토스 계약', '박민준', '970512-4567890', '인천광역시 미추홀구', 'minjoon@example.com', '010-4567-8901', '기아자동차', 'PERSONAL', 'CASH', 'SER_004', '기본옵션', 4000000, 250000, 12000000, 20000000, - '2024-04-01', '인천광역시 배송센터', 'CANCLED', 1, + '2024-04-01', '인천광역시 배송센터', 'DELIVERED', 1, '/contracts/4', NULL, TRUE, '2024-01-16 11:00:00', '2024-01-17 12:00:00', NULL, 'MEM_000000004', 'CEN_000000004', 'CUS_000000004', 'PRO_000000004'), @@ -669,7 +671,7 @@ VALUES ('CON_000000006', '모하비 계약', '김민주', '850824-6789012', '대전광역시 서구', 'minju@example.com', '010-6789-0123', '기아자동차', 'PERSONAL', 'CASH', 'SER_006', '풀옵션', 6000000, 500000, 34000000, 40000000, - '2024-05-01', '대전광역시 배송센터', 'APPROVED', 1, + '2024-05-01', '대전광역시 배송센터', 'CONFIRMED', 1, '/contracts/6', NULL, TRUE, '2024-01-20 15:00:00', '2024-01-21 16:00:00', NULL, 'MEM_000000006', 'CEN_000000006', 'CUS_000000006', 'PRO_000000006'), @@ -685,7 +687,7 @@ VALUES ('CON_000000008', '스팅어 계약', '이준호', '930910-8901234', '경기도 수원시', 'junho@example.com', '010-8901-2345', '기아자동차', 'PERSONAL', 'CREDIT', 'SER_008', '풀옵션', 5000000, 300000, 22000000, 27000000, - '2024-06-01', '수원시 배송센터', 'CANCLED', 1, + '2024-06-01', '수원시 배송센터', 'DELIVERED', 1, '/contracts/8', NULL, TRUE, '2024-01-24 19:00:00', '2024-01-25 20:00:00', NULL, 'MEM_000000008', 'CEN_000000008', 'CUS_000000008', 'PRO_000000008'), @@ -693,7 +695,7 @@ VALUES ('CON_000000009', '니로 계약', '박소영', '890321-9012345', '충청북도 청주시', 'soyoung@example.com', '010-9012-3456', '기아자동차', 'PERSONAL', 'CASH', 'SER_009', '기본옵션', 3500000, 150000, 17000000, 23000000, - '2024-06-15', '청주시 배송센터', 'APPROVED', 1, + '2024-06-15', '청주시 배송센터', 'CONFIRMED', 1, '/contracts/9', NULL, TRUE, '2024-01-26 21:00:00', '2024-01-27 22:00:00', NULL, 'MEM_000000009', 'CEN_000000009', 'CUS_000000009', 'PRO_000000009'), @@ -709,25 +711,25 @@ INSERT INTO tb_order ( ORD_ID, ORD_TTL, ORD_CONT, ACTIVE, CREATED_AT, UPDATED_AT, DELETED_AT, ORD_STAT, CONR_ID, MEM_ID, ADMIN_ID ) VALUES -- 주문 1 -('ORD_000000001', '쏘렌토 계약 주문', '쏘렌토 차량 계약 완료 후 주문 처리', TRUE, '2024-01-10 10:00:00', '2024-01-10 11:00:00', NULL, 'APPROVED', 'CON_000000001', 'MEM_000000001', 'MEM_000000005'), +('ORD_000000001', '쏘렌토 계약 주문', '쏘렌토 차량 계약 완료 후 주문 처리', TRUE, '2024-01-10 10:00:00', '2024-01-10 11:00:00', NULL, 'CONFIRMED', 'CON_000000001', 'MEM_000000001', 'MEM_000000005'), -- 주문 2 ('ORD_000000002', '스포티지 구매 주문', '스포티지 고객 상담 후 주문 확정', TRUE, '2024-01-11 12:00:00', '2024-01-11 13:00:00', NULL, 'WAIT', 'CON_000000002', 'MEM_000000002', 'MEM_000000005'), -- 주문 3 -('ORD_000000003', 'K7 리스 주문', 'K7 리스 계약 완료 후 주문 생성', TRUE, '2024-01-12 09:00:00', '2024-01-12 10:00:00', NULL, 'APPROVED', 'CON_000000003', 'MEM_000000003', 'MEM_000000005'), +('ORD_000000003', 'K7 리스 주문', 'K7 리스 계약 완료 후 주문 생성', TRUE, '2024-01-12 09:00:00', '2024-01-12 10:00:00', NULL, 'CONFIRMED', 'CON_000000003', 'MEM_000000003', 'MEM_000000005'), -- 주문 4 -('ORD_000000004', '셀토스 추가 주문', '셀토스 고객 추가 옵션 주문', TRUE, '2024-01-13 10:30:00', '2024-01-13 11:30:00', NULL, 'CANCLED', 'CON_000000004', 'MEM_000000004', 'MEM_000000005'), +('ORD_000000004', '셀토스 추가 주문', '셀토스 고객 추가 옵션 주문', TRUE, '2024-01-13 10:30:00', '2024-01-13 11:30:00', NULL, 'CANCELLED', 'CON_000000004', 'MEM_000000004', 'MEM_000000005'), -- 주문 5 ('ORD_000000005', 'K3 계약 주문', 'K3 차량 계약 후 최종 주문', TRUE, '2024-01-14 11:00:00', '2024-01-14 12:00:00', NULL, 'DELIVERED', 'CON_000000005', 'MEM_000000001', 'MEM_000000006'), -- 주문 6 ('ORD_000000006', '모하비 계약 수정 주문', '모하비 계약 변경에 따른 주문 수정', TRUE, '2024-01-15 13:00:00', '2024-01-15 14:00:00', NULL, 'WAIT', 'CON_000000006', 'MEM_000000002', 'MEM_000000006'), -- 주문 7 -('ORD_000000007', 'K8 리스 주문', 'K8 차량 리스 계약 주문', TRUE, '2024-01-16 14:30:00', '2024-01-16 15:30:00', NULL, 'APPROVED', 'CON_000000007', 'MEM_000000003', 'MEM_000000006'), +('ORD_000000007', 'K8 리스 주문', 'K8 차량 리스 계약 주문', TRUE, '2024-01-16 14:30:00', '2024-01-16 15:30:00', NULL, 'CONFIRMED', 'CON_000000007', 'MEM_000000003', 'MEM_000000006'), -- 주문 8 -('ORD_000000008', '스팅어 구매 주문', '스팅어 차량 구매 계약 후 주문 확정', TRUE, '2024-01-17 16:00:00', '2024-01-17 17:00:00', NULL, 'WAIT', 'CON_000000008', 'MEM_000000004', 'MEM_000000006'), +('ORD_000000008', '스팅어 구매 주문', '스팅어 차량 구매 계약 후 주문 확정', TRUE, '2024-01-17 16:00:00', '2024-01-17 17:00:00', NULL, 'DELIVERED', 'CON_000000008', 'MEM_000000004', 'MEM_000000006'), -- 주문 9 ('ORD_000000009', '니로 전기차 주문', '니로 전기차 계약 주문', TRUE, '2024-01-18 17:30:00', '2024-01-18 18:30:00', NULL, 'WAIT', 'CON_000000009', 'MEM_000000001', 'MEM_000000010'), -- 주문 10 -('ORD_000000010', 'K5 재고 주문', 'K5 재고 차량 추가 주문', TRUE, '2024-01-19 09:00:00', '2024-01-19 10:00:00', NULL, 'APPROVED', 'CON_000000010', 'MEM_000000001', 'MEM_00000009'); +('ORD_000000010', 'K5 재고 주문', 'K5 재고 차량 추가 주문', TRUE, '2024-01-19 09:00:00', '2024-01-19 10:00:00', NULL, 'CONFIRMED', 'CON_000000010', 'MEM_000000001', 'MEM_00000009'); INSERT INTO tb_problem (PROB_ID, PROB_TTL, PROB_CONT, CREATED_AT, UPDATED_AT, ACTIVE, CUST_ID, MEM_ID, PROD_ID) @@ -753,27 +755,27 @@ VALUES ('PROB_000000001', '쏘렌토 엔진 문제', '엔진에서 소음 발생 'MEM_000000010', 'PRO_000000010'); INSERT INTO tb_purchase_order (PUR_ORD_ID, PUR_ORD_TTL, PUR_ORD_CONT, CREATED_AT, UPDATED_AT, ACTIVE, PUR_ORD_STAT, ORD_ID, - MEM_ID) -VALUES ('PUR_000000001', '쏘렌토 부품 주문', '엔진 부품 요청', '2024-01-12 10:00:00', '2024-01-12 11:00:00', TRUE, 'WAIT', - 'ORD_000000001', 'MEM_000000001'), - ('PUR_000000002', '스포티지 타이어 주문', '타이어 4개 요청', '2024-01-13 12:00:00', '2024-01-13 13:00:00', TRUE, 'APPROVED', - 'ORD_000000002', 'MEM_000000002'), - ('PUR_000000003', 'K7 배터리 주문', '배터리 교체 요청', '2024-01-14 13:00:00', '2024-01-14 14:00:00', TRUE, 'CANCLED', - 'ORD_000000003', 'MEM_000000003'), - ('PUR_000000004', '셀토스 브레이크 주문', '브레이크 패드 요청', '2024-01-15 14:00:00', '2024-01-15 15:00:00', TRUE, 'WAIT', - 'ORD_000000004', 'MEM_000000004'), - ('PUR_000000005', 'K3 내비게이션 주문', '내비게이션 교체', '2024-01-16 15:00:00', '2024-01-16 16:00:00', TRUE, 'APPROVED', - 'ORD_000000005', 'MEM_000000005'), - ('PUR_000000006', '모하비 오일 필터 주문', '오일 필터 10개 요청', '2024-01-17 09:00:00', '2024-01-17 10:00:00', TRUE, - 'APPROVED', 'ORD_000000006', 'MEM_000000006'), - ('PUR_000000007', 'K8 헤드라이트 주문', 'LED 헤드라이트 2개 요청', '2024-01-18 10:00:00', '2024-01-18 11:00:00', TRUE, 'WAIT', - 'ORD_000000007', 'MEM_000000007'), - ('PUR_000000008', '스팅어 도어 핸들 주문', '도어 핸들 4개 요청', '2024-01-19 12:00:00', '2024-01-19 13:00:00', TRUE, 'APPROVED', - 'ORD_000000008', 'MEM_000000008'), - ('PUR_000000009', '니로 충전 케이블 주문', '전기차 충전 케이블 요청', '2024-01-20 14:00:00', '2024-01-20 15:00:00', TRUE, - 'CANCLED', 'ORD_000000009', 'MEM_000000009'), - ('PUR_000000010', 'K5 엔진오일 주문', '엔진오일 20L 요청', '2024-01-21 15:00:00', '2024-01-21 16:00:00', TRUE, 'WAIT', - 'ORD_000000010', 'MEM_000000010'); + MEM_ID, ADMIN_ID) +VALUES ('PUR_ORD_000000001', '쏘렌토 부품 주문', '엔진 부품 요청', '2024-01-12 10:00:00', '2024-01-12 11:00:00', TRUE, 'WAIT', + 'ORD_000000001', 'MEM_000000001', NULL), + ('PUR_ORD_000000002', '스포티지 타이어 주문', '타이어 4개 요청', '2024-01-13 12:00:00', '2024-01-13 13:00:00', TRUE, 'CONFIRMED', + 'ORD_000000002', 'MEM_000000002', NULL), + ('PUR_ORD_000000003', 'K7 배터리 주문', '배터리 교체 요청', '2024-01-14 13:00:00', '2024-01-14 14:00:00', TRUE, 'DELIVERED', + 'ORD_000000003', 'MEM_000000003', NULL), + ('PUR_ORD_000000004', '셀토스 브레이크 주문', '브레이크 패드 요청', '2024-01-15 14:00:00', '2024-01-15 15:00:00', TRUE, 'WAIT', + 'ORD_000000004', 'MEM_000000004', NULL), + ('PUR_ORD_000000005', 'K3 내비게이션 주문', '내비게이션 교체', '2024-01-16 15:00:00', '2024-01-16 16:00:00', TRUE, 'CANCELLED', + 'ORD_000000005', 'MEM_000000005', NULL), + ('PUR_ORD_000000006', '모하비 오일 필터 주문', '오일 필터 10개 요청', '2024-01-17 09:00:00', '2024-01-17 10:00:00', TRUE, + 'CONFIRMED', 'ORD_000000006', 'MEM_000000006', NULL), + ('PUR_ORD_000000007', 'K8 헤드라이트 주문', 'LED 헤드라이트 2개 요청', '2024-01-18 10:00:00', '2024-01-18 11:00:00', TRUE, 'WAIT', + 'ORD_000000007', 'MEM_000000007', NULL), + ('PUR_ORD_000000008', '스팅어 도어 핸들 주문', '도어 핸들 4개 요청', '2024-01-19 12:00:00', '2024-01-19 13:00:00', TRUE, 'CONFIRMED', + 'ORD_000000008', 'MEM_000000008', NULL), + ('PUR_ORD_000000009', '니로 충전 케이블 주문', '전기차 충전 케이블 요청', '2024-01-20 14:00:00', '2024-01-20 15:00:00', TRUE, + 'DELIVERED', 'ORD_000000009', 'MEM_000000009', NULL), + ('PUR_ORD_000000010', 'K5 엔진오일 주문', '엔진오일 20L 요청', '2024-01-21 15:00:00', '2024-01-21 16:00:00', TRUE, 'WAIT', + 'ORD_000000010', 'MEM_000000010', NULL); INSERT INTO tb_notice (NOT_ID, NOT_TTL, NOT_TAG, NOT_CLA, NOT_CONT, CREATED_AT, UPDATED_AT, ACTIVE, MEM_ID) VALUES ('NOT_000000001', '신차 출시 공지', 'ALL', 'IMPORTANT', '신형 쏘렌토가 출시되었습니다.', '2024-01-10 09:00:00', @@ -861,7 +863,7 @@ INSERT INTO tb_member_detail (MEM_DET_ID, MEM_DET_REL, MEM_DET_NAME, MEM_DET_BIR CERT_DATE, CERT_INST, CERT_NAME, CERT_SCO, CREATED_AT, UPDATED_AT, MEM_ID) VALUES ('DET_000000001', '배우자', '박미숙', '1988-05-12', '880512-1234567', '010-1111-2222', 'FEMALE', FALSE, FALSE, NULL, '2010', 'A', '대졸', '경영학', '기아', '2020-01-01', '2025-01-01', 'K5', '2023-06-01', '기아자동차', '세일즈', '95', - '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000001'), + '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000001'), ('DET_000000002', '자녀', '김철수', '2012-08-05', '120805-2345678', '010-2222-3333', 'MALE', FALSE, FALSE, NULL, '2022', 'B+', '고졸', '정보통신', '기아', '2022-01-15', '2027-01-15', 'K3', '2023-07-15', '기아자동차', '서비스', '88', '2024-11-22 14:00:00', '2024-11-22 16:00:00', 'MEM_000000002'), @@ -994,6 +996,4 @@ VALUES ('SAL_000000001', 'CON_000000001'), ('SAL_000000007', 'CON_000000007'), ('SAL_000000008', 'CON_000000008'), ('SAL_000000009', 'CON_000000009'), - ('SAL_000000010', 'CON_000000010'); - - + ('SAL_000000010', 'CON_000000010'); \ No newline at end of file From 3c9867da9b327dad094fd4b4b1480dde055a2152 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Mon, 18 Nov 2024 14:07:01 +0900 Subject: [PATCH 133/563] =?UTF-8?q?feat:=20=EA=B0=9C=EC=97=B0=EC=84=B1=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=EB=A1=9C=20Batch=20=EC=A0=81=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EA=B8=B0=EB=A1=9C=20=EA=B2=B0?= =?UTF-8?q?=EC=A0=95=EB=90=A8=EC=9C=BC=EB=A1=9C=20=EC=82=AD=EC=A0=9C=20(#5?= =?UTF-8?q?7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 - .../domain/batch/BatchConfig.java | 94 ------------------ .../domain/batch/BatchScheduler.java | 36 ------- src/main/resources/application.yml | 3 - src/main/resources/sql/ddl.sql | 99 ------------------- 5 files changed, 237 deletions(-) delete mode 100644 src/main/java/stanl_2/final_backend/domain/batch/BatchConfig.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/batch/BatchScheduler.java diff --git a/build.gradle b/build.gradle index 21322d10..1f9b7eb1 100644 --- a/build.gradle +++ b/build.gradle @@ -58,11 +58,6 @@ dependencies { //hibernate core implementation 'org.hibernate.orm:hibernate-core:6.5.2.Final' - // Spring batch + scheduler - implementation 'org.springframework.boot:spring-boot-starter-batch' - implementation 'org.springframework.boot:spring-boot-starter-quartz' - testImplementation 'org.springframework.batch:spring-batch-test' - } tasks.named('test') { diff --git a/src/main/java/stanl_2/final_backend/domain/batch/BatchConfig.java b/src/main/java/stanl_2/final_backend/domain/batch/BatchConfig.java deleted file mode 100644 index d68ce444..00000000 --- a/src/main/java/stanl_2/final_backend/domain/batch/BatchConfig.java +++ /dev/null @@ -1,94 +0,0 @@ -package stanl_2.final_backend.domain.batch; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.batch.core.Job; -import org.springframework.batch.core.Step; -import org.springframework.batch.core.configuration.annotation.JobScope; -import org.springframework.batch.core.configuration.annotation.StepScope; -import org.springframework.batch.core.job.builder.JobBuilder; -import org.springframework.batch.core.launch.support.RunIdIncrementer; -import org.springframework.batch.core.repository.JobRepository; -import org.springframework.batch.core.step.builder.StepBuilder; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.domain.Sort; -import org.springframework.transaction.PlatformTransactionManager; - -import java.util.Collections; -import java.util.List; - -@Slf4j -@Configuration -@RequiredArgsConstructor -public class BatchConfig { - - private final String JOB_NAME = "checkJob"; - private final String STEP_NAME = "checkStep"; - - - @Bean - public Job checkJob(JobRepository jobRepository, PlatformTransactionManager transactionManager){ - return new JobBuilder(JOB_NAME, jobRepository) - .incrementer(new RunIdIncrementer()) - .start(checkStep(jobRepository, transactionManager)) - .build(); - } - - @Bean - @JobScope - public Step checkStep(JobRepository jobRepository, PlatformTransactionManager transactionManager){ - return new StepBuilder(STEP_NAME, jobRepository) - .chunk(5,transactionManager) - .reader(checkReader()) - .processor(checkProcessor()) - .writer(checkWriter()) - .build(); - } - - @Bean - @StepScope - public RepositoryItemWriter checkWriter(){ - return new RepositoryItemWriter() - .repository(SalesStatisticsRepository) - .methodName("save") - .build(); - } - - @Bean - @StepScope - public ItemProcessor checkProcessor(){ - return new ItemProcessor() { - @Override - public SalesStatistics process(SalesHistory item) throws Exception { - return new SalesStatistics(item); - } - }; - } - - @Bean - @StepScope - public RepositoryItemReader checkReader(){ - return new RepositoryItemReaderBuilder() - .name("checkReader") - .repository(SalesHistoryRepository) - .methodName("findAll") - .pageSize(5) - .arguments(List.of()) - .sorts(Collections.singletonMap("id", Sort.Direction.ASC)) - .build(); - } - -// @Bean -// @StepScope -// public Tasklet checkTasklet(){ -// return new Tasklet(){ -// @Override -// public RepeatStatus execute(StepContribution contribution, ChunkContext context) throws Exception { -// log.info("Spring batch check Suceess"); -// // 원하는 비지니스 모델 추가 -// return RepeatStatus.FINISHED; -// } -// }; -// } -} diff --git a/src/main/java/stanl_2/final_backend/domain/batch/BatchScheduler.java b/src/main/java/stanl_2/final_backend/domain/batch/BatchScheduler.java deleted file mode 100644 index bc4347b1..00000000 --- a/src/main/java/stanl_2/final_backend/domain/batch/BatchScheduler.java +++ /dev/null @@ -1,36 +0,0 @@ -package stanl_2.final_backend.domain.batch; - -import org.springframework.batch.core.*; -import org.springframework.batch.core.launch.JobLauncher; -import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; -import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; -import org.springframework.batch.core.repository.JobRestartException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -import java.util.Collections; - -@Component -public class BatchScheduler { - - private final JobLauncher jobLauncher; - private final Job checkJob; - - @Autowired - public BatchScheduler(JobLauncher jobLauncher, Job checkJob) { - this.jobLauncher = jobLauncher; - this.checkJob = checkJob; - } - - @Scheduled(cron = "0 03 00 * * ?", zone = "Asia/Seoul") - public void checkJobRun() throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, - JobParametersInvalidException, JobRestartException { - - JobParameters jobParameters = new JobParameters( - Collections.singletonMap("requestTime", new JobParameter(System.currentTimeMillis(), Long.class)) - ); - - jobLauncher.run(checkJob, jobParameters); - } -} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 83c68f6a..2342bfd9 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -30,9 +30,6 @@ spring: batch: jdbc: initialize-schema: always - job: - name: checkJob - enabled: false jwt: secret-key: ${JWT_SECRET_KEY} diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index 032d74e1..0a269c64 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -25,15 +25,6 @@ DROP TABLE IF EXISTS tb_certification; DROP TABLE IF EXISTS tb_career; DROP TABLE IF EXISTS tb_product; DROP TABLE IF EXISTS tb_organization_chart; -DROP TABLE IF EXISTS BATCH_STEP_EXECUTION_CONTEXT; -DROP TABLE IF EXISTS BATCH_STEP_EXECUTION; -DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_CONTEXT; -DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_PARAMS; -DROP TABLE IF EXISTS BATCH_JOB_EXECUTION; -DROP TABLE IF EXISTS BATCH_JOB_INSTANCE; -DROP TABLE IF EXISTS BATCH_STEP_EXECUTION_SEQ; -DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_SEQ; -DROP TABLE IF EXISTS BATCH_JOB_SEQ; -- 조직 관련 테이블 생성 @@ -443,96 +434,6 @@ CREATE TABLE tb_sales_history CONR_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고' ); -# spring batch & scheduler 테이블 -CREATE TABLE BATCH_JOB_INSTANCE -( - JOB_INSTANCE_ID BIGINT NOT NULL PRIMARY KEY, - VERSION BIGINT, - JOB_NAME VARCHAR(100) NOT NULL, - JOB_KEY VARCHAR(32) NOT NULL, - constraint JOB_INST_UN unique (JOB_NAME, JOB_KEY) -) ENGINE = InnoDB; - -CREATE TABLE BATCH_JOB_EXECUTION -( - JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY, - VERSION BIGINT, - JOB_INSTANCE_ID BIGINT NOT NULL, - CREATE_TIME DATETIME(6) NOT NULL, - START_TIME DATETIME(6) DEFAULT NULL, - END_TIME DATETIME(6) DEFAULT NULL, - STATUS VARCHAR(10), - EXIT_CODE VARCHAR(2500), - EXIT_MESSAGE VARCHAR(2500), - LAST_UPDATED DATETIME(6), - constraint JOB_INST_EXEC_FK foreign key (JOB_INSTANCE_ID) - references BATCH_JOB_INSTANCE (JOB_INSTANCE_ID) -) ENGINE = InnoDB; - -CREATE TABLE BATCH_JOB_EXECUTION_PARAMS -( - JOB_EXECUTION_ID BIGINT NOT NULL, - PARAMETER_NAME VARCHAR(100) NOT NULL, - PARAMETER_TYPE VARCHAR(100) NOT NULL, - PARAMETER_VALUE VARCHAR(2500), - IDENTIFYING CHAR(1) NOT NULL, - constraint JOB_EXEC_PARAMS_FK foreign key (JOB_EXECUTION_ID) - references BATCH_JOB_EXECUTION (JOB_EXECUTION_ID) -) ENGINE = InnoDB; - -CREATE TABLE BATCH_STEP_EXECUTION -( - STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY, - VERSION BIGINT NOT NULL, - STEP_NAME VARCHAR(100) NOT NULL, - JOB_EXECUTION_ID BIGINT NOT NULL, - CREATE_TIME DATETIME(6) NOT NULL, - START_TIME DATETIME(6) DEFAULT NULL, - END_TIME DATETIME(6) DEFAULT NULL, - STATUS VARCHAR(10), - COMMIT_COUNT BIGINT, - READ_COUNT BIGINT, - FILTER_COUNT BIGINT, - WRITE_COUNT BIGINT, - READ_SKIP_COUNT BIGINT, - WRITE_SKIP_COUNT BIGINT, - PROCESS_SKIP_COUNT BIGINT, - ROLLBACK_COUNT BIGINT, - EXIT_CODE VARCHAR(2500), - EXIT_MESSAGE VARCHAR(2500), - LAST_UPDATED DATETIME(6), - constraint JOB_EXEC_STEP_FK foreign key (JOB_EXECUTION_ID) - references BATCH_JOB_EXECUTION (JOB_EXECUTION_ID) -) ENGINE = InnoDB; - -CREATE TABLE BATCH_STEP_EXECUTION_CONTEXT -( - STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY, - SHORT_CONTEXT VARCHAR(2500) NOT NULL, - SERIALIZED_CONTEXT TEXT, - constraint STEP_EXEC_CTX_FK foreign key (STEP_EXECUTION_ID) - references BATCH_STEP_EXECUTION (STEP_EXECUTION_ID) -) ENGINE = InnoDB; - -CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT -( - JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY, - SHORT_CONTEXT VARCHAR(2500) NOT NULL, - SERIALIZED_CONTEXT TEXT, - constraint JOB_EXEC_CTX_FK foreign key (JOB_EXECUTION_ID) - references BATCH_JOB_EXECUTION (JOB_EXECUTION_ID) -) ENGINE = InnoDB; - -CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ START WITH 1 MINVALUE 1 MAXVALUE 9223372036854775806 - INCREMENT BY 1 NOCACHE NOCYCLE ENGINE =InnoDB; - -CREATE SEQUENCE BATCH_JOB_EXECUTION_SEQ START WITH 1 MINVALUE 1 MAXVALUE 9223372036854775806 - INCREMENT BY 1 NOCACHE NOCYCLE ENGINE =InnoDB; - -CREATE SEQUENCE BATCH_JOB_SEQ START WITH 1 MINVALUE 1 MAXVALUE 9223372036854775806 - INCREMENT BY 1 NOCACHE NOCYCLE ENGINE =InnoDB; - - INSERT INTO tb_organization_chart (ORG_CHA_ID, ORG_CHA_NAME, ORG_CHA_DEPTH) VALUES ('ORG_000000001', '본사', 1), ('ORG_000000002', '서울지사', 2), From e1101e6338ce4c420069f706c99884dc49d75794 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 14:25:50 +0900 Subject: [PATCH 134/563] =?UTF-8?q?fix:=20ddl=20=EB=A9=A4=EB=B2=84=20?= =?UTF-8?q?=EB=8D=94=EB=AF=B8=EB=8D=B0=EC=9D=B4=ED=84=B0=20=ED=96=89=20?= =?UTF-8?q?=ED=95=98=EB=82=98=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/application/controller/CareerController.java | 2 +- .../career/command/application/dto/CareerRegistDTO.java | 2 +- .../application/controller/CertificationController.java | 3 +-- .../command/application/dto/CertificationRegisterDTO.java | 2 +- .../query/controller/CertificationController.java | 4 ++-- src/main/resources/sql/ddl.sql | 5 +---- 6 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java b/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java index 3dd868a6..95b0bca7 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java +++ b/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java @@ -38,7 +38,7 @@ public CareerController(CareerCommandService careerCommandService, @PostMapping("") public ResponseEntity postCareer(@RequestBody CareerRegistDTO careerRegistDTO){ - careerRegistDTO.setMemId(authQueryService.selectMemberIdByLoginId(careerRegistDTO.getMemberLoginId())); + careerRegistDTO.setMemberId(authQueryService.selectMemberIdByLoginId(careerRegistDTO.getMemberLoginId())); careerCommandService.registCareer(careerRegistDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerRegistDTO.java index c60f2743..ff37e248 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerRegistDTO.java @@ -13,5 +13,5 @@ public class CareerRegistDTO { private String resignDate; private String name; private String note; - private String memId; + private String memberId; } diff --git a/src/main/java/stanl_2/final_backend/domain/certification/command/application/controller/CertificationController.java b/src/main/java/stanl_2/final_backend/domain/certification/command/application/controller/CertificationController.java index 86dcae13..c782f232 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/command/application/controller/CertificationController.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/command/application/controller/CertificationController.java @@ -12,7 +12,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; -import stanl_2.final_backend.domain.career.command.application.dto.CareerRegistDTO; import stanl_2.final_backend.domain.career.common.response.CareerResponseMessage; import stanl_2.final_backend.domain.certification.command.application.dto.CertificationRegisterDTO; import stanl_2.final_backend.domain.certification.command.application.service.CertificationCommandService; @@ -40,7 +39,7 @@ public CertificationController(CertificationCommandService certificationCommandS @PostMapping("") public ResponseEntity postCertification(@RequestBody CertificationRegisterDTO certificationRegisterDTO){ - certificationRegisterDTO.setMemId(authQueryService.selectMemberIdByLoginId(certificationRegisterDTO.getMemberLoginId())); + certificationRegisterDTO.setMemberId(authQueryService.selectMemberIdByLoginId(certificationRegisterDTO.getMemberLoginId())); certificationCommandService.registCertification(certificationRegisterDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/certification/command/application/dto/CertificationRegisterDTO.java b/src/main/java/stanl_2/final_backend/domain/certification/command/application/dto/CertificationRegisterDTO.java index e48c9727..f8cbbe24 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/command/application/dto/CertificationRegisterDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/command/application/dto/CertificationRegisterDTO.java @@ -13,5 +13,5 @@ public class CertificationRegisterDTO { private String agency; private String acquisitionDate; private String note; - private String memId; + private String memberId; } diff --git a/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java b/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java index f9219f3b..3f83cfe9 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java @@ -50,7 +50,7 @@ public ResponseEntity getCertificationByOther(@PathVariab .build()); } -// @Operation(summary = "경력 조회(접속중인 사용자)") +// @Operation(summary = "자격증/외국어 조회(접속중인 사용자)") // @ApiResponses(value = { // @ApiResponse(responseCode = "200", description = "성공", // content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), @@ -58,7 +58,7 @@ public ResponseEntity getCertificationByOther(@PathVariab // content = @Content(mediaType = "application/json")) // }) // @GetMapping("") -// public ResponseEntity getCareer(Principal principal){ +// public ResponseEntity getCertification(Principal principal){ // // List careerList = careerQueryService.selectCareerList(principal.getName()); // diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index 032d74e1..2f9c85f2 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -596,10 +596,7 @@ VALUES ('MEM_000000001', 101, 'pwd1234', '김철수', 'chulsoo@example.com', 35, '2024-01-08 12:00:00', TRUE), ('MEM_000000009', 109, 'pwd9012', '윤아름', 'areum@example.com', 27, 'FEMALE', '901234-5678901', '010-1098-7654', '충청북도 청주시', 'Assistant', 'High School', 'Intern', 'CEN_000000009', 'ORG_000000006', '2024-01-09 12:00:00', - '2024-01-09 12:00:00', TRUE), - ('MEM_000000010', 110, 'pwd0123', '김정훈', 'junghoon@example.com', 33, 'MALE', '012345-6789012', '010-0987-6543', - '전라북도 전주시', 'Consultant', 'Master', 'Contract', 'CEN_000000010', 'ORG_000000009', '2024-01-10 12:00:00', - '2024-01-10 12:00:00', TRUE); + '2024-01-09 12:00:00', TRUE); INSERT INTO tb_member_role (MEM_ROL_ID, MEM_ROL_NAME, MEM_ID) VALUES ('MEM_ROL_000000001', '영업 사원', 'MEM_000000001'), From 59a1bf320d045341c2d973d78b4fb46fcbcfbe57 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 14:33:11 +0900 Subject: [PATCH 135/563] =?UTF-8?q?fix:=20ddl=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stanl_2/final_backend/FinalBackendApplication.java | 4 ---- src/main/resources/sql/ddl.sql | 8 +++----- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/FinalBackendApplication.java b/src/main/java/stanl_2/final_backend/FinalBackendApplication.java index 8045bf81..5f0cb0ad 100644 --- a/src/main/java/stanl_2/final_backend/FinalBackendApplication.java +++ b/src/main/java/stanl_2/final_backend/FinalBackendApplication.java @@ -1,12 +1,8 @@ package stanl_2.final_backend; -import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.scheduling.annotation.EnableScheduling; -@EnableScheduling -@EnableBatchProcessing @SpringBootApplication public class FinalBackendApplication { diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index c005acab..62db917e 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -2,6 +2,7 @@ SET FOREIGN_KEY_CHECKS = 0; -- 자식 테이블부터 삭제 +DROP TABLE IF EXISTS sample; DROP TABLE IF EXISTS tb_sales_history; DROP TABLE IF EXISTS tb_update_history; DROP TABLE IF EXISTS tb_product_option; @@ -195,7 +196,7 @@ CREATE TABLE tb_order FOREIGN KEY (CONR_ID) REFERENCES tb_contract (CONR_ID) ON DELETE CASCADE ); -CREATE TABLE tb_PROBLEM +CREATE TABLE tb_problem ( PROB_ID VARCHAR(255) NOT NULL, PROB_TTL VARCHAR(255) NOT NULL, @@ -494,10 +495,7 @@ VALUES ('MEM_000000001', 101, 'pwd1234', '김철수', 'chulsoo@example.com', 35, '2024-01-07 12:00:00', TRUE), ('MEM_000000008', 108, 'pwd8901', '이준호', 'junho@example.com', 36, 'MALE', '890123-4567890', '010-2109-8765', '경기도 수원시', 'Technician', 'Bachelor', 'Full-time', 'CEN_000000008', 'ORG_000000008', '2024-01-08 12:00:00', - '2024-01-08 12:00:00', TRUE), - ('MEM_000000009', 109, 'pwd9012', '윤아름', 'areum@example.com', 27, 'FEMALE', '901234-5678901', '010-1098-7654', - '충청북도 청주시', 'Assistant', 'High School', 'Intern', 'CEN_000000009', 'ORG_000000006', '2024-01-09 12:00:00', - '2024-01-09 12:00:00', TRUE); + '2024-01-08 12:00:00', TRUE); INSERT INTO tb_member_role (MEM_ROL_ID, MEM_ROL_NAME, MEM_ID) VALUES ('MEM_ROL_000000001', '영업 사원', 'MEM_000000001'), From ce9edfe93de83fe651bb0f70abf7633bf7ab1910 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 14:36:51 +0900 Subject: [PATCH 136/563] =?UTF-8?q?feat:=20=EC=9E=90=EA=B2=A9=EC=A6=9D/?= =?UTF-8?q?=EC=99=B8=EA=B5=AD=EC=96=B4=20=EC=A1=B0=ED=9A=8C(=EC=A0=91?= =?UTF-8?q?=EC=86=8D=EC=A4=91=EC=9D=B8=20=EC=82=AC=EC=9A=A9=EC=9E=90)(#69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CertificationController.java | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java b/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java index 3f83cfe9..99ed0ea8 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java @@ -13,7 +13,6 @@ import org.springframework.web.bind.annotation.RestController; import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.career.common.response.CareerResponseMessage; -import stanl_2.final_backend.domain.career.query.dto.CareerDTO; import stanl_2.final_backend.domain.certification.query.dto.CertificationDTO; import stanl_2.final_backend.domain.certification.query.service.CertificationQueryService; @@ -50,22 +49,22 @@ public ResponseEntity getCertificationByOther(@PathVariab .build()); } -// @Operation(summary = "자격증/외국어 조회(접속중인 사용자)") -// @ApiResponses(value = { -// @ApiResponse(responseCode = "200", description = "성공", -// content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), -// @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", -// content = @Content(mediaType = "application/json")) -// }) -// @GetMapping("") -// public ResponseEntity getCertification(Principal principal){ -// -// List careerList = careerQueryService.selectCareerList(principal.getName()); -// -// return ResponseEntity.ok(CareerResponseMessage.builder() -// .httpStatus(200) -// .msg("성공") -// .result(careerList) -// .build()); -// } + @Operation(summary = "자격증/외국어 조회(접속중인 사용자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("") + public ResponseEntity getCertification(Principal principal){ + + List careerList = certificationQueryService.selectCertificationList(principal.getName()); + + return ResponseEntity.ok(CareerResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(careerList) + .build()); + } } From 6ded408b6e47f8e176e3740cb47f503a5bf25579 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Mon, 18 Nov 2024 14:38:22 +0900 Subject: [PATCH 137/563] =?UTF-8?q?feat:=20=EC=9D=BC=EC=A0=95=ED=91=9C=20m?= =?UTF-8?q?emberLoginId=20=EC=A0=81=EC=9A=A9=20=EB=B0=8F=20=EB=A9=94?= =?UTF-8?q?=EC=86=8C=EB=93=9C=20=EC=A0=81=EC=9A=A9=20(#66)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stanl_2/final_backend/FinalBackendApplication.java | 4 ---- .../domain/service/ScheduleCommandServiceImpl.java | 6 +++--- .../query/service/ScheduleQueryServiceImpl.java | 6 +++--- src/main/resources/application.yml | 4 ---- src/main/resources/sql/ddl.sql | 10 ++-------- 5 files changed, 8 insertions(+), 22 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/FinalBackendApplication.java b/src/main/java/stanl_2/final_backend/FinalBackendApplication.java index 8045bf81..5f0cb0ad 100644 --- a/src/main/java/stanl_2/final_backend/FinalBackendApplication.java +++ b/src/main/java/stanl_2/final_backend/FinalBackendApplication.java @@ -1,12 +1,8 @@ package stanl_2.final_backend; -import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.scheduling.annotation.EnableScheduling; -@EnableScheduling -@EnableBatchProcessing @SpringBootApplication public class FinalBackendApplication { diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java index 1d3e9e94..6e43e346 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java @@ -49,7 +49,7 @@ private String getCurrentTime() { @Transactional public Boolean registSchedule(ScheduleRegistDTO scheduleRegistDTO) { - String memberId = authQueryService.selectMemberLoginId(scheduleRegistDTO.getMemberLoginId()); + String memberId = authQueryService.selectMemberIdByLoginId(scheduleRegistDTO.getMemberLoginId()); scheduleRegistDTO.setMemberId(memberId); try { @@ -71,7 +71,7 @@ public Boolean registSchedule(ScheduleRegistDTO scheduleRegistDTO) { @Transactional public Boolean modifySchedule(ScheduleModifyDTO scheduleModifyDTO) { - String memberId = authQueryService.selectMemberLoginId(scheduleModifyDTO.getMemberLoginId()); + String memberId = authQueryService.selectMemberIdByLoginId(scheduleModifyDTO.getMemberLoginId()); scheduleModifyDTO.setMemberId(memberId); Schedule schedule = scheduleRepository.findByScheduleId(scheduleModifyDTO.getScheduleId()) @@ -104,7 +104,7 @@ public Boolean modifySchedule(ScheduleModifyDTO scheduleModifyDTO) { @Transactional public Boolean deleteSchedule(ScheduleDeleteDTO scheduleDeleteDTO) { - String memberId = authQueryService.selectMemberLoginId(scheduleDeleteDTO.getMemberLoginId()); + String memberId = authQueryService.selectMemberIdByLoginId(scheduleDeleteDTO.getMemberLoginId()); Schedule schedule = scheduleRepository.findByScheduleId(scheduleDeleteDTO.getScheduleId()) .orElseThrow(() -> new ScheduleCommonException(ScheduleErrorCode.SCHEDULE_NOT_FOUND)); diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java index 9e7b4a8d..122e3a2b 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java @@ -43,7 +43,7 @@ public ScheduleQueryServiceImpl(ScheduleMapper scheduleMapper, AuthQueryService @Transactional(readOnly = true) public List selectAllSchedule(String memberLogindId) { - String memberId = authQueryService.selectMemberLoginId(memberLogindId); + String memberId = authQueryService.selectMemberIdByLoginId(memberLogindId); String currentMonth = getCurrentTime().substring(0,7); @@ -62,7 +62,7 @@ public List selectAllSchedule(String memberLogindId) { @Transactional(readOnly = true) public List selectYearMonthSchedule(ScheduleYearMonthDTO scheduleYearMonthDTO) { - String memberId = authQueryService.selectMemberLoginId(scheduleYearMonthDTO.getMemberLoginId()); + String memberId = authQueryService.selectMemberIdByLoginId(scheduleYearMonthDTO.getMemberLoginId()); scheduleYearMonthDTO.setMemberId(memberId); String yearMonth = scheduleYearMonthDTO.getYear() + "-" + scheduleYearMonthDTO.getMonth(); @@ -83,7 +83,7 @@ public List selectYearMonthSchedule(ScheduleYearMonthDTO s @Transactional(readOnly = true) public ScheduleDetailDTO selectDetailSchedule(ScheduleDetailDTO scheduleDetailDTO) { - String memberId = authQueryService.selectMemberLoginId(scheduleDetailDTO.getMemberLoginId()); + String memberId = authQueryService.selectMemberIdByLoginId(scheduleDetailDTO.getMemberLoginId()); scheduleDetailDTO.setMemberId(memberId); if(scheduleDetailDTO.getScheduleId() == null || scheduleDetailDTO.getScheduleId().trim().isEmpty()){ diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 2342bfd9..eee7170c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -27,10 +27,6 @@ spring: properties: hibernate.format_sql: true - batch: - jdbc: - initialize-schema: always - jwt: secret-key: ${JWT_SECRET_KEY} header: ${JWT_HEADER} diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index 0a269c64..9f8a7c15 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -195,7 +195,7 @@ CREATE TABLE tb_order FOREIGN KEY (CONR_ID) REFERENCES tb_contract (CONR_ID) ON DELETE CASCADE ); -CREATE TABLE tb_PROBLEM +CREATE TABLE tb_problem ( PROB_ID VARCHAR(255) NOT NULL, PROB_TTL VARCHAR(255) NOT NULL, @@ -494,13 +494,7 @@ VALUES ('MEM_000000001', 101, 'pwd1234', '김철수', 'chulsoo@example.com', 35, '2024-01-07 12:00:00', TRUE), ('MEM_000000008', 108, 'pwd8901', '이준호', 'junho@example.com', 36, 'MALE', '890123-4567890', '010-2109-8765', '경기도 수원시', 'Technician', 'Bachelor', 'Full-time', 'CEN_000000008', 'ORG_000000008', '2024-01-08 12:00:00', - '2024-01-08 12:00:00', TRUE), - ('MEM_000000009', 109, 'pwd9012', '윤아름', 'areum@example.com', 27, 'FEMALE', '901234-5678901', '010-1098-7654', - '충청북도 청주시', 'Assistant', 'High School', 'Intern', 'CEN_000000009', 'ORG_000000006', '2024-01-09 12:00:00', - '2024-01-09 12:00:00', TRUE), - ('MEM_000000010', 110, 'pwd0123', '김정훈', 'junghoon@example.com', 33, 'MALE', '012345-6789012', '010-0987-6543', - '전라북도 전주시', 'Consultant', 'Master', 'Contract', 'CEN_000000010', 'ORG_000000009', '2024-01-10 12:00:00', - '2024-01-10 12:00:00', TRUE); + '2024-01-08 12:00:00', TRUE); INSERT INTO tb_member_role (MEM_ROL_ID, MEM_ROL_NAME, MEM_ID) VALUES ('MEM_ROL_000000001', '영업 사원', 'MEM_000000001'), From 0a23412d47c31e9e36fd72e7fda4334568c0cf7d Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 15:01:30 +0900 Subject: [PATCH 138/563] =?UTF-8?q?feat:=20=ED=95=99=EB=A0=A5=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C(with=20=EC=82=AC=EB=B2=88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../career/query/repository/CareerMapper.java | 2 +- .../query/repository/CertificationMapper.java | 2 +- .../query/controller/EducationController.java | 54 +++++++++++++++++++ .../education/query/dto/EducationDTO.java | 1 - .../query/repository/EducationMapper.java | 5 ++ .../query/service/EducationQueryService.java | 5 ++ .../service/EducationQueryServiceImpl.java | 20 ++++++- .../career/query/repository/CareerMapper.xml | 2 +- .../query/repository/CertificationMapper.xml | 2 +- .../query/repository/EducationMapper.xml | 21 +++++++- 10 files changed, 107 insertions(+), 7 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/career/query/repository/CareerMapper.java b/src/main/java/stanl_2/final_backend/domain/career/query/repository/CareerMapper.java index 20ed2158..d5d6485c 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/query/repository/CareerMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/career/query/repository/CareerMapper.java @@ -8,5 +8,5 @@ @Mapper public interface CareerMapper { - List selectCareerInfo(@Param("loginId") String loginId); + List selectCareerInfo(@Param("memberId") String memberId); } diff --git a/src/main/java/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.java b/src/main/java/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.java index b98a6e1a..ebd7eee5 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.java @@ -8,5 +8,5 @@ @Mapper public interface CertificationMapper { - List selectCertificationInfo(@Param("loginId") String loginId); + List selectCertificationInfo(@Param("memberId") String memberId); } diff --git a/src/main/java/stanl_2/final_backend/domain/education/query/controller/EducationController.java b/src/main/java/stanl_2/final_backend/domain/education/query/controller/EducationController.java index ae20203b..cefda7ac 100644 --- a/src/main/java/stanl_2/final_backend/domain/education/query/controller/EducationController.java +++ b/src/main/java/stanl_2/final_backend/domain/education/query/controller/EducationController.java @@ -1,10 +1,26 @@ package stanl_2.final_backend.domain.education.query.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; +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.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; +import stanl_2.final_backend.domain.career.common.response.CareerResponseMessage; +import stanl_2.final_backend.domain.certification.query.dto.CertificationDTO; +import stanl_2.final_backend.domain.education.common.response.EducationResponseMessage; +import stanl_2.final_backend.domain.education.query.dto.EducationDTO; import stanl_2.final_backend.domain.education.query.service.EducationQueryService; +import java.security.Principal; +import java.util.List; + @RestController(value = "queryEducationController") @RequestMapping("/api/v1/education") public class EducationController { @@ -15,4 +31,42 @@ public class EducationController { public EducationController(EducationQueryService educationQueryService) { this.educationQueryService = educationQueryService; } + + @Operation(summary = "학력 조회(with 사번)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/other/{id}") + public ResponseEntity getEducation(@PathVariable String id){ + + List educationList = educationQueryService.selectEducationList(id); + + return ResponseEntity.ok(EducationResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(educationList) + .build()); + } + +// @Operation(summary = "자격증/외국어 조회(접속중인 사용자)") +// @ApiResponses(value = { +// @ApiResponse(responseCode = "200", description = "성공", +// content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), +// @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", +// content = @Content(mediaType = "application/json")) +// }) +// @GetMapping("") +// public ResponseEntity getCertification(Principal principal){ +// +// List careerList = certificationQueryService.selectCertificationList(principal.getName()); +// +// return ResponseEntity.ok(CareerResponseMessage.builder() +// .httpStatus(200) +// .msg("성공") +// .result(careerList) +// .build()); +// } } diff --git a/src/main/java/stanl_2/final_backend/domain/education/query/dto/EducationDTO.java b/src/main/java/stanl_2/final_backend/domain/education/query/dto/EducationDTO.java index ee2c7e87..f438a36b 100644 --- a/src/main/java/stanl_2/final_backend/domain/education/query/dto/EducationDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/education/query/dto/EducationDTO.java @@ -1,6 +1,5 @@ package stanl_2.final_backend.domain.education.query.dto; -import jakarta.persistence.Column; import lombok.*; @NoArgsConstructor diff --git a/src/main/java/stanl_2/final_backend/domain/education/query/repository/EducationMapper.java b/src/main/java/stanl_2/final_backend/domain/education/query/repository/EducationMapper.java index 07ec606c..30d67fcf 100644 --- a/src/main/java/stanl_2/final_backend/domain/education/query/repository/EducationMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/education/query/repository/EducationMapper.java @@ -1,7 +1,12 @@ package stanl_2.final_backend.domain.education.query.repository; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import stanl_2.final_backend.domain.education.query.dto.EducationDTO; + +import java.util.List; @Mapper public interface EducationMapper { + List selectEducationInfo(@Param("memberId") String memberId); } diff --git a/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryService.java b/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryService.java index 9d9b4b83..5e5efd20 100644 --- a/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryService.java @@ -1,4 +1,9 @@ package stanl_2.final_backend.domain.education.query.service; +import stanl_2.final_backend.domain.education.query.dto.EducationDTO; + +import java.util.List; + public interface EducationQueryService { + List selectEducationList(String id); } diff --git a/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryServiceImpl.java index 2ed9a56d..6f1c105d 100644 --- a/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryServiceImpl.java @@ -2,15 +2,33 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.certification.query.dto.CertificationDTO; +import stanl_2.final_backend.domain.education.query.dto.EducationDTO; import stanl_2.final_backend.domain.education.query.repository.EducationMapper; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; + +import java.util.List; @Service("queryEducationService") public class EducationQueryServiceImpl implements EducationQueryService{ private final EducationMapper educationMapper; + private final AuthQueryService authQueryService; @Autowired - public EducationQueryServiceImpl(EducationMapper educationMapper) { + public EducationQueryServiceImpl(EducationMapper educationMapper, + AuthQueryService authQueryService) { this.educationMapper = educationMapper; + this.authQueryService = authQueryService; + } + + @Override + public List selectEducationList(String id) { + + String loginId = authQueryService.selectMemberIdByLoginId(id); + + List educationList = educationMapper.selectEducationInfo(loginId); + + return educationList; } } diff --git a/src/main/resources/stanl_2/final_backend/domain/career/query/repository/CareerMapper.xml b/src/main/resources/stanl_2/final_backend/domain/career/query/repository/CareerMapper.xml index 2acfdef9..247a90fa 100644 --- a/src/main/resources/stanl_2/final_backend/domain/career/query/repository/CareerMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/career/query/repository/CareerMapper.xml @@ -18,6 +18,6 @@ A.CAR_NOTE FROM TB_CAREER A JOIN TB_MEMBER B ON(A.MEM_ID = B.MEM_ID) - WHERE A.MEM_ID = #{ loginId } + WHERE A.MEM_ID = #{ memberId } \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.xml b/src/main/resources/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.xml index 14ab71e0..71f155d0 100644 --- a/src/main/resources/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.xml @@ -20,6 +20,6 @@ A.CER_NOTE FROM TB_CERTIFICATION A JOIN TB_MEMBER B ON(A.MEM_ID = B.MEM_ID) - WHERE A.MEM_ID = #{ loginId } + WHERE A.MEM_ID = #{ memberId } \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/education/query/repository/EducationMapper.xml b/src/main/resources/stanl_2/final_backend/domain/education/query/repository/EducationMapper.xml index c11fc15b..6e9b3812 100644 --- a/src/main/resources/stanl_2/final_backend/domain/education/query/repository/EducationMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/education/query/repository/EducationMapper.xml @@ -3,5 +3,24 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> - + + + + + + + + + \ No newline at end of file From b1cc394b820353362c3c0d61894ce83396afc438 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 15:04:58 +0900 Subject: [PATCH 139/563] =?UTF-8?q?feat:=20=ED=95=99=EB=A0=A5=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C(=EC=A0=91=EC=86=8D=EC=A4=91=EC=9D=B8=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90)(#68)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/EducationController.java | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/education/query/controller/EducationController.java b/src/main/java/stanl_2/final_backend/domain/education/query/controller/EducationController.java index cefda7ac..95528cf1 100644 --- a/src/main/java/stanl_2/final_backend/domain/education/query/controller/EducationController.java +++ b/src/main/java/stanl_2/final_backend/domain/education/query/controller/EducationController.java @@ -51,22 +51,22 @@ public ResponseEntity getEducation(@PathVariable Strin .build()); } -// @Operation(summary = "자격증/외국어 조회(접속중인 사용자)") -// @ApiResponses(value = { -// @ApiResponse(responseCode = "200", description = "성공", -// content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), -// @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", -// content = @Content(mediaType = "application/json")) -// }) -// @GetMapping("") -// public ResponseEntity getCertification(Principal principal){ -// -// List careerList = certificationQueryService.selectCertificationList(principal.getName()); -// -// return ResponseEntity.ok(CareerResponseMessage.builder() -// .httpStatus(200) -// .msg("성공") -// .result(careerList) -// .build()); -// } + @Operation(summary = "학력 조회(접속중인 사용자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("") + public ResponseEntity getCertification(Principal principal){ + + List educationList = educationQueryService.selectEducationList(principal.getName()); + + return ResponseEntity.ok(EducationResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(educationList) + .build()); + } } From b77614f2779c780076fecc3999ba1fa7c648b6f7 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 15:22:10 +0900 Subject: [PATCH 140/563] =?UTF-8?q?feat:=20=EA=B0=80=EC=A1=B1=EA=B5=AC?= =?UTF-8?q?=EC=84=B1=EC=9B=90=20=EC=A1=B0=ED=9A=8C(with=20=EC=82=AC?= =?UTF-8?q?=EB=B2=88)(#67)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/CareerController.java | 22 ++++---- .../query/service/CareerQueryService.java | 2 +- .../query/service/CareerQueryServiceImpl.java | 6 +-- .../controller/CertificationController.java | 22 ++++---- .../service/CertificationQueryService.java | 2 +- .../CertificationQueryServiceImpl.java | 6 +-- .../query/controller/EducationController.java | 22 ++++---- .../query/service/EducationQueryService.java | 2 +- .../service/EducationQueryServiceImpl.java | 6 +-- .../query/controller/FamilyController.java | 54 +++++++++++++++++++ .../family/query/repository/FamilyMapper.java | 5 ++ .../query/service/FamilyQueryService.java | 5 ++ .../query/service/FamilyQueryServiceImpl.java | 19 ++++++- .../family/query/repository/FamilyMapper.xml | 27 +++++++++- 14 files changed, 153 insertions(+), 47 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/career/query/controller/CareerController.java b/src/main/java/stanl_2/final_backend/domain/career/query/controller/CareerController.java index 03e0e015..61ffde26 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/query/controller/CareerController.java +++ b/src/main/java/stanl_2/final_backend/domain/career/query/controller/CareerController.java @@ -39,16 +39,16 @@ public CareerController(CareerQueryService careerQueryService) { @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("/other/{id}") - public ResponseEntity getCareerByOther(@PathVariable String id){ + @GetMapping("/other/{loginId}") + public ResponseEntity getCareerByOther(@PathVariable String loginId){ - List careerList = careerQueryService.selectCareerList(id); + List careerList = careerQueryService.selectCareerList(loginId); return ResponseEntity.ok(CareerResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(careerList) - .build()); + .httpStatus(200) + .msg("성공") + .result(careerList) + .build()); } @Operation(summary = "경력 조회(접속중인 사용자)") @@ -64,9 +64,9 @@ public ResponseEntity getCareer(Principal principal){ List careerList = careerQueryService.selectCareerList(principal.getName()); return ResponseEntity.ok(CareerResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(careerList) - .build()); + .httpStatus(200) + .msg("성공") + .result(careerList) + .build()); } } diff --git a/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryService.java b/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryService.java index 5b8412b3..f910dc85 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryService.java @@ -5,5 +5,5 @@ import java.util.List; public interface CareerQueryService { - List selectCareerList(String id); + List selectCareerList(String loginId); } diff --git a/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryServiceImpl.java index db424ec7..dbd067dc 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryServiceImpl.java @@ -26,11 +26,11 @@ public CareerQueryServiceImpl(CareerMapper careerMapper, @Override @Transactional - public List selectCareerList(String id) { + public List selectCareerList(String loginId) { - String loginId = authQueryService.selectMemberIdByLoginId(id); + String memberId = authQueryService.selectMemberIdByLoginId(loginId); - List careerList = careerMapper.selectCareerInfo(loginId); + List careerList = careerMapper.selectCareerInfo(memberId); return careerList; } diff --git a/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java b/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java index 99ed0ea8..d38c2b7b 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java @@ -37,16 +37,16 @@ public CertificationController(CertificationQueryService certificationQueryServi @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("/other/{id}") - public ResponseEntity getCertificationByOther(@PathVariable String id){ + @GetMapping("/other/{loginId}") + public ResponseEntity getCertificationByOther(@PathVariable String loginId){ - List certificationList = certificationQueryService.selectCertificationList(id); + List certificationList = certificationQueryService.selectCertificationList(loginId); return ResponseEntity.ok(CareerResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(certificationList) - .build()); + .httpStatus(200) + .msg("성공") + .result(certificationList) + .build()); } @Operation(summary = "자격증/외국어 조회(접속중인 사용자)") @@ -62,9 +62,9 @@ public ResponseEntity getCertification(Principal principa List careerList = certificationQueryService.selectCertificationList(principal.getName()); return ResponseEntity.ok(CareerResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(careerList) - .build()); + .httpStatus(200) + .msg("성공") + .result(careerList) + .build()); } } diff --git a/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryService.java b/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryService.java index 86256090..f138996f 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryService.java @@ -5,5 +5,5 @@ import java.util.List; public interface CertificationQueryService { - List selectCertificationList(String id); + List selectCertificationList(String loginId); } diff --git a/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryServiceImpl.java index b93e1dc8..67eea073 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryServiceImpl.java @@ -24,11 +24,11 @@ public CertificationQueryServiceImpl(CertificationMapper certificationMapper, @Override @Transactional - public List selectCertificationList(String id) { + public List selectCertificationList(String loginId) { - String loginId = authQueryService.selectMemberIdByLoginId(id); + String memberId = authQueryService.selectMemberIdByLoginId(loginId); - List certificationList = certificationMapper.selectCertificationInfo(loginId); + List certificationList = certificationMapper.selectCertificationInfo(memberId); return certificationList; } diff --git a/src/main/java/stanl_2/final_backend/domain/education/query/controller/EducationController.java b/src/main/java/stanl_2/final_backend/domain/education/query/controller/EducationController.java index 95528cf1..5eb7672d 100644 --- a/src/main/java/stanl_2/final_backend/domain/education/query/controller/EducationController.java +++ b/src/main/java/stanl_2/final_backend/domain/education/query/controller/EducationController.java @@ -39,16 +39,16 @@ public EducationController(EducationQueryService educationQueryService) { @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("/other/{id}") - public ResponseEntity getEducation(@PathVariable String id){ + @GetMapping("/other/{loginId}") + public ResponseEntity getEducation(@PathVariable String loginId){ - List educationList = educationQueryService.selectEducationList(id); + List educationList = educationQueryService.selectEducationList(loginId); return ResponseEntity.ok(EducationResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(educationList) - .build()); + .httpStatus(200) + .msg("성공") + .result(educationList) + .build()); } @Operation(summary = "학력 조회(접속중인 사용자)") @@ -64,9 +64,9 @@ public ResponseEntity getCertification(Principal princ List educationList = educationQueryService.selectEducationList(principal.getName()); return ResponseEntity.ok(EducationResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(educationList) - .build()); + .httpStatus(200) + .msg("성공") + .result(educationList) + .build()); } } diff --git a/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryService.java b/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryService.java index 5e5efd20..57041bbe 100644 --- a/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryService.java @@ -5,5 +5,5 @@ import java.util.List; public interface EducationQueryService { - List selectEducationList(String id); + List selectEducationList(String loginId); } diff --git a/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryServiceImpl.java index 6f1c105d..03f4b927 100644 --- a/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryServiceImpl.java @@ -23,11 +23,11 @@ public EducationQueryServiceImpl(EducationMapper educationMapper, } @Override - public List selectEducationList(String id) { + public List selectEducationList(String loginId) { - String loginId = authQueryService.selectMemberIdByLoginId(id); + String memberId = authQueryService.selectMemberIdByLoginId(loginId); - List educationList = educationMapper.selectEducationInfo(loginId); + List educationList = educationMapper.selectEducationInfo(memberId); return educationList; } diff --git a/src/main/java/stanl_2/final_backend/domain/family/query/controller/FamilyController.java b/src/main/java/stanl_2/final_backend/domain/family/query/controller/FamilyController.java index db211482..61fde819 100644 --- a/src/main/java/stanl_2/final_backend/domain/family/query/controller/FamilyController.java +++ b/src/main/java/stanl_2/final_backend/domain/family/query/controller/FamilyController.java @@ -1,10 +1,26 @@ package stanl_2.final_backend.domain.family.query.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; +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.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; +import stanl_2.final_backend.domain.education.common.response.EducationResponseMessage; +import stanl_2.final_backend.domain.education.query.dto.EducationDTO; +import stanl_2.final_backend.domain.family.common.response.FamilyResponseMessage; +import stanl_2.final_backend.domain.family.query.dto.FamilyDTO; import stanl_2.final_backend.domain.family.query.service.FamilyQueryService; +import java.security.Principal; +import java.util.List; + @RestController(value = "queryFamilyController") @RequestMapping("/api/v1/family") public class FamilyController { @@ -15,4 +31,42 @@ public class FamilyController { public FamilyController(FamilyQueryService familyQueryService) { this.familyQueryService = familyQueryService; } + + @Operation(summary = "가족 구성원 조회(with 사번)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/other/{loginId}") + public ResponseEntity getEducation(@PathVariable String loginId){ + + List familyList = familyQueryService.selectFamilyList(loginId); + + return ResponseEntity.ok(FamilyResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(familyList) + .build()); + } + +// @Operation(summary = "학력 조회(접속중인 사용자)") +// @ApiResponses(value = { +// @ApiResponse(responseCode = "200", description = "성공", +// content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), +// @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", +// content = @Content(mediaType = "application/json")) +// }) +// @GetMapping("") +// public ResponseEntity getCertification(Principal principal){ +// +// List educationList = educationQueryService.selectEducationList(principal.getName()); +// +// return ResponseEntity.ok(EducationResponseMessage.builder() +// .httpStatus(200) +// .msg("성공") +// .result(educationList) +// .build()); +// } } diff --git a/src/main/java/stanl_2/final_backend/domain/family/query/repository/FamilyMapper.java b/src/main/java/stanl_2/final_backend/domain/family/query/repository/FamilyMapper.java index ffdac80f..0522e373 100644 --- a/src/main/java/stanl_2/final_backend/domain/family/query/repository/FamilyMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/family/query/repository/FamilyMapper.java @@ -1,7 +1,12 @@ package stanl_2.final_backend.domain.family.query.repository; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import stanl_2.final_backend.domain.family.query.dto.FamilyDTO; + +import java.util.List; @Mapper public interface FamilyMapper { + List selectFamilyInfo(@Param("memberId") String memberId); } diff --git a/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryService.java b/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryService.java index 50712b64..9c6507e7 100644 --- a/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryService.java @@ -1,4 +1,9 @@ package stanl_2.final_backend.domain.family.query.service; +import stanl_2.final_backend.domain.family.query.dto.FamilyDTO; + +import java.util.List; + public interface FamilyQueryService { + List selectFamilyList(String loginId); } diff --git a/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryServiceImpl.java index 2f21b36e..a6c32016 100644 --- a/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryServiceImpl.java @@ -2,19 +2,36 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.family.query.dto.FamilyDTO; import stanl_2.final_backend.domain.family.query.repository.FamilyMapper; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.global.utils.AESUtils; +import java.util.List; + @Service("queryFamilyService") public class FamilyQueryServiceImpl implements FamilyQueryService { private final FamilyMapper familyMapper; private final AESUtils aesUtils; + private final AuthQueryService authQueryService; @Autowired public FamilyQueryServiceImpl(FamilyMapper familyMapper, - AESUtils aesUtils) { + AESUtils aesUtils, + AuthQueryService authQueryService) { this.familyMapper = familyMapper; this.aesUtils = aesUtils; + this.authQueryService = authQueryService; + } + + @Override + public List selectFamilyList(String loginId) { + + String memberId = authQueryService.selectMemberIdByLoginId(loginId); + + List familyList = familyMapper.selectFamilyInfo(memberId); + + return familyList; } } diff --git a/src/main/resources/stanl_2/final_backend/domain/family/query/repository/FamilyMapper.xml b/src/main/resources/stanl_2/final_backend/domain/family/query/repository/FamilyMapper.xml index 7bf05da2..3790ebdf 100644 --- a/src/main/resources/stanl_2/final_backend/domain/family/query/repository/FamilyMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/family/query/repository/FamilyMapper.xml @@ -3,6 +3,31 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> - + + + + + + + + + + + + \ No newline at end of file From cba313969428cfd06f7fb79dcac1c8a65d7d052f Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 15:26:41 +0900 Subject: [PATCH 141/563] =?UTF-8?q?feat:=20=EA=B0=80=EC=A1=B1=EA=B5=AC?= =?UTF-8?q?=EC=84=B1=EC=9B=90=20=EC=A1=B0=ED=9A=8C(=EC=A0=91=EC=86=8D?= =?UTF-8?q?=EC=A4=91=EC=9D=B8=20=EC=82=AC=EC=9A=A9=EC=9E=90)(#67)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/FamilyController.java | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/family/query/controller/FamilyController.java b/src/main/java/stanl_2/final_backend/domain/family/query/controller/FamilyController.java index 61fde819..32464b61 100644 --- a/src/main/java/stanl_2/final_backend/domain/family/query/controller/FamilyController.java +++ b/src/main/java/stanl_2/final_backend/domain/family/query/controller/FamilyController.java @@ -45,28 +45,28 @@ public ResponseEntity getEducation(@PathVariable String l List familyList = familyQueryService.selectFamilyList(loginId); return ResponseEntity.ok(FamilyResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(familyList) - .build()); + .httpStatus(200) + .msg("성공") + .result(familyList) + .build()); } -// @Operation(summary = "학력 조회(접속중인 사용자)") -// @ApiResponses(value = { -// @ApiResponse(responseCode = "200", description = "성공", -// content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), -// @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", -// content = @Content(mediaType = "application/json")) -// }) -// @GetMapping("") -// public ResponseEntity getCertification(Principal principal){ -// -// List educationList = educationQueryService.selectEducationList(principal.getName()); -// -// return ResponseEntity.ok(EducationResponseMessage.builder() -// .httpStatus(200) -// .msg("성공") -// .result(educationList) -// .build()); -// } + @Operation(summary = "가족 구성원 조회(접속중인 사용자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("") + public ResponseEntity getCertification(Principal principal){ + + List familyList = familyQueryService.selectFamilyList(principal.getName()); + + return ResponseEntity.ok(FamilyResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(familyList) + .build()); + } } From 39e8b4f07b120eef551106299892de09f3507625 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Mon, 18 Nov 2024 16:09:48 +0900 Subject: [PATCH 142/563] =?UTF-8?q?feat:=20=EB=B0=9C=EC=A3=BC=EC=84=9C=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84=20(#5?= =?UTF-8?q?9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PurchaseOrderCommandServiceImpl.java | 8 ++-- .../controller/PurchaseOrderController.java | 47 +++++++++++++++++++ .../query/dto/PurchaseOrderSelectIdDTO.java | 28 +++++++++++ .../query/repository/PurchaseOrderMapper.java | 4 ++ .../service/PurchaseOrderQueryService.java | 3 ++ .../PurchaseOrderQueryServiceImpl.java | 46 ++++++++++++++++++ .../query/repository/PurchaseOrderMapper.xml | 41 ++++++++++++++++ 7 files changed, 173 insertions(+), 4 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectIdDTO.java create mode 100644 src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java index 7f0c5ff4..f0085a99 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java @@ -53,7 +53,7 @@ private String getCurrentTime() { @Transactional public void registerPurchaseOrder(PurchaseOrderRegistDTO purchaseOrderRegistDTO) { - String memberId = authQueryService.selectMemberLoginId(purchaseOrderRegistDTO.getMemberId()); + String memberId = authQueryService.selectMemberIdByLoginId(purchaseOrderRegistDTO.getMemberId()); // 수주서가 존재하는지 확인 Order order = orderRepository.findByOrderIdAndMemberId(purchaseOrderRegistDTO.getOrderId(), memberId); @@ -76,7 +76,7 @@ public void registerPurchaseOrder(PurchaseOrderRegistDTO purchaseOrderRegistDTO) @Transactional public PurchaseOrderModifyDTO modifyPurchaseOrder(PurchaseOrderModifyDTO purchaseOrderModifyDTO) { - String memberId = authQueryService.selectMemberLoginId(purchaseOrderModifyDTO.getMemberId()); + String memberId = authQueryService.selectMemberIdByLoginId(purchaseOrderModifyDTO.getMemberId()); // 회원인지 확인 및 발주서 조회 PurchaseOrder purchaseOrder = (PurchaseOrder) purchaseOrderRepository.findByPurchaseOrderIdAndMemberId( @@ -113,7 +113,7 @@ public PurchaseOrderModifyDTO modifyPurchaseOrder(PurchaseOrderModifyDTO purchas @Transactional public void deletePurchaseOrder(String id, String loginId) { - String memberId = authQueryService.selectMemberLoginId(loginId); + String memberId = authQueryService.selectMemberIdByLoginId(loginId); // 발주서가 해당 회원의 것인지 확인 PurchaseOrder purchaseOrder = (PurchaseOrder) purchaseOrderRepository.findByPurchaseOrderIdAndMemberId(id, memberId) @@ -129,7 +129,7 @@ public void deletePurchaseOrder(String id, String loginId) { @Transactional public void modifyPurchaseOrderStatus(PurchaseOrderStatusModifyDTO purchaseOrderStatusModifyDTO) { - String adminId = authQueryService.selectMemberLoginId(purchaseOrderStatusModifyDTO.getAdminId()); + String adminId = authQueryService.selectMemberIdByLoginId(purchaseOrderStatusModifyDTO.getAdminId()); if ("[ROLE_REPRESENTATIVE]".equals(purchaseOrderStatusModifyDTO.getRole())) { PurchaseOrder purchaseOrder = purchaseOrderRepository.findByPurchaseOrderId(purchaseOrderStatusModifyDTO.getPurchaseOrderId()) diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java index 195d8f49..f4d45f14 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java @@ -1,9 +1,56 @@ package stanl_2.final_backend.domain.purchase_order.query.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.purchase_order.common.response.PurchaseOrderResponseMessage; +import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectIdDTO; +import stanl_2.final_backend.domain.purchase_order.query.service.PurchaseOrderQueryService; + +import java.security.Principal; @RestController("PurchaseOrderQueryController") @RequestMapping("/api/v1/purchase-order") public class PurchaseOrderController { + + private final PurchaseOrderQueryService purchaseOrderQueryService; + + @Autowired + public PurchaseOrderController(PurchaseOrderQueryService purchaseOrderQueryService) { + this.purchaseOrderQueryService = purchaseOrderQueryService; + } + + @Operation(summary = "발주서 상세조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "발주서 상세조회 성공", + content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) + }) + @GetMapping("{id}") + public ResponseEntity getDetailPurchaseOrder(@PathVariable("id") String id, + Authentication authentication) { + + PurchaseOrderSelectIdDTO purchaseOrderSelectIdDTO = new PurchaseOrderSelectIdDTO(); + purchaseOrderSelectIdDTO.setPurchaseOrderId(id); + purchaseOrderSelectIdDTO.setMemberId(authentication.getName()); + purchaseOrderSelectIdDTO.setRoles(authentication.getAuthorities()); + + System.out.println("사용자권한!!: " + authentication.getAuthorities()); + + PurchaseOrderSelectIdDTO responsePurchaseOrder = purchaseOrderQueryService.selectDetailPurchaseOrder(purchaseOrderSelectIdDTO); + + return ResponseEntity.ok(PurchaseOrderResponseMessage.builder() + .httpStatus(200) + .msg("발주서 상세 조회 성공") + .result(responsePurchaseOrder) + .build()); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectIdDTO.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectIdDTO.java new file mode 100644 index 00000000..a07a1bf6 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectIdDTO.java @@ -0,0 +1,28 @@ +package stanl_2.final_backend.domain.purchase_order.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class PurchaseOrderSelectIdDTO { + private String PurchaseOrderId; + private String title; + private String content; + private Boolean active; + private String status; + private String createdAt; + private String updatedAt; + private String deletedAt; + private String OrderId; + private String adminId; + private String memberId; + private Collection roles; +} diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java index a6d2f934..517b2351 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java @@ -1,7 +1,11 @@ package stanl_2.final_backend.domain.purchase_order.query.repository; import org.apache.ibatis.annotations.Mapper; +import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectIdDTO; @Mapper public interface PurchaseOrderMapper { + PurchaseOrderSelectIdDTO findPurchaseOrderByIdAndMemberId(String purchaseOrderId, String memberId); + + PurchaseOrderSelectIdDTO findPurchaseOrderById(String purchaseOrderId); } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java index 421f6e8d..0dd76188 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java @@ -1,4 +1,7 @@ package stanl_2.final_backend.domain.purchase_order.query.service; +import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectIdDTO; + public interface PurchaseOrderQueryService { + PurchaseOrderSelectIdDTO selectDetailPurchaseOrder(PurchaseOrderSelectIdDTO purchaseOrderSelectIdDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java index a70e780d..97068e6b 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java @@ -1,7 +1,53 @@ package stanl_2.final_backend.domain.purchase_order.query.service; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.purchase_order.common.exception.PurchaseOrderCommonException; +import stanl_2.final_backend.domain.purchase_order.common.exception.PurchaseOrderErrorCode; +import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectIdDTO; +import stanl_2.final_backend.domain.purchase_order.query.repository.PurchaseOrderMapper; +import stanl_2.final_backend.global.exception.GlobalCommonException; +import stanl_2.final_backend.global.exception.GlobalErrorCode; @Service +@Transactional(readOnly = true) public class PurchaseOrderQueryServiceImpl implements PurchaseOrderQueryService { + + private final PurchaseOrderMapper purchaseOrderMapper; + private final AuthQueryService authQueryService; + + @Autowired + public PurchaseOrderQueryServiceImpl(PurchaseOrderMapper purchaseOrderMapper, AuthQueryService authQueryService) { + this.purchaseOrderMapper = purchaseOrderMapper; + this.authQueryService = authQueryService; + } + + @Override + public PurchaseOrderSelectIdDTO selectDetailPurchaseOrder(PurchaseOrderSelectIdDTO purchaseOrderSelectIdDTO) { + if(purchaseOrderSelectIdDTO.getRoles().stream() + .anyMatch(role -> "ROLE_EMPLOYEE".equals(role.getAuthority()))) { + + String memberId = authQueryService.selectMemberIdByLoginId(purchaseOrderSelectIdDTO.getMemberId()); + + PurchaseOrderSelectIdDTO purchaseOrder = purchaseOrderMapper.findPurchaseOrderByIdAndMemberId(purchaseOrderSelectIdDTO.getPurchaseOrderId(), memberId); + + if (purchaseOrder == null) { + throw new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND); + } + return purchaseOrder; + } else if (purchaseOrderSelectIdDTO.getRoles().stream() + .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()) || "ROLE_REPRESENTATIVE".equals(role.getAuthority()))) { + + PurchaseOrderSelectIdDTO purchaseOrder = purchaseOrderMapper.findPurchaseOrderById(purchaseOrderSelectIdDTO.getPurchaseOrderId()); + + if (purchaseOrder == null) { + throw new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND); + } + return purchaseOrder; + } else { + throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + } + } } \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml b/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml new file mode 100644 index 00000000..5f09483b --- /dev/null +++ b/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 50659ed242ffcd894b388673b35139688e2326bc Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 16:52:35 +0900 Subject: [PATCH 143/563] =?UTF-8?q?feat:=20=EA=B3=A0=EA=B0=9D=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EB=93=B1=EB=A1=9D(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/dto/SampleModifyDTO.java | 1 - .../application/dto/SampleRegistDTO.java | 1 - .../domain/A_sample/query/dto/SampleDTO.java | 7 +- .../controller/CareerController.java | 7 +- .../application/dto/CareerRegistDTO.java | 2 - .../domain/aggregate/entity/Career.java | 2 +- .../service/CareerCommandServiceImpl.java | 3 + .../domain/career/query/dto/CareerDTO.java | 1 - .../controller/CertificationController.java | 15 ++-- .../dto/CertificationRegisterDTO.java | 7 +- .../aggregate/entity/Certification.java | 5 +- .../query/dto/CertificationDTO.java | 1 - .../controller/CustomerController.java | 57 +++++++++++++ .../application/dto/CustomerRegistDTO.java | 20 +++++ .../service/CustomerCommandService.java | 7 ++ .../domain/aggregate/entity/Customer.java | 79 +++++++++++++++++++ .../domain/repository/CustomerRepository.java | 9 +++ .../service/CustomerCommandServiceImpl.java | 36 +++++++++ .../exception/CustomerCommonException.java | 16 ++++ .../common/exception/CustomerErrorCode.java | 52 ++++++++++++ .../exception/CustomerExceptionResponse.java | 22 ++++++ .../response/CustomerResponseMessage.java | 14 ++++ .../query/config/MybatisConfiguration.java | 9 +++ .../query/controller/CustomerController.java | 18 +++++ .../customer/query/dto/CustomerDTO.java | 20 +++++ .../query/repository/CustomerMapper.java | 7 ++ .../query/service/CustomerQueryService.java | 4 + .../service/CustomerQueryServiceImpl.java | 20 +++++ .../application/dto/EducationModifyDTO.java | 1 - .../application/dto/EducationRegistDTO.java | 1 - .../education/query/dto/EducationDTO.java | 1 - .../service/EducationQueryServiceImpl.java | 2 + .../application/dto/FamilyModifyDTO.java | 1 - .../application/dto/FamilyRegistDTO.java | 1 - .../domain/family/query/dto/FamilyDTO.java | 1 - .../query/service/FamilyQueryServiceImpl.java | 2 + src/main/resources/sql/ddl.sql | 26 +++--- 37 files changed, 436 insertions(+), 42 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/customer/command/application/dto/CustomerRegistDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/customer/command/application/service/CustomerCommandService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/customer/command/domain/aggregate/entity/Customer.java create mode 100644 src/main/java/stanl_2/final_backend/domain/customer/command/domain/repository/CustomerRepository.java create mode 100644 src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java create mode 100644 src/main/java/stanl_2/final_backend/domain/customer/common/exception/CustomerCommonException.java create mode 100644 src/main/java/stanl_2/final_backend/domain/customer/common/exception/CustomerErrorCode.java create mode 100644 src/main/java/stanl_2/final_backend/domain/customer/common/exception/CustomerExceptionResponse.java create mode 100644 src/main/java/stanl_2/final_backend/domain/customer/common/response/CustomerResponseMessage.java create mode 100644 src/main/java/stanl_2/final_backend/domain/customer/query/config/MybatisConfiguration.java create mode 100644 src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/customer/query/dto/CustomerDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java create mode 100644 src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/SampleModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/SampleModifyDTO.java index f6d16e27..57e4d857 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/SampleModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/SampleModifyDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Setter @Getter -@ToString public class SampleModifyDTO { private String id; private String name; diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/SampleRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/SampleRegistDTO.java index ad5a9ea5..593fe3ed 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/SampleRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/SampleRegistDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Setter @Getter -@ToString public class SampleRegistDTO { private String id; private String name; diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/query/dto/SampleDTO.java b/src/main/java/stanl_2/final_backend/domain/A_sample/query/dto/SampleDTO.java index a3732d58..ae0985c1 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/query/dto/SampleDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/query/dto/SampleDTO.java @@ -1,16 +1,13 @@ package stanl_2.final_backend.domain.A_sample.query.dto; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; +import lombok.*; import java.sql.Timestamp; @NoArgsConstructor @AllArgsConstructor @Getter -@ToString +@Setter public class SampleDTO { private String id; private String name; diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java b/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java index 95b0bca7..10d061a9 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java +++ b/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java @@ -15,6 +15,8 @@ import stanl_2.final_backend.domain.career.common.response.CareerResponseMessage; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import java.security.Principal; + @Slf4j @RestController("commandCareerController") @RequestMapping("/api/v1/career") @@ -36,9 +38,10 @@ public CareerController(CareerCommandService careerCommandService, content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postCareer(@RequestBody CareerRegistDTO careerRegistDTO){ + public ResponseEntity postCareer(@RequestBody CareerRegistDTO careerRegistDTO, + Principal principal){ - careerRegistDTO.setMemberId(authQueryService.selectMemberIdByLoginId(careerRegistDTO.getMemberLoginId())); + careerRegistDTO.setMemberId(authQueryService.selectMemberIdByLoginId(principal.getName())); careerCommandService.registCareer(careerRegistDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerRegistDTO.java index ff37e248..966cd7ab 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerRegistDTO.java @@ -6,9 +6,7 @@ @NoArgsConstructor @Setter @Getter -@ToString public class CareerRegistDTO { - private String memberLoginId; private String emplDate; private String resignDate; private String name; diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/domain/aggregate/entity/Career.java b/src/main/java/stanl_2/final_backend/domain/career/command/domain/aggregate/entity/Career.java index a95b5737..1a1b32c0 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/command/domain/aggregate/entity/Career.java +++ b/src/main/java/stanl_2/final_backend/domain/career/command/domain/aggregate/entity/Career.java @@ -44,7 +44,7 @@ public class Career { private String createdAt; @Column(name = "MEM_ID", nullable = false) - private String memId; + private String memberId; @PrePersist diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/domain/service/CareerCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/career/command/domain/service/CareerCommandServiceImpl.java index 57a35447..d37e767b 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/command/domain/service/CareerCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/career/command/domain/service/CareerCommandServiceImpl.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.career.command.domain.service; +import lombok.extern.slf4j.Slf4j; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -9,6 +10,7 @@ import stanl_2.final_backend.domain.career.command.domain.aggregate.entity.Career; import stanl_2.final_backend.domain.career.command.domain.repository.CareerRepository; +@Slf4j @Service("commandCareerService") public class CareerCommandServiceImpl implements CareerCommandService { @@ -25,6 +27,7 @@ public CareerCommandServiceImpl(CareerRepository careerRepository, @Override @Transactional public void registCareer(CareerRegistDTO careerRegistDTO) { + Career career = modelMapper.map(careerRegistDTO, Career.class); careerRepository.save(career); diff --git a/src/main/java/stanl_2/final_backend/domain/career/query/dto/CareerDTO.java b/src/main/java/stanl_2/final_backend/domain/career/query/dto/CareerDTO.java index 69b6ee69..2561953f 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/query/dto/CareerDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/career/query/dto/CareerDTO.java @@ -6,7 +6,6 @@ @AllArgsConstructor @Setter @Getter -@ToString public class CareerDTO { private String emplDate; private String resignDate; diff --git a/src/main/java/stanl_2/final_backend/domain/certification/command/application/controller/CertificationController.java b/src/main/java/stanl_2/final_backend/domain/certification/command/application/controller/CertificationController.java index c782f232..2d2a8ce1 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/command/application/controller/CertificationController.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/command/application/controller/CertificationController.java @@ -17,6 +17,8 @@ import stanl_2.final_backend.domain.certification.command.application.service.CertificationCommandService; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import java.security.Principal; + @RestController("commandCertificationController") @RequestMapping("/api/v1/certification") public class CertificationController { @@ -37,16 +39,17 @@ public CertificationController(CertificationCommandService certificationCommandS content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postCertification(@RequestBody CertificationRegisterDTO certificationRegisterDTO){ + public ResponseEntity postCertification(@RequestBody CertificationRegisterDTO certificationRegisterDTO, + Principal principal){ - certificationRegisterDTO.setMemberId(authQueryService.selectMemberIdByLoginId(certificationRegisterDTO.getMemberLoginId())); + certificationRegisterDTO.setMemberId(authQueryService.selectMemberIdByLoginId(principal.getName())); certificationCommandService.registCertification(certificationRegisterDTO); return ResponseEntity.ok(CareerResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(null) - .build()); + .httpStatus(200) + .msg("성공") + .result(null) + .build()); } } diff --git a/src/main/java/stanl_2/final_backend/domain/certification/command/application/dto/CertificationRegisterDTO.java b/src/main/java/stanl_2/final_backend/domain/certification/command/application/dto/CertificationRegisterDTO.java index f8cbbe24..4735b7c0 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/command/application/dto/CertificationRegisterDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/command/application/dto/CertificationRegisterDTO.java @@ -6,12 +6,11 @@ @NoArgsConstructor @Setter @Getter -@ToString public class CertificationRegisterDTO { - private String memberLoginId; - private String name; - private String agency; private String acquisitionDate; + private String agency; + private String name; + private String score; private String note; private String memberId; } diff --git a/src/main/java/stanl_2/final_backend/domain/certification/command/domain/aggregate/entity/Certification.java b/src/main/java/stanl_2/final_backend/domain/certification/command/domain/aggregate/entity/Certification.java index 162b8e0c..eac2c4c0 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/command/domain/aggregate/entity/Certification.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/command/domain/aggregate/entity/Certification.java @@ -37,6 +37,9 @@ public class Certification { @Column(name = "CER_DATE", nullable = false) private String acquisitionDate; + @Column(name = "CER_SCO", nullable = false) + private String score; + @Column(name = "CER_NOTE") private String note; @@ -44,7 +47,7 @@ public class Certification { private String createdAt; @Column(name = "MEM_ID", nullable = false) - private String memId; + private String memberId; // Insert 되기 전에 실행 @PrePersist diff --git a/src/main/java/stanl_2/final_backend/domain/certification/query/dto/CertificationDTO.java b/src/main/java/stanl_2/final_backend/domain/certification/query/dto/CertificationDTO.java index 349b2493..791c4cee 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/query/dto/CertificationDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/query/dto/CertificationDTO.java @@ -6,7 +6,6 @@ @AllArgsConstructor @Setter @Getter -@ToString public class CertificationDTO { private String name; private String agency; diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java b/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java new file mode 100644 index 00000000..3373b9cf --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java @@ -0,0 +1,57 @@ +package stanl_2.final_backend.domain.customer.command.application.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +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.RestController; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; +import stanl_2.final_backend.domain.customer.command.application.dto.CustomerRegistDTO; +import stanl_2.final_backend.domain.customer.command.application.service.CustomerCommandService; +import stanl_2.final_backend.domain.customer.common.response.CustomerResponseMessage; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; + +import java.security.Principal; + +@RestController("commandCustomerController") +@RequestMapping("/api/v1/customer") +public class CustomerController { + + private final CustomerCommandService customerCommandService; + private final AuthQueryService authQueryService; + + @Autowired + public CustomerController(CustomerCommandService customerCommandService, + AuthQueryService authQueryService) { + this.customerCommandService = customerCommandService; + this.authQueryService = authQueryService; + } + + @Operation(summary = "고객정보 등록") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) + }) + @PostMapping("") + public ResponseEntity postCustomer(@RequestBody CustomerRegistDTO customerRegistDTO, + Principal principal){ + + String memberId = authQueryService.selectMemberIdByLoginId(principal.getName()); + + customerRegistDTO.setMemberId(memberId); + + customerCommandService.registerCustomerInfo(customerRegistDTO); + + return ResponseEntity.ok(CustomerResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/application/dto/CustomerRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/customer/command/application/dto/CustomerRegistDTO.java new file mode 100644 index 00000000..1bc6b4a5 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/application/dto/CustomerRegistDTO.java @@ -0,0 +1,20 @@ +package stanl_2.final_backend.domain.customer.command.application.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class CustomerRegistDTO { + private String name; + private Integer age; + private String sex; + private String phone; + private String emergePhone; + private String email; + private String memberId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/application/service/CustomerCommandService.java b/src/main/java/stanl_2/final_backend/domain/customer/command/application/service/CustomerCommandService.java new file mode 100644 index 00000000..5c0fa88e --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/application/service/CustomerCommandService.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.customer.command.application.service; + +import stanl_2.final_backend.domain.customer.command.application.dto.CustomerRegistDTO; + +public interface CustomerCommandService { + void registerCustomerInfo(CustomerRegistDTO customerRegistDTO); +} diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/aggregate/entity/Customer.java b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/aggregate/entity/Customer.java new file mode 100644 index 00000000..cc32dbba --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/aggregate/entity/Customer.java @@ -0,0 +1,79 @@ +package stanl_2.final_backend.domain.customer.command.domain.aggregate.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "TB_CUSTOMER_INFO") +public class Customer { + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "CUS") + ) + @Column(name = "CUST_ID", nullable = false) + private String customerId; + + @Column(name = "CUST_NAME", nullable = false) + private String name; + + @Column(name = "CUST_AGE", nullable = false) + private Integer age; + + @Column(name = "CUST_SEX", nullable = false) + private String sex; + + @Column(name = "CUST_PHO", nullable = false) + private String phone; + + @Column(name = "CUST_EMER_PHO", nullable = false) + private String emergePhone; + + @Column(name = "CUST_EMA", nullable = false) + private String email; + + @Column(name = "ACTIVE", nullable = false) + private Boolean active = true; + + @Column(name = "CREATED_AT", nullable = false, updatable = false) + private String createdAt; + + @Column(name = "UPDATED_AT", nullable = false) + private String updatedAt; + + @Column(name = "DELETED_AT") + private String deletedAt; + + @Column(name = "MEM_ID", nullable = false) + private String memberId; + + @PrePersist + private void prePersist() { + this.createdAt = getCurrentTime(); + this.updatedAt = this.createdAt; + } + + @PreUpdate + private void preUpdate() { + this.updatedAt = getCurrentTime(); + } + + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/repository/CustomerRepository.java b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/repository/CustomerRepository.java new file mode 100644 index 00000000..78a65ae2 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/repository/CustomerRepository.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.customer.command.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import stanl_2.final_backend.domain.customer.command.domain.aggregate.entity.Customer; + +@Repository +public interface CustomerRepository extends JpaRepository { +} diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java new file mode 100644 index 00000000..e2057de5 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java @@ -0,0 +1,36 @@ +package stanl_2.final_backend.domain.customer.command.domain.service; + +import lombok.extern.slf4j.Slf4j; +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.customer.command.application.dto.CustomerRegistDTO; +import stanl_2.final_backend.domain.customer.command.application.service.CustomerCommandService; +import stanl_2.final_backend.domain.customer.command.domain.aggregate.entity.Customer; +import stanl_2.final_backend.domain.customer.command.domain.repository.CustomerRepository; + +@Slf4j +@Service("commandCustomerService") +public class CustomerCommandServiceImpl implements CustomerCommandService { + + private final CustomerRepository customerRepository; + private final ModelMapper modelMapper; + + @Autowired + public CustomerCommandServiceImpl(CustomerRepository customerRepository, + ModelMapper modelMapper) { + this.customerRepository = customerRepository; + this.modelMapper = modelMapper; + } + + @Override + @Transactional + public void registerCustomerInfo(CustomerRegistDTO customerRegistDTO) { + + Customer customer = modelMapper.map(customerRegistDTO, Customer.class); + + log.info(customer.getMemberId()); + customerRepository.save(customer); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/customer/common/exception/CustomerCommonException.java b/src/main/java/stanl_2/final_backend/domain/customer/common/exception/CustomerCommonException.java new file mode 100644 index 00000000..251e7702 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/customer/common/exception/CustomerCommonException.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.customer.common.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class CustomerCommonException extends RuntimeException { + private final CustomerErrorCode sampleErrorCode; + + // 에러 발생시 ErroCode 별 메시지 + @Override + public String getMessage() { + return this.sampleErrorCode.getMsg(); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/customer/common/exception/CustomerErrorCode.java b/src/main/java/stanl_2/final_backend/domain/customer/common/exception/CustomerErrorCode.java new file mode 100644 index 00000000..ea85f92c --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/customer/common/exception/CustomerErrorCode.java @@ -0,0 +1,52 @@ +package stanl_2.final_backend.domain.customer.common.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum CustomerErrorCode { + + /** + * 400(Bad Request) + * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. + */ + + + + /** + * 401(Unauthorized) + * 비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만, + * 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다. + * 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다. + */ + + + /** + * 403(Forbidden) + * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. + * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다. + */ + + + + /** + * 404(Not Found) + * 서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다. + * 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있습니다. + * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. + * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. + */ + SAMPLE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "sample 데이터를 찾지 못했습니다"), + CENTER_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "center 데이터를 찾지 못했습니다."), + /** + * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. + */ + INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); + + + private final Integer code; + private final HttpStatus httpStatus; + private final String msg; +} diff --git a/src/main/java/stanl_2/final_backend/domain/customer/common/exception/CustomerExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/customer/common/exception/CustomerExceptionResponse.java new file mode 100644 index 00000000..383f18f9 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/customer/common/exception/CustomerExceptionResponse.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.customer.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class CustomerExceptionResponse { + private final Integer code; + private final String msg; + private final HttpStatus httpStatus; + + public CustomerExceptionResponse(CustomerErrorCode sampleErrorCode) { + this.code = sampleErrorCode.getCode(); + this.msg = sampleErrorCode.getMsg(); + this.httpStatus = sampleErrorCode.getHttpStatus(); + } + + public static CustomerExceptionResponse of(CustomerErrorCode sampleErrorCode) { + return new CustomerExceptionResponse(sampleErrorCode); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/customer/common/response/CustomerResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/customer/common/response/CustomerResponseMessage.java new file mode 100644 index 00000000..acefa132 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/customer/common/response/CustomerResponseMessage.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.customer.common.response; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class CustomerResponseMessage { + private int httpStatus; + private String msg; + private Object result; +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/config/MybatisConfiguration.java b/src/main/java/stanl_2/final_backend/domain/customer/query/config/MybatisConfiguration.java new file mode 100644 index 00000000..de0804f2 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/config/MybatisConfiguration.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.customer.query.config; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Configuration; + +@Configuration("customerMybatisConfiguration") +@MapperScan(basePackages = "stanl_2.final_backend.domain.customer.query.repository") +public class MybatisConfiguration { +} diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java b/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java new file mode 100644 index 00000000..3ac382e8 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java @@ -0,0 +1,18 @@ +package stanl_2.final_backend.domain.customer.query.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.customer.query.service.CustomerQueryService; + +@RestController(value = "queryCustomerController") +@RequestMapping("/api/v1/customer") +public class CustomerController { + + private final CustomerQueryService customerQueryService; + + @Autowired + public CustomerController(CustomerQueryService customerQueryService) { + this.customerQueryService = customerQueryService; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/dto/CustomerDTO.java b/src/main/java/stanl_2/final_backend/domain/customer/query/dto/CustomerDTO.java new file mode 100644 index 00000000..435b91cd --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/dto/CustomerDTO.java @@ -0,0 +1,20 @@ +package stanl_2.final_backend.domain.customer.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +public class CustomerDTO { + private String name; + private Integer age; + private String sex; + private String phone; + private String emergePhone; + private String email; + private String memberId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java b/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java new file mode 100644 index 00000000..7f44b3b4 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.customer.query.repository; + +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface CustomerMapper { +} diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java new file mode 100644 index 00000000..1cba4bf9 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java @@ -0,0 +1,4 @@ +package stanl_2.final_backend.domain.customer.query.service; + +public interface CustomerQueryService { +} diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java new file mode 100644 index 00000000..9d3f05c3 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java @@ -0,0 +1,20 @@ +package stanl_2.final_backend.domain.customer.query.service; + +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.customer.query.repository.CustomerMapper; + +@Service("queryCustomerService") +public class CustomerQueryServiceImpl implements CustomerQueryService{ + + private final CustomerMapper customerMapper; + private final ModelMapper modelMapper; + + @Autowired + public CustomerQueryServiceImpl(CustomerMapper customerMapper, + ModelMapper modelMapper) { + this.customerMapper = customerMapper; + this.modelMapper = modelMapper; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/education/command/application/dto/EducationModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/education/command/application/dto/EducationModifyDTO.java index 3fb24d85..da10b246 100644 --- a/src/main/java/stanl_2/final_backend/domain/education/command/application/dto/EducationModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/education/command/application/dto/EducationModifyDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Setter @Getter -@ToString public class EducationModifyDTO { private String graduationDate; private String note; diff --git a/src/main/java/stanl_2/final_backend/domain/education/command/application/dto/EducationRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/education/command/application/dto/EducationRegistDTO.java index 68290deb..4818a9e9 100644 --- a/src/main/java/stanl_2/final_backend/domain/education/command/application/dto/EducationRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/education/command/application/dto/EducationRegistDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Setter @Getter -@ToString public class EducationRegistDTO { private String entranceDate; private String graduationDate; diff --git a/src/main/java/stanl_2/final_backend/domain/education/query/dto/EducationDTO.java b/src/main/java/stanl_2/final_backend/domain/education/query/dto/EducationDTO.java index f438a36b..bc6aa52e 100644 --- a/src/main/java/stanl_2/final_backend/domain/education/query/dto/EducationDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/education/query/dto/EducationDTO.java @@ -6,7 +6,6 @@ @AllArgsConstructor @Getter @Setter -@ToString public class EducationDTO { private String entranceDate; private String graduationDate; diff --git a/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryServiceImpl.java index 03f4b927..d9133650 100644 --- a/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryServiceImpl.java @@ -2,6 +2,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.certification.query.dto.CertificationDTO; import stanl_2.final_backend.domain.education.query.dto.EducationDTO; import stanl_2.final_backend.domain.education.query.repository.EducationMapper; @@ -23,6 +24,7 @@ public EducationQueryServiceImpl(EducationMapper educationMapper, } @Override + @Transactional public List selectEducationList(String loginId) { String memberId = authQueryService.selectMemberIdByLoginId(loginId); diff --git a/src/main/java/stanl_2/final_backend/domain/family/command/application/dto/FamilyModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/family/command/application/dto/FamilyModifyDTO.java index 6ded2089..12d20579 100644 --- a/src/main/java/stanl_2/final_backend/domain/family/command/application/dto/FamilyModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/family/command/application/dto/FamilyModifyDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Setter @Getter -@ToString public class FamilyModifyDTO { private String relation; private String phone; diff --git a/src/main/java/stanl_2/final_backend/domain/family/command/application/dto/FamilyRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/family/command/application/dto/FamilyRegistDTO.java index 1aac1e86..0e3a340e 100644 --- a/src/main/java/stanl_2/final_backend/domain/family/command/application/dto/FamilyRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/family/command/application/dto/FamilyRegistDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Setter @Getter -@ToString public class FamilyRegistDTO { private String name; private String relation; diff --git a/src/main/java/stanl_2/final_backend/domain/family/query/dto/FamilyDTO.java b/src/main/java/stanl_2/final_backend/domain/family/query/dto/FamilyDTO.java index b8c9b2e9..3f86794c 100644 --- a/src/main/java/stanl_2/final_backend/domain/family/query/dto/FamilyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/family/query/dto/FamilyDTO.java @@ -6,7 +6,6 @@ @AllArgsConstructor @Getter @Setter -@ToString public class FamilyDTO { private String name; private String relation; diff --git a/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryServiceImpl.java index a6c32016..4a1c04e3 100644 --- a/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryServiceImpl.java @@ -2,6 +2,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.family.query.dto.FamilyDTO; import stanl_2.final_backend.domain.family.query.repository.FamilyMapper; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; @@ -26,6 +27,7 @@ public FamilyQueryServiceImpl(FamilyMapper familyMapper, } @Override + @Transactional public List selectFamilyList(String loginId) { String memberId = authQueryService.selectMemberIdByLoginId(loginId); diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index 62db917e..2213cfed 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -103,6 +103,9 @@ CREATE TABLE tb_customer_info CUST_EMA VARCHAR(255) NOT NULL, ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, MEM_ID VARCHAR(255) NOT NULL, + CREATED_AT CHAR(19) NOT NULL, + UPDATED_AT CHAR(19) NOT NULL, + DELETED_AT CHAR(19) NULL, PRIMARY KEY (CUST_ID), FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) ); @@ -509,27 +512,28 @@ VALUES ('MEM_ROL_000000001', '영업 사원', 'MEM_000000001'), ('MEM_ROL_000000009', '영업 담당자', 'MEM_000000010'), ('MEM_ROL_000000010', '영업 담당자', 'MEM_000000006'); -INSERT INTO tb_customer_info (CUST_ID, CUST_NAME, CUST_AGE, CUST_SEX, CUST_PHO, CUST_EMER_PHO, CUST_EMA, ACTIVE, MEM_ID) +INSERT INTO tb_customer_info (CUST_ID, CUST_NAME, CUST_AGE, CUST_SEX, CUST_PHO, CUST_EMER_PHO, CUST_EMA, ACTIVE, MEM_ID, CREATED_AT, UPDATED_AT, DELETED_AT) VALUES ('CUS_000000001', '홍길동', 45, 'MALE', '010-1111-2222', '010-2222-3333', 'gildong@example.com', TRUE, - 'MEM_000000001'), + 'MEM_000000001', '2024-01-01 10:00:00', '2024-01-01 10:00:00', NULL), ('CUS_000000002', '김민수', 38, 'MALE', '010-3333-4444', '010-4444-5555', 'minsoo@example.com', TRUE, - 'MEM_000000002'), + 'MEM_000000002', '2024-01-02 10:00:00', '2024-01-02 10:00:00', NULL), ('CUS_000000003', '이영희', 32, 'FEMALE', '010-5555-6666', '010-6666-7777', 'younghee@example.com', TRUE, - 'MEM_000000003'), + 'MEM_000000003', '2024-01-03 10:00:00', '2024-01-03 10:00:00', NULL), ('CUS_000000004', '박지훈', 27, 'MALE', '010-7777-8888', '010-8888-9999', 'jihun@example.com', TRUE, - 'MEM_000000004'), + 'MEM_000000004', '2024-01-04 10:00:00', '2024-01-04 10:00:00', NULL), ('CUS_000000005', '최정민', 22, 'FEMALE', '010-9999-0000', '010-0000-1111', 'jungmin@example.com', TRUE, - 'MEM_000000005'), + 'MEM_000000005', '2024-01-05 10:00:00', '2024-01-05 10:00:00', NULL), ('CUS_000000006', '정수민', 31, 'MALE', '010-1212-3434', '010-2323-4545', 'suming@example.com', TRUE, - 'MEM_000000006'), + 'MEM_000000006', '2024-01-06 10:00:00', '2024-01-06 10:00:00', NULL), ('CUS_000000007', '한민정', 29, 'FEMALE', '010-4545-5656', '010-5656-6767', 'hanmj@example.com', TRUE, - 'MEM_000000007'), + 'MEM_000000007', '2024-01-07 10:00:00', '2024-01-07 10:00:00', NULL), ('CUS_000000008', '이동수', 41, 'MALE', '010-6767-7878', '010-7878-8989', 'dongsu@example.com', TRUE, - 'MEM_000000008'), + 'MEM_000000008', '2024-01-08 10:00:00', '2024-01-08 10:00:00', NULL), ('CUS_000000009', '윤소라', 33, 'FEMALE', '010-8989-9090', '010-9090-1010', 'sora@example.com', TRUE, - 'MEM_000000009'), + 'MEM_000000009', '2024-01-09 10:00:00', '2024-01-09 10:00:00', NULL), ('CUS_000000010', '박성훈', 36, 'MALE', '010-1010-1112', '010-1212-1313', 'seonghun@example.com', TRUE, - 'MEM_000000010'); + 'MEM_000000010', '2024-01-10 10:00:00', '2024-01-10 10:00:00', NULL); + INSERT INTO tb_product (PROD_ID, PROD_SER_NO, PROD_COST, PROD_NAME, PROD_STCK, CREATED_AT, UPDATED_AT, ACTIVE) VALUES ('PRO_000000001', 'KNAHAA4AALU1A00001', 25000000, '쏘렌토', 10, '2024-01-10 10:00:00', '2024-01-10 11:00:00', TRUE), From 30d2cd44aa48ca21056cd284f4b468bb4ae41879 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 17:21:36 +0900 Subject: [PATCH 144/563] =?UTF-8?q?feat:=20=EA=B3=A0=EA=B0=9D=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=88=98=EC=A0=95(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CustomerController.java | 26 ++++++++++++++++--- .../application/dto/CustomerModifyDTO.java | 20 ++++++++++++++ .../service/CustomerCommandService.java | 3 +++ .../service/CustomerCommandServiceImpl.java | 16 +++++++++++- .../common/exception/CustomerErrorCode.java | 4 +-- 5 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/customer/command/application/dto/CustomerModifyDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java b/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java index 3373b9cf..c17887c1 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java @@ -7,11 +7,9 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; -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.RestController; +import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; +import stanl_2.final_backend.domain.customer.command.application.dto.CustomerModifyDTO; import stanl_2.final_backend.domain.customer.command.application.dto.CustomerRegistDTO; import stanl_2.final_backend.domain.customer.command.application.service.CustomerCommandService; import stanl_2.final_backend.domain.customer.common.response.CustomerResponseMessage; @@ -54,4 +52,24 @@ public ResponseEntity postCustomer(@RequestBody Custome .result(null) .build()); } + + @Operation(summary = "고객정보 수정") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) + }) + @PostMapping("/{customerId}") + public ResponseEntity postCustomer(@PathVariable String customerId, + @RequestBody CustomerModifyDTO customerModifyDTO){ + + customerModifyDTO.setCustomerId(customerId); + + customerCommandService.modifyCustomerId(customerModifyDTO); + + return ResponseEntity.ok(CustomerResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/application/dto/CustomerModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/customer/command/application/dto/CustomerModifyDTO.java new file mode 100644 index 00000000..c1a42121 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/application/dto/CustomerModifyDTO.java @@ -0,0 +1,20 @@ +package stanl_2.final_backend.domain.customer.command.application.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class CustomerModifyDTO { + private String customerId; + private String name; + private Integer age; + private String sex; + private String phone; + private String emergePhone; + private String email; +} diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/application/service/CustomerCommandService.java b/src/main/java/stanl_2/final_backend/domain/customer/command/application/service/CustomerCommandService.java index 5c0fa88e..65b2a1d6 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/application/service/CustomerCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/application/service/CustomerCommandService.java @@ -1,7 +1,10 @@ package stanl_2.final_backend.domain.customer.command.application.service; +import stanl_2.final_backend.domain.customer.command.application.dto.CustomerModifyDTO; import stanl_2.final_backend.domain.customer.command.application.dto.CustomerRegistDTO; public interface CustomerCommandService { void registerCustomerInfo(CustomerRegistDTO customerRegistDTO); + + void modifyCustomerId(CustomerModifyDTO customerModifyDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java index e2057de5..49cbc4c6 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java @@ -5,10 +5,13 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.customer.command.application.dto.CustomerModifyDTO; import stanl_2.final_backend.domain.customer.command.application.dto.CustomerRegistDTO; import stanl_2.final_backend.domain.customer.command.application.service.CustomerCommandService; import stanl_2.final_backend.domain.customer.command.domain.aggregate.entity.Customer; import stanl_2.final_backend.domain.customer.command.domain.repository.CustomerRepository; +import stanl_2.final_backend.domain.customer.common.exception.CustomerCommonException; +import stanl_2.final_backend.domain.customer.common.exception.CustomerErrorCode; @Slf4j @Service("commandCustomerService") @@ -30,7 +33,18 @@ public void registerCustomerInfo(CustomerRegistDTO customerRegistDTO) { Customer customer = modelMapper.map(customerRegistDTO, Customer.class); - log.info(customer.getMemberId()); + customerRepository.save(customer); + } + + @Override + @Transactional + public void modifyCustomerId(CustomerModifyDTO customerModifyDTO) { + + Customer customer = customerRepository.findById(customerModifyDTO.getCustomerId()) + .orElseThrow(() -> new CustomerCommonException(CustomerErrorCode.CUSTOMER_NOT_FOUND)); + + modelMapper.map(customerModifyDTO, customer); + customerRepository.save(customer); } } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/common/exception/CustomerErrorCode.java b/src/main/java/stanl_2/final_backend/domain/customer/common/exception/CustomerErrorCode.java index ea85f92c..ab3e3b07 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/common/exception/CustomerErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/common/exception/CustomerErrorCode.java @@ -38,8 +38,8 @@ public enum CustomerErrorCode { * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. */ - SAMPLE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "sample 데이터를 찾지 못했습니다"), - CENTER_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "center 데이터를 찾지 못했습니다."), + CUSTOMER_NOT_FOUND(40401, HttpStatus.NOT_FOUND, "고객 정보를 찾을 수 없습니다."), + /** * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. */ From a9d9f95f05f471cd3f258fd2ed41fb4e804ce0c6 Mon Sep 17 00:00:00 2001 From: giuseog Date: Mon, 18 Nov 2024 17:48:34 +0900 Subject: [PATCH 145/563] =?UTF-8?q?feat:=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EC=A4=91=20=EB=94=B0=EB=9D=BC=EA=B0=80=EB=A0=A4?= =?UTF-8?q?=EA=B3=A0=20merge(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../evaluation/query/controller/EvaluationController.java | 2 +- .../{respository => repository}/EvaluationMapper.java | 5 +++-- .../query/service/EvaluationQueryServiceImpl.java | 5 +++-- .../evaluation/query/repository/EvaluationMapper.xml | 8 ++++---- 4 files changed, 11 insertions(+), 9 deletions(-) rename src/main/java/stanl_2/final_backend/domain/evaluation/query/{respository => repository}/EvaluationMapper.java (69%) diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java index f5b69b4b..f7f88336 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java @@ -62,7 +62,7 @@ public ResponseEntity getAllEvaluations(@PageableDefa @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("{centerId}") + @GetMapping("/{centerId}") public ResponseEntity getEvaluationByCenter(@PathVariable String centerId, Pageable pageable){ Page> responseEvaluations = evaluationQueryService.selectEvaluationByCenter(centerId, pageable); diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/respository/EvaluationMapper.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.java similarity index 69% rename from src/main/java/stanl_2/final_backend/domain/evaluation/query/respository/EvaluationMapper.java rename to src/main/java/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.java index 6b054d16..8a273cde 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/respository/EvaluationMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.java @@ -1,6 +1,7 @@ -package stanl_2.final_backend.domain.evaluation.query.respository; +package stanl_2.final_backend.domain.evaluation.query.repository; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; import stanl_2.final_backend.domain.evaluation.common.util.EvaluationRequestList; import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationDTO; @@ -10,7 +11,7 @@ @Mapper public interface EvaluationMapper { - List> findEvaluationByCenterId(String centerId, EvaluationRequestList requestList); + List> findEvaluationByCenterId(@Param("requestList") EvaluationRequestList requestList); EvaluationDTO findEvaluationById(String id); diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java index a8f95ced..54f890bb 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java @@ -10,7 +10,7 @@ import stanl_2.final_backend.domain.evaluation.common.exception.EvaluationErrorCode; import stanl_2.final_backend.domain.evaluation.common.util.EvaluationRequestList; import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationDTO; -import stanl_2.final_backend.domain.evaluation.query.respository.EvaluationMapper; +import stanl_2.final_backend.domain.evaluation.query.repository.EvaluationMapper; import java.util.List; import java.util.Map; @@ -48,10 +48,11 @@ public Page> selectAllEvaluations(Pageable pageable) { @Transactional(readOnly = true) public Page> selectEvaluationByCenter(String centerId, Pageable pageable) { EvaluationRequestList requestList = EvaluationRequestList.builder() + .data(centerId) .pageable(pageable) .build(); - List> evaluationList = evaluationMapper.findEvaluationByCenterId(centerId, requestList); + List> evaluationList = evaluationMapper.findEvaluationByCenterId(requestList); int total = evaluationMapper.findEvaluationCountByCenterId(); diff --git a/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml b/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml index 6e2c5f80..81873fe3 100644 --- a/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml @@ -2,7 +2,7 @@ - + @@ -15,7 +15,7 @@ - SELECT A.EVAL_ID , A.EVAL_TTL @@ -30,7 +30,7 @@ OFFSET #{pageable.offset} ROWS FETCH NEXT #{pageable.pageSize} ROWS ONLY; - SELECT A.EVAL_ID , A.EVAL_TTL @@ -40,7 +40,7 @@ , A.CREATED_AT , A.UPDATED_AT FROM TB_EVALUATION A - WHERE A.CENT_ID = #{id} AND A.ACTIVE = TRUE + WHERE A.CENT_ID = #{data.centerId} AND A.ACTIVE = TRUE ORDER BY CREATED_AT DESC OFFSET #{pageable.offset} ROWS FETCH NEXT #{pageable.pageSize} ROWS ONLY; From 4a950b0c0880068e3a7a3fb88755eeb7f38ff50f Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 18 Nov 2024 18:16:35 +0900 Subject: [PATCH 146/563] =?UTF-8?q?feat:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=EC=8B=9C=20=ED=9A=8C=EC=9B=90=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EC=B6=94=EA=B0=80=20(#82)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/controller/NoticeController.java | 12 +++++++++--- .../application/controller/ProblemController.java | 12 +++++++++--- .../application/controller/PromotionController.java | 11 +++++++++-- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java index a19dc10a..de142480 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java @@ -8,21 +8,25 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeModifyDTO; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; import stanl_2.final_backend.domain.notices.command.application.service.NoticeCommandService; import stanl_2.final_backend.domain.notices.common.response.NoticeResponseMessage; +import java.security.Principal; + @RestController("commandNoticeController") @RequestMapping("/api/v1/notice") public class NoticeController { private final NoticeCommandService noticeCommandService; + private final AuthQueryService authQueryService; @Autowired - public NoticeController(NoticeCommandService noticeCommandService) { + public NoticeController(NoticeCommandService noticeCommandService, AuthQueryService authQueryService){ this.noticeCommandService = noticeCommandService; + this.authQueryService =authQueryService; } @Operation(summary = "공지사항 작성") @@ -31,7 +35,9 @@ public NoticeController(NoticeCommandService noticeCommandService) { content = {@Content(schema = @Schema(implementation = NoticeResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postNotice(@RequestBody NoticeRegistDTO noticeRegistDTO){ + public ResponseEntity postNotice(@RequestBody NoticeRegistDTO noticeRegistDTO, Principal principal){ + String memberId =authQueryService.selectMemberIdByLoginId(principal.getName()); + noticeRegistDTO.setMemberId(memberId); noticeCommandService.registerNotice(noticeRegistDTO); return ResponseEntity.ok(NoticeResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java index 61e363f5..9f97d4b7 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java @@ -8,19 +8,23 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.problem.command.application.dto.ProblemModifyDTO; import stanl_2.final_backend.domain.problem.command.application.dto.ProblemRegistDTO; import stanl_2.final_backend.domain.problem.command.application.service.ProblemCommandService; import stanl_2.final_backend.domain.problem.common.response.ProblemResponseMessage; +import java.security.Principal; + @RestController("commandProblemController") @RequestMapping("/api/v1/problem") public class ProblemController { private final ProblemCommandService problemCommandService; - + private final AuthQueryService authQueryService; @Autowired - public ProblemController(ProblemCommandService problemCommandService) { + public ProblemController(ProblemCommandService problemCommandService, AuthQueryService authQueryService) { this.problemCommandService = problemCommandService; + this.authQueryService = authQueryService; } @Operation(summary = "문제사항 작성") @@ -29,7 +33,9 @@ public ProblemController(ProblemCommandService problemCommandService) { content = {@Content(schema = @Schema(implementation = ProblemResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postNotice(@RequestBody ProblemRegistDTO problemRegistDTO){ + public ResponseEntity postNotice(@RequestBody ProblemRegistDTO problemRegistDTO, Principal principal){ + String memberId = authQueryService.selectMemberIdByLoginId(principal.getName()); + problemRegistDTO.setMemberId(memberId); problemCommandService.registerProblem(problemRegistDTO); return ResponseEntity.ok(ProblemResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java index 257e3822..2c97992e 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java @@ -8,19 +8,24 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionModifyDTO; import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionRegistDTO; import stanl_2.final_backend.domain.promotion.command.application.service.PromotionCommandService; import stanl_2.final_backend.domain.promotion.common.response.PromotionResponseMessage; +import java.security.Principal; + @RestController("commandPromotionController") @RequestMapping("/api/v1/promotion") public class PromotionController { private final PromotionCommandService promotionCommandService; + private final AuthQueryService authQueryService; @Autowired - public PromotionController(PromotionCommandService promotionCommandService) { + public PromotionController(PromotionCommandService promotionCommandService, AuthQueryService authQueryService) { this.promotionCommandService = promotionCommandService; + this.authQueryService =authQueryService; } @Operation(summary = "프로모션 작성") @@ -29,7 +34,9 @@ public PromotionController(PromotionCommandService promotionCommandService) { content = {@Content(schema = @Schema(implementation = PromotionResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postNotice(@RequestBody PromotionRegistDTO prmotionRegistDTO){ + public ResponseEntity postNotice(@RequestBody PromotionRegistDTO prmotionRegistDTO, Principal principal){ + String memberId = authQueryService.selectMemberIdByLoginId(principal.getName()); + prmotionRegistDTO.setMemberId(memberId); promotionCommandService.registerPromotion(prmotionRegistDTO); return ResponseEntity.ok(PromotionResponseMessage.builder() .httpStatus(200) From 82e88430c6d4ad1b081499ca44bd368c70d420ec Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 18:20:50 +0900 Subject: [PATCH 147/563] =?UTF-8?q?feat:=20=EA=B3=A0=EA=B0=9D=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=88=98=EC=A0=95(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/controller/CustomerController.java | 5 ++++- .../domain/service/CustomerCommandServiceImpl.java | 2 +- .../security/filter/JWTTokenValidatorFilter.java | 10 +--------- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java b/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java index c17887c1..c894d970 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java @@ -5,8 +5,10 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.customer.command.application.dto.CustomerModifyDTO; @@ -17,6 +19,7 @@ import java.security.Principal; +@Slf4j @RestController("commandCustomerController") @RequestMapping("/api/v1/customer") public class CustomerController { @@ -58,7 +61,7 @@ public ResponseEntity postCustomer(@RequestBody Custome @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) }) - @PostMapping("/{customerId}") + @PutMapping("/{customerId}") public ResponseEntity postCustomer(@PathVariable String customerId, @RequestBody CustomerModifyDTO customerModifyDTO){ diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java index 49cbc4c6..dc458d41 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java @@ -44,7 +44,7 @@ public void modifyCustomerId(CustomerModifyDTO customerModifyDTO) { .orElseThrow(() -> new CustomerCommonException(CustomerErrorCode.CUSTOMER_NOT_FOUND)); modelMapper.map(customerModifyDTO, customer); - + log.info(customer.toString()); customerRepository.save(customer); } } diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java index 30421d07..d3c5a094 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java @@ -55,16 +55,10 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse String username = claims.get("username", String.class); String authorities = claims.get("authorities", String.class); - // 예외 처리: authorities가 null인 경우 // 예외 처리: 토큰에 유효한 권한이 없을 경우 if (username == null || authorities == null || authorities.isEmpty()) { throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); } -// if (authorities == null || authorities.isEmpty()) { -// SecurityContextHolder.clearContext(); -// filterChain.doFilter(request, response); -// return; -// } List grantedAuthorities = Arrays.stream(authorities.split(",")) .map(SimpleGrantedAuthority::new) @@ -77,10 +71,8 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse SecurityContextHolder.getContext().setAuthentication(authentication); } } catch (JwtException e) { - log.error("Invalid JWT token: {}", e.getMessage()); + log.error("유효하지 않은 토큰입니다: {}", e.getMessage()); throw new GlobalCommonException(GlobalErrorCode.INVALID_TOKEN_ERROR); -// logger.error("Invalid JWT token", e); -// SecurityContextHolder.clearContext(); } }else { // 토큰이 없을 경우 예외 처리 From 355654928d850db4c4e669c8861d4768a7e23db5 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 19:04:56 +0900 Subject: [PATCH 148/563] =?UTF-8?q?feat:=20=EA=B3=A0=EA=B0=9D=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=82=AD=EC=A0=9C(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CustomerController.java | 31 ++++++++++++++----- .../service/CustomerCommandService.java | 4 ++- .../service/CustomerCommandServiceImpl.java | 14 ++++++++- 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java b/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java index c894d970..8fdf06e2 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java @@ -41,7 +41,7 @@ public CustomerController(CustomerCommandService customerCommandService, }) @PostMapping("") public ResponseEntity postCustomer(@RequestBody CustomerRegistDTO customerRegistDTO, - Principal principal){ + Principal principal) { String memberId = authQueryService.selectMemberIdByLoginId(principal.getName()); @@ -63,16 +63,33 @@ public ResponseEntity postCustomer(@RequestBody Custome }) @PutMapping("/{customerId}") public ResponseEntity postCustomer(@PathVariable String customerId, - @RequestBody CustomerModifyDTO customerModifyDTO){ + @RequestBody CustomerModifyDTO customerModifyDTO) { customerModifyDTO.setCustomerId(customerId); - customerCommandService.modifyCustomerId(customerModifyDTO); + customerCommandService.modifyCustomerInfo(customerModifyDTO); return ResponseEntity.ok(CustomerResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(null) - .build()); + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + } + + @Operation(summary = "고객정보 수정") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) + }) + @DeleteMapping("/{customerId}") + public ResponseEntity deleteCustomer(@PathVariable String customerId) { + + customerCommandService.deleteCustomerId(customerId); + + return ResponseEntity.ok(CustomerResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); } } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/application/service/CustomerCommandService.java b/src/main/java/stanl_2/final_backend/domain/customer/command/application/service/CustomerCommandService.java index 65b2a1d6..9bdceed8 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/application/service/CustomerCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/application/service/CustomerCommandService.java @@ -6,5 +6,7 @@ public interface CustomerCommandService { void registerCustomerInfo(CustomerRegistDTO customerRegistDTO); - void modifyCustomerId(CustomerModifyDTO customerModifyDTO); + void modifyCustomerInfo(CustomerModifyDTO customerModifyDTO); + + void deleteCustomerId(String customerId); } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java index dc458d41..c3897edc 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java @@ -38,7 +38,7 @@ public void registerCustomerInfo(CustomerRegistDTO customerRegistDTO) { @Override @Transactional - public void modifyCustomerId(CustomerModifyDTO customerModifyDTO) { + public void modifyCustomerInfo(CustomerModifyDTO customerModifyDTO) { Customer customer = customerRepository.findById(customerModifyDTO.getCustomerId()) .orElseThrow(() -> new CustomerCommonException(CustomerErrorCode.CUSTOMER_NOT_FOUND)); @@ -47,4 +47,16 @@ public void modifyCustomerId(CustomerModifyDTO customerModifyDTO) { log.info(customer.toString()); customerRepository.save(customer); } + + @Override + @Transactional + public void deleteCustomerId(String customerId) { + + Customer customer = customerRepository.findById(customerId) + .orElseThrow(() -> new CustomerCommonException(CustomerErrorCode.CUSTOMER_NOT_FOUND)); + + customer.setActive(false); + + customerRepository.save(customer); + } } From 539a86a5eaf6d3f3e989a23b1f350080038c5cb5 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 18 Nov 2024 19:22:12 +0900 Subject: [PATCH 149/563] =?UTF-8?q?feat:=20redis=20=EC=84=B8=ED=8C=85=20?= =?UTF-8?q?=EC=A4=91(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 26 +++++++++++++++++++++++++ src/main/resources/application_prod.yml | 4 ++++ 2 files changed, 30 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 90d751dc..2dddb707 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,10 +8,30 @@ services: container_name: backend environment: - SPRING_PROFILES_ACTIVE=prod + - REDIS_HOST=redis + - REDIS_PORT=6379 expose: - "8081" + depends_on: + - redis networks: - app-network + restart: always + + redis: + image: redis:7.0-alpine + container_name: redis + ports: + - "6379:6379" + environment: + - REDIS_PASSWORD=1234 + command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}"] + networks: + - app-network + volumes: + - redis-data:/data + restart: always + nginx: build: @@ -24,6 +44,12 @@ services: - "80:80" # Nginx가 8080 포트로 내부 백엔드에 접근 networks: - app-network + restart: always + networks: app-network: driver: bridge + +volumes: + redis-data: + driver: local \ No newline at end of file diff --git a/src/main/resources/application_prod.yml b/src/main/resources/application_prod.yml index bdb395a7..c3c7e660 100644 --- a/src/main/resources/application_prod.yml +++ b/src/main/resources/application_prod.yml @@ -2,6 +2,10 @@ spring: jpa: hibernate: ddl-auto: create + redis: + host: redis + port: 6379 + password: ${REDIS_PASSWORD} logging: level: From 0ad5b26adedb807f2add254732b5ee4486e2cb7e Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 18 Nov 2024 19:24:00 +0900 Subject: [PATCH 150/563] =?UTF-8?q?feat:=20redis=20=EC=84=B8=ED=8C=85=20?= =?UTF-8?q?=EC=A4=91(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 4b1f86cd..96b174ec 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -5,7 +5,7 @@ on: branches: - main - feat/backend-operation - + - feat/integration jobs: deploy: runs-on: ubuntu-latest From d511e77c0188a6f347d74abe1cdc5ea5463e45b2 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 19:32:04 +0900 Subject: [PATCH 151/563] =?UTF-8?q?feat:=20=EA=B3=A0=EA=B0=9D=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=83=81=EC=84=B8=EC=A1=B0=ED=9A=8C(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/CustomerController.java | 31 ++++++++++++++++ .../customer/query/dto/CustomerDTO.java | 5 +++ .../query/repository/CustomerMapper.java | 3 ++ .../query/service/CustomerQueryService.java | 3 ++ .../service/CustomerQueryServiceImpl.java | 13 +++++++ .../query/repository/CustomerMapper.xml | 37 +++++++++++++++++++ 6 files changed, 92 insertions(+) create mode 100644 src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java b/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java index 3ac382e8..373b57dd 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java @@ -1,8 +1,20 @@ package stanl_2.final_backend.domain.customer.query.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +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.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; +import stanl_2.final_backend.domain.customer.common.response.CustomerResponseMessage; +import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; import stanl_2.final_backend.domain.customer.query.service.CustomerQueryService; @RestController(value = "queryCustomerController") @@ -15,4 +27,23 @@ public class CustomerController { public CustomerController(CustomerQueryService customerQueryService) { this.customerQueryService = customerQueryService; } + + @Operation(summary = "고객정보 (상세)조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/{customerId}") + public ResponseEntity getCustomerInfo(@PathVariable String customerId){ + + CustomerDTO customerInfoDTO = customerQueryService.selectCustomerInfo(customerId); + + return ResponseEntity.ok(CustomerResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(customerInfoDTO) + .build()); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/dto/CustomerDTO.java b/src/main/java/stanl_2/final_backend/domain/customer/query/dto/CustomerDTO.java index 435b91cd..4f9c63db 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/dto/CustomerDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/dto/CustomerDTO.java @@ -10,6 +10,7 @@ @Getter @Setter public class CustomerDTO { + private String customerId; private String name; private Integer age; private String sex; @@ -17,4 +18,8 @@ public class CustomerDTO { private String emergePhone; private String email; private String memberId; + private Boolean active; + private String createdAt; + private String updatedAt; + private String deletedAt; } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java b/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java index 7f44b3b4..4a3c037f 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java @@ -1,7 +1,10 @@ package stanl_2.final_backend.domain.customer.query.repository; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; @Mapper public interface CustomerMapper { + CustomerDTO selectCustomerInfoById(@Param("customerId") String customerId); } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java index 1cba4bf9..c70ce1d3 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java @@ -1,4 +1,7 @@ package stanl_2.final_backend.domain.customer.query.service; +import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; + public interface CustomerQueryService { + CustomerDTO selectCustomerInfo(String customerId); } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java index 9d3f05c3..c774db4e 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java @@ -1,10 +1,14 @@ package stanl_2.final_backend.domain.customer.query.service; +import lombok.extern.slf4j.Slf4j; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; import stanl_2.final_backend.domain.customer.query.repository.CustomerMapper; +@Slf4j @Service("queryCustomerService") public class CustomerQueryServiceImpl implements CustomerQueryService{ @@ -17,4 +21,13 @@ public CustomerQueryServiceImpl(CustomerMapper customerMapper, this.customerMapper = customerMapper; this.modelMapper = modelMapper; } + + @Override + @Transactional + public CustomerDTO selectCustomerInfo(String customerId) { + + CustomerDTO customerInfoDTO = customerMapper.selectCustomerInfoById(customerId); + + return customerInfoDTO; + } } diff --git a/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml b/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml new file mode 100644 index 00000000..bb29b758 --- /dev/null +++ b/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 4aaaf15bcf70a5938353e3a39f50d31aaf0d8d57 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Mon, 18 Nov 2024 19:41:44 +0900 Subject: [PATCH 152/563] =?UTF-8?q?feat:=20=EB=B0=9C=EC=A3=BC=EC=84=9C=20?= =?UTF-8?q?=EC=A0=84=EC=B2=B4=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84=20(#5?= =?UTF-8?q?9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/service/OrderQueryServiceImpl.java | 2 +- .../controller/PurchaseOrderController.java | 27 ++++++++- .../query/dto/PurchaseOrderSelectAllDTO.java | 26 +++++++++ .../query/dto/PurchaseOrderSelectIdDTO.java | 2 +- .../query/repository/PurchaseOrderMapper.java | 9 ++- .../service/PurchaseOrderQueryService.java | 5 ++ .../PurchaseOrderQueryServiceImpl.java | 55 ++++++++++++++----- .../order/query/repository/OrderMapper.xml | 2 +- .../query/repository/PurchaseOrderMapper.xml | 47 +++++++++++++++- 9 files changed, 152 insertions(+), 23 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectAllDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java index 7a8f66fb..f1053581 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java @@ -48,7 +48,7 @@ public OrderSelectIdDTO selectDetailOrder(OrderSelectIdDTO orderSelectIdDTO) { OrderSelectIdDTO order = orderMapper.findOrderByIdAndMemberId(orderSelectIdDTO.getOrderId(), orderSelectIdDTO.getMemberId()); - if(order == null) { + if (order == null) { throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java index f4d45f14..d031e879 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java @@ -6,6 +6,9 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.GetMapping; @@ -13,6 +16,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import stanl_2.final_backend.domain.purchase_order.common.response.PurchaseOrderResponseMessage; +import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectAllDTO; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectIdDTO; import stanl_2.final_backend.domain.purchase_order.query.service.PurchaseOrderQueryService; @@ -43,8 +47,6 @@ public ResponseEntity getDetailPurchaseOrder(@Path purchaseOrderSelectIdDTO.setMemberId(authentication.getName()); purchaseOrderSelectIdDTO.setRoles(authentication.getAuthorities()); - System.out.println("사용자권한!!: " + authentication.getAuthorities()); - PurchaseOrderSelectIdDTO responsePurchaseOrder = purchaseOrderQueryService.selectDetailPurchaseOrder(purchaseOrderSelectIdDTO); return ResponseEntity.ok(PurchaseOrderResponseMessage.builder() @@ -53,4 +55,25 @@ public ResponseEntity getDetailPurchaseOrder(@Path .result(responsePurchaseOrder) .build()); } + + @Operation(summary = "발주서 전체조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "발주서 전체조회 성공", + content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) + }) + @GetMapping("") + public ResponseEntity getAllPurchaseOrders(Authentication authentication, + @PageableDefault(size = 10) Pageable pageable) { + PurchaseOrderSelectAllDTO purchaseOrderSelectAllDTO = new PurchaseOrderSelectAllDTO(); + purchaseOrderSelectAllDTO.setRoles(authentication.getAuthorities()); + + Page responsePurchaseOrders = purchaseOrderQueryService.selectAllPurchaseOrder(pageable, purchaseOrderSelectAllDTO); + + return ResponseEntity.ok(PurchaseOrderResponseMessage.builder() + .httpStatus(200) + .msg("발주서 전체 조회 성공") + .result(responsePurchaseOrders) + .build()); + } } + diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectAllDTO.java new file mode 100644 index 00000000..dbf2a228 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectAllDTO.java @@ -0,0 +1,26 @@ +package stanl_2.final_backend.domain.purchase_order.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class PurchaseOrderSelectAllDTO { + private String PurchaseOrderId; + private String title; + private String status; + private String orderId; + private String adminName; + private String memberName; + private String productName; + private String adminId; + private String memberId; + private Collection roles; +} diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectIdDTO.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectIdDTO.java index a07a1bf6..e71e2065 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectIdDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectIdDTO.java @@ -21,7 +21,7 @@ public class PurchaseOrderSelectIdDTO { private String createdAt; private String updatedAt; private String deletedAt; - private String OrderId; + private String orderId; private String adminId; private String memberId; private Collection roles; diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java index 517b2351..b87e65a0 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java @@ -1,11 +1,16 @@ package stanl_2.final_backend.domain.purchase_order.query.repository; import org.apache.ibatis.annotations.Mapper; +import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectAllDTO; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectIdDTO; +import java.util.List; + @Mapper public interface PurchaseOrderMapper { - PurchaseOrderSelectIdDTO findPurchaseOrderByIdAndMemberId(String purchaseOrderId, String memberId); - PurchaseOrderSelectIdDTO findPurchaseOrderById(String purchaseOrderId); + + List findAllPurchaseOrder(int offset, int pageSize); + + int findAllPurchaseOrderCount(); } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java index 0dd76188..fa112ff2 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java @@ -1,7 +1,12 @@ package stanl_2.final_backend.domain.purchase_order.query.service; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectAllDTO; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectIdDTO; public interface PurchaseOrderQueryService { PurchaseOrderSelectIdDTO selectDetailPurchaseOrder(PurchaseOrderSelectIdDTO purchaseOrderSelectIdDTO); + + Page selectAllPurchaseOrder(Pageable pageable, PurchaseOrderSelectAllDTO purchaseOrderSelectAllDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java index 97068e6b..6213ed32 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java @@ -1,51 +1,80 @@ package stanl_2.final_backend.domain.purchase_order.query.service; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.purchase_order.common.exception.PurchaseOrderCommonException; import stanl_2.final_backend.domain.purchase_order.common.exception.PurchaseOrderErrorCode; +import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectAllDTO; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectIdDTO; import stanl_2.final_backend.domain.purchase_order.query.repository.PurchaseOrderMapper; import stanl_2.final_backend.global.exception.GlobalCommonException; import stanl_2.final_backend.global.exception.GlobalErrorCode; +import stanl_2.final_backend.global.utils.AESUtils; + +import java.security.GeneralSecurityException; +import java.util.List; @Service @Transactional(readOnly = true) public class PurchaseOrderQueryServiceImpl implements PurchaseOrderQueryService { private final PurchaseOrderMapper purchaseOrderMapper; - private final AuthQueryService authQueryService; + private final AESUtils aesUtils; @Autowired - public PurchaseOrderQueryServiceImpl(PurchaseOrderMapper purchaseOrderMapper, AuthQueryService authQueryService) { + public PurchaseOrderQueryServiceImpl(PurchaseOrderMapper purchaseOrderMapper, AESUtils aesUtils) { this.purchaseOrderMapper = purchaseOrderMapper; - this.authQueryService = authQueryService; + this.aesUtils = aesUtils; } @Override + @Transactional(readOnly = true) public PurchaseOrderSelectIdDTO selectDetailPurchaseOrder(PurchaseOrderSelectIdDTO purchaseOrderSelectIdDTO) { - if(purchaseOrderSelectIdDTO.getRoles().stream() - .anyMatch(role -> "ROLE_EMPLOYEE".equals(role.getAuthority()))) { - - String memberId = authQueryService.selectMemberIdByLoginId(purchaseOrderSelectIdDTO.getMemberId()); + if (purchaseOrderSelectIdDTO.getRoles().stream() + .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()) || "ROLE_REPRESENTATIVE".equals(role.getAuthority()))) { - PurchaseOrderSelectIdDTO purchaseOrder = purchaseOrderMapper.findPurchaseOrderByIdAndMemberId(purchaseOrderSelectIdDTO.getPurchaseOrderId(), memberId); + PurchaseOrderSelectIdDTO purchaseOrder = purchaseOrderMapper.findPurchaseOrderById(purchaseOrderSelectIdDTO.getPurchaseOrderId()); if (purchaseOrder == null) { throw new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND); } return purchaseOrder; - } else if (purchaseOrderSelectIdDTO.getRoles().stream() + } else { + throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + } + } + + @Override + @Transactional(readOnly = true) + public Page selectAllPurchaseOrder(Pageable pageable, PurchaseOrderSelectAllDTO purchaseOrderSelectAllDTO) { + + int offset = Math.toIntExact(pageable.getOffset()); + int pageSize = pageable.getPageSize(); + + if (purchaseOrderSelectAllDTO.getRoles().stream() .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()) || "ROLE_REPRESENTATIVE".equals(role.getAuthority()))) { - PurchaseOrderSelectIdDTO purchaseOrder = purchaseOrderMapper.findPurchaseOrderById(purchaseOrderSelectIdDTO.getPurchaseOrderId()); + List purchaseOrders = purchaseOrderMapper.findAllPurchaseOrder(offset, pageSize); - if (purchaseOrder == null) { + if (purchaseOrders == null || purchaseOrders.isEmpty()) { throw new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND); } - return purchaseOrder; + + purchaseOrders.forEach(purchaseOrder -> { + try { + purchaseOrder.setMemberName(aesUtils.decrypt(purchaseOrder.getMemberName())); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } + }); + + int total = purchaseOrderMapper.findAllPurchaseOrderCount(); + + return new PageImpl<>(purchaseOrders, pageable, total); } else { throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); } diff --git a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml index eb371a51..785764e5 100644 --- a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml @@ -57,7 +57,7 @@ COUNT(*) AS CNT FROM TB_ORDER A WHERE A.MEM_ID = #{memberId} - AND A.ACTIVE = TRUE + AND A.ACTIVE = TRUE TRUE + + + + + + + + + + + + + + + \ No newline at end of file From 11c590714550c4a51ff1b732e16c21cc9e60d1f8 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 18 Nov 2024 19:43:58 +0900 Subject: [PATCH 153/563] =?UTF-8?q?feat:=20redis=20=EC=84=B8=ED=8C=85=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/config/RedisConfig.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/main/java/stanl_2/final_backend/global/config/RedisConfig.java diff --git a/src/main/java/stanl_2/final_backend/global/config/RedisConfig.java b/src/main/java/stanl_2/final_backend/global/config/RedisConfig.java new file mode 100644 index 00000000..48478715 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/config/RedisConfig.java @@ -0,0 +1,25 @@ +package stanl_2.final_backend.global.config; + +import jakarta.annotation.PostConstruct; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; + +@Configuration +public class RedisConfig { + + private final RedisConnectionFactory redisConnectionFactory; + + public RedisConfig(RedisConnectionFactory redisConnectionFactory) { + this.redisConnectionFactory = redisConnectionFactory; + } + + @PostConstruct + public void testRedisConnection() { + try { + redisConnectionFactory.getConnection().ping(); + System.out.println("✅ Successfully connected to Redis"); + } catch (Exception e) { + System.err.println("❌ Failed to connect to Redis: " + e.getMessage()); + } + } +} \ No newline at end of file From 511f75be78c6b676b7e67d5668e08599c05c7420 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 19:45:29 +0900 Subject: [PATCH 154/563] =?UTF-8?q?fix:=20=EA=B3=A0=EA=B0=9D=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=96=91=EB=B0=A9=ED=96=A5=20=EC=95=94=ED=98=B8?= =?UTF-8?q?=ED=99=94(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CustomerController.java | 5 +++-- .../service/CustomerCommandService.java | 6 +++-- .../service/CustomerCommandServiceImpl.java | 22 +++++++++++++++---- .../query/controller/CustomerController.java | 4 +++- .../query/service/CustomerQueryService.java | 4 +++- .../service/CustomerQueryServiceImpl.java | 15 +++++++++---- 6 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java b/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java index 8fdf06e2..d945039a 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java @@ -17,6 +17,7 @@ import stanl_2.final_backend.domain.customer.common.response.CustomerResponseMessage; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import java.security.GeneralSecurityException; import java.security.Principal; @Slf4j @@ -41,7 +42,7 @@ public CustomerController(CustomerCommandService customerCommandService, }) @PostMapping("") public ResponseEntity postCustomer(@RequestBody CustomerRegistDTO customerRegistDTO, - Principal principal) { + Principal principal) throws GeneralSecurityException { String memberId = authQueryService.selectMemberIdByLoginId(principal.getName()); @@ -63,7 +64,7 @@ public ResponseEntity postCustomer(@RequestBody Custome }) @PutMapping("/{customerId}") public ResponseEntity postCustomer(@PathVariable String customerId, - @RequestBody CustomerModifyDTO customerModifyDTO) { + @RequestBody CustomerModifyDTO customerModifyDTO) throws GeneralSecurityException { customerModifyDTO.setCustomerId(customerId); diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/application/service/CustomerCommandService.java b/src/main/java/stanl_2/final_backend/domain/customer/command/application/service/CustomerCommandService.java index 9bdceed8..e3ccfdbb 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/application/service/CustomerCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/application/service/CustomerCommandService.java @@ -3,10 +3,12 @@ import stanl_2.final_backend.domain.customer.command.application.dto.CustomerModifyDTO; import stanl_2.final_backend.domain.customer.command.application.dto.CustomerRegistDTO; +import java.security.GeneralSecurityException; + public interface CustomerCommandService { - void registerCustomerInfo(CustomerRegistDTO customerRegistDTO); + void registerCustomerInfo(CustomerRegistDTO customerRegistDTO) throws GeneralSecurityException; - void modifyCustomerInfo(CustomerModifyDTO customerModifyDTO); + void modifyCustomerInfo(CustomerModifyDTO customerModifyDTO) throws GeneralSecurityException; void deleteCustomerId(String customerId); } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java index c3897edc..e88a5ae1 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java @@ -12,6 +12,9 @@ import stanl_2.final_backend.domain.customer.command.domain.repository.CustomerRepository; import stanl_2.final_backend.domain.customer.common.exception.CustomerCommonException; import stanl_2.final_backend.domain.customer.common.exception.CustomerErrorCode; +import stanl_2.final_backend.global.utils.AESUtils; + +import java.security.GeneralSecurityException; @Slf4j @Service("commandCustomerService") @@ -19,32 +22,43 @@ public class CustomerCommandServiceImpl implements CustomerCommandService { private final CustomerRepository customerRepository; private final ModelMapper modelMapper; + private final AESUtils aesUtils; @Autowired public CustomerCommandServiceImpl(CustomerRepository customerRepository, - ModelMapper modelMapper) { + ModelMapper modelMapper, + AESUtils aesUtils) { this.customerRepository = customerRepository; this.modelMapper = modelMapper; + this.aesUtils = aesUtils; } @Override @Transactional - public void registerCustomerInfo(CustomerRegistDTO customerRegistDTO) { + public void registerCustomerInfo(CustomerRegistDTO customerRegistDTO) throws GeneralSecurityException { Customer customer = modelMapper.map(customerRegistDTO, Customer.class); + customer.setPhone(aesUtils.encrypt(customer.getPhone())); + customer.setEmergePhone(aesUtils.encrypt(customer.getEmergePhone())); + customer.setEmail(aesUtils.encrypt(customer.getEmail())); + customerRepository.save(customer); } @Override @Transactional - public void modifyCustomerInfo(CustomerModifyDTO customerModifyDTO) { + public void modifyCustomerInfo(CustomerModifyDTO customerModifyDTO) throws GeneralSecurityException { Customer customer = customerRepository.findById(customerModifyDTO.getCustomerId()) .orElseThrow(() -> new CustomerCommonException(CustomerErrorCode.CUSTOMER_NOT_FOUND)); + customerModifyDTO.setPhone(aesUtils.encrypt(customerModifyDTO.getPhone())); + customerModifyDTO.setEmergePhone(aesUtils.encrypt(customerModifyDTO.getEmergePhone())); + customerModifyDTO.setEmail(aesUtils.encrypt(customerModifyDTO.getEmail())); + modelMapper.map(customerModifyDTO, customer); - log.info(customer.toString()); + customerRepository.save(customer); } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java b/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java index 373b57dd..0931d7c7 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java @@ -17,6 +17,8 @@ import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; import stanl_2.final_backend.domain.customer.query.service.CustomerQueryService; +import java.security.GeneralSecurityException; + @RestController(value = "queryCustomerController") @RequestMapping("/api/v1/customer") public class CustomerController { @@ -36,7 +38,7 @@ public CustomerController(CustomerQueryService customerQueryService) { content = @Content(mediaType = "application/json")) }) @GetMapping("/{customerId}") - public ResponseEntity getCustomerInfo(@PathVariable String customerId){ + public ResponseEntity getCustomerInfo(@PathVariable String customerId) throws GeneralSecurityException { CustomerDTO customerInfoDTO = customerQueryService.selectCustomerInfo(customerId); diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java index c70ce1d3..91a26687 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java @@ -2,6 +2,8 @@ import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; +import java.security.GeneralSecurityException; + public interface CustomerQueryService { - CustomerDTO selectCustomerInfo(String customerId); + CustomerDTO selectCustomerInfo(String customerId) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java index c774db4e..0fcc504c 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java @@ -7,27 +7,34 @@ import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; import stanl_2.final_backend.domain.customer.query.repository.CustomerMapper; +import stanl_2.final_backend.global.utils.AESUtils; + +import java.security.GeneralSecurityException; @Slf4j @Service("queryCustomerService") public class CustomerQueryServiceImpl implements CustomerQueryService{ private final CustomerMapper customerMapper; - private final ModelMapper modelMapper; + private final AESUtils aesUtils; @Autowired public CustomerQueryServiceImpl(CustomerMapper customerMapper, - ModelMapper modelMapper) { + AESUtils aesUtils) { this.customerMapper = customerMapper; - this.modelMapper = modelMapper; + this.aesUtils = aesUtils; } @Override @Transactional - public CustomerDTO selectCustomerInfo(String customerId) { + public CustomerDTO selectCustomerInfo(String customerId) throws GeneralSecurityException { CustomerDTO customerInfoDTO = customerMapper.selectCustomerInfoById(customerId); + customerInfoDTO.setPhone(aesUtils.decrypt(customerInfoDTO.getPhone())); + customerInfoDTO.setEmergePhone(aesUtils.decrypt(customerInfoDTO.getEmergePhone())); + customerInfoDTO.setEmail(aesUtils.decrypt(customerInfoDTO.getEmail())); + return customerInfoDTO; } } From 6bb65d4bb51275d32d03c4c436521da5a174cbcf Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 18 Nov 2024 19:53:39 +0900 Subject: [PATCH 155/563] =?UTF-8?q?feat:=20redis=20=EC=84=B8=ED=8C=85=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 2dddb707..ff816cf4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,14 +23,16 @@ services: container_name: redis ports: - "6379:6379" - environment: - - REDIS_PASSWORD=1234 - command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}"] networks: - app-network volumes: - redis-data:/data restart: always + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 3s + retries: 5 nginx: From 2e96efac4a4efdeac2463080e54e0ce73aa5845a Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 18 Nov 2024 20:11:08 +0900 Subject: [PATCH 156/563] =?UTF-8?q?feat:=20redis=20=EC=84=B8=ED=8C=85=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/stanl_2/final_backend/global/config/RedisConfig.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/global/config/RedisConfig.java b/src/main/java/stanl_2/final_backend/global/config/RedisConfig.java index 48478715..cb9e48af 100644 --- a/src/main/java/stanl_2/final_backend/global/config/RedisConfig.java +++ b/src/main/java/stanl_2/final_backend/global/config/RedisConfig.java @@ -6,7 +6,6 @@ @Configuration public class RedisConfig { - private final RedisConnectionFactory redisConnectionFactory; public RedisConfig(RedisConnectionFactory redisConnectionFactory) { From 36e46d0af84fd428b275e5ffafbeacffd07f80b9 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 18 Nov 2024 20:21:56 +0900 Subject: [PATCH 157/563] =?UTF-8?q?feat:=20redis=20=EC=84=B8=ED=8C=85=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 8 +++++--- src/main/resources/application_prod.yml | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index ff816cf4..e8733768 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,7 +13,8 @@ services: expose: - "8081" depends_on: - - redis + redis: + condition: service_healthy networks: - app-network restart: always @@ -31,7 +32,7 @@ services: healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s - timeout: 3s + timeout: 5s retries: 5 @@ -41,7 +42,8 @@ services: dockerfile: Dockerfile container_name: nginx depends_on: - - backend + backend: + condition: service_healthy ports: - "80:80" # Nginx가 8080 포트로 내부 백엔드에 접근 networks: diff --git a/src/main/resources/application_prod.yml b/src/main/resources/application_prod.yml index c3c7e660..482197bb 100644 --- a/src/main/resources/application_prod.yml +++ b/src/main/resources/application_prod.yml @@ -6,6 +6,7 @@ spring: host: redis port: 6379 password: ${REDIS_PASSWORD} + timeout: 5000ms logging: level: From 05b7bdbea7ee483f085dd340ff5fb0307818d075 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Mon, 18 Nov 2024 20:23:28 +0900 Subject: [PATCH 158/563] =?UTF-8?q?feat:=20=EB=B0=9C=EC=A3=BC=EC=84=9C=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PurchaseOrderController.java | 33 +++++++-- .../query/dto/PurchaseOrderSelectAllDTO.java | 1 - .../dto/PurchaseOrderSelectSearchDTO.java | 38 ++++++++++ .../query/repository/PurchaseOrderMapper.java | 6 ++ .../service/PurchaseOrderQueryService.java | 3 + .../PurchaseOrderQueryServiceImpl.java | 42 +++++++++++ .../query/repository/PurchaseOrderMapper.xml | 70 ++++++++++++++++++- 7 files changed, 186 insertions(+), 7 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectSearchDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java index d031e879..e3bdcb2a 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java @@ -11,13 +11,11 @@ import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.purchase_order.common.response.PurchaseOrderResponseMessage; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectAllDTO; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectIdDTO; +import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectSearchDTO; import stanl_2.final_backend.domain.purchase_order.query.service.PurchaseOrderQueryService; import java.security.Principal; @@ -75,5 +73,32 @@ public ResponseEntity getAllPurchaseOrders(Authent .result(responsePurchaseOrders) .build()); } + + @Operation(summary = "발주서 검색조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "발주서 검색조회 성공", + content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) + }) + @GetMapping("/search") + public ResponseEntity getSearchPurchaseOrders(@RequestParam(required = false) String title, + @RequestParam(required = false) String status, + @RequestParam(required = false) String adminId, + @RequestParam(required = false) String searchMemberId, + @RequestParam(required = false) String startDate, + @RequestParam(required = false) String endDate, + Authentication authentication, + @PageableDefault(size = 10) Pageable pageable) { + + PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO = new PurchaseOrderSelectSearchDTO(title, status, + adminId, searchMemberId, startDate, endDate, authentication.getAuthorities()); + + Page responsePurchaseOrder = purchaseOrderQueryService.selectSearchPurchaseOrder(purchaseOrderSelectSearchDTO, pageable); + + return ResponseEntity.ok(PurchaseOrderResponseMessage.builder() + .httpStatus(200) + .msg("발주서 검색 조회 성공") + .result(responsePurchaseOrder) + .build()); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectAllDTO.java index dbf2a228..5b45eb4b 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectAllDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectAllDTO.java @@ -20,7 +20,6 @@ public class PurchaseOrderSelectAllDTO { private String adminName; private String memberName; private String productName; - private String adminId; private String memberId; private Collection roles; } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectSearchDTO.java new file mode 100644 index 00000000..e8addb1e --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectSearchDTO.java @@ -0,0 +1,38 @@ +package stanl_2.final_backend.domain.purchase_order.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class PurchaseOrderSelectSearchDTO { + private String PurchaseOrderId; + private String title; + private String status; + private String orderId; + private String adminName; + private String memberName; + private String productName; + private String adminId; + private String searchMemberId; + private String startDate; + private String endDate; + private Collection roles; + + public PurchaseOrderSelectSearchDTO(String title, String status, String adminId, String searchMemberId, String startDate, String endDate, Collection roles) { + this.title = title; + this.status = status; + this.adminId = adminId; + this.searchMemberId = searchMemberId; + this.startDate = startDate; + this.endDate = endDate; + this.roles = roles; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java index b87e65a0..3c3409be 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java @@ -3,8 +3,10 @@ import org.apache.ibatis.annotations.Mapper; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectAllDTO; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectIdDTO; +import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectSearchDTO; import java.util.List; +import java.util.Map; @Mapper public interface PurchaseOrderMapper { @@ -13,4 +15,8 @@ public interface PurchaseOrderMapper { List findAllPurchaseOrder(int offset, int pageSize); int findAllPurchaseOrderCount(); + + List findSearchPurchaseOrder(Map map); + + int findSearchPurchaseOrderCount(); } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java index fa112ff2..881ae4c9 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java @@ -4,9 +4,12 @@ import org.springframework.data.domain.Pageable; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectAllDTO; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectIdDTO; +import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectSearchDTO; public interface PurchaseOrderQueryService { PurchaseOrderSelectIdDTO selectDetailPurchaseOrder(PurchaseOrderSelectIdDTO purchaseOrderSelectIdDTO); Page selectAllPurchaseOrder(Pageable pageable, PurchaseOrderSelectAllDTO purchaseOrderSelectAllDTO); + + Page selectSearchPurchaseOrder(PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO, Pageable pageable); } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java index 6213ed32..64051c83 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java @@ -10,13 +10,16 @@ import stanl_2.final_backend.domain.purchase_order.common.exception.PurchaseOrderErrorCode; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectAllDTO; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectIdDTO; +import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectSearchDTO; import stanl_2.final_backend.domain.purchase_order.query.repository.PurchaseOrderMapper; import stanl_2.final_backend.global.exception.GlobalCommonException; import stanl_2.final_backend.global.exception.GlobalErrorCode; import stanl_2.final_backend.global.utils.AESUtils; import java.security.GeneralSecurityException; +import java.util.HashMap; import java.util.List; +import java.util.Map; @Service @Transactional(readOnly = true) @@ -79,4 +82,43 @@ public Page selectAllPurchaseOrder(Pageable pageable, throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); } } + + @Override + public Page selectSearchPurchaseOrder(PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO, Pageable pageable) { + + if (purchaseOrderSelectSearchDTO.getRoles().stream() + .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()) || "ROLE_REPRESENTATIVE".equals(role.getAuthority()))) { + + Map map = new HashMap<>(); + map.put("title", purchaseOrderSelectSearchDTO.getTitle()); + map.put("status", purchaseOrderSelectSearchDTO.getStatus()); + map.put("adminId", purchaseOrderSelectSearchDTO.getAdminId()); + map.put("searchMemberId", purchaseOrderSelectSearchDTO.getSearchMemberId()); + map.put("startDate", purchaseOrderSelectSearchDTO.getStartDate()); + map.put("endDate", purchaseOrderSelectSearchDTO.getEndDate()); + map.put("roles", purchaseOrderSelectSearchDTO.getRoles()); + map.put("pageSize", pageable.getPageSize()); + map.put("offset", pageable.getOffset()); + + List purchaseOrders = purchaseOrderMapper.findSearchPurchaseOrder(map); + + if (purchaseOrders == null || purchaseOrders.isEmpty()) { + throw new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND); + } + + purchaseOrders.forEach(purchaseOrder -> { + try { + purchaseOrder.setMemberName(aesUtils.decrypt(purchaseOrder.getMemberName())); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } + }); + + int total = purchaseOrderMapper.findSearchPurchaseOrderCount(); + + return new PageImpl<>(purchaseOrders, pageable, total); + } else { + throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + } + } } \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml b/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml index 1495241d..dce1bbb7 100644 --- a/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml @@ -18,7 +18,7 @@ - + @@ -46,7 +46,7 @@ AND A.PUR_ORD_ID = #{purchaseOrderId} - SELECT A.PUR_ORD_ID, A.PUR_ORD_TTL, @@ -78,5 +78,71 @@ WHERE A.ACTIVE = TRUE + + + + \ No newline at end of file From 32abe826b4945ae034e50d08164ad44ab35d88c8 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 18 Nov 2024 20:29:08 +0900 Subject: [PATCH 159/563] =?UTF-8?q?feat:=20redis=20=EC=84=B8=ED=8C=85=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index e8733768..99a6079d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,11 +13,11 @@ services: expose: - "8081" depends_on: - redis: - condition: service_healthy + - redis networks: - app-network restart: always + entrypoint: [ "sh", "-c", "sleep 10 && java -jar app.jar" ] redis: image: redis:7.0-alpine @@ -30,7 +30,7 @@ services: - redis-data:/data restart: always healthcheck: - test: ["CMD", "redis-cli", "ping"] + test: ["CMD", "redis-cli", "-h", "localhost", "ping"] interval: 5s timeout: 5s retries: 5 @@ -42,10 +42,9 @@ services: dockerfile: Dockerfile container_name: nginx depends_on: - backend: - condition: service_healthy + - backend ports: - - "80:80" # Nginx가 8080 포트로 내부 백엔드에 접근 + - "80:80" networks: - app-network restart: always From 7a5d98a9cb471dc0073eb04b24cd1448e70aec1a Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Mon, 18 Nov 2024 20:38:21 +0900 Subject: [PATCH 160/563] =?UTF-8?q?fix:=20pr=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/application/dto/PurchaseOrderModifyDTO.java | 1 - .../command/application/dto/PurchaseOrderRegistDTO.java | 1 - .../command/application/dto/PurchaseOrderStatusModifyDTO.java | 1 - .../query/service/PurchaseOrderQueryServiceImpl.java | 2 -- .../final_backend/domain/order/query/repository/OrderMapper.xml | 2 +- 5 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderModifyDTO.java index e5db540c..12abc2cf 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderModifyDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Setter @Getter -@ToString public class PurchaseOrderModifyDTO { private String purchaseOrderId; private String title; diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderRegistDTO.java index 88376c48..55a05a39 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderRegistDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Setter @Getter -@ToString public class PurchaseOrderRegistDTO { private String title; private String content; diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderStatusModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderStatusModifyDTO.java index 9d34c7d0..9f1af2c1 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderStatusModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderStatusModifyDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Setter @Getter -@ToString public class PurchaseOrderStatusModifyDTO { private String purchaseOrderId; private String status; diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java index 64051c83..7688538b 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java @@ -35,7 +35,6 @@ public PurchaseOrderQueryServiceImpl(PurchaseOrderMapper purchaseOrderMapper, AE } @Override - @Transactional(readOnly = true) public PurchaseOrderSelectIdDTO selectDetailPurchaseOrder(PurchaseOrderSelectIdDTO purchaseOrderSelectIdDTO) { if (purchaseOrderSelectIdDTO.getRoles().stream() .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()) || "ROLE_REPRESENTATIVE".equals(role.getAuthority()))) { @@ -52,7 +51,6 @@ public PurchaseOrderSelectIdDTO selectDetailPurchaseOrder(PurchaseOrderSelectIdD } @Override - @Transactional(readOnly = true) public Page selectAllPurchaseOrder(Pageable pageable, PurchaseOrderSelectAllDTO purchaseOrderSelectAllDTO) { int offset = Math.toIntExact(pageable.getOffset()); diff --git a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml index 785764e5..eb371a51 100644 --- a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml @@ -57,7 +57,7 @@ COUNT(*) AS CNT FROM TB_ORDER A WHERE A.MEM_ID = #{memberId} - AND A.ACTIVE = TRUE TRUE + AND A.ACTIVE = TRUE - SELECT A.EVAL_ID , A.EVAL_TTL @@ -40,12 +40,12 @@ , A.CREATED_AT , A.UPDATED_AT FROM TB_EVALUATION A - WHERE A.CENT_ID = #{data.centerId} AND A.ACTIVE = TRUE + WHERE A.CENT_ID = #{centerId} AND A.ACTIVE = TRUE ORDER BY CREATED_AT DESC - OFFSET #{pageable.offset} ROWS FETCH NEXT #{pageable.pageSize} ROWS ONLY; + LIMIT #{size} OFFSET #{offset} - SELECT A.EVAL_ID , A.EVAL_TTL @@ -56,7 +56,7 @@ , A.CREATED_AT , A.UPDATED_AT FROM TB_EVALUATION A - WHERE A.ACTIVE = TRUE + WHERE A.EVAL_ID = #{id} AND A.ACTIVE = TRUE @@ -64,14 +64,14 @@ SELECT COUNT(*) AS CNT FROM TB_EVALUATION A - WHERE A.ACTIVE = TRUE; + WHERE A.ACTIVE = TRUE - SELECT COUNT(*) AS CNT FROM TB_EVALUATION A - WHERE A.ACTIVE = TRUE; + WHERE A.CENT_ID = #{centerId} AND A.ACTIVE = TRUE; From 903c71f368614976e7e546695eabdd5c18f3cb1d Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 18 Nov 2024 21:17:02 +0900 Subject: [PATCH 163/563] =?UTF-8?q?feat:=20redis=20=EC=84=B8=ED=8C=85=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index f94fcefa..9a8f6127 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -49,6 +49,7 @@ services: networks: - app-network restart: always + entrypoint: ["sh", "-c", "sleep 15 && nginx -g 'daemon off;'"] networks: app-network: From 60b6c1b4813dd4989dce66b4a643bf1a931ad17a Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 18 Nov 2024 21:28:26 +0900 Subject: [PATCH 164/563] =?UTF-8?q?feat:=20redis=20=EC=84=B8=ED=8C=85=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 4 ++-- docker-compose.yml | 2 +- nginx/default.conf | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 89150d20..20b46941 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ WORKDIR /app COPY --from=build /app/build/libs/*.jar app.jar # 포트 노출 -EXPOSE 8081 +EXPOSE 8080 # JAR 파일 실행 -ENTRYPOINT ["java", "-jar", "app.jar", "--server.port=8081"] \ No newline at end of file +ENTRYPOINT ["java", "-jar", "app.jar", "--server.port=8080"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 9a8f6127..e3538bad 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,7 @@ services: - REDIS_HOST=redis - REDIS_PORT=6379 expose: - - "8081" + - "8080" depends_on: redis: condition: service_healthy diff --git a/nginx/default.conf b/nginx/default.conf index d4d08e66..50fa47fc 100644 --- a/nginx/default.conf +++ b/nginx/default.conf @@ -1,5 +1,5 @@ upstream backend { - server backend:8081; # 백엔드 컨테이너의 포트를 8080으로 수정 + server backend:8080; # 백엔드 컨테이너의 포트를 8080으로 수정 } server { From 06e07442d3dbbd6d04e634834486dd3af1b2d364 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 18 Nov 2024 21:37:42 +0900 Subject: [PATCH 165/563] =?UTF-8?q?feat:=20=EA=B9=94=EB=81=94=ED=95=98?= =?UTF-8?q?=EA=B2=8C=20=EC=99=84=EB=A3=8C~!=20(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/stanl_2/final_backend/global/config/RedisConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/stanl_2/final_backend/global/config/RedisConfig.java b/src/main/java/stanl_2/final_backend/global/config/RedisConfig.java index 6f1b0df1..3db25d9d 100644 --- a/src/main/java/stanl_2/final_backend/global/config/RedisConfig.java +++ b/src/main/java/stanl_2/final_backend/global/config/RedisConfig.java @@ -7,6 +7,7 @@ @Component public class RedisConfig { + private final RedisConnectionFactory redisConnectionFactory; public RedisConfig(RedisConnectionFactory redisConnectionFactory) { From 2d9d45c44d7ad16d08a4c2323b817ccff1254e90 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 18 Nov 2024 21:41:31 +0900 Subject: [PATCH 166/563] =?UTF-8?q?feat:=20=EA=B3=A0=EA=B0=9D=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=A1=B0=ED=9A=8C(=ED=8E=98=EC=9D=B4=EC=A7=95=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC)(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/CustomerController.java | 36 +++++++++++++++---- .../query/dto/CustomerNameListDTO.java | 14 ++++++++ .../query/repository/CustomerMapper.java | 6 ++++ .../query/service/CustomerQueryService.java | 6 ++++ .../service/CustomerQueryServiceImpl.java | 16 ++++++++- .../query/repository/CustomerMapper.xml | 27 ++++++++++++++ 6 files changed, 98 insertions(+), 7 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/customer/query/dto/CustomerNameListDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java b/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java index 0931d7c7..292cbd2a 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java @@ -5,13 +5,12 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; -import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; 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.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.customer.common.response.CustomerResponseMessage; import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; @@ -30,7 +29,7 @@ public CustomerController(CustomerQueryService customerQueryService) { this.customerQueryService = customerQueryService; } - @Operation(summary = "고객정보 (상세)조회") + @Operation(summary = "고객정보 상세조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), @@ -48,4 +47,29 @@ public ResponseEntity getCustomerInfo(@PathVariable Str .result(customerInfoDTO) .build()); } + + + @Operation(summary = "고객번호로 전체 목록 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/list") + public ResponseEntity getCustomers( + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "10") int size + ){ + Pageable pageable = PageRequest.of(page, size); + + Page customerDTOPage = customerQueryService.selectCustomerList(pageable); + + return ResponseEntity.ok(CustomerResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(customerDTOPage) + .build()); + } + } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/dto/CustomerNameListDTO.java b/src/main/java/stanl_2/final_backend/domain/customer/query/dto/CustomerNameListDTO.java new file mode 100644 index 00000000..0d124769 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/dto/CustomerNameListDTO.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.customer.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +public class CustomerNameListDTO{ + private String name; +} diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java b/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java index 4a3c037f..60fba85f 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java @@ -4,7 +4,13 @@ import org.apache.ibatis.annotations.Param; import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; +import java.util.List; + @Mapper public interface CustomerMapper { CustomerDTO selectCustomerInfoById(@Param("customerId") String customerId); + + List selectCustomerList(int offset, int size); + + int selectCustomerCount(); } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java index 91a26687..96133475 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java @@ -1,9 +1,15 @@ package stanl_2.final_backend.domain.customer.query.service; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; +import stanl_2.final_backend.domain.customer.query.dto.CustomerNameListDTO; import java.security.GeneralSecurityException; +import java.util.List; public interface CustomerQueryService { CustomerDTO selectCustomerInfo(String customerId) throws GeneralSecurityException; + + Page selectCustomerList(Pageable pageable); } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java index 0fcc504c..7108e3dc 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java @@ -1,8 +1,10 @@ package stanl_2.final_backend.domain.customer.query.service; import lombok.extern.slf4j.Slf4j; -import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; @@ -10,6 +12,7 @@ import stanl_2.final_backend.global.utils.AESUtils; import java.security.GeneralSecurityException; +import java.util.List; @Slf4j @Service("queryCustomerService") @@ -37,4 +40,15 @@ public CustomerDTO selectCustomerInfo(String customerId) throws GeneralSecurityE return customerInfoDTO; } + + @Override + @Transactional + public Page selectCustomerList(Pageable pageable) { + int page = pageable.getPageNumber(); + int size = pageable.getPageSize(); + List customerList = customerMapper.selectCustomerList(page*size, size); + int totalElements = customerMapper.selectCustomerCount(); + + return new PageImpl<>(customerList, pageable, totalElements); + } } diff --git a/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml b/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml index bb29b758..e882f73a 100644 --- a/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml @@ -3,6 +3,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> + @@ -17,6 +18,7 @@ + + + + + \ No newline at end of file From 2f9707da538bd185777f3047ef7d5f5f3b3de277 Mon Sep 17 00:00:00 2001 From: giuseog Date: Tue, 19 Nov 2024 00:33:38 +0900 Subject: [PATCH 167/563] =?UTF-8?q?feat:=20=EC=83=81=EC=83=81=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=EC=9D=80=20=EC=99=84=EB=A3=8C(=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=ED=95=84=EC=9A=94)=20(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/EvaluationController.java | 85 +++++++------ .../evaluation/query/dto/EvaluationDTO.java | 1 - .../query/dto/EvaluationSearchDTO.java | 21 ++- .../query/repository/EvaluationMapper.java | 18 ++- .../query/service/EvaluationQueryService.java | 7 +- .../service/EvaluationQueryServiceImpl.java | 117 +++++++++++++---- .../domain/member/query/dto/MemberDTO.java | 1 + .../query/service/MemberQueryServiceImpl.java | 1 + .../query/repository/EvaluationMapper.xml | 120 +++++++++++++++++- 9 files changed, 301 insertions(+), 70 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java index 29e08506..0378ec97 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java @@ -11,14 +11,16 @@ import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.evaluation.common.response.EvaluationResponseMessage; import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationDTO; +import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationSearchDTO; import stanl_2.final_backend.domain.evaluation.query.service.EvaluationQueryService; +import stanl_2.final_backend.domain.product.common.response.ProductResponseMessage; +import stanl_2.final_backend.domain.product.query.dto.ProductSearchRequestDTO; +import java.security.GeneralSecurityException; +import java.util.HashMap; import java.util.Map; @RestController(value = "queryEvaluationController") @@ -31,51 +33,30 @@ public EvaluationController(EvaluationQueryService evaluationQueryService) { this.evaluationQueryService = evaluationQueryService; } - /** - * [GET] http://localhost:7777/api/v1/sample/SAM_000000001 - * */ - @Operation(summary = "평가서 담당자 조회") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = EvaluationResponseMessage.class))}), - @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", - content = @Content(mediaType = "application/json")) - }) - @GetMapping("") - public ResponseEntity getAllEvaluations(@PageableDefault(size = 20) Pageable pageable, - Authentication authentication){ - - EvaluationDTO evaluationDTO = new EvaluationDTO(); - - evaluationDTO.setRoles(authentication.getAuthorities()); - - Page> responseEvaluations = evaluationQueryService.selectAllEvaluations(pageable, evaluationDTO); - - return ResponseEntity.ok(EvaluationResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(responseEvaluations) - .build()); - } /** * [GET] http://localhost:7777/api/v1/sample/SAM_000000001 * */ - @Operation(summary = "평가서 관리자 조회") + @Operation(summary = "평가서 전체 조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = EvaluationResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("/{centerId}") - public ResponseEntity getEvaluationByCenter(@PathVariable String centerId, Pageable pageable){ + @GetMapping("") + public ResponseEntity getAllEvaluations(Authentication authentication, + Pageable pageable) throws GeneralSecurityException { + EvaluationDTO evaluationDTO = new EvaluationDTO(); + + evaluationDTO.setRoles(authentication.getAuthorities()); + evaluationDTO.setMemberId(authentication.getName()); - Page responseEvaluations = evaluationQueryService.selectEvaluationByCenter(centerId, pageable); + Page responseEvaluations = evaluationQueryService.selectAllEvaluations(evaluationDTO, pageable); return ResponseEntity.ok(EvaluationResponseMessage.builder() .httpStatus(200) - .msg("성공") + .msg("평가서 전체 조회 성공") .result(responseEvaluations) .build()); } @@ -97,9 +78,41 @@ public ResponseEntity getEvaluationDetail(@PathVariab return ResponseEntity.ok(EvaluationResponseMessage.builder() .httpStatus(200) - .msg("성공") + .msg("평가서 상세 조회 성공") .result(evaluationDTO) .build()); } + @Operation(summary = "평가서 검색 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = EvaluationResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/search") + public ResponseEntity getEvaluationBySearch(@RequestParam Map params + ,Authentication authentication + , @PageableDefault(size = 20) Pageable pageable) throws GeneralSecurityException { + + EvaluationSearchDTO evaluationSearchDTO = new EvaluationSearchDTO(); + evaluationSearchDTO.setEvalId(params.get("evalId")); + evaluationSearchDTO.setTitle(params.get("title")); + evaluationSearchDTO.setWriterName(params.get("writerName")); + evaluationSearchDTO.setMemberName(params.get("memberName")); + evaluationSearchDTO.setCenterId(params.get("centerId")); + evaluationSearchDTO.setStartDate(params.get("startDate")); + evaluationSearchDTO.setEndDate(params.get("endDate")); + evaluationSearchDTO.setRoles(authentication.getAuthorities()); + evaluationSearchDTO.setSearcherName(authentication.getName()); + + Page responseEvaluations = evaluationQueryService.selectEvaluationBySearch(pageable, evaluationSearchDTO); + + return ResponseEntity.ok(EvaluationResponseMessage.builder() + .httpStatus(200) + .msg("검색 조회 성공") + .result(responseEvaluations) + .build()); + } + } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java index 59e90bdd..1d8bea8f 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java @@ -9,7 +9,6 @@ @NoArgsConstructor @Getter @Setter -@ToString public class EvaluationDTO { private String evalId; private String title; diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationSearchDTO.java index 95326991..b94e638e 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationSearchDTO.java @@ -1,10 +1,25 @@ package stanl_2.final_backend.domain.evaluation.query.dto; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter public class EvaluationSearchDTO { private String evalId; private String title; - private String writerId; - private String memberId; + private String writerName; + private String memberName; private String centerId; - private String createdAt; + private String startDate; + private String endDate; + private Collection roles; + private String searcherName; } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.java index 193d27b3..ae89e38d 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.java @@ -2,8 +2,8 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; -import stanl_2.final_backend.domain.evaluation.common.util.EvaluationRequestList; import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationDTO; +import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationSearchDTO; import java.util.List; import java.util.Map; @@ -17,9 +17,23 @@ List findEvaluationByCenterId(@Param("size") int size EvaluationDTO findEvaluationById(String id); - List> findAllEvaluations(EvaluationRequestList requestList); + List findAllEvaluations(@Param("size") int size + ,@Param("offset") int offset); int findEvaluationCount(); int findEvaluationCountByCenterId(@Param("centerId") String centerId); + + List findEvaluationBySearch(@Param("size") int size + ,@Param("offset") int offset + ,@Param("evaluationSearchDTO") EvaluationSearchDTO evaluationSearchDTO); + + List findEvaluationByCenterIdAndSearch(@Param("size") int size + ,@Param("offset") int offset + ,@Param("evaluationSearchDTO") EvaluationSearchDTO evaluationSearchDTO + ,@Param("centerId") String centerId); + + int findEvaluationBySearchCount(@Param("evaluationSearchDTO") EvaluationSearchDTO evaluationSearchDTO); + + int findEvaluationByCenterIdAndSearchCount(@Param("evaluationSearchDTO") EvaluationSearchDTO evaluationSearchDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryService.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryService.java index ffd4d132..505df489 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryService.java @@ -4,13 +4,16 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationDTO; +import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationSearchDTO; +import java.security.GeneralSecurityException; import java.util.Map; public interface EvaluationQueryService { - Page> selectAllEvaluations(Pageable pageable, EvaluationDTO evaluationDTO); EvaluationDTO selectEvaluationById(String id); - Page selectEvaluationByCenter(String centerId, Pageable pageable); + Page selectAllEvaluations(EvaluationDTO evaluationDTO, Pageable pageable) throws GeneralSecurityException; + + Page selectEvaluationBySearch(Pageable pageable, EvaluationSearchDTO evaluationSearchDTO) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java index 17b6603b..21aa27ed 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java @@ -10,9 +10,16 @@ import stanl_2.final_backend.domain.evaluation.common.exception.EvaluationErrorCode; import stanl_2.final_backend.domain.evaluation.common.util.EvaluationRequestList; import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationDTO; +import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationSearchDTO; import stanl_2.final_backend.domain.evaluation.query.repository.EvaluationMapper; +import stanl_2.final_backend.domain.member.query.dto.MemberDTO; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; +import stanl_2.final_backend.global.exception.GlobalCommonException; +import stanl_2.final_backend.global.exception.GlobalErrorCode; import stanl_2.final_backend.global.utils.AESUtils; +import java.security.GeneralSecurityException; import java.util.List; import java.util.Map; @@ -21,46 +28,60 @@ public class EvaluationQueryServiceImpl implements EvaluationQueryService { private final EvaluationMapper evaluationMapper; private final AESUtils aesUtils; + private final MemberQueryService memberQueryService; + private final AuthQueryService authQueryService; @Autowired - public EvaluationQueryServiceImpl(EvaluationMapper evaluationMapper, AESUtils aesUtils) { + public EvaluationQueryServiceImpl(EvaluationMapper evaluationMapper, AESUtils aesUtils, MemberQueryService memberQueryService, AuthQueryService authQueryService) { this.evaluationMapper = evaluationMapper; this.aesUtils = aesUtils; + this.memberQueryService = memberQueryService; + this.authQueryService = authQueryService; } @Override @Transactional(readOnly = true) - public Page> selectAllEvaluations(Pageable pageable, EvaluationDTO evaluationDTO) { + public Page selectAllEvaluations(EvaluationDTO evaluationDTO, Pageable pageable) throws GeneralSecurityException { + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); - EvaluationRequestList requestList = EvaluationRequestList.builder() - .pageable(pageable) - .build(); + if(evaluationDTO.getRoles().stream() + .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()))){ - List> evaluationList = evaluationMapper.findAllEvaluations(requestList); + MemberDTO memberDTO = memberQueryService.selectMemberInfo(evaluationDTO.getMemberId()); + String centerId = aesUtils.decrypt(memberDTO.getCenterId()); - int total = evaluationMapper.findEvaluationCount(); + List evaluationList = evaluationMapper.findEvaluationByCenterId(size,offset, centerId); - if(evaluationList.isEmpty() || total == 0){ - throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); - } - return new PageImpl<>(evaluationList, pageable, total); - } + int total = evaluationMapper.findEvaluationCountByCenterId(centerId); + if(evaluationList.isEmpty() || total == 0){ + throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); + } +/* 설명. ID를 LOGINID로 바꿔주는 메소드 찾아서... */ +// evaluationList.forEach(evaluation -> { +// try { +// evaluation.setMemberId(evaluation.getMemberName())); +// } catch (GeneralSecurityException e) { +// throw new RuntimeException(e); +// } +// }); + return new PageImpl<>(evaluationList, pageable, total); + } else if(evaluationDTO.getRoles().stream() + .anyMatch(role -> "ROLE_REPRESENTATIVE".equals(role.getAuthority()))) { - @Override - @Transactional(readOnly = true) - public Page selectEvaluationByCenter(String centerId, Pageable pageable) { - int offset = Math.toIntExact(pageable.getOffset()); - int size = pageable.getPageSize(); + List evaluationList = evaluationMapper.findAllEvaluations(size,offset); - List evaluationList = evaluationMapper.findEvaluationByCenterId(size,offset, centerId); + int total = evaluationMapper.findEvaluationCount(); - int total = evaluationMapper.findEvaluationCountByCenterId(centerId); + if(evaluationList.isEmpty() || total == 0) { + throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); + } - if(evaluationList.isEmpty() || total == 0){ - throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); + return new PageImpl<>(evaluationList, pageable, total); + } else{ + throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); } - return new PageImpl<>(evaluationList, pageable, total); } @Override @@ -73,8 +94,58 @@ public EvaluationDTO selectEvaluationById(String id) { throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); } return evaluationDTO; - } + @Override + @Transactional(readOnly = true) + public Page selectEvaluationBySearch(Pageable pageable, EvaluationSearchDTO evaluationSearchDTO) throws GeneralSecurityException { + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); + if(!evaluationSearchDTO.getWriterName().isEmpty()) { + evaluationSearchDTO.setWriterName(authQueryService.selectMemberIdByLoginId(evaluationSearchDTO.getWriterName())); + } + if(!evaluationSearchDTO.getWriterName().isEmpty()) { + evaluationSearchDTO.setMemberName(authQueryService.selectMemberIdByLoginId(evaluationSearchDTO.getMemberName())); + } + + if(evaluationSearchDTO.getRoles().stream() + .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()))){ + + MemberDTO memberDTO = memberQueryService.selectMemberInfo(evaluationSearchDTO.getSearcherName()); + String centerId = aesUtils.decrypt(memberDTO.getCenterId()); + + List evaluationList = evaluationMapper.findEvaluationByCenterIdAndSearch(size,offset, evaluationSearchDTO, centerId); + + int total = evaluationMapper.findEvaluationByCenterIdAndSearchCount(evaluationSearchDTO); + + if(evaluationList.isEmpty() || total == 0){ + throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); + } + /* 설명. ID를 LOGINID로 바꿔주는 메소드 찾아서... */ +// evaluationList.forEach(evaluation -> { +// try { +// evaluation.setMemberId(evaluation.getMemberName())); +// } catch (GeneralSecurityException e) { +// throw new RuntimeException(e); +// } +// }); + return new PageImpl<>(evaluationList, pageable, total); + } else if(evaluationSearchDTO.getRoles().stream() + .anyMatch(role -> "ROLE_REPRESENTATIVE".equals(role.getAuthority()))) { + + List evaluationList = evaluationMapper.findEvaluationBySearch(size,offset, evaluationSearchDTO); + + int total = evaluationMapper.findEvaluationBySearchCount(evaluationSearchDTO); + + if(evaluationList.isEmpty() || total == 0) { + throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); + } + + return new PageImpl<>(evaluationList, pageable, total); + } else{ + throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + } + + } } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java index 7fe73930..6c01021f 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java @@ -26,4 +26,5 @@ public class MemberDTO { private String military; private String bankName; private String account; + private String centerId; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index 670f9c20..b35f448b 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -42,6 +42,7 @@ public MemberDTO selectMemberInfo(String name) throws GeneralSecurityException { memberInfo.setAddress(aesUtils.decrypt(memberInfo.getAddress())); memberInfo.setBankName(aesUtils.decrypt(memberInfo.getBankName())); memberInfo.setAccount(aesUtils.decrypt(memberInfo.getAccount())); + memberInfo.setCenterId(memberInfo.getCenterId()); return memberInfo; } diff --git a/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml b/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml index a6a060dc..59ecad46 100644 --- a/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml @@ -23,11 +23,10 @@ , A.MEM_ID , A.WRI_ID , A.CREATED_AT - , A.UPDATED_AT FROM TB_EVALUATION A WHERE A.ACTIVE = TRUE ORDER BY A.CREATED_AT DESC - OFFSET #{pageable.offset} ROWS FETCH NEXT #{pageable.pageSize} ROWS ONLY; + LIMIT #{size} OFFSET #{offset} + + + + + + + + \ No newline at end of file From 438c7f5569980cb16f7b549a5a5de6651bd76278 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Tue, 19 Nov 2024 01:03:55 +0900 Subject: [PATCH 168/563] =?UTF-8?q?refactor:=20=EA=B3=84=EC=95=BD=EC=84=9C?= =?UTF-8?q?=20=ED=9A=8C=EC=9B=90Id=20=EB=B0=9B=EC=95=84=EC=98=A4=EA=B8=B0?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20(#85)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ContractController.java | 22 ++++++++++++++----- .../application/dto/ContractDeleteDTO.java | 19 ++++++++++++++++ .../application/dto/ContractModifyDTO.java | 5 ++++- .../application/dto/ContractRegistDTO.java | 5 ++++- .../dto/ContractStatusModifyDTO.java | 13 +++++++++++ .../service/ContractCommandService.java | 3 ++- .../domain/repository/ContractRepository.java | 3 +++ .../service/ContractCommandServiceImpl.java | 16 +++++++++----- .../service/PurchaseOrderCommandService.java | 2 -- .../PurchaseOrderCommandServiceImpl.java | 4 ---- 10 files changed, 72 insertions(+), 20 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractDeleteDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractStatusModifyDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java index db9483da..77b657b4 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java @@ -7,7 +7,9 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.contract.command.application.dto.ContractDeleteDTO; import stanl_2.final_backend.domain.contract.command.application.dto.ContractModifyDTO; import stanl_2.final_backend.domain.contract.command.application.dto.ContractRegistDTO; import stanl_2.final_backend.domain.contract.command.application.service.ContractCommandService; @@ -57,8 +59,11 @@ public ContractController(ContractCommandService contractCommandService) { content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postTest(@RequestBody ContractRegistDTO contractRegistRequestDTO) { + public ResponseEntity postTest(@RequestBody ContractRegistDTO contractRegistRequestDTO, + Authentication authentication) { + contractRegistRequestDTO.setMemberId(authentication.getName()); + contractRegistRequestDTO.setRoles(authentication.getAuthorities()); contractCommandService.registerContract(contractRegistRequestDTO); return ResponseEntity.ok(ContractResponseMessage.builder() @@ -101,9 +106,12 @@ public ResponseEntity postTest(@RequestBody ContractReg }) @PutMapping("{id}") public ResponseEntity putContract(@PathVariable String id, - @RequestBody ContractModifyDTO contractModifyRequestDTO) { + @RequestBody ContractModifyDTO contractModifyRequestDTO, + Authentication authentication) { contractModifyRequestDTO.setContractId(id); + contractModifyRequestDTO.setMemberId(authentication.getName()); + contractModifyRequestDTO.setRoles(authentication.getAuthorities()); ContractModifyDTO contractModifyDTO = contractCommandService.modifyContract(contractModifyRequestDTO); return ResponseEntity.ok(ContractResponseMessage.builder() @@ -122,10 +130,14 @@ public ResponseEntity putContract(@PathVariable String content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) @DeleteMapping("{id}") - public ResponseEntity deleteContract(@PathVariable String id) { + public ResponseEntity deleteContract(@PathVariable String id, + Authentication authentication) { - // 회원 아이디 받아와야 함 - contractCommandService.deleteContract(id); + ContractDeleteDTO contractDeleteDTO = new ContractDeleteDTO(); + contractDeleteDTO.setContractId(id); + contractDeleteDTO.setMemberId(authentication.getName()); + contractDeleteDTO.setRoles(authentication.getAuthorities()); + contractCommandService.deleteContract(contractDeleteDTO); return ResponseEntity.ok(ContractResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractDeleteDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractDeleteDTO.java new file mode 100644 index 00000000..6248c747 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractDeleteDTO.java @@ -0,0 +1,19 @@ +package stanl_2.final_backend.domain.contract.command.application.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class ContractDeleteDTO { + private String contractId; + private String memberId; + private Collection roles; +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java index a0e88191..8ef0fe92 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java @@ -1,12 +1,14 @@ package stanl_2.final_backend.domain.contract.command.application.dto; import lombok.*; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; @AllArgsConstructor @NoArgsConstructor @Setter @Getter -@ToString public class ContractModifyDTO { private String contractId; @@ -37,4 +39,5 @@ public class ContractModifyDTO { private String centerId; private String customerId; private String productId; + private Collection roles; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java index 2921af20..5e1134c9 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java @@ -1,12 +1,14 @@ package stanl_2.final_backend.domain.contract.command.application.dto; import lombok.*; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; @AllArgsConstructor @NoArgsConstructor @Setter @Getter -@ToString public class ContractRegistDTO { private String name; @@ -30,4 +32,5 @@ public class ContractRegistDTO { private String numberOfVehicles; private String createdUrl; private String memberId; + private Collection roles; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractStatusModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractStatusModifyDTO.java new file mode 100644 index 00000000..57d547c3 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractStatusModifyDTO.java @@ -0,0 +1,13 @@ +package stanl_2.final_backend.domain.contract.command.application.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class ContractStatusModifyDTO { +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java index 449d5def..042daf55 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.contract.command.application.service; +import stanl_2.final_backend.domain.contract.command.application.dto.ContractDeleteDTO; import stanl_2.final_backend.domain.contract.command.application.dto.ContractModifyDTO; import stanl_2.final_backend.domain.contract.command.application.dto.ContractRegistDTO; @@ -8,5 +9,5 @@ public interface ContractCommandService { ContractModifyDTO modifyContract(ContractModifyDTO contractModifyRequestDTO); - void deleteContract(String id); + void deleteContract(ContractDeleteDTO contractDeleteDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/repository/ContractRepository.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/repository/ContractRepository.java index 51c8d8a4..5d7c202f 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/repository/ContractRepository.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/repository/ContractRepository.java @@ -3,5 +3,8 @@ import org.springframework.data.jpa.repository.JpaRepository; import stanl_2.final_backend.domain.contract.command.domain.aggregate.entity.Contract; +import java.util.Optional; + public interface ContractRepository extends JpaRepository { + Optional findByContractIdAndMemberId(String contractId, String memberId); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index 641b8e34..674d939d 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -3,6 +3,7 @@ import org.modelmapper.ModelMapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.contract.command.application.dto.ContractDeleteDTO; import stanl_2.final_backend.domain.contract.command.application.dto.ContractModifyDTO; import stanl_2.final_backend.domain.contract.command.application.dto.ContractRegistDTO; import stanl_2.final_backend.domain.contract.command.application.service.ContractCommandService; @@ -10,6 +11,7 @@ import stanl_2.final_backend.domain.contract.command.domain.repository.ContractRepository; import stanl_2.final_backend.domain.contract.common.exception.ContractCommonException; import stanl_2.final_backend.domain.contract.common.exception.ContractErrorCode; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -19,10 +21,12 @@ public class ContractCommandServiceImpl implements ContractCommandService { private final ContractRepository contractRepository; + private final AuthQueryService authQueryService; private final ModelMapper modelMapper; - public ContractCommandServiceImpl(ContractRepository contractRepository, ModelMapper modelMapper) { + public ContractCommandServiceImpl(ContractRepository contractRepository, AuthQueryService authQueryService, ModelMapper modelMapper) { this.contractRepository = contractRepository; + this.authQueryService = authQueryService; this.modelMapper = modelMapper; } @@ -53,7 +57,7 @@ public void registerContract(ContractRegistDTO contractRegistRequestDTO) { @Transactional public ContractModifyDTO modifyContract(ContractModifyDTO contractModifyRequestDTO) { - // 회원인지 확인여부 및 값 가져오기 + String memberId = authQueryService.selectMemberIdByLoginId(contractModifyRequestDTO.getMemberId()); // 일련번호로 제품테이블의 총식별번호 찾아서 제품 가져오기 @@ -63,7 +67,7 @@ public ContractModifyDTO modifyContract(ContractModifyDTO contractModifyRequestD // 가져온 고객 정보에 수정된 값 넣기 - Contract contract = contractRepository.findById(contractModifyRequestDTO.getContractId()) + Contract contract = (Contract) contractRepository.findByContractIdAndMemberId(contractModifyRequestDTO.getContractId(), memberId) .orElseThrow(() -> new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND)); Contract updateContract = modelMapper.map(contractModifyRequestDTO, Contract.class); @@ -83,11 +87,11 @@ public ContractModifyDTO modifyContract(ContractModifyDTO contractModifyRequestD @Override @Transactional - public void deleteContract(String id) { + public void deleteContract(ContractDeleteDTO contractDeleteDTO) { - // 회원 확인 + String memberId = authQueryService.selectMemberIdByLoginId(contractDeleteDTO.getMemberId()); - Contract contract = contractRepository.findById(id) + Contract contract = (Contract) contractRepository.findByContractIdAndMemberId(contractDeleteDTO.getContractId(), memberId) .orElseThrow(() -> new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND)); contract.setActive(false); diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/service/PurchaseOrderCommandService.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/service/PurchaseOrderCommandService.java index fbca5bb0..69d52d98 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/service/PurchaseOrderCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/service/PurchaseOrderCommandService.java @@ -1,11 +1,9 @@ package stanl_2.final_backend.domain.purchase_order.command.application.service; -import org.springframework.security.core.GrantedAuthority; import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderModifyDTO; import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderRegistDTO; import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderStatusModifyDTO; -import java.util.Collection; public interface PurchaseOrderCommandService { void registerPurchaseOrder(PurchaseOrderRegistDTO purchaseOrderRegistDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java index f0085a99..c3c05ee4 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java @@ -2,11 +2,9 @@ import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.GrantedAuthority; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; -import stanl_2.final_backend.domain.member.query.service.MemberQueryServiceImpl; import stanl_2.final_backend.domain.order.command.domain.aggregate.entity.Order; import stanl_2.final_backend.domain.order.command.domain.repository.OrderRepository; import stanl_2.final_backend.domain.order.common.exception.OrderCommonException; @@ -25,8 +23,6 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; -import java.util.Collection; -import java.util.Optional; @Service public class PurchaseOrderCommandServiceImpl implements PurchaseOrderCommandService { From 805850cd7489114328c05f4697d7f199b5afab09 Mon Sep 17 00:00:00 2001 From: giuseog Date: Tue, 19 Nov 2024 09:07:53 +0900 Subject: [PATCH 169/563] =?UTF-8?q?refactor:=20=EC=A4=84=20=EB=B0=94?= =?UTF-8?q?=EA=BF=88(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/evaluation/query/repository/EvaluationMapper.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml b/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml index 59ecad46..ce699c09 100644 --- a/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml @@ -157,7 +157,7 @@ AND A.EVAL_TTL LIKE CONCAT('%', #{evaluationSearchDTO.title}, '%') - + ` AND A.CREATED_AT BETWEEN #{evaluationSearchDTO.startDate} AND #{evaluationSearchDTO.endDate} From 9f461c52d38c445ca0c1401dcf3bbf1e30912020 Mon Sep 17 00:00:00 2001 From: giuseog Date: Tue, 19 Nov 2024 09:31:50 +0900 Subject: [PATCH 170/563] =?UTF-8?q?fix:=20=EC=88=98=EC=A0=95=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EB=B0=98=EC=98=81(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/controller/EvaluationController.java | 6 +++--- .../command/domain/aggregate/entity/Evaluation.java | 2 +- .../domain/service/EvaluationCommandServiceImpl.java | 2 +- .../evaluation/common/exception/EvaluationErrorCode.java | 1 - 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/controller/EvaluationController.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/controller/EvaluationController.java index 1ce67b7f..e8ad3713 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/controller/EvaluationController.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/controller/EvaluationController.java @@ -31,7 +31,7 @@ public EvaluationController(EvaluationCommandService evaluationCommandService) { content = {@Content(schema = @Schema(implementation = EvaluationResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postTest(@RequestBody EvaluationRegistDTO evaluationRegistRequestDTO) { + public ResponseEntity postEvaluation(@RequestBody EvaluationRegistDTO evaluationRegistRequestDTO) { evaluationCommandService.registerEvaluation(evaluationRegistRequestDTO); @@ -55,7 +55,7 @@ public ResponseEntity postTest(@RequestBody Evaluatio content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) }) @PutMapping("{id}") - public ResponseEntity putTest(@PathVariable String id, + public ResponseEntity putEvaluation(@PathVariable String id, @RequestBody EvaluationModifyDTO evaluationModifyRequestDTO) { evaluationCommandService.modifyEvaluation(id,evaluationModifyRequestDTO); @@ -76,7 +76,7 @@ public ResponseEntity putTest(@PathVariable String id content = {@Content(schema = @Schema(implementation = EvaluationResponseMessage.class))}) }) @DeleteMapping("{id}") - public ResponseEntity deleteTest(@PathVariable String id) { + public ResponseEntity deleteEvaluation(@PathVariable String id) { evaluationCommandService.deleteEvaluation(id); diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/aggregate/entity/Evaluation.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/aggregate/entity/Evaluation.java index a98cbfdd..d44731b1 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/aggregate/entity/Evaluation.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/aggregate/entity/Evaluation.java @@ -26,7 +26,7 @@ public class Evaluation { parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "EVAL") ) @Column(name = "EVAL_ID") - private String id; + private String evaluationId; @Column(name = "EVAL_TTL") private String title; diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/service/EvaluationCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/service/EvaluationCommandServiceImpl.java index ff6bca72..6708a479 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/service/EvaluationCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/service/EvaluationCommandServiceImpl.java @@ -50,7 +50,7 @@ public void modifyEvaluation(String id, EvaluationModifyDTO evaluationModifyRequ Evaluation updateEvaluation = modelMapper.map(evaluationModifyRequestDTO, Evaluation.class); - updateEvaluation.setId(evaluation.getId()); + updateEvaluation.setEvaluationId(evaluation.getEvaluationId()); updateEvaluation.setCreatedAt(evaluation.getCreatedAt()); updateEvaluation.setUpdatedAt(getCurrentTime()); updateEvaluation.setActive(evaluation.getActive()); diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/common/exception/EvaluationErrorCode.java b/src/main/java/stanl_2/final_backend/domain/evaluation/common/exception/EvaluationErrorCode.java index 1df3bab8..88630557 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/common/exception/EvaluationErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/common/exception/EvaluationErrorCode.java @@ -39,7 +39,6 @@ public enum EvaluationErrorCode { * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. */ SAMPLE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "sample 데이터를 찾지 못했습니다"), - CENTER_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "center 데이터를 찾지 못했습니다."), EVALUATION_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "evaluation 데이터를 찾지 못했습니다."), /** * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. From 6c7140aac8928047e815ccfe9b6c5885d60e364e Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 10:12:41 +0900 Subject: [PATCH 171/563] =?UTF-8?q?feat:=203=EC=B0=A8=20=ED=86=B5=ED=95=A9?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/stanl_2/final_backend/domain/operation_test.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/operation_test.java b/src/main/java/stanl_2/final_backend/domain/operation_test.java index 704a8918..36bbbcc9 100644 --- a/src/main/java/stanl_2/final_backend/domain/operation_test.java +++ b/src/main/java/stanl_2/final_backend/domain/operation_test.java @@ -3,7 +3,6 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; - @RestController public class operation_test { @GetMapping("health") From e3457fbac8893f4b11aab2bc645b5d7f6d8be8b0 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 10:27:03 +0900 Subject: [PATCH 172/563] =?UTF-8?q?feat:=203=EC=B0=A8=20=ED=86=B5=ED=95=A9?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index e3538bad..b84c3c07 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,6 +10,7 @@ services: - SPRING_PROFILES_ACTIVE=prod - REDIS_HOST=redis - REDIS_PORT=6379 + - ALGORITHM=AES/GCM/NoPadding expose: - "8080" depends_on: From ba94e6731e97486a192c1c9523a346ecbcc4ef3b Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 10:40:41 +0900 Subject: [PATCH 173/563] =?UTF-8?q?feat:=203=EC=B0=A8=20=ED=86=B5=ED=95=A9?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index d31f5448..f8da1da3 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -34,9 +34,9 @@ jwt: header: ${JWT_HEADER} encryption: - algorithm: ${ALGORITHM} - transformation: ${TRANSFORMATION} - secret-key: ${SECRET_DEFAULT_KEY} + algorithm: AES + transformation: AES/ECB/PKCS5Padding + secret-key: haWh*9teA@2sT!nLaO$i0lEcj3cU282d logging: From 076a108592445fdddd887234e476673d7ed0a0f9 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 10:52:00 +0900 Subject: [PATCH 174/563] =?UTF-8?q?feat:=203=EC=B0=A8=20=ED=86=B5=ED=95=A9?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index b84c3c07..328fd9b8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,7 +10,8 @@ services: - SPRING_PROFILES_ACTIVE=prod - REDIS_HOST=redis - REDIS_PORT=6379 - - ALGORITHM=AES/GCM/NoPadding + - ALGORITHM=AES + - TRANSFORMATION=AES/GCM/NoPadding expose: - "8080" depends_on: From 08cd94c6ddf0f6be3c624b29fc98b7117837d0ac Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 11:03:22 +0900 Subject: [PATCH 175/563] =?UTF-8?q?feat:=203=EC=B0=A8=20=ED=86=B5=ED=95=A9?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/stanl_2/final_backend/global/utils/AESUtils.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/stanl_2/final_backend/global/utils/AESUtils.java b/src/main/java/stanl_2/final_backend/global/utils/AESUtils.java index 7b94f296..018afaa6 100644 --- a/src/main/java/stanl_2/final_backend/global/utils/AESUtils.java +++ b/src/main/java/stanl_2/final_backend/global/utils/AESUtils.java @@ -50,6 +50,7 @@ public String decrypt(String encryptedData) throws GeneralSecurityException { return null; } + Cipher cipher = Cipher.getInstance(transformation); SecretKey secretKey = new SecretKeySpec(secretKeyValue.getBytes(StandardCharsets.UTF_8), algorithm); cipher.init(Cipher.DECRYPT_MODE, secretKey); From cea850d2224396b5a183bcc5a8d802ee076cd471 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 11:15:26 +0900 Subject: [PATCH 176/563] =?UTF-8?q?feat:=203=EC=B0=A8=20=ED=86=B5=ED=95=A9?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 1 + src/main/resources/application.yml | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 328fd9b8..e26a45d9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,6 +12,7 @@ services: - REDIS_PORT=6379 - ALGORITHM=AES - TRANSFORMATION=AES/GCM/NoPadding + - JWT_SECRET_KEY=s4G7tL9qH8vI2eP0j5W1uF3bM6nK8rXc expose: - "8080" depends_on: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f8da1da3..642cc108 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -7,12 +7,12 @@ spring: datasource: driver-class-name: org.mariadb.jdbc.Driver -# url: jdbc:mariadb://${DATABASE_URL}:${MARIA_DATABASE_PORT}/${MARIA_DATABASE_NAME} + # url: jdbc:mariadb://${DATABASE_URL}:${MARIA_DATABASE_PORT}/${MARIA_DATABASE_NAME} url: jdbc:mariadb://motive.cdw8kw8go27z.ap-northeast-2.rds.amazonaws.com:3306/motive username: admin password: motivepassword -# username: ${DB_USERNAME} -# password: ${DB_PASSWORD} + # username: ${DB_USERNAME} + # password: ${DB_PASSWORD} profiles: active: ${SPRING_PROFILES_ACTIVE} From ed48ebd05ee7c97a41c8914a7bdf11c9d205e974 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 11:32:54 +0900 Subject: [PATCH 177/563] =?UTF-8?q?feat:=203=EC=B0=A8=20=ED=86=B5=ED=95=A9?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/A_sample/query/controller/SampleController.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java b/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java index 8dfda062..ecb121b7 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java @@ -38,11 +38,9 @@ public SampleController(SampleQueryService sampleQueryService) { content = @Content(mediaType = "application/json")) }) @GetMapping("{id}") - public ResponseEntity getTest(@PathVariable String id, - Principal principal){ + public ResponseEntity getTest(@PathVariable String id){ log.info("현재 접속한 회원정보(MEM_LOGIN_ID)"); - log.info(principal.getName()); SampleDTO sampleDTO = sampleQueryService.selectSampleInfo(id); From 0a40679859a44eae0ffc684902641b1ce1af62b5 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 11:37:03 +0900 Subject: [PATCH 178/563] =?UTF-8?q?feat:=203=EC=B0=A8=20=ED=86=B5=ED=95=A9?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/A_sample/query/controller/SampleController.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java b/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java index ecb121b7..8dfda062 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java @@ -38,9 +38,11 @@ public SampleController(SampleQueryService sampleQueryService) { content = @Content(mediaType = "application/json")) }) @GetMapping("{id}") - public ResponseEntity getTest(@PathVariable String id){ + public ResponseEntity getTest(@PathVariable String id, + Principal principal){ log.info("현재 접속한 회원정보(MEM_LOGIN_ID)"); + log.info(principal.getName()); SampleDTO sampleDTO = sampleQueryService.selectSampleInfo(id); From 1041b0ad26bd706e4ee360b2a8944af478536aa5 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 11:54:18 +0900 Subject: [PATCH 179/563] =?UTF-8?q?feat:=203=EC=B0=A8=20=ED=86=B5=ED=95=A9?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 642cc108..b1e8bd5f 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -30,8 +30,9 @@ spring: properties: hibernate.format_sql: true jwt: - secret-key: ${JWT_SECRET_KEY} - header: ${JWT_HEADER} + secret-key: ${SECRET_KEY} + secret-default-value: c29tZSBzZWN1cmUgMTI4LWJpdCBzZWNyZXQga2V5IGZvciBKU1VfUElQ + header: Authorization encryption: algorithm: AES From 791ccab3028fbba0b0fdd12220587e25c5271509 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 12:03:56 +0900 Subject: [PATCH 180/563] =?UTF-8?q?feat:=203=EC=B0=A8=20=ED=86=B5=ED=95=A9?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nginx/default.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nginx/default.conf b/nginx/default.conf index 50fa47fc..39a66a2c 100644 --- a/nginx/default.conf +++ b/nginx/default.conf @@ -6,7 +6,7 @@ server { listen 80; location / { - proxy_pass http://backend; + proxy_pass http://backend:8080; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; From 22a51adb677efaa2f5d13bf63513b8978f2dc8f3 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 12:16:25 +0900 Subject: [PATCH 181/563] =?UTF-8?q?feat:=203=EC=B0=A8=20=ED=86=B5=ED=95=A9?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 2 +- nginx/default.conf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index e26a45d9..ffd607a2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -52,7 +52,7 @@ services: networks: - app-network restart: always - entrypoint: ["sh", "-c", "sleep 15 && nginx -g 'daemon off;'"] + entrypoint: ["sh", "-c", "sleep 30 && nginx -g 'daemon off;'"] networks: app-network: diff --git a/nginx/default.conf b/nginx/default.conf index 39a66a2c..50fa47fc 100644 --- a/nginx/default.conf +++ b/nginx/default.conf @@ -6,7 +6,7 @@ server { listen 80; location / { - proxy_pass http://backend:8080; + proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; From d9b8b4f1f47b9259b5668cd7ac6372db05b208fb Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Tue, 19 Nov 2024 12:27:23 +0900 Subject: [PATCH 182/563] =?UTF-8?q?feat:=20=EA=B3=A0=EA=B0=9D=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EA=B2=80=EC=83=89(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/CustomerController.java | 30 +++++++++++ .../customer/query/dto/CustomerSearchDTO.java | 17 +++++++ .../query/repository/CustomerMapper.java | 9 +++- .../query/service/CustomerQueryService.java | 5 +- .../service/CustomerQueryServiceImpl.java | 24 +++++++++ .../final_backend/global/utils/AESUtils.java | 1 - .../query/repository/CustomerMapper.xml | 50 +++++++++++++++++++ 7 files changed, 132 insertions(+), 4 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/customer/query/dto/CustomerSearchDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java b/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java index 292cbd2a..82ab44e4 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -14,10 +15,12 @@ import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.customer.common.response.CustomerResponseMessage; import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; +import stanl_2.final_backend.domain.customer.query.dto.CustomerSearchDTO; import stanl_2.final_backend.domain.customer.query.service.CustomerQueryService; import java.security.GeneralSecurityException; +@Slf4j @RestController(value = "queryCustomerController") @RequestMapping("/api/v1/customer") public class CustomerController { @@ -72,4 +75,31 @@ public ResponseEntity getCustomers( .build()); } + + @Operation(summary = "고객정보 조건별 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/search") + public ResponseEntity searchCustomer( + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "10") int size, + @RequestParam(required = false) String name, + @RequestParam(required = false) String sex, + @RequestParam(required = false) String phone + ) { + Pageable pageable = PageRequest.of(page, size); + CustomerSearchDTO customerSearchDTO = new CustomerSearchDTO(null , name, sex, phone); + Page customerDTOPage = customerQueryService.findCustomerByCondition(pageable, customerSearchDTO); + + return ResponseEntity.ok(CustomerResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(customerDTOPage) + .build()); + } + } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/dto/CustomerSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/customer/query/dto/CustomerSearchDTO.java new file mode 100644 index 00000000..4912cb05 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/dto/CustomerSearchDTO.java @@ -0,0 +1,17 @@ +package stanl_2.final_backend.domain.customer.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +public class CustomerSearchDTO { + private String customerId; + private String name; + private String sex; + private String phone; +} diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java b/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java index 60fba85f..72044984 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java @@ -3,14 +3,21 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; +import stanl_2.final_backend.domain.customer.query.dto.CustomerSearchDTO; import java.util.List; +import java.util.Map; @Mapper public interface CustomerMapper { CustomerDTO selectCustomerInfoById(@Param("customerId") String customerId); - List selectCustomerList(int offset, int size); + List selectCustomerList(@Param("offset") int offset, @Param("size") int size); int selectCustomerCount(); + + List findCustomerByConditions(Map map); + + int findCustomerCnt(Map map); + } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java index 96133475..5a66c6cb 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java @@ -3,13 +3,14 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; -import stanl_2.final_backend.domain.customer.query.dto.CustomerNameListDTO; +import stanl_2.final_backend.domain.customer.query.dto.CustomerSearchDTO; import java.security.GeneralSecurityException; -import java.util.List; public interface CustomerQueryService { CustomerDTO selectCustomerInfo(String customerId) throws GeneralSecurityException; Page selectCustomerList(Pageable pageable); + + Page findCustomerByCondition(Pageable pageable, CustomerSearchDTO customerSearchDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java index 7108e3dc..3960e179 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java @@ -8,11 +8,14 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; +import stanl_2.final_backend.domain.customer.query.dto.CustomerSearchDTO; import stanl_2.final_backend.domain.customer.query.repository.CustomerMapper; import stanl_2.final_backend.global.utils.AESUtils; import java.security.GeneralSecurityException; +import java.util.HashMap; import java.util.List; +import java.util.Map; @Slf4j @Service("queryCustomerService") @@ -51,4 +54,25 @@ public Page selectCustomerList(Pageable pageable) { return new PageImpl<>(customerList, pageable, totalElements); } + + @Override + @Transactional + public Page findCustomerByCondition(Pageable pageable, CustomerSearchDTO customerSearchDTO) { + int page = pageable.getPageNumber(); + int size = pageable.getPageSize(); + int offset = page*size; + + Map params = new HashMap<>(); + params.put("offset", offset); + params.put("size", size); + params.put("name", customerSearchDTO.getName()); + params.put("sex", customerSearchDTO.getSex()); + params.put("phone", customerSearchDTO.getPhone()); + + List customerList = customerMapper.findCustomerByConditions(params); + + Integer count = customerMapper.findCustomerCnt(params); + + return new PageImpl<>(customerList, pageable, count); + } } diff --git a/src/main/java/stanl_2/final_backend/global/utils/AESUtils.java b/src/main/java/stanl_2/final_backend/global/utils/AESUtils.java index 7b94f296..4ee7ff34 100644 --- a/src/main/java/stanl_2/final_backend/global/utils/AESUtils.java +++ b/src/main/java/stanl_2/final_backend/global/utils/AESUtils.java @@ -1,6 +1,5 @@ package stanl_2.final_backend.global.utils; -import jakarta.annotation.PostConstruct; import lombok.extern.slf4j.Slf4j; import org.apache.tomcat.util.codec.binary.Base64; import org.springframework.beans.factory.annotation.Value; diff --git a/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml b/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml index e882f73a..65d1432e 100644 --- a/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml @@ -19,6 +19,13 @@ + + + + + + + + + + + + + \ No newline at end of file From 1e4f375b6b3fd1f49b6653b1cd00a3d3fffadfb0 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Tue, 19 Nov 2024 12:34:14 +0900 Subject: [PATCH 183/563] =?UTF-8?q?fix:=20=EA=B3=A0=EA=B0=9D=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20=EB=B3=B5=ED=98=B8=ED=99=94=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/CustomerController.java | 6 +++--- .../query/service/CustomerQueryService.java | 4 ++-- .../service/CustomerQueryServiceImpl.java | 19 +++++++++++++++++-- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java b/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java index 82ab44e4..15fd9880 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java @@ -63,7 +63,7 @@ public ResponseEntity getCustomerInfo(@PathVariable Str public ResponseEntity getCustomers( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size - ){ + ) throws GeneralSecurityException { Pageable pageable = PageRequest.of(page, size); Page customerDTOPage = customerQueryService.selectCustomerList(pageable); @@ -90,11 +90,11 @@ public ResponseEntity searchCustomer( @RequestParam(required = false) String name, @RequestParam(required = false) String sex, @RequestParam(required = false) String phone - ) { + ) throws GeneralSecurityException { Pageable pageable = PageRequest.of(page, size); CustomerSearchDTO customerSearchDTO = new CustomerSearchDTO(null , name, sex, phone); Page customerDTOPage = customerQueryService.findCustomerByCondition(pageable, customerSearchDTO); - + return ResponseEntity.ok(CustomerResponseMessage.builder() .httpStatus(200) .msg("성공") diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java index 5a66c6cb..14e14efe 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java @@ -10,7 +10,7 @@ public interface CustomerQueryService { CustomerDTO selectCustomerInfo(String customerId) throws GeneralSecurityException; - Page selectCustomerList(Pageable pageable); + Page selectCustomerList(Pageable pageable) throws GeneralSecurityException; - Page findCustomerByCondition(Pageable pageable, CustomerSearchDTO customerSearchDTO); + Page findCustomerByCondition(Pageable pageable, CustomerSearchDTO customerSearchDTO) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java index 3960e179..066dc4da 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java @@ -46,18 +46,26 @@ public CustomerDTO selectCustomerInfo(String customerId) throws GeneralSecurityE @Override @Transactional - public Page selectCustomerList(Pageable pageable) { + public Page selectCustomerList(Pageable pageable) throws GeneralSecurityException { int page = pageable.getPageNumber(); int size = pageable.getPageSize(); List customerList = customerMapper.selectCustomerList(page*size, size); int totalElements = customerMapper.selectCustomerCount(); + // 복호화 + for(int i=0;i< customerList.size();i++){ + customerList.get(i).setPhone(aesUtils.decrypt(customerList.get(i).getPhone())); + customerList.get(i).setEmergePhone(aesUtils.decrypt(customerList.get(i).getEmergePhone())); + customerList.get(i).setEmail(aesUtils.decrypt(customerList.get(i).getEmail())); + } + + return new PageImpl<>(customerList, pageable, totalElements); } @Override @Transactional - public Page findCustomerByCondition(Pageable pageable, CustomerSearchDTO customerSearchDTO) { + public Page findCustomerByCondition(Pageable pageable, CustomerSearchDTO customerSearchDTO) throws GeneralSecurityException { int page = pageable.getPageNumber(); int size = pageable.getPageSize(); int offset = page*size; @@ -73,6 +81,13 @@ public Page findCustomerByCondition(Pageable pageable, CustomerSear Integer count = customerMapper.findCustomerCnt(params); + for(int i=0;i< customerList.size();i++){ + customerList.get(i).setPhone(aesUtils.decrypt(customerList.get(i).getPhone())); + customerList.get(i).setEmergePhone(aesUtils.decrypt(customerList.get(i).getEmergePhone())); + customerList.get(i).setEmail(aesUtils.decrypt(customerList.get(i).getEmail())); + } + + return new PageImpl<>(customerList, pageable, count); } } From 4f0379ddd2d42dd4a1f4c5193ac1da0353f4636d Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Tue, 19 Nov 2024 12:36:04 +0900 Subject: [PATCH 184/563] =?UTF-8?q?fix:=20=EC=95=88=EC=93=B0=EB=8A=94=20im?= =?UTF-8?q?port=20=EC=82=AD=EC=A0=9C(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/application/controller/CustomerController.java | 1 - .../domain/customer/query/repository/CustomerMapper.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java b/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java index d945039a..eca8340a 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java @@ -8,7 +8,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; -import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.customer.command.application.dto.CustomerModifyDTO; diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java b/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java index 72044984..7816ee9d 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java @@ -3,7 +3,6 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; -import stanl_2.final_backend.domain.customer.query.dto.CustomerSearchDTO; import java.util.List; import java.util.Map; From 4444898748ae4361a8f08c684e6c93a67715af18 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 12:36:05 +0900 Subject: [PATCH 185/563] =?UTF-8?q?feat:=203=EC=B0=A8=20=ED=86=B5=ED=95=A9?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 1 - src/main/resources/application.yml | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index ffd607a2..c5b62445 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,7 +12,6 @@ services: - REDIS_PORT=6379 - ALGORITHM=AES - TRANSFORMATION=AES/GCM/NoPadding - - JWT_SECRET_KEY=s4G7tL9qH8vI2eP0j5W1uF3bM6nK8rXc expose: - "8080" depends_on: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index b1e8bd5f..fbb4a4f3 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -29,9 +29,9 @@ spring: ddl-auto: update properties: hibernate.format_sql: true + jwt: - secret-key: ${SECRET_KEY} - secret-default-value: c29tZSBzZWN1cmUgMTI4LWJpdCBzZWNyZXQga2V5IGZvciBKU1VfUElQ + secret-key: s4G7tL9qH8vI2eP0j5W1uF3bM6nK8rXc header: Authorization encryption: From b5a18415e23ff967de26c6dafdc5a54b9255cfc8 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Tue, 19 Nov 2024 12:38:17 +0900 Subject: [PATCH 186/563] =?UTF-8?q?fix:=20=EC=A3=BC=EC=84=9D=20=EA=B6=8C?= =?UTF-8?q?=ED=95=9C=20=EC=B2=B4=ED=81=AC(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/application/controller/CustomerController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java b/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java index eca8340a..a2b44b07 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java @@ -56,7 +56,7 @@ public ResponseEntity postCustomer(@RequestBody Custome .build()); } - @Operation(summary = "고객정보 수정") + @Operation(summary = "고객정보 수정(자기 고객만)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) @@ -76,7 +76,7 @@ public ResponseEntity postCustomer(@PathVariable String .build()); } - @Operation(summary = "고객정보 수정") + @Operation(summary = "고객정보 삭제(자기 고객만)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) From f8fb4af731ac1bfaeffe1caf0e5495739fdc3e7f Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Tue, 19 Nov 2024 14:06:02 +0900 Subject: [PATCH 187/563] =?UTF-8?q?fix:=20pr=20=EB=B0=98=EC=98=81=EC=A4=91?= =?UTF-8?q?~ing(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/application/controller/CustomerController.java | 6 +++++- .../customer/command/application/dto/CustomerModifyDTO.java | 1 + .../command/domain/repository/CustomerRepository.java | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java b/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java index a2b44b07..9fe3d31f 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java @@ -63,9 +63,13 @@ public ResponseEntity postCustomer(@RequestBody Custome }) @PutMapping("/{customerId}") public ResponseEntity postCustomer(@PathVariable String customerId, - @RequestBody CustomerModifyDTO customerModifyDTO) throws GeneralSecurityException { + @RequestBody CustomerModifyDTO customerModifyDTO, + Principal principal) throws GeneralSecurityException { + + String memberId = principal.getName(); customerModifyDTO.setCustomerId(customerId); + customerModifyDTO.setMemberId(memberId); customerCommandService.modifyCustomerInfo(customerModifyDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/application/dto/CustomerModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/customer/command/application/dto/CustomerModifyDTO.java index c1a42121..937a5f8e 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/application/dto/CustomerModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/application/dto/CustomerModifyDTO.java @@ -10,6 +10,7 @@ @Setter @Getter public class CustomerModifyDTO { + private String memberId; private String customerId; private String name; private Integer age; diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/repository/CustomerRepository.java b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/repository/CustomerRepository.java index 78a65ae2..de571e93 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/repository/CustomerRepository.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/repository/CustomerRepository.java @@ -4,6 +4,8 @@ import org.springframework.stereotype.Repository; import stanl_2.final_backend.domain.customer.command.domain.aggregate.entity.Customer; +import java.util.Optional; + @Repository public interface CustomerRepository extends JpaRepository { } From 5a2ddf218f24e2a61f7395ce32a7c5d846274f14 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 14:14:58 +0900 Subject: [PATCH 188/563] =?UTF-8?q?feat:=203=EC=B0=A8=20=ED=86=B5=ED=95=A9?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index fbb4a4f3..de2a0a23 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,6 +1,7 @@ server: forward-headers-strategy: native + spring: application: name: finalbackend From 9de3ca0c68bfc3d9f85ed67fc9ee3abfbdaba117 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 14:37:17 +0900 Subject: [PATCH 189/563] =?UTF-8?q?feat:=203=EC=B0=A8=20=ED=86=B5=ED=95=A9?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 2 +- src/main/resources/sql/ddl.sql | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index de2a0a23..bb6f461a 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -27,7 +27,7 @@ spring: jpa: show-sql: true hibernate: - ddl-auto: update + ddl-auto: create properties: hibernate.format_sql: true diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index 06cb3f61..7c1ba1d2 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -939,6 +939,4 @@ VALUES ('SAL_000000001', 'CON_000000001'), ('SAL_000000007', 'CON_000000007'), ('SAL_000000008', 'CON_000000008'), ('SAL_000000009', 'CON_000000009'), - ('SAL_000000010', 'CON_000000010'); - - + ('SAL_000000010', 'CON_000000010'); \ No newline at end of file From 6a63a970b5fe253220107ca987782c0beaac4782 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 14:47:51 +0900 Subject: [PATCH 190/563] =?UTF-8?q?feat:=203=EC=B0=A8=20=ED=86=B5=ED=95=A9?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../final_backend/global/security/config/DevSecurityConfig.java | 2 +- .../global/security/config/ProdSecurityConfig.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java index d7ed8e7d..5a490381 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java @@ -49,7 +49,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Excepti "/api/v1/auth/signin", "/api/v1/auth/signup", "/api/v1/auth/refresh", - "/api/v1/auth" // 권한 부여때문에(일단 열어둠) + "/api/v1/auth/**" // 권한 부여때문에(일단 열어둠) ).permitAll() // [Example] member는 ADMIN 권한만 접근 가능 설정 예시 .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasRole("ADMIN") diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index 3ba77ab0..b583c1b8 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -48,7 +48,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication "/api/v1/auth/signin", "/api/v1/auth/signup", "/api/v1/auth/refresh", - "/api/v1/auth" // 권한 부여때문에(일단 열어둠) + "/api/v1/auth/**" // 권한 부여때문에(일단 열어둠) ).permitAll() // [Example] member는 ADMIN 권한만 접근 가능 설정 예시 // .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasRole("ADMIN") From 790b261a0d54b767074453d6c6d6ecb27e6572ac Mon Sep 17 00:00:00 2001 From: giuseog Date: Tue, 19 Nov 2024 15:08:32 +0900 Subject: [PATCH 191/563] =?UTF-8?q?feat:=20=ED=8C=90=EB=A7=A4=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20cd=20=EA=B5=AC=ED=98=84=20-=20column=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=ED=95=84=EC=9A=94(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/aggregate/entity/Evaluation.java | 12 +-- .../dto/SalesHistoryRegistDTO.java | 21 +++++ .../service/SalesHistoryCommandService.java | 11 +++ .../domain/aggregate/entity/SalesHistory.java | 73 ++++++++++++++++ .../repository/SalesHistoryRepository.java | 9 ++ .../SalesHistoryCommandServiceImpl.java | 87 +++++++++++++++++++ .../exception/SalesHistoryErrorCode.java | 3 +- src/main/resources/sql/ddl.sql | 41 ++++++--- 8 files changed, 238 insertions(+), 19 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/sales_history/command/application/dto/SalesHistoryRegistDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/sales_history/command/application/service/SalesHistoryCommandService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/aggregate/entity/SalesHistory.java create mode 100644 src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/repository/SalesHistoryRepository.java create mode 100644 src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/service/SalesHistoryCommandServiceImpl.java diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/aggregate/entity/Evaluation.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/aggregate/entity/Evaluation.java index d44731b1..a582a4b9 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/aggregate/entity/Evaluation.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/aggregate/entity/Evaluation.java @@ -28,10 +28,10 @@ public class Evaluation { @Column(name = "EVAL_ID") private String evaluationId; - @Column(name = "EVAL_TTL") + @Column(name = "EVAL_TTL", nullable = false) private String title; - @Column(name = "EVAL_CONT") + @Column(name = "EVAL_CONT", nullable = false) private String content; @Column(name = "CREATED_AT", nullable = false, updatable = false) @@ -43,16 +43,16 @@ public class Evaluation { @Column(name = "DELETED_AT") private String deletedAt; - @Column(name = "ACTIVE") + @Column(name = "ACTIVE", nullable = false) private Boolean active = true; - @Column(name = "CENT_ID") + @Column(name = "CENT_ID", nullable = false) private String centerId; - @Column(name = "MEM_ID") + @Column(name = "MEM_ID", nullable = false) private String memberId; - @Column(name = "WRI_ID") + @Column(name = "WRI_ID", nullable = false) private String writerId; /* 설명. updatedAt 자동화 */ diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/command/application/dto/SalesHistoryRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/command/application/dto/SalesHistoryRegistDTO.java new file mode 100644 index 00000000..5f51d4d5 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/command/application/dto/SalesHistoryRegistDTO.java @@ -0,0 +1,21 @@ +package stanl_2.final_backend.domain.sales_history.command.application.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class SalesHistoryRegistDTO { + private Integer numberOfVehicles; + private Integer totalSales; + private Integer incentive; + private String contractId; + private String customerInfoId; + private String productId; + private String memberId; + private String centerId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/command/application/service/SalesHistoryCommandService.java b/src/main/java/stanl_2/final_backend/domain/sales_history/command/application/service/SalesHistoryCommandService.java new file mode 100644 index 00000000..8047eefa --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/command/application/service/SalesHistoryCommandService.java @@ -0,0 +1,11 @@ +package stanl_2.final_backend.domain.sales_history.command.application.service; + +import stanl_2.final_backend.domain.sales_history.command.application.dto.SalesHistoryRegistDTO; +import stanl_2.final_backend.domain.sales_history.command.domain.repository.SalesHistoryRepository; + +public interface SalesHistoryCommandService { + + void registerSalesHistory(String contractId); + + void deleteSalesHistory(String id); +} diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/aggregate/entity/SalesHistory.java b/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/aggregate/entity/SalesHistory.java new file mode 100644 index 00000000..740dc6d9 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/aggregate/entity/SalesHistory.java @@ -0,0 +1,73 @@ +package stanl_2.final_backend.domain.sales_history.command.domain.aggregate.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "TB_SALES_HISTORY") +public class SalesHistory { + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "SAL") + ) + @Column(name = "SAL_HIST_ID") + private String salesHistoryId; + + @Column(name = "SAL_HIST_NO_OF_VEH", nullable = false) + private Integer numberOfVehicles; + + @Column(name = "SAL_HIST_TOTA_SALE", nullable = false) + private Integer totalSales; + + @Column(name = "SAL_HIST_INCE", nullable = false) + private Integer incentive; + + @Column(name = "CREATED_AT", nullable = false) + private String createdAt; + + @Column(name = "DELETED_AT") + private String deletedAt; + + @Column(name = "ACTIVE", nullable = false) + private Boolean active = true; + + @Column(name = "CONR_ID", nullable = false) + private String contractId; + + @Column(name = "CUST_ID", nullable = false) + private String customerInfoId; + + @Column(name = "PROD_ID", nullable = false) + private String productId; + + @Column(name = "MEM_ID", nullable = false) + private String memberId; + + @Column(name = "CENT_ID", nullable = false) + private String centerId; + + @PrePersist + private void prePersist() { + this.createdAt = getCurrentTime(); + } + + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/repository/SalesHistoryRepository.java b/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/repository/SalesHistoryRepository.java new file mode 100644 index 00000000..f8eb8cf2 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/repository/SalesHistoryRepository.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.sales_history.command.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import stanl_2.final_backend.domain.sales_history.command.domain.aggregate.entity.SalesHistory; + +@Repository +public interface SalesHistoryRepository extends JpaRepository { +} diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/service/SalesHistoryCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/service/SalesHistoryCommandServiceImpl.java new file mode 100644 index 00000000..08b93977 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/service/SalesHistoryCommandServiceImpl.java @@ -0,0 +1,87 @@ +package stanl_2.final_backend.domain.sales_history.command.domain.service; + +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.contract.query.dto.ContractSeletIdDTO; +import stanl_2.final_backend.domain.contract.query.service.ContractQueryService; +import stanl_2.final_backend.domain.sales_history.command.application.dto.SalesHistoryRegistDTO; +import stanl_2.final_backend.domain.sales_history.command.application.service.SalesHistoryCommandService; +import stanl_2.final_backend.domain.sales_history.command.domain.aggregate.entity.SalesHistory; +import stanl_2.final_backend.domain.sales_history.command.domain.repository.SalesHistoryRepository; +import stanl_2.final_backend.domain.sales_history.common.exception.SalesHistoryCommonException; +import stanl_2.final_backend.domain.sales_history.common.exception.SalesHistoryErrorCode; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@Service +@Transactional(readOnly = true) +public class SalesHistoryCommandServiceImpl implements SalesHistoryCommandService { + + private final SalesHistoryRepository salesHistoryRepository; + private final ModelMapper modelMapper; + private final ContractQueryService contractQueryService; + + @Autowired + public SalesHistoryCommandServiceImpl(SalesHistoryRepository salesHistoryRepository, ModelMapper modelMapper, ContractQueryService contractQueryService) { + this.salesHistoryRepository = salesHistoryRepository; + this.modelMapper = modelMapper; + this.contractQueryService = contractQueryService; + } + + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } + + @Override + @Transactional + public void registerSalesHistory(String contractId) { + /* 설명. 넘어오는 값은 계약서 승인 후 - 계약서 id */ + ContractSeletIdDTO salesHistoryDTO = new ContractSeletIdDTO(); + /* 설명. 값 주입 dto */ + SalesHistoryRegistDTO salesHistoryRegistDTO = new SalesHistoryRegistDTO(); + + salesHistoryDTO.setContractId(contractId); + + ContractSeletIdDTO responseContract = contractQueryService.selectDetailContract(salesHistoryDTO); + + if (responseContract == null) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.CONTRACT_NOT_FOUND); + } + + salesHistoryRegistDTO.setContractId(contractId); + /* 설명. contract 자료형 변환 후 주석 풀 예정 */ +// salesHistoryRegistDTO.setTotalSales(responseContract.getTotalSales()); +// salesHistoryRegistDTO.setNumberOfVehicles(responseContract.getNumberOfVehicles()); + salesHistoryRegistDTO.setCustomerInfoId(responseContract.getCustomerId()); + salesHistoryRegistDTO.setProductId(responseContract.getProductId()); + salesHistoryRegistDTO.setCenterId(responseContract.getCenterId()); + salesHistoryRegistDTO.setMemberId(responseContract.getMemberId()); + + String customerPurchaseCondition = responseContract.getCustomerPurchaseCondition(); + +// if(customerPurchaseCondition.equals("CASH")){ +// salesHistoryRegistDTO.setIncentive(responseContract.getTotalSales() * 0.035); +// }else if(customerPurchaseCondition.equals("INSTALLMENT")){ +// salesHistoryRegistDTO.setIncentive(responseContract.getTotalSales() * 0.04); +// }else if(customerPurchaseCondition.equals("LEASE")){ +// salesHistoryRegistDTO.setIncentive(responseContract.getTotalSales() * 0.045); +// } + } + + @Override + @Transactional + public void deleteSalesHistory(String id) { + SalesHistory salesHistory = salesHistoryRepository.findById(id) + .orElseThrow(() -> new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND)); + + salesHistory.setActive(false); + salesHistory.setDeletedAt(getCurrentTime()); + + salesHistoryRepository.save(salesHistory); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/common/exception/SalesHistoryErrorCode.java b/src/main/java/stanl_2/final_backend/domain/sales_history/common/exception/SalesHistoryErrorCode.java index 657336ea..e177ec74 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/common/exception/SalesHistoryErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/common/exception/SalesHistoryErrorCode.java @@ -39,7 +39,8 @@ public enum SalesHistoryErrorCode { * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. */ SAMPLE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "sample 데이터를 찾지 못했습니다"), - CENTER_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "center 데이터를 찾지 못했습니다."), + SALES_HISTORY_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "판매내역 데이터를 찾지 못했습니다"), + CONTRACT_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "계약서 데이터를 찾지 못했습니다"), /** * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. */ diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index 06cb3f61..5b82a3a5 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -434,7 +434,23 @@ CREATE TABLE tb_PRODUCT_OPTION CREATE TABLE tb_sales_history ( SAL_HIST_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', - CONR_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고' + SAL_HIST_NO_OF_VEH INT NOT NULL COMMENT '차량 대수', + SAL_HIST_TOTA_SALE INT NOT NULL COMMENT '총 판매가', + SAL_HIST_INCE INT NOT NULL COMMENT '수당', + CREATED_AT CHAR(19) NOT NULL, + DELETED_AT CHAR(19) NULL, + ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, + CONR_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', + CUST_ID VARCHAR(255) NOT NULL, + PROD_ID VARCHAR(255) NOT NULL, + MEM_ID VARCHAR(255) NOT NULL, + CENT_ID VARCHAR(255) NOT NULL, + PRIMARY KEY (SAL_HIST_ID), + FOREIGN KEY (CONR_ID) REFERENCES tb_contract (CONR_ID), + FOREIGN KEY (CUST_ID) REFERENCES tb_customer_info (CUST_ID), + FOREIGN KEY (PROD_ID) REFERENCES tb_product (PROD_ID), + FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), + FOREIGN KEY (CENT_ID) REFERENCES tb_center (CENT_ID) ); INSERT INTO tb_organization_chart (ORG_CHA_ID, ORG_CHA_NAME, ORG_CHA_DEPTH) @@ -929,16 +945,17 @@ VALUES ('PRO_000000010', 'KNFHC54CAMR1A00010', 'K', 'N', 'F', 'F', 'N', '4', '3', 'C', 'R', 'K', 'A', '0', '1', '1', 'W', 'B', '1', '1', '0', '0', '0', '1', '0', '1', '1', TRUE); -INSERT INTO tb_sales_history (SAL_HIST_ID, CONR_ID) -VALUES ('SAL_000000001', 'CON_000000001'), - ('SAL_000000002', 'CON_000000002'), - ('SAL_000000003', 'CON_000000003'), - ('SAL_000000004', 'CON_000000004'), - ('SAL_000000005', 'CON_000000005'), - ('SAL_000000006', 'CON_000000006'), - ('SAL_000000007', 'CON_000000007'), - ('SAL_000000008', 'CON_000000008'), - ('SAL_000000009', 'CON_000000009'), - ('SAL_000000010', 'CON_000000010'); +INSERT INTO tb_sales_history (SAL_HIST_ID, SAL_HIST_NO_OF_VEH, SAL_HIST_TOTA_SALE, SAL_HIST_INCE, CREATED_AT, DELETED_AT, ACTIVE, CONR_ID, CUST_ID, PROD_ID, MEM_ID, CENT_ID) +VALUES + ('SAL_000000001', '1', '50000', '200', '2024-02-05 18:00:00', null, TRUE, 'CON_000000001', 'CUS_000000001', 'PRO_000000001', 'MEM_000000001', 'CEN_000000001'), + ('SAL_000000002', '2', '100000', '300', '2024-02-06 12:00:00', null, TRUE, 'CON_000000002', 'CUS_000000002', 'PRO_000000002', 'MEM_000000002', 'CEN_000000002'), + ('SAL_000000003', '1', '70000', '150', '2024-02-07 09:30:00', null, TRUE, 'CON_000000003', 'CUS_000000003', 'PRO_000000003', 'MEM_000000003', 'CEN_000000003'), + ('SAL_000000004', '3', '150000', '500', '2024-02-08 15:45:00', null, TRUE, 'CON_000000004', 'CUS_000000004', 'PRO_000000004', 'MEM_000000004', 'CEN_000000004'), + ('SAL_000000005', '2', '110000', '400', '2024-02-09 17:00:00', null, TRUE, 'CON_000000005', 'CUS_000000005', 'PRO_000000005', 'MEM_000000005', 'CEN_000000005'), + ('SAL_000000006', '1', '60000', '250', '2024-02-10 14:20:00', null, TRUE, 'CON_000000006', 'CUS_000000006', 'PRO_000000006', 'MEM_000000006', 'CEN_000000006'), + ('SAL_000000007', '4', '200000', '600', '2024-02-11 10:10:00', null, TRUE, 'CON_000000007', 'CUS_000000007', 'PRO_000000007', 'MEM_000000007', 'CEN_000000007'), + ('SAL_000000008', '2', '95000', '300', '2024-02-12 16:50:00', null, TRUE, 'CON_000000008', 'CUS_000000008', 'PRO_000000008', 'MEM_000000008', 'CEN_000000008'), + ('SAL_000000009', '3', '180000', '550', '2024-02-13 11:25:00', null, TRUE, 'CON_000000009', 'CUS_000000009', 'PRO_000000009', 'MEM_000000009', 'CEN_000000009'), + ('SAL_000000010', '1', '75000', '200', '2024-02-14 13:40:00', null, TRUE, 'CON_000000010', 'CUS_000000010', 'PRO_000000010', 'MEM_000000010', 'CEN_000000010'); From a81c676bf04c3f7febbd6751dcb4f9e921277cc3 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 15:37:12 +0900 Subject: [PATCH 192/563] =?UTF-8?q?fix:=20ddl=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/sql/ddl.sql | 942 --------------------------------- 1 file changed, 942 deletions(-) diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index 7c1ba1d2..e69de29b 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -1,942 +0,0 @@ --- 외래 키 제약 조건 비활성화 -SET FOREIGN_KEY_CHECKS = 0; - --- 자식 테이블부터 삭제 -DROP TABLE IF EXISTS sample; -DROP TABLE IF EXISTS tb_sales_history; -DROP TABLE IF EXISTS tb_update_history; -DROP TABLE IF EXISTS tb_product_option; -DROP TABLE IF EXISTS tb_alarm; -DROP TABLE IF EXISTS tb_schedule; -DROP TABLE IF EXISTS tb_file; -DROP TABLE IF EXISTS tb_promotion; -DROP TABLE IF EXISTS tb_evaluation; -DROP TABLE IF EXISTS tb_purchase_order; -DROP TABLE IF EXISTS tb_problem; -DROP TABLE IF EXISTS tb_order; -DROP TABLE IF EXISTS tb_notice; -DROP TABLE IF EXISTS tb_contract; -DROP TABLE IF EXISTS tb_customer_info; -DROP TABLE IF EXISTS tb_member_role; -DROP TABLE IF EXISTS tb_member; -DROP TABLE IF EXISTS tb_center; -DROP TABLE IF EXISTS tb_family; -DROP TABLE IF EXISTS tb_education; -DROP TABLE IF EXISTS tb_certification; -DROP TABLE IF EXISTS tb_career; -DROP TABLE IF EXISTS tb_product; -DROP TABLE IF EXISTS tb_organization_chart; - - --- 조직 관련 테이블 생성 -CREATE TABLE tb_organization_chart -( - ORG_CHA_ID VARCHAR(255) NOT NULL, - ORG_CHA_NAME VARCHAR(255) NOT NULL, - ORG_CHA_DEPTH INT NOT NULL, - PRIMARY KEY (ORG_CHA_ID) -); - -CREATE TABLE tb_center -( - CENT_ID VARCHAR(255) NOT NULL, - CENT_NAME VARCHAR(255) NOT NULL, - CENT_ADR VARCHAR(255) NOT NULL, - CENT_PHO VARCHAR(255) NOT NULL, - CENT_MEM_CNT INT NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL DEFAULT 'CURRENT_TIMESTAMP', - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL, - CENT_OPR_AT VARCHAR(255) NOT NULL, - PRIMARY KEY (CENT_ID) -); - -CREATE TABLE tb_member -( - MEM_ID VARCHAR(255) NOT NULL, - MEM_LOGIN_ID VARCHAR(255) NOT NULL, - MEM_PWD VARCHAR(255) NOT NULL, - MEM_NAME VARCHAR(255) NOT NULL, - MEM_EMA VARCHAR(255) NOT NULL, - MEM_AGE INT NOT NULL, - MEM_SEX VARCHAR(255) NOT NULL DEFAULT 'FEMALE', - MEM_IDEN_NO VARCHAR(255) NOT NULL, - MEM_PHO VARCHAR(255) NOT NULL, - MEM_EMER_PHO VARCHAR(255) NULL, - MEM_ADR VARCHAR(255) NOT NULL, - MEM_NOTE VARCHAR(255) NULL, - MEM_POS VARCHAR(255) NOT NULL DEFAULT 'INTERN', - MEM_GRD VARCHAR(255) NOT NULL DEFAULT 'High School', - MEM_JOB_TYPE VARCHAR(255) NOT NULL, - MEM_MIL VARCHAR(255) NOT NULL DEFAULT 'exemption', - MEM_BANK_NAME VARCHAR(255) NULL, - MEM_ACC VARCHAR(255) NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - CENTER_ID VARCHAR(255) NOT NULL, - ORG_CHA_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (MEM_ID), - FOREIGN KEY (CENTER_ID) REFERENCES tb_center (CENT_ID), - FOREIGN KEY (ORG_CHA_ID) REFERENCES tb_organization_chart (ORG_CHA_ID) -); - -CREATE TABLE tb_member_role -( - MEM_ROL_ID VARCHAR(255) NOT NULL, - MEM_ROL_NAME VARCHAR(255) NOT NULL DEFAULT '영업 사원', - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (MEM_ROL_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_customer_info -( - CUST_ID VARCHAR(255) NOT NULL, - CUST_NAME VARCHAR(255) NOT NULL, - CUST_AGE INT NOT NULL, - CUST_SEX VARCHAR(255) NOT NULL DEFAULT 'FEMALE', - CUST_PHO VARCHAR(255) NOT NULL, - CUST_EMER_PHO VARCHAR(255) NOT NULL, - CUST_EMA VARCHAR(255) NOT NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (CUST_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - --- 부모 테이블 생성 -CREATE TABLE tb_product -( - PROD_ID VARCHAR(255) NOT NULL, - PROD_SER_NO VARCHAR(255) NOT NULL, - PROD_COST INT NOT NULL, - PROD_NAME VARCHAR(255) NOT NULL, - PROD_STCK INT NOT NULL DEFAULT 0, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL DEFAULT 'CURRENT_TIMESTAMP', - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - PRIMARY KEY (PROD_ID, PROD_SER_NO) -); - -CREATE TABLE tb_contract -( - CONR_ID VARCHAR(255) NOT NULL, - CONR_NAME VARCHAR(255) NOT NULL, - CONR_CUST_NAME VARCHAR(255) NOT NULL, - CONR_CUST_IDEN_NO VARCHAR(255) NOT NULL, - CONR_CUST_ADR VARCHAR(255) NOT NULL, - CONR_CUST_EMA VARCHAR(255) NOT NULL, - CONR_CUST_PHO VARCHAR(255) NOT NULL, - CONR_COMP_NAME VARCHAR(255) NULL, - CONR_CUST_CLA VARCHAR(255) NOT NULL DEFAULT 'PERSONAL', - CONR_CUST_PUR_COND VARCHAR(255) NOT NULL DEFAULT 'CASH', - CONR_SERI_NUM VARCHAR(255) NOT NULL, - CONR_SELE_OPTI VARCHAR(255) NOT NULL, - CONR_DOWN_PAY INT NOT NULL, - CONR_INTE_PAY INT NOT NULL, - CONR_REM_PAY INT NOT NULL, - CONR_CONS_PAY INT NOT NULL, - CONR_DELV_DATE VARCHAR(255) NULL, - CONR_DELV_LOC VARCHAR(255) NULL, - CONR_STAT VARCHAR(255) NULL DEFAULT 'WAIT', - CONR_NO_OF_VEH INT NOT NULL DEFAULT 1, - CREATED_URL TEXT NOT NULL, - DELETED_URL TEXT NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - MEM_ID VARCHAR(255) NOT NULL, - CENT_ID VARCHAR(255) NOT NULL, - CUST_ID VARCHAR(255) NULL, - PROD_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (CONR_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE, - FOREIGN KEY (CUST_ID) REFERENCES tb_customer_info (CUST_ID) ON DELETE CASCADE, - FOREIGN KEY (PROD_ID) REFERENCES tb_product (PROD_ID) ON DELETE CASCADE, - FOREIGN KEY (CENT_ID) REFERENCES tb_center (CENT_ID) ON DELETE CASCADE -); - -CREATE TABLE tb_notice -( - NOT_ID VARCHAR(255) NOT NULL, - NOT_TTL VARCHAR(255) NOT NULL, - NOT_TAG VARCHAR(255) NOT NULL DEFAULT 'ALL', - NOT_CLA VARCHAR(255) NOT NULL DEFAULT 'NORMAL', - NOT_CONT TEXT NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (NOT_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_order -( - ORD_ID VARCHAR(255) NOT NULL, - ORD_TTL VARCHAR(255) NOT NULL, - ORD_CONT TEXT NOT NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ORD_STAT VARCHAR(255) NOT NULL DEFAULT 'WAIT', - CONR_ID VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - ADMIN_ID VARCHAR(255) NULL, - PRIMARY KEY (ORD_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE, - FOREIGN KEY (ADMIN_ID) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE, - FOREIGN KEY (CONR_ID) REFERENCES tb_contract (CONR_ID) ON DELETE CASCADE -); - -CREATE TABLE tb_problem -( - PROB_ID VARCHAR(255) NOT NULL, - PROB_TTL VARCHAR(255) NOT NULL, - PROB_CONT VARCHAR(255) NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - DELETED_AT CHAR(19) NULL, - CUST_ID VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PROD_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (PROB_ID), - FOREIGN KEY (CUST_ID) REFERENCES tb_customer_info (CUST_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), - FOREIGN KEY (PROD_ID) REFERENCES tb_product (PROD_ID) -); - -CREATE TABLE tb_purchase_order -( - PUR_ORD_ID VARCHAR(255) NOT NULL, - PUR_ORD_TTL VARCHAR(255) NOT NULL, - PUR_ORD_CONT TEXT NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - PUR_ORD_STAT VARCHAR(255) NOT NULL DEFAULT 'WAIT', - ORD_ID VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - ADMIN_ID VARCHAR(255) NULL, - PRIMARY KEY (PUR_ORD_ID), - FOREIGN KEY (ORD_ID) REFERENCES tb_order (ORD_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), - FOREIGN KEY (ADMIN_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_EVALUATION -( - EVAL_ID VARCHAR(255) NOT NULL, - EVAL_TTL VARCHAR(255) NOT NULL, - EVAL_CONT VARCHAR(255) NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - CENT_ID VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - WRI_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (EVAL_ID), - FOREIGN KEY (CENT_ID) REFERENCES tb_center (CENT_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), - FOREIGN KEY (WRI_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_promotion -( - PROM_ID VARCHAR(255) NOT NULL, - PROM_TTL VARCHAR(255) NOT NULL, - PROM_CONT VARCHAR(255) NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (PROM_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_file -( - FILE_ID VARCHAR(255) NOT NULL, - FILE_NAME VARCHAR(255) NOT NULL, - FILE_URL VARCHAR(255) NOT NULL, - FILE_TYPE VARCHAR(255) NOT NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - CREATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - MEM_ID VARCHAR(255) NULL, - NOT_ID VARCHAR(255) NULL, - CONR_ID VARCHAR(255) NULL, - PROB_ID VARCHAR(255) NULL, - CENT_ID VARCHAR(255) NULL, - PROM_ID VARCHAR(255) NULL, - EVAL_ID VARCHAR(255) NULL, - CONR_ID2 VARCHAR(255) NULL, - PRIMARY KEY (FILE_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), - FOREIGN KEY (NOT_ID) REFERENCES tb_notice (NOT_ID), - FOREIGN KEY (CONR_ID) REFERENCES tb_contract (CONR_ID), - FOREIGN KEY (PROB_ID) REFERENCES tb_problem (PROB_ID), - FOREIGN KEY (CENT_ID) REFERENCES tb_center (CENT_ID), - FOREIGN KEY (PROM_ID) REFERENCES tb_promotion (PROM_ID), - FOREIGN KEY (EVAL_ID) REFERENCES tb_evaluation (EVAL_ID), - FOREIGN KEY (CONR_ID2) REFERENCES tb_contract (CONR_ID) -); - -CREATE TABLE tb_schedule -( - SCH_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', - SCH_NAME VARCHAR(255) NOT NULL, - SCH_CONT VARCHAR(255) NOT NULL, - SCH_TAG VARCHAR(255) NOT NULL, - SCH_SRT_AT CHAR(19) NOT NULL, - SCH_END_AT CHAR(19) NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL COMMENT 'TRUE/FALSE', - MEM_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', - PRIMARY KEY (SCH_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_alarm -( - ALR_ID VARCHAR(255) NOT NULL, - ALR_MSG VARCHAR(255) NOT NULL, - ALR_URL VARCHAR(255) NOT NULL, - ALR_READ_STAT BOOLEAN NOT NULL DEFAULT FALSE, - CREATED_AT CHAR(19) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (ALR_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_family -( - FAM_ID VARCHAR(255) NOT NULL, - FAM_NAME VARCHAR(255) NOT NULL, - FAM_REL VARCHAR(255) NOT NULL, - FAM_BIR VARCHAR(255) NOT NULL, - FAM_IDEN_NO VARCHAR(255) NOT NULL, - FAM_PHO VARCHAR(255) NOT NULL, - FAM_SEX VARCHAR(255) NOT NULL, - FAM_DIS BOOLEAN NOT NULL, - FAM_DIE BOOLEAN NOT NULL, - FAM_NOTE VARCHAR(255) NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (FAM_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) -); - -CREATE TABLE tb_education -( - EDU_ID VARCHAR(255) NOT NULL, - EDU_ENTD VARCHAR(255) NOT NULL, - EDU_GRAD VARCHAR(255) NOT NULL, - EDU_NAME VARCHAR(255) NOT NULL, - EDU_MJR VARCHAR(255) NOT NULL, - EDU_SCO VARCHAR(255) NULL, - EDU_NOTE VARCHAR(255) NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (EDU_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) -); - -CREATE TABLE tb_certification -( - CER_ID VARCHAR(255) NOT NULL, - CER_DATE VARCHAR(255) NOT NULL, - CER_INST VARCHAR(255) NOT NULL, - CER_NAME VARCHAR(255) NOT NULL, - CER_SCO VARCHAR(255) NOT NULL, - CER_NOTE VARCHAR(255) NULL, - CREATED_AT CHAR(19) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (CER_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) -); - -CREATE TABLE tb_career -( - CAR_ID VARCHAR(255) NOT NULL, - CAR_EMP_DATE VARCHAR(255) NOT NULL, - CAR_RTR_DATE VARCHAR(255) NULL, - CAR_NAME VARCHAR(255) NOT NULL, - CAR_NOTE VARCHAR(255) NULL, - CREATED_AT CHAR(19) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (CAR_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) -); - -CREATE TABLE tb_UPDATE_HISTORY -( - UPD_ID VARCHAR(255) NOT NULL, - UPD_IP VARCHAR(255) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - UPDATED_URL VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - CONR_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (UPD_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), - FOREIGN KEY (CONR_ID) REFERENCES tb_contract (CONR_ID) -); - -CREATE TABLE tb_PRODUCT_OPTION -( - PROD_ID VARCHAR(255) NOT NULL, - PROD_SER_NO VARCHAR(255) NOT NULL, - OPT_CNTY CHAR(1) NOT NULL DEFAULT 'K', - OPT_MNFR CHAR(1) NOT NULL DEFAULT 'N', - OPT_VHC_TYPE CHAR(1) NOT NULL, - OPT_CHSS CHAR(1) NOT NULL, - OPT_DTIL_TYPE CHAR(1) NOT NULL, - OPT_BODY_TYPE CHAR(1) NOT NULL, - OPT_SFTY_DVCE CHAR(1) NOT NULL, - OPT_ENGN_CPCT CHAR(1) NOT NULL, - OPT_SCRT_CODE CHAR(1) NOT NULL, - OPT_PRDC_YEAR CHAR(1) NOT NULL, - OPT_PRDC_PLNT CHAR(1) NOT NULL, - OPT_ENGN CHAR(1) NOT NULL DEFAULT '0', - OPT_MSSN CHAR(1) NOT NULL DEFAULT '0', - OPT_TRIM CHAR(1) NOT NULL DEFAULT '0', - OPT_XTNL_COLR CHAR(1) NOT NULL, - OPT_ITNL_COLR CHAR(1) NOT NULL, - OPT_HUD CHAR(1) NOT NULL DEFAULT '0', - OPT_NAVI CHAR(1) NOT NULL DEFAULT '0', - OPT_DRVE_WISE CHAR(1) NOT NULL DEFAULT '0', - OPT_SMRT_CNCT CHAR(1) NOT NULL DEFAULT '0', - OPT_STYL CHAR(1) NOT NULL DEFAULT '0', - OPT_MY_CFRT_PCKG CHAR(1) NOT NULL DEFAULT '0', - OPT_OTDR_PCKG CHAR(1) NOT NULL DEFAULT '0', - OPT_SUN_ROOF CHAR(1) NOT NULL DEFAULT '0', - OPT_SOND CHAR(1) NOT NULL DEFAULT '0', - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - PRIMARY KEY (PROD_ID, PROD_SER_NO), - FOREIGN KEY (PROD_ID, PROD_SER_NO) REFERENCES tb_product (PROD_ID, PROD_SER_NO) ON DELETE CASCADE -); - -CREATE TABLE tb_sales_history -( - SAL_HIST_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', - CONR_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고' -); - -INSERT INTO tb_organization_chart (ORG_CHA_ID, ORG_CHA_NAME, ORG_CHA_DEPTH) -VALUES ('ORG_000000001', '본사', 1), - ('ORG_000000002', '서울지사', 2), - ('ORG_000000003', '부산지사', 2), - ('ORG_000000004', '인천지사', 2), - ('ORG_000000005', '대전지사', 2), - ('ORG_000000006', '영업부', 3), - ('ORG_000000007', '기술부', 3), - ('ORG_000000008', '고객지원부', 3), - ('ORG_000000009', '마케팅부', 3), - ('ORG_000000010', '재무부', 3); - -INSERT INTO tb_center (CENT_ID, CENT_NAME, CENT_ADR, CENT_PHO, CENT_MEM_CNT, CREATED_AT, UPDATED_AT, ACTIVE, - CENT_OPR_AT) -VALUES ('CEN_000000001', '서울센터', '서울특별시 중구', '010-1234-5678', 50, '2024-01-01 12:00:00', '2024-01-01 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000002', '부산센터', '부산광역시 해운대구', '010-2345-6789', 40, '2024-01-02 12:00:00', '2024-01-02 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000003', '대구센터', '대구광역시', '010-3456-7890', 30, '2024-01-03 12:00:00', '2024-01-03 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000004', '인천센터', '인천광역시', '010-4567-8901', 20, '2024-01-04 12:00:00', '2024-01-04 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000005', '울산센터', '울산광역시', '010-5678-9012', 25, '2024-01-05 12:00:00', '2024-01-05 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000006', '대전센터', '대전광역시', '010-6789-0123', 35, '2024-01-06 12:00:00', '2024-01-06 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000007', '광주센터', '광주광역시', '010-7890-1234', 45, '2024-01-07 12:00:00', '2024-01-07 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000008', '경기센터', '경기도 수원시', '010-8901-2345', 20, '2024-01-08 12:00:00', '2024-01-08 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000009', '청주센터', '충청북도 청주시', '010-9012-3456', 10, '2024-01-09 12:00:00', '2024-01-09 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000010', '전주센터', '전라북도 전주시', '010-0123-4567', 15, '2024-01-10 12:00:00', '2024-01-10 12:00:00', TRUE, - '09:00-18:00'); - -INSERT INTO tb_member (MEM_ID, MEM_LOGIN_ID, MEM_PWD, MEM_NAME, MEM_EMA, MEM_AGE, MEM_SEX, MEM_IDEN_NO, MEM_PHO, MEM_ADR, - MEM_POS, MEM_GRD, MEM_JOB_TYPE, CENTER_ID, ORG_CHA_ID, CREATED_AT, UPDATED_AT, ACTIVE) -VALUES ('MEM_000000001', 101, 'pwd1234', '김철수', 'chulsoo@example.com', 35, 'MALE', '123456-7890123', '010-9876-5432', - '서울특별시', 'Manager', 'Bachelor', 'Full-time', 'CEN_000000001', 'ORG_000000006', '2024-01-01 12:00:00', - '2024-01-01 12:00:00', TRUE), - ('MEM_000000002', 102, 'pwd2345', '이영희', 'younghee@example.com', 28, 'FEMALE', '234567-8901234', '010-8765-4321', - '부산광역시', 'Staff', 'Master', 'Full-time', 'CEN_000000002', 'ORG_000000006', '2024-01-02 12:00:00', - '2024-01-02 12:00:00', TRUE), - ('MEM_000000003', 103, 'pwd3456', '박지훈', 'jihun@example.com', 42, 'MALE', '345678-9012345', '010-7654-3210', - '대구광역시', 'Engineer', 'Doctorate', 'Contract', 'CEN_000000003', 'ORG_000000007', '2024-01-03 12:00:00', - '2024-01-03 12:00:00', TRUE), - ('MEM_000000004', 104, 'pwd4567', '최민수', 'minsoo@example.com', 38, 'MALE', '456789-0123456', '010-6543-2109', - '인천광역시', 'Manager', 'Bachelor', 'Part-time', 'CEN_000000004', 'ORG_000000007', '2024-01-04 12:00:00', - '2024-01-04 12:00:00', TRUE), - ('MEM_000000005', 105, 'pwd5678', '박미영', 'miyoung@example.com', 29, 'FEMALE', '567890-1234567', '010-5432-1098', - '울산광역시', 'Intern', 'High School', 'Full-time', 'CEN_000000005', 'ORG_000000008', '2024-01-05 12:00:00', - '2024-01-05 12:00:00', TRUE), - ('MEM_000000006', 106, 'pwd6789', '정수현', 'soohyun@example.com', 31, 'FEMALE', '678901-2345678', '010-4321-0987', - '대전광역시', 'Senior Engineer', 'Master', 'Full-time', 'CEN_000000006', 'ORG_000000007', '2024-01-06 12:00:00', - '2024-01-06 12:00:00', TRUE), - ('MEM_000000007', 107, 'pwd7890', '한지민', 'jimin@example.com', 45, 'FEMALE', '789012-3456789', '010-3210-9876', - '광주광역시', 'Sales Rep', 'Bachelor', 'Part-time', 'CEN_000000007', 'ORG_000000006', '2024-01-07 12:00:00', - '2024-01-07 12:00:00', TRUE), - ('MEM_000000008', 108, 'pwd8901', '이준호', 'junho@example.com', 36, 'MALE', '890123-4567890', '010-2109-8765', - '경기도 수원시', 'Technician', 'Bachelor', 'Full-time', 'CEN_000000008', 'ORG_000000008', '2024-01-08 12:00:00', - '2024-01-08 12:00:00', TRUE); - -INSERT INTO tb_member_role (MEM_ROL_ID, MEM_ROL_NAME, MEM_ID) -VALUES ('MEM_ROL_000000001', '영업 사원', 'MEM_000000001'), - ('MEM_ROL_000000002', '영업 사원', 'MEM_000000002'), - ('MEM_ROL_000000003', '영업 사원', 'MEM_000000003'), - ('MEM_ROL_000000004', '영업 사원', 'MEM_000000004'), - ('MEM_ROL_000000005', '영업 관리자', 'MEM_000000005'), - ('MEM_ROL_000000006', '영업 관리자', 'MEM_000000009'), - ('MEM_ROL_000000007', '영업 관리자', 'MEM_000000007'), - ('MEM_ROL_000000008', '영업 담당자', 'MEM_000000008'), - ('MEM_ROL_000000009', '영업 담당자', 'MEM_000000010'), - ('MEM_ROL_000000010', '영업 담당자', 'MEM_000000006'); - -INSERT INTO tb_customer_info (CUST_ID, CUST_NAME, CUST_AGE, CUST_SEX, CUST_PHO, CUST_EMER_PHO, CUST_EMA, ACTIVE, MEM_ID) -VALUES ('CUS_000000001', '홍길동', 45, 'MALE', '010-1111-2222', '010-2222-3333', 'gildong@example.com', TRUE, - 'MEM_000000001'), - ('CUS_000000002', '김민수', 38, 'MALE', '010-3333-4444', '010-4444-5555', 'minsoo@example.com', TRUE, - 'MEM_000000002'), - ('CUS_000000003', '이영희', 32, 'FEMALE', '010-5555-6666', '010-6666-7777', 'younghee@example.com', TRUE, - 'MEM_000000003'), - ('CUS_000000004', '박지훈', 27, 'MALE', '010-7777-8888', '010-8888-9999', 'jihun@example.com', TRUE, - 'MEM_000000004'), - ('CUS_000000005', '최정민', 22, 'FEMALE', '010-9999-0000', '010-0000-1111', 'jungmin@example.com', TRUE, - 'MEM_000000005'), - ('CUS_000000006', '정수민', 31, 'MALE', '010-1212-3434', '010-2323-4545', 'suming@example.com', TRUE, - 'MEM_000000006'), - ('CUS_000000007', '한민정', 29, 'FEMALE', '010-4545-5656', '010-5656-6767', 'hanmj@example.com', TRUE, - 'MEM_000000007'), - ('CUS_000000008', '이동수', 41, 'MALE', '010-6767-7878', '010-7878-8989', 'dongsu@example.com', TRUE, - 'MEM_000000008'), - ('CUS_000000009', '윤소라', 33, 'FEMALE', '010-8989-9090', '010-9090-1010', 'sora@example.com', TRUE, - 'MEM_000000009'), - ('CUS_000000010', '박성훈', 36, 'MALE', '010-1010-1112', '010-1212-1313', 'seonghun@example.com', TRUE, - 'MEM_000000010'); - -INSERT INTO tb_product (PROD_ID, PROD_SER_NO, PROD_COST, PROD_NAME, PROD_STCK, CREATED_AT, UPDATED_AT, ACTIVE) -VALUES ('PRO_000000001', 'KNAHAA4AALU1A00001', 25000000, '쏘렌토', 10, '2024-01-10 10:00:00', '2024-01-10 11:00:00', TRUE), - ('PRO_000000002', 'KNAHBA4BALR2Z00002', 22000000, '스포티지', 15, '2024-01-11 11:00:00', '2024-01-11 12:00:00', - TRUE), - ('PRO_000000003', 'KMBHC64CAMJ5A00003', 28000000, 'K7', 8, '2024-01-12 12:00:00', '2024-01-12 13:00:00', TRUE), - ('PRO_000000004', 'KNJFA42DALU3C00004', 19000000, '셀토스', 12, '2024-01-13 13:00:00', '2024-01-13 14:00:00', TRUE), - ('PRO_000000005', 'KFBGBM5EARP1M00005', 18000000, 'K3', 20, '2024-01-14 14:00:00', '2024-01-14 15:00:00', TRUE), - ('PRO_000000006', 'KNHGA6BALUP7A00006', 34000000, '모하비', 7, '2024-01-15 15:00:00', '2024-01-15 16:00:00', TRUE), - ('PRO_000000007', 'KNJFA34AALU4Z00007', 32000000, 'K8', 5, '2024-01-16 16:00:00', '2024-01-16 17:00:00', TRUE), - ('PRO_000000008', 'KMAHDA2AAMJ3T00008', 27000000, '스팅어', 9, '2024-01-17 17:00:00', '2024-01-17 18:00:00', TRUE), - ('PRO_000000009', 'KNAHCA5BALU5C00009', 23000000, '니로', 14, '2024-01-18 18:00:00', '2024-01-18 19:00:00', TRUE), - ('PRO_000000010', 'KNFHC54CAMR1A00010', 28000000, 'K5', 6, '2024-01-19 19:00:00', '2024-01-19 20:00:00', TRUE); - - -INSERT INTO tb_contract (CONR_ID, CONR_NAME, CONR_CUST_NAME, CONR_CUST_IDEN_NO, CONR_CUST_ADR, - CONR_CUST_EMA, CONR_CUST_PHO, CONR_COMP_NAME, CONR_CUST_CLA, CONR_CUST_PUR_COND, - CONR_SERI_NUM, CONR_SELE_OPTI, CONR_DOWN_PAY, CONR_INTE_PAY, CONR_REM_PAY, - CONR_CONS_PAY, CONR_DELV_DATE, CONR_DELV_LOC, CONR_STAT, CONR_NO_OF_VEH, - CREATED_URL, DELETED_URL, ACTIVE, CREATED_AT, UPDATED_AT, DELETED_AT, - MEM_ID, CENT_ID, CUST_ID, PROD_ID) -VALUES --- 계약 1 -('CON_000000001', '쏘렌토 계약', '박지훈', '880512-1234567', '서울특별시 강남구', - 'jihun@example.com', '010-1234-5678', '기아자동차', 'PERSONAL', 'CASH', - 'SER_001', '풀옵션', 5000000, 300000, 25000000, 30000000, - '2024-02-20', '서울특별시 강남구 배송센터', 'WAIT', 1, - '/contracts/1', NULL, TRUE, '2024-01-10 12:00:00', '2024-01-11 13:00:00', NULL, - 'MEM_000000001', 'CEN_000000001', 'CUS_000000001', 'PRO_000000001'), - --- 계약 2 -('CON_000000002', '스포티지 계약', '김영희', '900123-2345678', '부산광역시 해운대구', - 'younghee@example.com', '010-2345-6789', '기아자동차', 'PERSONAL', 'CREDIT', - 'SER_002', '기본옵션', 3000000, 200000, 17000000, 25000000, - '2024-03-05', '부산광역시 해운대구 배송센터', 'CONFIRMED', 1, - '/contracts/2', NULL, TRUE, '2024-01-12 14:00:00', '2024-01-13 15:00:00', NULL, - 'MEM_000000002', 'CEN_000000002', 'CUS_000000002', 'PRO_000000002'), - --- 계약 3 -('CON_000000003', 'K7 계약', '이철수', '950405-3456789', '대구광역시 수성구', - 'chulsoo@example.com', '010-3456-7890', '기아자동차', 'BUSINESS', 'LEASE', - 'SER_003', '풀옵션', 8000000, 400000, 28000000, 35000000, - '2024-03-10', '대구광역시 배송센터', 'WAIT', 2, - '/contracts/3', NULL, TRUE, '2024-01-14 09:00:00', '2024-01-15 10:00:00', NULL, - 'MEM_000000003', 'CEN_000000003', 'CUS_000000003', 'PRO_000000003'), - --- 계약 4 -('CON_000000004', '셀토스 계약', '박민준', '970512-4567890', '인천광역시 미추홀구', - 'minjoon@example.com', '010-4567-8901', '기아자동차', 'PERSONAL', 'CASH', - 'SER_004', '기본옵션', 4000000, 250000, 12000000, 20000000, - '2024-04-01', '인천광역시 배송센터', 'DELIVERED', 1, - '/contracts/4', NULL, TRUE, '2024-01-16 11:00:00', '2024-01-17 12:00:00', NULL, - 'MEM_000000004', 'CEN_000000004', 'CUS_000000004', 'PRO_000000004'), - --- 계약 5 -('CON_000000005', 'K3 계약', '최민수', '980618-5678901', '울산광역시 중구', - 'minsoo@example.com', '010-5678-9012', '기아자동차', 'BUSINESS', 'INSTALLMENT', - 'SER_005', '기본옵션', 2000000, 100000, 13000000, 18000000, - '2024-04-15', '울산광역시 배송센터', 'CANCELLED', 1, - '/contracts/5', NULL, TRUE, '2024-01-18 13:00:00', '2024-01-19 14:00:00', NULL, - 'MEM_000000005', 'CEN_000000005', 'CUS_000000005', 'PRO_000000005'), - --- 계약 6 -('CON_000000006', '모하비 계약', '김민주', '850824-6789012', '대전광역시 서구', - 'minju@example.com', '010-6789-0123', '기아자동차', 'PERSONAL', 'CASH', - 'SER_006', '풀옵션', 6000000, 500000, 34000000, 40000000, - '2024-05-01', '대전광역시 배송센터', 'CONFIRMED', 1, - '/contracts/6', NULL, TRUE, '2024-01-20 15:00:00', '2024-01-21 16:00:00', NULL, - 'MEM_000000006', 'CEN_000000006', 'CUS_000000006', 'PRO_000000006'), - --- 계약 7 -('CON_000000007', 'K8 계약', '정수영', '940705-7890123', '광주광역시 북구', - 'suyoung@example.com', '010-7890-1234', '기아자동차', 'LEASE', 'LEASE', - 'SER_007', '럭셔리 패키지', 7000000, 350000, 25000000, 32000000, - '2024-05-20', '광주광역시 배송센터', 'WAIT', 1, - '/contracts/7', NULL, TRUE, '2024-01-22 17:00:00', '2024-01-23 18:00:00', NULL, - 'MEM_000000007', 'CEN_000000007', 'CUS_000000007', 'PRO_000000007'), - --- 계약 8 -('CON_000000008', '스팅어 계약', '이준호', '930910-8901234', '경기도 수원시', - 'junho@example.com', '010-8901-2345', '기아자동차', 'PERSONAL', 'CREDIT', - 'SER_008', '풀옵션', 5000000, 300000, 22000000, 27000000, - '2024-06-01', '수원시 배송센터', 'DELIVERED', 1, - '/contracts/8', NULL, TRUE, '2024-01-24 19:00:00', '2024-01-25 20:00:00', NULL, - 'MEM_000000008', 'CEN_000000008', 'CUS_000000008', 'PRO_000000008'), - --- 계약 9 -('CON_000000009', '니로 계약', '박소영', '890321-9012345', '충청북도 청주시', - 'soyoung@example.com', '010-9012-3456', '기아자동차', 'PERSONAL', 'CASH', - 'SER_009', '기본옵션', 3500000, 150000, 17000000, 23000000, - '2024-06-15', '청주시 배송센터', 'CONFIRMED', 1, - '/contracts/9', NULL, TRUE, '2024-01-26 21:00:00', '2024-01-27 22:00:00', NULL, - 'MEM_000000009', 'CEN_000000009', 'CUS_000000009', 'PRO_000000009'), - --- 계약 10 -('CON_000000010', 'K5 계약', '김정훈', '900815-0123456', '전라북도 전주시', - 'junghoon@example.com', '010-0123-4567', '기아자동차', 'BUSINESS', 'LEASE', - 'SER_010', '풀옵션', 6000000, 250000, 20000000, 28000000, - '2024-07-01', '전주시 배송센터', 'WAIT', 1, - '/contracts/10', NULL, TRUE, '2024-01-28 23:00:00', '2024-01-29 23:59:00', NULL, - 'MEM_000000010', 'CEN_000000010', 'CUS_000000010', 'PRO_000000010'); - -INSERT INTO tb_order ( - ORD_ID, ORD_TTL, ORD_CONT, ACTIVE, CREATED_AT, UPDATED_AT, DELETED_AT, ORD_STAT, CONR_ID, MEM_ID, ADMIN_ID -) VALUES --- 주문 1 -('ORD_000000001', '쏘렌토 계약 주문', '쏘렌토 차량 계약 완료 후 주문 처리', TRUE, '2024-01-10 10:00:00', '2024-01-10 11:00:00', NULL, 'CONFIRMED', 'CON_000000001', 'MEM_000000001', 'MEM_000000005'), --- 주문 2 -('ORD_000000002', '스포티지 구매 주문', '스포티지 고객 상담 후 주문 확정', TRUE, '2024-01-11 12:00:00', '2024-01-11 13:00:00', NULL, 'WAIT', 'CON_000000002', 'MEM_000000002', 'MEM_000000005'), --- 주문 3 -('ORD_000000003', 'K7 리스 주문', 'K7 리스 계약 완료 후 주문 생성', TRUE, '2024-01-12 09:00:00', '2024-01-12 10:00:00', NULL, 'CONFIRMED', 'CON_000000003', 'MEM_000000003', 'MEM_000000005'), --- 주문 4 -('ORD_000000004', '셀토스 추가 주문', '셀토스 고객 추가 옵션 주문', TRUE, '2024-01-13 10:30:00', '2024-01-13 11:30:00', NULL, 'CANCELLED', 'CON_000000004', 'MEM_000000004', 'MEM_000000005'), --- 주문 5 -('ORD_000000005', 'K3 계약 주문', 'K3 차량 계약 후 최종 주문', TRUE, '2024-01-14 11:00:00', '2024-01-14 12:00:00', NULL, 'DELIVERED', 'CON_000000005', 'MEM_000000001', 'MEM_000000006'), --- 주문 6 -('ORD_000000006', '모하비 계약 수정 주문', '모하비 계약 변경에 따른 주문 수정', TRUE, '2024-01-15 13:00:00', '2024-01-15 14:00:00', NULL, 'WAIT', 'CON_000000006', 'MEM_000000002', 'MEM_000000006'), --- 주문 7 -('ORD_000000007', 'K8 리스 주문', 'K8 차량 리스 계약 주문', TRUE, '2024-01-16 14:30:00', '2024-01-16 15:30:00', NULL, 'CONFIRMED', 'CON_000000007', 'MEM_000000003', 'MEM_000000006'), --- 주문 8 -('ORD_000000008', '스팅어 구매 주문', '스팅어 차량 구매 계약 후 주문 확정', TRUE, '2024-01-17 16:00:00', '2024-01-17 17:00:00', NULL, 'DELIVERED', 'CON_000000008', 'MEM_000000004', 'MEM_000000006'), --- 주문 9 -('ORD_000000009', '니로 전기차 주문', '니로 전기차 계약 주문', TRUE, '2024-01-18 17:30:00', '2024-01-18 18:30:00', NULL, 'WAIT', 'CON_000000009', 'MEM_000000001', 'MEM_000000010'), --- 주문 10 -('ORD_000000010', 'K5 재고 주문', 'K5 재고 차량 추가 주문', TRUE, '2024-01-19 09:00:00', '2024-01-19 10:00:00', NULL, 'CONFIRMED', 'CON_000000010', 'MEM_000000001', 'MEM_00000009'); - - -INSERT INTO tb_problem (PROB_ID, PROB_TTL, PROB_CONT, CREATED_AT, UPDATED_AT, ACTIVE, CUST_ID, MEM_ID, PROD_ID) -VALUES ('PROB_000000001', '쏘렌토 엔진 문제', '엔진에서 소음 발생', '2024-01-12 14:00:00', '2024-01-12 15:00:00', TRUE, - 'CUS_000000001', 'MEM_000000001', 'PRO_000000001'), - ('PROB_000000002', '스포티지 브레이크 문제', '브레이크 페달 작동 불량', '2024-01-13 10:00:00', '2024-01-13 11:00:00', TRUE, - 'CUS_000000002', 'MEM_000000002', 'PRO_000000002'), - ('PROB_000000003', 'K7 전자 장비 문제', '내비게이션 오류', '2024-01-14 09:00:00', '2024-01-14 10:00:00', TRUE, - 'CUS_000000003', 'MEM_000000003', 'PRO_000000003'), - ('PROB_000000004', '셀토스 에어컨 문제', '에어컨 작동 안됨', '2024-01-15 11:00:00', '2024-01-15 12:00:00', TRUE, - 'CUS_000000004', 'MEM_000000004', 'PRO_000000004'), - ('PROB_000000005', 'K3 연비 문제', '연비가 예상보다 낮음', '2024-01-16 12:00:00', '2024-01-16 13:00:00', TRUE, - 'CUS_000000005', 'MEM_000000005', 'PRO_000000005'), - ('PROB_000000006', '모하비 변속기 문제', '변속기 오류 발생', '2024-01-17 13:00:00', '2024-01-17 14:00:00', TRUE, - 'CUS_000000006', 'MEM_000000006', 'PRO_000000006'), - ('PROB_000000007', 'K8 배터리 문제', '배터리 수명 짧음', '2024-01-18 14:00:00', '2024-01-18 15:00:00', TRUE, 'CUS_000000007', - 'MEM_000000007', 'PRO_000000007'), - ('PROB_000000008', '스팅어 오디오 문제', '오디오 스피커 잡음', '2024-01-19 15:00:00', '2024-01-19 16:00:00', TRUE, - 'CUS_000000008', 'MEM_000000008', 'PRO_000000008'), - ('PROB_000000009', '니로 전기 문제', '충전 불량', '2024-01-20 16:00:00', '2024-01-20 17:00:00', TRUE, 'CUS_000000009', - 'MEM_000000009', 'PRO_000000009'), - ('PROB_000000010', 'K5 시동 문제', '시동이 걸리지 않음', '2024-01-21 17:00:00', '2024-01-21 18:00:00', TRUE, 'CUS_000000010', - 'MEM_000000010', 'PRO_000000010'); - -INSERT INTO tb_purchase_order (PUR_ORD_ID, PUR_ORD_TTL, PUR_ORD_CONT, CREATED_AT, UPDATED_AT, ACTIVE, PUR_ORD_STAT, ORD_ID, - MEM_ID, ADMIN_ID) -VALUES ('PUR_ORD_000000001', '쏘렌토 부품 주문', '엔진 부품 요청', '2024-01-12 10:00:00', '2024-01-12 11:00:00', TRUE, 'WAIT', - 'ORD_000000001', 'MEM_000000001', NULL), - ('PUR_ORD_000000002', '스포티지 타이어 주문', '타이어 4개 요청', '2024-01-13 12:00:00', '2024-01-13 13:00:00', TRUE, 'CONFIRMED', - 'ORD_000000002', 'MEM_000000002', NULL), - ('PUR_ORD_000000003', 'K7 배터리 주문', '배터리 교체 요청', '2024-01-14 13:00:00', '2024-01-14 14:00:00', TRUE, 'DELIVERED', - 'ORD_000000003', 'MEM_000000003', NULL), - ('PUR_ORD_000000004', '셀토스 브레이크 주문', '브레이크 패드 요청', '2024-01-15 14:00:00', '2024-01-15 15:00:00', TRUE, 'WAIT', - 'ORD_000000004', 'MEM_000000004', NULL), - ('PUR_ORD_000000005', 'K3 내비게이션 주문', '내비게이션 교체', '2024-01-16 15:00:00', '2024-01-16 16:00:00', TRUE, 'CANCELLED', - 'ORD_000000005', 'MEM_000000005', NULL), - ('PUR_ORD_000000006', '모하비 오일 필터 주문', '오일 필터 10개 요청', '2024-01-17 09:00:00', '2024-01-17 10:00:00', TRUE, - 'CONFIRMED', 'ORD_000000006', 'MEM_000000006', NULL), - ('PUR_ORD_000000007', 'K8 헤드라이트 주문', 'LED 헤드라이트 2개 요청', '2024-01-18 10:00:00', '2024-01-18 11:00:00', TRUE, 'WAIT', - 'ORD_000000007', 'MEM_000000007', NULL), - ('PUR_ORD_000000008', '스팅어 도어 핸들 주문', '도어 핸들 4개 요청', '2024-01-19 12:00:00', '2024-01-19 13:00:00', TRUE, 'CONFIRMED', - 'ORD_000000008', 'MEM_000000008', NULL), - ('PUR_ORD_000000009', '니로 충전 케이블 주문', '전기차 충전 케이블 요청', '2024-01-20 14:00:00', '2024-01-20 15:00:00', TRUE, - 'DELIVERED', 'ORD_000000009', 'MEM_000000009', NULL), - ('PUR_ORD_000000010', 'K5 엔진오일 주문', '엔진오일 20L 요청', '2024-01-21 15:00:00', '2024-01-21 16:00:00', TRUE, 'WAIT', - 'ORD_000000010', 'MEM_000000010', NULL); - -INSERT INTO tb_notice (NOT_ID, NOT_TTL, NOT_TAG, NOT_CLA, NOT_CONT, CREATED_AT, UPDATED_AT, ACTIVE, MEM_ID) -VALUES ('NOT_000000001', '신차 출시 공지', 'ALL', 'IMPORTANT', '신형 쏘렌토가 출시되었습니다.', '2024-01-10 09:00:00', - '2024-01-10 09:30:00', TRUE, 'MEM_000000001'), - ('NOT_000000002', '정기 점검 공지', 'SERVICE', 'NORMAL', '1월 정기 점검 안내입니다.', '2024-01-11 10:00:00', - '2024-01-11 10:30:00', TRUE, 'MEM_000000002'), - ('NOT_000000003', '할인 프로모션', 'SALES', 'IMPORTANT', 'K5 한정 할인 프로모션 안내', '2024-01-12 11:00:00', - '2024-01-12 11:30:00', TRUE, 'MEM_000000003'), - ('NOT_000000004', '서비스 센터 이전 안내', 'SERVICE', 'NORMAL', '서울센터가 이전되었습니다.', '2024-01-13 12:00:00', - '2024-01-13 12:30:00', TRUE, 'MEM_000000004'), - ('NOT_000000005', '부품 재고 부족', 'ALL', 'WARNING', '모하비 부품 재고가 부족합니다.', '2024-01-14 13:00:00', - '2024-01-14 13:30:00', TRUE, 'MEM_000000005'), - ('NOT_000000006', '설 연휴 휴무 안내', 'ALL', 'NORMAL', '설 연휴 기간 동안 휴무합니다.', '2024-01-15 14:00:00', - '2024-01-15 14:30:00', TRUE, 'MEM_000000006'), - ('NOT_000000007', 'K8 시승 이벤트', 'EVENT', 'NORMAL', 'K8 시승 이벤트에 참여하세요.', '2024-01-16 15:00:00', - '2024-01-16 15:30:00', TRUE, 'MEM_000000007'), - ('NOT_000000008', '스포티지 리콜 안내', 'SERVICE', 'CRITICAL', '스포티지 일부 모델 리콜 안내', '2024-01-17 16:00:00', - '2024-01-17 16:30:00', TRUE, 'MEM_000000008'), - ('NOT_000000009', '신입사원 모집', 'HR', 'NORMAL', '기아자동차 신입사원 모집 공고', '2024-01-18 17:00:00', '2024-01-18 17:30:00', - TRUE, 'MEM_000000009'), - ('NOT_000000010', '고객 감사 이벤트', 'EVENT', 'NORMAL', '고객 감사 사은품 증정 이벤트', '2024-01-19 18:00:00', - '2024-01-19 18:30:00', TRUE, 'MEM_000000010'); - -INSERT INTO tb_file (FILE_ID, FILE_NAME, FILE_URL, FILE_TYPE, ACTIVE, CREATED_AT, MEM_ID, NOT_ID) -VALUES ('FIL_000000001', '쏘렌토_계약서.pdf', '/files/contract1.pdf', 'pdf', TRUE, '2024-01-10 10:00:00', 'MEM_000000001', - 'NOT_000000001'), - ('FIL_000000002', '스포티지_메뉴얼.pdf', '/files/manual2.pdf', 'pdf', TRUE, '2024-01-11 10:30:00', 'MEM_000000002', - 'NOT_000000002'), - ('FIL_000000003', 'K7_견적서.pdf', '/files/estimate3.pdf', 'pdf', TRUE, '2024-01-12 11:00:00', 'MEM_000000003', - 'NOT_000000003'), - ('FIL_000000004', '셀토스_리콜_공지.pdf', '/files/recall4.pdf', 'pdf', TRUE, '2024-01-13 12:00:00', 'MEM_000000004', - 'NOT_000000004'), - ('FIL_000000005', 'K3_정비보고서.pdf', '/files/report5.pdf', 'pdf', TRUE, '2024-01-14 13:00:00', 'MEM_000000005', - 'NOT_000000005'), - ('FIL_000000006', '모하비_오일교환.pdf', '/files/oil6.pdf', 'pdf', TRUE, '2024-01-15 14:00:00', 'MEM_000000006', - 'NOT_000000006'), - ('FIL_000000007', 'K8_이벤트_포스터.png', '/files/event7.png', 'image', TRUE, '2024-01-16 15:00:00', 'MEM_000000007', - 'NOT_000000007'), - ('FIL_000000008', '스팅어_사용설명서.pdf', '/files/manual8.pdf', 'pdf', TRUE, '2024-01-17 16:00:00', 'MEM_000000008', - 'NOT_000000008'), - ('FIL_000000009', '니로_충전가이드.pdf', '/files/guide9.pdf', 'pdf', TRUE, '2024-01-18 17:00:00', 'MEM_000000009', - 'NOT_000000009'), - ('FIL_000000010', 'K5_고객이벤트_배너.jpg', '/files/banner10.jpg', 'image', TRUE, '2024-01-19 18:00:00', - 'MEM_000000010', 'NOT_000000010'); - -INSERT INTO tb_schedule (SCH_ID, SCH_NAME, SCH_CONT, SCH_TAG, SCH_SRT_AT, SCH_END_AT, CREATED_AT, UPDATED_AT, DELETED_AT, ACTIVE, MEM_ID) -VALUES - ('SCH_000000001', '쏘렌토 신차 발표회', '신차 발표회 준비 및 진행', 'MEETING', '2024-11-01 09:00:00', '2024-11-01 12:00:00', - '2024-01-20 10:00:00', '2024-01-20 10:30:00', NULL, TRUE, 'MEM_000000001'), - ('SCH_000000002', '스포티지 고객 상담', '신차 구매 고객 상담 일정', 'CONSULTATION', '2024-11-05 10:00:00', '2024-11-05 11:30:00', - '2024-01-21 11:00:00', '2024-01-21 11:30:00', NULL, TRUE, 'MEM_000000002'), - ('SCH_000000003', 'K7 프로모션 회의', '프로모션 기획 및 준비 회의', 'MEETING', '2024-11-10 13:00:00', '2024-11-10 15:00:00', - '2024-01-22 12:00:00', '2024-01-22 12:30:00', NULL, TRUE, 'MEM_000000003'), - ('SCH_000000004', '셀토스 시승 이벤트', '고객 대상 시승 이벤트', 'CONSULTATION', '2024-11-12 14:00:00', '2024-11-12 17:00:00', - '2024-01-23 13:00:00', '2024-01-23 13:30:00', NULL, TRUE, 'MEM_000000004'), - ('SCH_000000005', 'K3 서비스 교육', '서비스 센터 직원 교육', 'TRAINING', '2024-11-15 09:00:00', '2024-11-15 12:00:00', - '2024-01-24 14:00:00', '2024-01-24 14:30:00', NULL, TRUE, 'MEM_000000005'), - ('SCH_000000006', '모하비 유지보수 회의', '유지보수 전략 논의', 'MEETING', '2024-11-17 10:00:00', '2024-11-17 11:00:00', - '2024-01-25 15:00:00', '2024-01-25 15:30:00', NULL, TRUE, 'MEM_000000006'), - ('SCH_000000007', 'K8 내부 검토', '신차 내부 디자인 검토', 'MEETING', '2024-11-20 11:00:00', '2024-11-20 13:00:00', - '2024-01-26 16:00:00', '2024-01-26 16:30:00', NULL, TRUE, 'MEM_000000007'), - ('SCH_000000008', '스팅어 기술 세미나', '스팅어 기술 세미나 및 발표', 'TRAINING', '2024-11-22 14:00:00', '2024-11-22 16:00:00', - '2024-01-27 17:00:00', '2024-01-27 17:30:00', NULL, TRUE, 'MEM_000000008'), - ('SCH_000000009', '연말 휴가', '연말 휴가 계획', 'VACATION', '2024-11-25 09:00:00', '2024-11-25 17:00:00', - '2024-01-28 18:00:00', '2024-01-28 18:30:00', NULL, TRUE, 'MEM_000000009'), - ('SCH_000000010', 'K5 재고 점검', 'K5 재고 관리 및 점검', 'MEETING', '2024-11-28 15:00:00', '2024-11-28 17:00:00', - '2024-01-29 19:00:00', '2024-01-29 19:30:00', NULL, TRUE, 'MEM_000000010'); - -INSERT INTO tb_alarm (ALR_ID, ALR_MSG, ALR_URL, ALR_READ_STAT, CREATED_AT, MEM_ID) -VALUES ('ALR_000000001', '신차 출시 공지 알림', '/notices/1', FALSE, '2024-01-20 12:00:00', 'MEM_000000001'), - ('ALR_000000002', '스포티지 리콜 안내', '/notices/2', FALSE, '2024-01-21 14:00:00', 'MEM_000000002'), - ('ALR_000000003', 'K7 고객 이벤트 초대', '/events/3', TRUE, '2024-01-22 09:00:00', 'MEM_000000003'), - ('ALR_000000004', '셀토스 정비 완료', '/service/4', FALSE, '2024-01-23 10:00:00', 'MEM_000000004'), - ('ALR_000000005', 'K3 테스트 드라이브 일정 변경', '/schedules/5', TRUE, '2024-01-24 11:00:00', 'MEM_000000005'), - ('ALR_000000006', '모하비 부품 입고 알림', '/inventory/6', FALSE, '2024-01-25 12:00:00', 'MEM_000000006'), - ('ALR_000000007', 'K8 디자인 변경 확정', '/design/7', TRUE, '2024-01-26 13:00:00', 'MEM_000000007'), - ('ALR_000000008', '스팅어 성능 테스트 보고서', '/reports/8', FALSE, '2024-01-27 14:00:00', 'MEM_000000008'), - ('ALR_000000009', '니로 전기차 충전소 위치', '/maps/9', TRUE, '2024-01-28 15:00:00', 'MEM_000000009'), - ('ALR_000000010', 'K5 재고 부족 경고', '/inventory/10', FALSE, '2024-01-29 16:00:00', 'MEM_000000010'); - -INSERT INTO tb_family (FAM_ID, FAM_NAME, FAM_REL, FAM_BIR, FAM_IDEN_NO, FAM_PHO, FAM_SEX, FAM_DIS, FAM_DIE, FAM_NOTE, CREATED_AT, UPDATED_AT, MEM_ID) -VALUES - ('FAM_000000001', '김영희', '배우자', '1988-03-25', '880325-1234567', '010-1234-5678', 'FEMALE', FALSE, FALSE, NULL, '2024-01-10 10:00:00', '2024-01-10 11:00:00', 'MEM_000000001'), - ('FAM_000000002', '이수진', '자녀', '2015-05-12', '150512-2345678', '010-2345-6789', 'FEMALE', FALSE, FALSE, '초등학교 3학년', '2024-01-11 12:00:00', '2024-01-11 13:00:00', 'MEM_000000002'), - ('FAM_000000003', '박철수', '배우자', '1985-07-11', '850711-3456789', '010-3456-7890', 'MALE', FALSE, FALSE, NULL, '2024-01-12 14:00:00', '2024-01-12 15:00:00', 'MEM_000000003'), - ('FAM_000000004', '최민수', '자녀', '2012-11-03', '121103-4567890', '010-4567-8901', 'MALE', TRUE, FALSE, '특수 교육 필요', '2024-01-13 09:00:00', '2024-01-13 10:00:00', 'MEM_000000004'), - ('FAM_000000005', '정수정', '배우자', '1990-09-20', '900920-5678901', '010-5678-9012', 'FEMALE', FALSE, FALSE, NULL, '2024-01-14 11:00:00', '2024-01-14 12:00:00', 'MEM_000000005'), - ('FAM_000000006', '김지훈', '자녀', '2010-06-15', '100615-6789012', '010-6789-0123', 'MALE', FALSE, FALSE, '중학교 1학년', '2024-01-15 13:00:00', '2024-01-15 14:00:00', 'MEM_000000006'), - ('FAM_000000007', '한지민', '배우자', '1987-02-28', '870228-7890123', '010-7890-1234', 'FEMALE', FALSE, FALSE, NULL, '2024-01-16 15:00:00', '2024-01-16 16:00:00', 'MEM_000000007'), - ('FAM_000000008', '이동수', '자녀', '2017-12-10', '171210-8901234', '010-8901-2345', 'MALE', FALSE, FALSE, '유치원생', '2024-01-17 17:00:00', '2024-01-17 18:00:00', 'MEM_000000008'), - ('FAM_000000009', '윤소희', '배우자', '1989-04-03', '890403-9012345', '010-9012-3456', 'FEMALE', FALSE, FALSE, NULL, '2024-01-18 19:00:00', '2024-01-18 20:00:00', 'MEM_000000009'), - ('FAM_000000010', '박성준', '자녀', '2014-08-25', '140825-0123456', '010-0123-4567', 'MALE', FALSE, FALSE, '초등학교 5학년', '2024-01-19 09:00:00', '2024-01-19 10:00:00', 'MEM_000000010'); - -INSERT INTO tb_education (EDU_ID, EDU_ENTD, EDU_GRAD, EDU_NAME, EDU_MJR, EDU_SCO, EDU_NOTE, CREATED_AT, UPDATED_AT, MEM_ID) -VALUES - ('EDU_000000001', '2006-03-01', '2010-02-28', '서울대학교', '경영학', '3.8', NULL, '2024-01-10 10:00:00', '2024-01-10 11:00:00', 'MEM_000000001'), - ('EDU_000000002', '2008-03-01', '2012-02-28', '연세대학교', '경제학', '3.5', '졸업 논문 우수상', '2024-01-11 12:00:00', '2024-01-11 13:00:00', 'MEM_000000002'), - ('EDU_000000003', '2005-03-01', '2009-02-28', '고려대학교', '컴퓨터공학', '3.9', NULL, '2024-01-12 14:00:00', '2024-01-12 15:00:00', 'MEM_000000003'), - ('EDU_000000004', '2010-03-01', '2014-02-28', '서강대학교', '심리학', '3.6', '사회봉사 활동 참여', '2024-01-13 09:00:00', '2024-01-13 10:00:00', 'MEM_000000004'), - ('EDU_000000005', '2007-03-01', '2011-02-28', '성균관대학교', '화학공학', '3.7', NULL, '2024-01-14 11:00:00', '2024-01-14 12:00:00', 'MEM_000000005'), - ('EDU_000000006', '2009-03-01', '2013-02-28', '한양대학교', '전자공학', '4.0', '학과 대표', '2024-01-15 13:00:00', '2024-01-15 14:00:00', 'MEM_000000006'), - ('EDU_000000007', '2004-03-01', '2008-02-28', '이화여자대학교', '디자인', '3.8', '졸업 작품전 참여', '2024-01-16 15:00:00', '2024-01-16 16:00:00', 'MEM_000000007'), - ('EDU_000000008', '2011-03-01', '2015-02-28', '중앙대학교', '물리학', '3.4', NULL, '2024-01-17 17:00:00', '2024-01-17 18:00:00', 'MEM_000000008'), - ('EDU_000000009', '2003-03-01', '2007-02-28', '부산대학교', '수학', '3.9', '우수 장학금 수상', '2024-01-18 19:00:00', '2024-01-18 20:00:00', 'MEM_000000009'), - ('EDU_000000010', '2012-03-01', '2016-02-28', '경북대학교', '환경공학', '3.6', NULL, '2024-01-19 09:00:00', '2024-01-19 10:00:00', 'MEM_000000010'); - -INSERT INTO tb_certification (CER_ID, CER_DATE, CER_INST, CER_NAME, CER_SCO, CER_NOTE, CREATED_AT, MEM_ID) -VALUES - ('CER_000000001', '2018-06-20', '한국무역협회', '무역영어 1급', 'PASS', NULL, '2024-01-10 10:00:00', 'MEM_000000001'), - ('CER_000000002', '2019-03-15', '한국산업인력공단', '정보처리기사', 'PASS', '필기 및 실기 합격', '2024-01-11 12:00:00', 'MEM_000000002'), - ('CER_000000003', '2017-11-05', '대한상공회의소', '전산회계 2급', '90', NULL, '2024-01-12 14:00:00', 'MEM_000000003'), - ('CER_000000004', '2020-08-10', '국제공인회계사협회', 'CPA', 'PASS', '국제 회계 자격증', '2024-01-13 09:00:00', 'MEM_000000004'), - ('CER_000000005', '2016-02-22', '대한건설협회', '건축기사', 'PASS', NULL, '2024-01-14 11:00:00', 'MEM_000000005'), - ('CER_000000006', '2021-05-30', '한국능률협회', 'PMP', 'PASS', '프로젝트 관리 자격증', '2024-01-15 13:00:00', 'MEM_000000006'), - ('CER_000000007', '2015-12-12', '한국관광공사', '국내여행안내사', '85', NULL, '2024-01-16 15:00:00', 'MEM_000000007'), - ('CER_000000008', '2018-09-18', '한국소방안전협회', '소방안전관리자 1급', 'PASS', NULL, '2024-01-17 17:00:00', 'MEM_000000008'), - ('CER_000000009', '2022-04-25', '대한적십자사', '응급처치 강사', 'PASS', '응급처치 교육 수료', '2024-01-18 19:00:00', 'MEM_000000009'), - ('CER_000000010', '2019-11-11', '한국정보통신기술협회', '정보보안기사', 'PASS', NULL, '2024-01-19 09:00:00', 'MEM_000000010'); - -INSERT INTO tb_career (CAR_ID, CAR_EMP_DATE, CAR_RTR_DATE, CAR_NAME, CAR_NOTE, CREATED_AT, MEM_ID) -VALUES - ('CAR_000000001', '2012-03-01', '2016-12-31', '삼성전자', '마케팅팀 근무', '2024-01-10 10:00:00', 'MEM_000000001'), - ('CAR_000000002', '2015-05-01', '2019-08-31', 'LG화학', '연구개발팀 근무', '2024-01-11 12:00:00', 'MEM_000000002'), - ('CAR_000000003', '2010-09-01', '2015-02-28', '포스코', '공정관리팀', '2024-01-12 14:00:00', 'MEM_000000003'), - ('CAR_000000004', '2013-01-01', '2018-03-31', '현대자동차', '생산기술팀', '2024-01-13 09:00:00', 'MEM_000000004'), - ('CAR_000000005', '2017-06-01', '2021-11-30', 'SK텔레콤', '네트워크 운영팀', '2024-01-14 11:00:00', 'MEM_000000005'), - ('CAR_000000006', '2011-02-01', '2016-07-31', '네이버', '프론트엔드 개발자', '2024-01-15 13:00:00', 'MEM_000000006'), - ('CAR_000000007', '2014-04-01', '2019-12-31', '카카오', '백엔드 개발자', '2024-01-16 15:00:00', 'MEM_000000007'), - ('CAR_000000008', '2018-01-01', '2022-06-30', 'CJ제일제당', '품질관리팀', '2024-01-17 17:00:00', 'MEM_000000008'), - ('CAR_000000009', '2010-07-01', '2014-09-30', '롯데케미칼', '안전관리팀', '2024-01-18 19:00:00', 'MEM_000000009'), - ('CAR_000000010', '2016-03-01', '2020-10-31', '한화생명', '재무팀', '2024-01-19 09:00:00', 'MEM_000000010'); - -INSERT INTO tb_update_history (UPD_ID, UPD_IP, UPDATED_AT, UPDATED_URL, MEM_ID, CONR_ID) -VALUES ('UPD_000000001', '192.168.1.10', '2024-01-10 12:00:00', '/contracts/1', 'MEM_000000001', 'CON_000000001'), - ('UPD_000000002', '192.168.1.20', '2024-01-11 14:00:00', '/contracts/2', 'MEM_000000002', 'CON_000000002'), - ('UPD_000000003', '192.168.1.30', '2024-01-12 16:00:00', '/contracts/3', 'MEM_000000003', 'CON_000000003'), - ('UPD_000000004', '192.168.1.40', '2024-01-13 09:00:00', '/contracts/4', 'MEM_000000004', 'CON_000000004'), - ('UPD_000000005', '192.168.1.50', '2024-01-14 10:00:00', '/contracts/5', 'MEM_000000005', 'CON_000000005'), - ('UPD_000000006', '192.168.1.60', '2024-01-15 11:00:00', '/contracts/6', 'MEM_000000006', 'CON_000000006'), - ('UPD_000000007', '192.168.1.70', '2024-01-16 12:00:00', '/contracts/7', 'MEM_000000007', 'CON_000000007'), - ('UPD_000000008', '192.168.1.80', '2024-01-17 13:00:00', '/contracts/8', 'MEM_000000008', 'CON_000000008'), - ('UPD_000000009', '192.168.1.90', '2024-01-18 14:00:00', '/contracts/9', 'MEM_000000009', 'CON_000000009'), - ('UPD_000000010', '192.168.1.100', '2024-01-19 15:00:00', '/contracts/10', 'MEM_000000010', 'CON_000000010'); - -INSERT INTO tb_evaluation (EVAL_ID, EVAL_TTL, EVAL_CONT, CREATED_AT, UPDATED_AT, ACTIVE, CENT_ID, MEM_ID, WRI_ID) -VALUES ('EVAL_000000001', '쏘렌토 성능 평가', '쏘렌토의 성능에 대한 평가입니다.', '2024-01-20 10:00:00', '2024-01-21 10:30:00', TRUE, - 'CEN_000000001', 'MEM_000000001', 'MEM_000000002'), - ('EVAL_000000002', '스포티지 안전 평가', '스포티지의 안전성 평가 보고서.', '2024-01-21 11:00:00', '2024-01-22 11:30:00', TRUE, - 'CEN_000000002', 'MEM_000000002', 'MEM_000000003'), - ('EVAL_000000003', 'K7 고객 만족도 평가', '고객 만족도 조사 결과입니다.', '2024-01-23 12:00:00', '2024-01-24 12:30:00', TRUE, - 'CEN_000000003', 'MEM_000000003', 'MEM_000000004'), - ('EVAL_000000004', '셀토스 디자인 평가', '셀토스 디자인 피드백.', '2024-01-25 13:00:00', '2024-01-26 13:30:00', TRUE, - 'CEN_000000004', 'MEM_000000004', 'MEM_000000005'), - ('EVAL_000000005', 'K3 내부 공간 평가', '내부 공간의 효율성 평가.', '2024-01-27 14:00:00', '2024-01-28 14:30:00', TRUE, - 'CEN_000000005', 'MEM_000000005', 'MEM_000000006'), - ('EVAL_000000006', '모하비 유지비 평가', '유지 비용과 효율성 평가.', '2024-01-29 15:00:00', '2024-01-30 15:30:00', TRUE, - 'CEN_000000006', 'MEM_000000006', 'MEM_000000007'), - ('EVAL_000000007', 'K8 디자인 리뉴얼', '새로운 디자인에 대한 평가.', '2024-02-01 16:00:00', '2024-02-02 16:30:00', TRUE, - 'CEN_000000007', 'MEM_000000007', 'MEM_000000008'), - ('EVAL_000000008', '스팅어 출력 평가', '스팅어의 출력 성능 보고서.', '2024-02-03 17:00:00', '2024-02-04 17:30:00', TRUE, - 'CEN_000000008', 'MEM_000000008', 'MEM_000000009'), - ('EVAL_000000009', '니로 전기차 평가', '전기차 효율성 평가 결과.', '2024-02-05 18:00:00', '2024-02-06 18:30:00', TRUE, - 'CEN_000000009', 'MEM_000000009', 'MEM_000000010'), - ('EVAL_000000010', 'K5 연비 평가', 'K5의 연비 효율성 보고서.', '2024-02-07 19:00:00', '2024-02-08 19:30:00', TRUE, - 'CEN_000000010', 'MEM_000000010', 'MEM_000000001'); - -INSERT INTO tb_promotion (PROM_ID, PROM_TTL, PROM_CONT, CREATED_AT, UPDATED_AT, ACTIVE, MEM_ID) -VALUES ('PROM_000000001', '쏘렌토 겨울 특별 할인', '쏘렌토 구매 시 15% 할인 제공', '2024-01-05 09:00:00', '2024-01-06 10:00:00', TRUE, - 'MEM_000000001'), - ('PROM_000000002', '스포티지 리스 프로모션', '리스 계약 시 추가 혜택 제공', '2024-01-10 11:00:00', '2024-01-10 12:00:00', TRUE, - 'MEM_000000002'), - ('PROM_000000003', 'K7 고객 감사 이벤트', 'K7 구매 고객 대상 사은품 증정', '2024-01-15 14:00:00', '2024-01-15 15:00:00', TRUE, - 'MEM_000000003'), - ('PROM_000000004', '셀토스 한정 프로모션', '셀토스 구매 시 무료 보증 연장', '2024-01-20 09:00:00', '2024-01-20 10:00:00', TRUE, - 'MEM_000000004'), - ('PROM_000000005', 'K3 연비 보장 이벤트', 'K3 연비 테스트 이벤트 참여 시 혜택 제공', '2024-01-25 13:00:00', '2024-01-25 14:00:00', - TRUE, 'MEM_000000005'), - ('PROM_000000006', '모하비 프리미엄 서비스', '모하비 구매 고객 대상 프리미엄 서비스 제공', '2024-01-30 11:00:00', '2024-01-31 12:00:00', - TRUE, 'MEM_000000006'), - ('PROM_000000007', 'K8 럭셔리 패키지 할인', '럭셔리 패키지 선택 시 10% 할인 제공', '2024-02-01 12:00:00', '2024-02-02 13:00:00', TRUE, - 'MEM_000000007'), - ('PROM_000000008', '스팅어 시승 이벤트', '스팅어 시승 후 계약 시 추가 혜택', '2024-02-05 10:00:00', '2024-02-05 11:00:00', TRUE, - 'MEM_000000008'), - ('PROM_000000009', '니로 전기차 구매 지원', '전기차 구매 시 충전기 설치 지원', '2024-02-10 09:00:00', '2024-02-10 10:00:00', TRUE, - 'MEM_000000009'), - ('PROM_000000010', 'K5 재고 한정 특별 할인', '재고 한정 K5 모델에 대한 추가 할인 제공', '2024-02-15 15:00:00', '2024-02-16 16:00:00', - TRUE, 'MEM_000000010'); - -INSERT INTO tb_product_option (PROD_ID, PROD_SER_NO, OPT_CNTY, OPT_MNFR, OPT_VHC_TYPE, OPT_CHSS, OPT_DTIL_TYPE, - OPT_BODY_TYPE, - OPT_SFTY_DVCE, OPT_ENGN_CPCT, OPT_SCRT_CODE, OPT_PRDC_YEAR, OPT_PRDC_PLNT, OPT_ENGN, - OPT_MSSN, - OPT_TRIM, OPT_XTNL_COLR, OPT_ITNL_COLR, OPT_HUD, OPT_NAVI, OPT_DRVE_WISE, OPT_SMRT_CNCT, - OPT_STYL, OPT_MY_CFRT_PCKG, OPT_OTDR_PCKG, OPT_SUN_ROOF, OPT_SOND, ACTIVE) -VALUES --- 데이터 1 -('PRO_000000001', 'KNAHAA4AALU1A00001', 'K', 'N', 'A', 'A', 'L', '4', '2', 'A', 'P', 'A', 'U', '1', '0', '1', 'B', 'W', - '1', '1', '1', '1', '1', '0', '1', '1', '1', TRUE), --- 데이터 2 -('PRO_000000002', 'KNAHBA4BALR2Z00002', 'K', 'N', 'H', 'B', 'L', '4', '4', 'B', 'R', 'B', 'Z', '1', '1', '0', 'G', 'B', - '0', '1', '0', '1', '0', '1', '0', '0', '1', TRUE), --- 데이터 3 -('PRO_000000003', 'KMBHC64CAMJ5A00003', 'K', 'M', 'H', 'C', 'M', '6', '3', 'C', 'P', 'C', 'A', '0', '1', '1', 'R', 'G', - '1', '1', '1', '0', '1', '1', '0', '1', '0', TRUE), --- 데이터 4 -('PRO_000000004', 'KNJFA42DALU3C00004', 'K', 'N', 'J', 'D', 'L', '2', '4', 'A', 'R', 'D', 'C', '1', '0', '0', 'B', 'R', - '0', '0', '0', '1', '0', '0', '1', '0', '1', TRUE), --- 데이터 5 -('PRO_000000005', 'KFBGBM5EARP1M00005', 'K', 'B', 'F', 'B', 'M', '5', '1', 'B', 'P', 'E', 'M', '1', '1', '1', 'W', 'B', - '1', '1', '0', '0', '1', '1', '0', '1', '1', TRUE), --- 데이터 6 -('PRO_000000006', 'KNHGA6BALUP7A00006', 'K', 'N', 'H', 'G', 'L', '6', '3', 'A', 'R', 'F', 'A', '0', '0', '1', 'B', 'G', - '1', '1', '1', '1', '0', '1', '1', '0', '0', TRUE), --- 데이터 7 -('PRO_000000007', 'KNJFA34AALU4Z00007', 'K', 'N', 'J', 'A', 'M', '4', '4', 'C', 'P', 'G', 'Z', '1', '1', '0', 'R', 'W', - '0', '0', '1', '0', '1', '0', '1', '1', '1', TRUE), --- 데이터 8 -('PRO_000000008', 'KMAHDA2AAMJ3T00008', 'K', 'M', 'H', 'D', 'L', '2', '2', 'A', 'R', 'H', 'T', '0', '0', '1', 'B', 'G', - '1', '1', '0', '1', '0', '1', '1', '0', '0', TRUE), --- 데이터 9 -('PRO_000000009', 'KNAHCA5BALU5C00009', 'K', 'N', 'A', 'C', 'M', '5', '1', 'B', 'P', 'J', 'C', '1', '1', '0', 'R', 'R', - '0', '1', '1', '1', '1', '0', '1', '1', '0', TRUE), --- 데이터 10 -('PRO_000000010', 'KNFHC54CAMR1A00010', 'K', 'N', 'F', 'F', 'N', '4', '3', 'C', 'R', 'K', 'A', '0', '1', '1', 'W', 'B', - '1', '1', '0', '0', '0', '1', '0', '1', '1', TRUE); - -INSERT INTO tb_sales_history (SAL_HIST_ID, CONR_ID) -VALUES ('SAL_000000001', 'CON_000000001'), - ('SAL_000000002', 'CON_000000002'), - ('SAL_000000003', 'CON_000000003'), - ('SAL_000000004', 'CON_000000004'), - ('SAL_000000005', 'CON_000000005'), - ('SAL_000000006', 'CON_000000006'), - ('SAL_000000007', 'CON_000000007'), - ('SAL_000000008', 'CON_000000008'), - ('SAL_000000009', 'CON_000000009'), - ('SAL_000000010', 'CON_000000010'); \ No newline at end of file From 28f6d5b4c8cdc5f494b6bff774d741aa5844c1ec Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 15:37:58 +0900 Subject: [PATCH 193/563] =?UTF-8?q?fix:=20ddl=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/sql/ddl.sql | 894 --------------------------------- 1 file changed, 894 deletions(-) delete mode 100644 src/main/resources/sql/ddl.sql diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql deleted file mode 100644 index 7f477d32..00000000 --- a/src/main/resources/sql/ddl.sql +++ /dev/null @@ -1,894 +0,0 @@ --- 외래 키 제약 조건 비활성화 -SET FOREIGN_KEY_CHECKS = 0; - --- 자식 테이블부터 삭제 -DROP TABLE IF EXISTS tb_product_option; -DROP TABLE IF EXISTS tb_update_history; -DROP TABLE IF EXISTS tb_member_detail; -DROP TABLE IF EXISTS tb_alarm; -DROP TABLE IF EXISTS tb_schedule; -DROP TABLE IF EXISTS tb_file; -DROP TABLE IF EXISTS tb_promotion; -DROP TABLE IF EXISTS tb_evaluation; -DROP TABLE IF EXISTS tb_purchase_order; -DROP TABLE IF EXISTS tb_problem; -DROP TABLE IF EXISTS tb_order; -DROP TABLE IF EXISTS tb_notice; -DROP TABLE IF EXISTS tb_contract; -DROP TABLE IF EXISTS tb_product; -DROP TABLE IF EXISTS tb_customer_info; -DROP TABLE IF EXISTS tb_member_role; -DROP TABLE IF EXISTS tb_member; -DROP TABLE IF EXISTS tb_center; -DROP TABLE IF EXISTS tb_organization_chart; -DROP TABLE IF EXISTS tb_sales_history; - --- 조직 관련 테이블 생성 -CREATE TABLE tb_organization_chart -( - ORG_CHA_ID VARCHAR(255) NOT NULL, - ORG_CHA_NAME VARCHAR(255) NOT NULL, - ORG_CHA_DEPTH INT NOT NULL, - PRIMARY KEY (ORG_CHA_ID) -); - -CREATE TABLE tb_center -( - CENT_ID VARCHAR(255) NOT NULL, - CENT_NAME VARCHAR(255) NOT NULL, - CENT_ADR VARCHAR(255) NOT NULL, - CENT_PHO VARCHAR(255) NOT NULL, - CENT_MEM_CNT INT NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL DEFAULT 'CURRENT_TIMESTAMP', - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL, - CENT_OPR_AT VARCHAR(255) NOT NULL, - PRIMARY KEY (CENT_ID) -); - -CREATE TABLE tb_member -( - MEM_ID VARCHAR(255) NOT NULL, - MEM_LOGN_ID INT NOT NULL, - MEM_PWD VARCHAR(255) NOT NULL, - MEM_NAME VARCHAR(255) NOT NULL, - MEM_EMA VARCHAR(255) NOT NULL, - MEM_AGE INT NOT NULL, - MEM_SEX VARCHAR(255) NOT NULL DEFAULT 'FEMALE', - MEM_IDEN_NO VARCHAR(255) NOT NULL, - MEM_PHO VARCHAR(255) NOT NULL, - MEM_EMER_PHO VARCHAR(255) NULL, - MEM_ADR VARCHAR(255) NOT NULL, - MEM_NOTE VARCHAR(255) NULL, - MEM_POS VARCHAR(255) NOT NULL DEFAULT 'INTERN', - MEM_GRD VARCHAR(255) NOT NULL DEFAULT 'High School', - MEM_JOB_TYPE VARCHAR(255) NOT NULL, - MEM_MIL VARCHAR(255) NOT NULL DEFAULT 'exemption', - MEM_BANK_NAME VARCHAR(255) NULL, - MEM_ACC VARCHAR(255) NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - CENTER_ID VARCHAR(255) NOT NULL, - ORG_CHA_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (MEM_ID), - FOREIGN KEY (CENTER_ID) REFERENCES tb_center (CENT_ID), - FOREIGN KEY (ORG_CHA_ID) REFERENCES tb_organization_chart (ORG_CHA_ID) -); - -CREATE TABLE tb_member_role -( - MEM_ROL_ID VARCHAR(255) NOT NULL, - MEM_ROL_NAME VARCHAR(255) NOT NULL DEFAULT '영업 사원', - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (MEM_ROL_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_customer_info -( - CUST_ID VARCHAR(255) NOT NULL, - CUST_NAME VARCHAR(255) NOT NULL, - CUST_AGE INT NOT NULL, - CUST_SEX VARCHAR(255) NOT NULL DEFAULT 'FEMALE', - CUST_PHO VARCHAR(255) NOT NULL, - CUST_EMER_PHO VARCHAR(255) NOT NULL, - CUST_EMA VARCHAR(255) NOT NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (CUST_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - --- 부모 테이블 생성 -CREATE TABLE tb_product -( - PROD_ID VARCHAR(255) NOT NULL, - PROD_SER_NO VARCHAR(255) NOT NULL, - PROD_COST INT NOT NULL, - PROD_NAME VARCHAR(255) NOT NULL, - PROD_STCK INT NOT NULL DEFAULT 0, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL DEFAULT 'CURRENT_TIMESTAMP', - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - PRIMARY KEY (PROD_ID, PROD_SER_NO) -); - -CREATE TABLE tb_contract -( - CONR_ID VARCHAR(255) NOT NULL, - CONR_NAME VARCHAR(255) NOT NULL, - CONR_CUST_NAME VARCHAR(255) NOT NULL, - CONR_CUST_IDEN_NO VARCHAR(255) NOT NULL, - CONR_CUST_ADR VARCHAR(255) NOT NULL, - CONR_CUST_EMA VARCHAR(255) NOT NULL, - CONR_CUST_PHO VARCHAR(255) NOT NULL, - CONR_COMP_NAME VARCHAR(255) NULL, - CONR_CUST_CLA VARCHAR(255) NOT NULL DEFAULT 'PERSONAL', - CONR_CUST_PUR_COND VARCHAR(255) NOT NULL DEFAULT 'CASH', - CONR_SERI_NUM VARCHAR(255) NOT NULL, - CONR_SELE_OPTI VARCHAR(255) NOT NULL, - CONR_DOWN_PAY INT NOT NULL, - CONR_INTE_PAY INT NOT NULL, - CONR_REM_PAY INT NOT NULL, - CONR_CONS_PAY INT NOT NULL, - CONR_DELV_DATE VARCHAR(255) NULL, - CONR_DELV_LOC VARCHAR(255) NULL, - CONR_STAT VARCHAR(255) NULL DEFAULT 'WAIT', - CONR_NO_OF_VEH INT NOT NULL DEFAULT 1, - CREATED_URL TEXT NOT NULL, - DELETED_URL TEXT NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - MEM_ID VARCHAR(255) NOT NULL, - CENT_ID VARCHAR(255) NOT NULL, - CUST_ID VARCHAR(255) NULL, - PROD_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (CONR_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE, - FOREIGN KEY (CUST_ID) REFERENCES tb_customer_info (CUST_ID) ON DELETE CASCADE, - FOREIGN KEY (PROD_ID) REFERENCES tb_product (PROD_ID) ON DELETE CASCADE, - FOREIGN KEY (CENT_ID) REFERENCES tb_center (CENT_ID) ON DELETE CASCADE -); - -CREATE TABLE tb_notice -( - NOT_ID VARCHAR(255) NOT NULL, - NOT_TTL VARCHAR(255) NOT NULL, - NOT_TAG VARCHAR(255) NOT NULL DEFAULT 'ALL', - NOT_CLA VARCHAR(255) NOT NULL DEFAULT 'NORMAL', - NOT_CONT TEXT NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (NOT_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_order -( - ORD_ID VARCHAR(255) NOT NULL, - ORD_TTL VARCHAR(255) NOT NULL, - ORD_CONT TEXT NOT NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ORD_STAT VARCHAR(255) NOT NULL DEFAULT 'WAIT', - CONR_ID VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - ADMIN_ID VARCHAR(255) NULL, - PRIMARY KEY (ORD_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE, - FOREIGN KEY (ADMIN_ID) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE, - FOREIGN KEY (CONR_ID) REFERENCES tb_contract (CONR_ID) ON DELETE CASCADE -); - -CREATE TABLE tb_PROBLEM -( - PROB_ID VARCHAR(255) NOT NULL, - PROB_TTL VARCHAR(255) NOT NULL, - PROB_CONT VARCHAR(255) NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - DELETED_AT CHAR(19) NULL, - CUST_ID VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PROD_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (PROB_ID), - FOREIGN KEY (CUST_ID) REFERENCES tb_customer_info (CUST_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), - FOREIGN KEY (PROD_ID) REFERENCES tb_product (PROD_ID) -); - -CREATE TABLE tb_purchase_order -( - PUR_ORD_ID VARCHAR(255) NOT NULL, - PUR_ORD_TTL VARCHAR(255) NOT NULL, - PUR_CONT TEXT NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - PUR_ORD_STAT VARCHAR(255) NOT NULL DEFAULT 'WAIT', - ORD_ID VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (PUR_ORD_ID), - FOREIGN KEY (ORD_ID) REFERENCES tb_order (ORD_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_EVALUATION -( - EVAL_ID VARCHAR(255) NOT NULL, - EVAL_TTL VARCHAR(255) NOT NULL, - EVAL_CONT VARCHAR(255) NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - CENT_ID VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - WRI_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (EVAL_ID), - FOREIGN KEY (CENT_ID) REFERENCES tb_center (CENT_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), - FOREIGN KEY (WRI_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_promotion -( - PROM_ID VARCHAR(255) NOT NULL, - PROM_TTL VARCHAR(255) NOT NULL, - PROM_CONT VARCHAR(255) NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (PROM_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_file -( - FILE_ID VARCHAR(255) NOT NULL, - FILE_NAME VARCHAR(255) NOT NULL, - FILE_URL VARCHAR(255) NOT NULL, - FILE_TYPE VARCHAR(255) NOT NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - CREATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - MEM_ID VARCHAR(255) NULL, - NOT_ID VARCHAR(255) NULL, - CONR_ID VARCHAR(255) NULL, - PROB_ID VARCHAR(255) NULL, - CENT_ID VARCHAR(255) NULL, - PROM_ID VARCHAR(255) NULL, - EVAL_ID VARCHAR(255) NULL, - CONR_ID2 VARCHAR(255) NULL, - PRIMARY KEY (FILE_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), - FOREIGN KEY (NOT_ID) REFERENCES tb_notice (NOT_ID), - FOREIGN KEY (CONR_ID) REFERENCES tb_contract (CONR_ID), - FOREIGN KEY (PROB_ID) REFERENCES tb_problem (PROB_ID), - FOREIGN KEY (CENT_ID) REFERENCES tb_center (CENT_ID), - FOREIGN KEY (PROM_ID) REFERENCES tb_promotion (PROM_ID), - FOREIGN KEY (EVAL_ID) REFERENCES tb_evaluation (EVAL_ID), - FOREIGN KEY (CONR_ID2) REFERENCES tb_contract (CONR_ID) -); - -CREATE TABLE tb_schedule -( - SCH_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', - SCH_NAME VARCHAR(255) NOT NULL, - SCH_CONT VARCHAR(255) NOT NULL, - SCH_TAG VARCHAR(255) NOT NULL, - SCH_SRT_AT CHAR(19) NOT NULL, - SCH_END_AT CHAR(19) NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL COMMENT 'TRUE/FALSE', - MEM_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', - PRIMARY KEY (SCH_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_alarm -( - ALR_ID VARCHAR(255) NOT NULL, - ALR_MSG VARCHAR(255) NOT NULL, - ALR_URL VARCHAR(255) NOT NULL, - ALR_READ_STAT BOOLEAN NOT NULL DEFAULT FALSE, - CREATED_AT CHAR(19) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (ALR_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_member_detail -( - MEM_DET_ID VARCHAR(255) NOT NULL, - MEM_DET_REL VARCHAR(255) NOT NULL, - MEM_DET_NAME VARCHAR(255) NOT NULL, - MEM_DET_BIR VARCHAR(255) NOT NULL, - MEM_DET_IDEN_NO VARCHAR(255) NOT NULL, - MEM_DET_PHO VARCHAR(255) NOT NULL, - MEM_DET_SEX VARCHAR(255) NOT NULL DEFAULT 'FEMALE' COMMENT 'FEMALE/MALE', - MEM_DET_DIS BOOLEAN NOT NULL, - MEM_DET_DIE BOOLEAN NOT NULL, - MEM_DET_NOTE VARCHAR(255) NULL, - MEM_DET_ENTD VARCHAR(255) NOT NULL, - MEM_DET_GRAD VARCHAR(255) NOT NULL, - MEM_DET_FNL_EDC VARCHAR(255) NOT NULL, - MEM_DET_EDU VARCHAR(255) NOT NULL, - MEM_DET_MJR VARCHAR(255) NOT NULL, - MEM_DET_EMP_DATE VARCHAR(255) NOT NULL, - MEM_DET_RTR_DATE VARCHAR(255) NOT NULL, - CAR_INFO VARCHAR(255) NOT NULL, - CERT_DATE VARCHAR(255) NOT NULL, - CERT_INST VARCHAR(255) NOT NULL, - CERT_NAME VARCHAR(255) NULL, - CERT_SCO VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', - PRIMARY KEY (MEM_DET_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_UPDATE_HISTORY -( - UPD_ID VARCHAR(255) NOT NULL, - UPD_IP VARCHAR(255) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - UPDATED_URL VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - CONR_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (UPD_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), - FOREIGN KEY (CONR_ID) REFERENCES tb_contract (CONR_ID) -); - -CREATE TABLE tb_PRODUCT_OPTION -( - PROD_ID VARCHAR(255) NOT NULL, - PROD_SER_NO VARCHAR(255) NOT NULL, - OPT_CNTY CHAR(1) NOT NULL DEFAULT 'K', - OPT_MNFR CHAR(1) NOT NULL DEFAULT 'N', - OPT_VHC_TYPE CHAR(1) NOT NULL, - OPT_CHSS CHAR(1) NOT NULL, - OPT_DTIL_TYPE CHAR(1) NOT NULL, - OPT_BODY_TYPE CHAR(1) NOT NULL, - OPT_SFTY_DVCE CHAR(1) NOT NULL, - OPT_ENGN_CPCT CHAR(1) NOT NULL, - OPT_SCRT_CODE CHAR(1) NOT NULL, - OPT_PRDC_YEAR CHAR(1) NOT NULL, - OPT_PRDC_PLNT CHAR(1) NOT NULL, - OPT_ENGN CHAR(1) NOT NULL DEFAULT '0', - OPT_MSSN CHAR(1) NOT NULL DEFAULT '0', - OPT_TRIM CHAR(1) NOT NULL DEFAULT '0', - OPT_XTNL_COLR CHAR(1) NOT NULL, - OPT_ITNL_COLR CHAR(1) NOT NULL, - OPT_HUD CHAR(1) NOT NULL DEFAULT '0', - OPT_NAVI CHAR(1) NOT NULL DEFAULT '0', - OPT_DRVE_WISE CHAR(1) NOT NULL DEFAULT '0', - OPT_SMRT_CNCT CHAR(1) NOT NULL DEFAULT '0', - OPT_STYL CHAR(1) NOT NULL DEFAULT '0', - OPT_MY_CFRT_PCKG CHAR(1) NOT NULL DEFAULT '0', - OPT_OTDR_PCKG CHAR(1) NOT NULL DEFAULT '0', - OPT_SUN_ROOF CHAR(1) NOT NULL DEFAULT '0', - OPT_SOND CHAR(1) NOT NULL DEFAULT '0', - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - PRIMARY KEY (PROD_ID, PROD_SER_NO), - FOREIGN KEY (PROD_ID, PROD_SER_NO) REFERENCES tb_product (PROD_ID, PROD_SER_NO) ON DELETE CASCADE -); - -CREATE TABLE tb_sales_history -( - SAL_HIST_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', - CONR_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고' -); - -INSERT INTO tb_organization_chart (ORG_CHA_ID, ORG_CHA_NAME, ORG_CHA_DEPTH) -VALUES ('ORG_000000001', '본사', 1), - ('ORG_000000002', '서울지사', 2), - ('ORG_000000003', '부산지사', 2), - ('ORG_000000004', '인천지사', 2), - ('ORG_000000005', '대전지사', 2), - ('ORG_000000006', '영업부', 3), - ('ORG_000000007', '기술부', 3), - ('ORG_000000008', '고객지원부', 3), - ('ORG_000000009', '마케팅부', 3), - ('ORG_000000010', '재무부', 3); - -INSERT INTO tb_center (CENT_ID, CENT_NAME, CENT_ADR, CENT_PHO, CENT_MEM_CNT, CREATED_AT, UPDATED_AT, ACTIVE, - CENT_OPR_AT) -VALUES ('CEN_000000001', '서울센터', '서울특별시 중구', '010-1234-5678', 50, '2024-01-01 12:00:00', '2024-01-01 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000002', '부산센터', '부산광역시 해운대구', '010-2345-6789', 40, '2024-01-02 12:00:00', '2024-01-02 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000003', '대구센터', '대구광역시', '010-3456-7890', 30, '2024-01-03 12:00:00', '2024-01-03 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000004', '인천센터', '인천광역시', '010-4567-8901', 20, '2024-01-04 12:00:00', '2024-01-04 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000005', '울산센터', '울산광역시', '010-5678-9012', 25, '2024-01-05 12:00:00', '2024-01-05 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000006', '대전센터', '대전광역시', '010-6789-0123', 35, '2024-01-06 12:00:00', '2024-01-06 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000007', '광주센터', '광주광역시', '010-7890-1234', 45, '2024-01-07 12:00:00', '2024-01-07 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000008', '경기센터', '경기도 수원시', '010-8901-2345', 20, '2024-01-08 12:00:00', '2024-01-08 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000009', '청주센터', '충청북도 청주시', '010-9012-3456', 10, '2024-01-09 12:00:00', '2024-01-09 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000010', '전주센터', '전라북도 전주시', '010-0123-4567', 15, '2024-01-10 12:00:00', '2024-01-10 12:00:00', TRUE, - '09:00-18:00'); - -INSERT INTO tb_member (MEM_ID, MEM_LOGN_ID, MEM_PWD, MEM_NAME, MEM_EMA, MEM_AGE, MEM_SEX, MEM_IDEN_NO, MEM_PHO, MEM_ADR, - MEM_POS, MEM_GRD, MEM_JOB_TYPE, CENTER_ID, ORG_CHA_ID, CREATED_AT, UPDATED_AT, ACTIVE) -VALUES ('MEM_000000001', 101, 'pwd1234', '김철수', 'chulsoo@example.com', 35, 'MALE', '123456-7890123', '010-9876-5432', - '서울특별시', 'Manager', 'Bachelor', 'Full-time', 'CEN_000000001', 'ORG_000000006', '2024-01-01 12:00:00', - '2024-01-01 12:00:00', TRUE), - ('MEM_000000002', 102, 'pwd2345', '이영희', 'younghee@example.com', 28, 'FEMALE', '234567-8901234', '010-8765-4321', - '부산광역시', 'Staff', 'Master', 'Full-time', 'CEN_000000002', 'ORG_000000006', '2024-01-02 12:00:00', - '2024-01-02 12:00:00', TRUE), - ('MEM_000000003', 103, 'pwd3456', '박지훈', 'jihun@example.com', 42, 'MALE', '345678-9012345', '010-7654-3210', - '대구광역시', 'Engineer', 'Doctorate', 'Contract', 'CEN_000000003', 'ORG_000000007', '2024-01-03 12:00:00', - '2024-01-03 12:00:00', TRUE), - ('MEM_000000004', 104, 'pwd4567', '최민수', 'minsoo@example.com', 38, 'MALE', '456789-0123456', '010-6543-2109', - '인천광역시', 'Manager', 'Bachelor', 'Part-time', 'CEN_000000004', 'ORG_000000007', '2024-01-04 12:00:00', - '2024-01-04 12:00:00', TRUE), - ('MEM_000000005', 105, 'pwd5678', '박미영', 'miyoung@example.com', 29, 'FEMALE', '567890-1234567', '010-5432-1098', - '울산광역시', 'Intern', 'High School', 'Full-time', 'CEN_000000005', 'ORG_000000008', '2024-01-05 12:00:00', - '2024-01-05 12:00:00', TRUE), - ('MEM_000000006', 106, 'pwd6789', '정수현', 'soohyun@example.com', 31, 'FEMALE', '678901-2345678', '010-4321-0987', - '대전광역시', 'Senior Engineer', 'Master', 'Full-time', 'CEN_000000006', 'ORG_000000007', '2024-01-06 12:00:00', - '2024-01-06 12:00:00', TRUE), - ('MEM_000000007', 107, 'pwd7890', '한지민', 'jimin@example.com', 45, 'FEMALE', '789012-3456789', '010-3210-9876', - '광주광역시', 'Sales Rep', 'Bachelor', 'Part-time', 'CEN_000000007', 'ORG_000000006', '2024-01-07 12:00:00', - '2024-01-07 12:00:00', TRUE), - ('MEM_000000008', 108, 'pwd8901', '이준호', 'junho@example.com', 36, 'MALE', '890123-4567890', '010-2109-8765', - '경기도 수원시', 'Technician', 'Bachelor', 'Full-time', 'CEN_000000008', 'ORG_000000008', '2024-01-08 12:00:00', - '2024-01-08 12:00:00', TRUE), - ('MEM_000000009', 109, 'pwd9012', '윤아름', 'areum@example.com', 27, 'FEMALE', '901234-5678901', '010-1098-7654', - '충청북도 청주시', 'Assistant', 'High School', 'Intern', 'CEN_000000009', 'ORG_000000006', '2024-01-09 12:00:00', - '2024-01-09 12:00:00', TRUE), - ('MEM_000000010', 110, 'pwd0123', '김정훈', 'junghoon@example.com', 33, 'MALE', '012345-6789012', '010-0987-6543', - '전라북도 전주시', 'Consultant', 'Master', 'Contract', 'CEN_000000010', 'ORG_000000009', '2024-01-10 12:00:00', - '2024-01-10 12:00:00', TRUE); - -INSERT INTO tb_member_role (MEM_ROL_ID, MEM_ROL_NAME, MEM_ID) -VALUES ('ROL_000000001', '영업 사원', 'MEM_000000001'), - ('ROL_000000002', '영업 사원', 'MEM_000000002'), - ('ROL_000000003', '영업 사원', 'MEM_000000003'), - ('ROL_000000004', '영업 사원', 'MEM_000000004'), - ('ROL_000000005', '영업 관리자', 'MEM_000000005'), - ('ROL_000000006', '영업 관리자', 'MEM_000000009'), - ('ROL_000000007', '영업 관리자', 'MEM_000000007'), - ('ROL_000000008', '영업 담당자', 'MEM_000000008'), - ('ROL_000000009', '영업 담당자', 'MEM_000000010'), - ('ROL_000000010', '영업 담당자', 'MEM_000000006'); - -INSERT INTO tb_customer_info (CUST_ID, CUST_NAME, CUST_AGE, CUST_SEX, CUST_PHO, CUST_EMER_PHO, CUST_EMA, ACTIVE, MEM_ID) -VALUES ('CUS_000000001', '홍길동', 45, 'MALE', '010-1111-2222', '010-2222-3333', 'gildong@example.com', TRUE, - 'MEM_000000001'), - ('CUS_000000002', '김민수', 38, 'MALE', '010-3333-4444', '010-4444-5555', 'minsoo@example.com', TRUE, - 'MEM_000000002'), - ('CUS_000000003', '이영희', 32, 'FEMALE', '010-5555-6666', '010-6666-7777', 'younghee@example.com', TRUE, - 'MEM_000000003'), - ('CUS_000000004', '박지훈', 27, 'MALE', '010-7777-8888', '010-8888-9999', 'jihun@example.com', TRUE, - 'MEM_000000004'), - ('CUS_000000005', '최정민', 22, 'FEMALE', '010-9999-0000', '010-0000-1111', 'jungmin@example.com', TRUE, - 'MEM_000000005'), - ('CUS_000000006', '정수민', 31, 'MALE', '010-1212-3434', '010-2323-4545', 'suming@example.com', TRUE, - 'MEM_000000006'), - ('CUS_000000007', '한민정', 29, 'FEMALE', '010-4545-5656', '010-5656-6767', 'hanmj@example.com', TRUE, - 'MEM_000000007'), - ('CUS_000000008', '이동수', 41, 'MALE', '010-6767-7878', '010-7878-8989', 'dongsu@example.com', TRUE, - 'MEM_000000008'), - ('CUS_000000009', '윤소라', 33, 'FEMALE', '010-8989-9090', '010-9090-1010', 'sora@example.com', TRUE, - 'MEM_000000009'), - ('CUS_000000010', '박성훈', 36, 'MALE', '010-1010-1112', '010-1212-1313', 'seonghun@example.com', TRUE, - 'MEM_000000010'); - -INSERT INTO tb_product (PROD_ID, PROD_SER_NO, PROD_COST, PROD_NAME, PROD_STCK, CREATED_AT, UPDATED_AT, ACTIVE) -VALUES ('PRO_000000001', 'KNAHAA4AALU1A00001', 25000000, '쏘렌토', 10, '2024-01-10 10:00:00', '2024-01-10 11:00:00', TRUE), - ('PRO_000000002', 'KNAHBA4BALR2Z00002', 22000000, '스포티지', 15, '2024-01-11 11:00:00', '2024-01-11 12:00:00', - TRUE), - ('PRO_000000003', 'KMBHC64CAMJ5A00003', 28000000, 'K7', 8, '2024-01-12 12:00:00', '2024-01-12 13:00:00', TRUE), - ('PRO_000000004', 'KNJFA42DALU3C00004', 19000000, '셀토스', 12, '2024-01-13 13:00:00', '2024-01-13 14:00:00', TRUE), - ('PRO_000000005', 'KFBGBM5EARP1M00005', 18000000, 'K3', 20, '2024-01-14 14:00:00', '2024-01-14 15:00:00', TRUE), - ('PRO_000000006', 'KNHGA6BALUP7A00006', 34000000, '모하비', 7, '2024-01-15 15:00:00', '2024-01-15 16:00:00', TRUE), - ('PRO_000000007', 'KNJFA34AALU4Z00007', 32000000, 'K8', 5, '2024-01-16 16:00:00', '2024-01-16 17:00:00', TRUE), - ('PRO_000000008', 'KMAHDA2AAMJ3T00008', 27000000, '스팅어', 9, '2024-01-17 17:00:00', '2024-01-17 18:00:00', TRUE), - ('PRO_000000009', 'KNAHCA5BALU5C00009', 23000000, '니로', 14, '2024-01-18 18:00:00', '2024-01-18 19:00:00', TRUE), - ('PRO_000000010', 'KNFHC54CAMR1A00010', 28000000, 'K5', 6, '2024-01-19 19:00:00', '2024-01-19 20:00:00', TRUE); - - -INSERT INTO tb_contract (CONR_ID, CONR_NAME, CONR_CUST_NAME, CONR_CUST_IDEN_NO, CONR_CUST_ADR, - CONR_CUST_EMA, CONR_CUST_PHO, CONR_COMP_NAME, CONR_CUST_CLA, CONR_CUST_PUR_COND, - CONR_SERI_NUM, CONR_SELE_OPTI, CONR_DOWN_PAY, CONR_INTE_PAY, CONR_REM_PAY, - CONR_CONS_PAY, CONR_DELV_DATE, CONR_DELV_LOC, CONR_STAT, CONR_NO_OF_VEH, - CREATED_URL, DELETED_URL, ACTIVE, CREATED_AT, UPDATED_AT, DELETED_AT, - MEM_ID, CENT_ID, CUST_ID, PROD_ID) -VALUES --- 계약 1 -('CON_000000001', '쏘렌토 계약', '박지훈', '880512-1234567', '서울특별시 강남구', - 'jihun@example.com', '010-1234-5678', '기아자동차', 'PERSONAL', 'CASH', - 'SER_001', '풀옵션', 5000000, 300000, 25000000, 30000000, - '2024-02-20', '서울특별시 강남구 배송센터', 'WAIT', 1, - '/contracts/1', NULL, TRUE, '2024-01-10 12:00:00', '2024-01-11 13:00:00', NULL, - 'MEM_000000001', 'CEN_000000001', 'CUS_000000001', 'PRO_000000001'), - --- 계약 2 -('CON_000000002', '스포티지 계약', '김영희', '900123-2345678', '부산광역시 해운대구', - 'younghee@example.com', '010-2345-6789', '기아자동차', 'PERSONAL', 'CREDIT', - 'SER_002', '기본옵션', 3000000, 200000, 17000000, 25000000, - '2024-03-05', '부산광역시 해운대구 배송센터', 'CONFIRMED', 1, - '/contracts/2', NULL, TRUE, '2024-01-12 14:00:00', '2024-01-13 15:00:00', NULL, - 'MEM_000000002', 'CEN_000000002', 'CUS_000000002', 'PRO_000000002'), - --- 계약 3 -('CON_000000003', 'K7 계약', '이철수', '950405-3456789', '대구광역시 수성구', - 'chulsoo@example.com', '010-3456-7890', '기아자동차', 'BUSINESS', 'LEASE', - 'SER_003', '풀옵션', 8000000, 400000, 28000000, 35000000, - '2024-03-10', '대구광역시 배송센터', 'WAIT', 2, - '/contracts/3', NULL, TRUE, '2024-01-14 09:00:00', '2024-01-15 10:00:00', NULL, - 'MEM_000000003', 'CEN_000000003', 'CUS_000000003', 'PRO_000000003'), - --- 계약 4 -('CON_000000004', '셀토스 계약', '박민준', '970512-4567890', '인천광역시 미추홀구', - 'minjoon@example.com', '010-4567-8901', '기아자동차', 'PERSONAL', 'CASH', - 'SER_004', '기본옵션', 4000000, 250000, 12000000, 20000000, - '2024-04-01', '인천광역시 배송센터', 'DELIVERED', 1, - '/contracts/4', NULL, TRUE, '2024-01-16 11:00:00', '2024-01-17 12:00:00', NULL, - 'MEM_000000004', 'CEN_000000004', 'CUS_000000004', 'PRO_000000004'), - --- 계약 5 -('CON_000000005', 'K3 계약', '최민수', '980618-5678901', '울산광역시 중구', - 'minsoo@example.com', '010-5678-9012', '기아자동차', 'BUSINESS', 'INSTALLMENT', - 'SER_005', '기본옵션', 2000000, 100000, 13000000, 18000000, - '2024-04-15', '울산광역시 배송센터', 'CANCELLED', 1, - '/contracts/5', NULL, TRUE, '2024-01-18 13:00:00', '2024-01-19 14:00:00', NULL, - 'MEM_000000005', 'CEN_000000005', 'CUS_000000005', 'PRO_000000005'), - --- 계약 6 -('CON_000000006', '모하비 계약', '김민주', '850824-6789012', '대전광역시 서구', - 'minju@example.com', '010-6789-0123', '기아자동차', 'PERSONAL', 'CASH', - 'SER_006', '풀옵션', 6000000, 500000, 34000000, 40000000, - '2024-05-01', '대전광역시 배송센터', 'CONFIRMED', 1, - '/contracts/6', NULL, TRUE, '2024-01-20 15:00:00', '2024-01-21 16:00:00', NULL, - 'MEM_000000006', 'CEN_000000006', 'CUS_000000006', 'PRO_000000006'), - --- 계약 7 -('CON_000000007', 'K8 계약', '정수영', '940705-7890123', '광주광역시 북구', - 'suyoung@example.com', '010-7890-1234', '기아자동차', 'LEASE', 'LEASE', - 'SER_007', '럭셔리 패키지', 7000000, 350000, 25000000, 32000000, - '2024-05-20', '광주광역시 배송센터', 'WAIT', 1, - '/contracts/7', NULL, TRUE, '2024-01-22 17:00:00', '2024-01-23 18:00:00', NULL, - 'MEM_000000007', 'CEN_000000007', 'CUS_000000007', 'PRO_000000007'), - --- 계약 8 -('CON_000000008', '스팅어 계약', '이준호', '930910-8901234', '경기도 수원시', - 'junho@example.com', '010-8901-2345', '기아자동차', 'PERSONAL', 'CREDIT', - 'SER_008', '풀옵션', 5000000, 300000, 22000000, 27000000, - '2024-06-01', '수원시 배송센터', 'DELIVERED', 1, - '/contracts/8', NULL, TRUE, '2024-01-24 19:00:00', '2024-01-25 20:00:00', NULL, - 'MEM_000000008', 'CEN_000000008', 'CUS_000000008', 'PRO_000000008'), - --- 계약 9 -('CON_000000009', '니로 계약', '박소영', '890321-9012345', '충청북도 청주시', - 'soyoung@example.com', '010-9012-3456', '기아자동차', 'PERSONAL', 'CASH', - 'SER_009', '기본옵션', 3500000, 150000, 17000000, 23000000, - '2024-06-15', '청주시 배송센터', 'CONFIRMED', 1, - '/contracts/9', NULL, TRUE, '2024-01-26 21:00:00', '2024-01-27 22:00:00', NULL, - 'MEM_000000009', 'CEN_000000009', 'CUS_000000009', 'PRO_000000009'), - --- 계약 10 -('CON_000000010', 'K5 계약', '김정훈', '900815-0123456', '전라북도 전주시', - 'junghoon@example.com', '010-0123-4567', '기아자동차', 'BUSINESS', 'LEASE', - 'SER_010', '풀옵션', 6000000, 250000, 20000000, 28000000, - '2024-07-01', '전주시 배송센터', 'WAIT', 1, - '/contracts/10', NULL, TRUE, '2024-01-28 23:00:00', '2024-01-29 23:59:00', NULL, - 'MEM_000000010', 'CEN_000000010', 'CUS_000000010', 'PRO_000000010'); - -INSERT INTO tb_order ( - ORD_ID, ORD_TTL, ORD_CONT, ACTIVE, CREATED_AT, UPDATED_AT, DELETED_AT, ORD_STAT, CONR_ID, MEM_ID, ADMIN_ID -) VALUES --- 주문 1 -('ORD_000000001', '쏘렌토 계약 주문', '쏘렌토 차량 계약 완료 후 주문 처리', TRUE, '2024-01-10 10:00:00', '2024-01-10 11:00:00', NULL, 'CONFIRMED', 'CON_000000001', 'MEM_000000001', 'MEM_000000005'), --- 주문 2 -('ORD_000000002', '스포티지 구매 주문', '스포티지 고객 상담 후 주문 확정', TRUE, '2024-01-11 12:00:00', '2024-01-11 13:00:00', NULL, 'WAIT', 'CON_000000002', 'MEM_000000002', 'MEM_000000005'), --- 주문 3 -('ORD_000000003', 'K7 리스 주문', 'K7 리스 계약 완료 후 주문 생성', TRUE, '2024-01-12 09:00:00', '2024-01-12 10:00:00', NULL, 'CONFIRMED', 'CON_000000003', 'MEM_000000003', 'MEM_000000005'), --- 주문 4 -('ORD_000000004', '셀토스 추가 주문', '셀토스 고객 추가 옵션 주문', TRUE, '2024-01-13 10:30:00', '2024-01-13 11:30:00', NULL, 'CANCELLED', 'CON_000000004', 'MEM_000000004', 'MEM_000000005'), --- 주문 5 -('ORD_000000005', 'K3 계약 주문', 'K3 차량 계약 후 최종 주문', TRUE, '2024-01-14 11:00:00', '2024-01-14 12:00:00', NULL, 'DELIVERED', 'CON_000000005', 'MEM_000000001', 'MEM_000000006'), --- 주문 6 -('ORD_000000006', '모하비 계약 수정 주문', '모하비 계약 변경에 따른 주문 수정', TRUE, '2024-01-15 13:00:00', '2024-01-15 14:00:00', NULL, 'WAIT', 'CON_000000006', 'MEM_000000002', 'MEM_000000006'), --- 주문 7 -('ORD_000000007', 'K8 리스 주문', 'K8 차량 리스 계약 주문', TRUE, '2024-01-16 14:30:00', '2024-01-16 15:30:00', NULL, 'CONFIRMED', 'CON_000000007', 'MEM_000000003', 'MEM_000000006'), --- 주문 8 -('ORD_000000008', '스팅어 구매 주문', '스팅어 차량 구매 계약 후 주문 확정', TRUE, '2024-01-17 16:00:00', '2024-01-17 17:00:00', NULL, 'DELIVERED', 'CON_000000008', 'MEM_000000004', 'MEM_000000006'), --- 주문 9 -('ORD_000000009', '니로 전기차 주문', '니로 전기차 계약 주문', TRUE, '2024-01-18 17:30:00', '2024-01-18 18:30:00', NULL, 'WAIT', 'CON_000000009', 'MEM_000000001', 'MEM_000000010'), --- 주문 10 -('ORD_000000010', 'K5 재고 주문', 'K5 재고 차량 추가 주문', TRUE, '2024-01-19 09:00:00', '2024-01-19 10:00:00', NULL, 'CONFIRMED', 'CON_000000010', 'MEM_000000001', 'MEM_00000009'); - - -INSERT INTO tb_problem (PROB_ID, PROB_TTL, PROB_CONT, CREATED_AT, UPDATED_AT, ACTIVE, CUST_ID, MEM_ID, PROD_ID) -VALUES ('PROB_000000001', '쏘렌토 엔진 문제', '엔진에서 소음 발생', '2024-01-12 14:00:00', '2024-01-12 15:00:00', TRUE, - 'CUS_000000001', 'MEM_000000001', 'PRO_000000001'), - ('PROB_000000002', '스포티지 브레이크 문제', '브레이크 페달 작동 불량', '2024-01-13 10:00:00', '2024-01-13 11:00:00', TRUE, - 'CUS_000000002', 'MEM_000000002', 'PRO_000000002'), - ('PROB_000000003', 'K7 전자 장비 문제', '내비게이션 오류', '2024-01-14 09:00:00', '2024-01-14 10:00:00', TRUE, - 'CUS_000000003', 'MEM_000000003', 'PRO_000000003'), - ('PROB_000000004', '셀토스 에어컨 문제', '에어컨 작동 안됨', '2024-01-15 11:00:00', '2024-01-15 12:00:00', TRUE, - 'CUS_000000004', 'MEM_000000004', 'PRO_000000004'), - ('PROB_000000005', 'K3 연비 문제', '연비가 예상보다 낮음', '2024-01-16 12:00:00', '2024-01-16 13:00:00', TRUE, - 'CUS_000000005', 'MEM_000000005', 'PRO_000000005'), - ('PROB_000000006', '모하비 변속기 문제', '변속기 오류 발생', '2024-01-17 13:00:00', '2024-01-17 14:00:00', TRUE, - 'CUS_000000006', 'MEM_000000006', 'PRO_000000006'), - ('PROB_000000007', 'K8 배터리 문제', '배터리 수명 짧음', '2024-01-18 14:00:00', '2024-01-18 15:00:00', TRUE, 'CUS_000000007', - 'MEM_000000007', 'PRO_000000007'), - ('PROB_000000008', '스팅어 오디오 문제', '오디오 스피커 잡음', '2024-01-19 15:00:00', '2024-01-19 16:00:00', TRUE, - 'CUS_000000008', 'MEM_000000008', 'PRO_000000008'), - ('PROB_000000009', '니로 전기 문제', '충전 불량', '2024-01-20 16:00:00', '2024-01-20 17:00:00', TRUE, 'CUS_000000009', - 'MEM_000000009', 'PRO_000000009'), - ('PROB_000000010', 'K5 시동 문제', '시동이 걸리지 않음', '2024-01-21 17:00:00', '2024-01-21 18:00:00', TRUE, 'CUS_000000010', - 'MEM_000000010', 'PRO_000000010'); - -INSERT INTO tb_purchase_order (PUR_ORD_ID, PUR_ORD_TTL, PUR_CONT, CREATED_AT, UPDATED_AT, ACTIVE, PUR_ORD_STAT, ORD_ID, - MEM_ID) -VALUES ('PUR_000000001', '쏘렌토 부품 주문', '엔진 부품 요청', '2024-01-12 10:00:00', '2024-01-12 11:00:00', TRUE, 'WAIT', - 'ORD_000000001', 'MEM_000000001'), - ('PUR_000000002', '스포티지 타이어 주문', '타이어 4개 요청', '2024-01-13 12:00:00', '2024-01-13 13:00:00', TRUE, 'CONFIRMED', - 'ORD_000000002', 'MEM_000000002'), - ('PUR_000000003', 'K7 배터리 주문', '배터리 교체 요청', '2024-01-14 13:00:00', '2024-01-14 14:00:00', TRUE, 'DELIVERED', - 'ORD_000000003', 'MEM_000000003'), - ('PUR_000000004', '셀토스 브레이크 주문', '브레이크 패드 요청', '2024-01-15 14:00:00', '2024-01-15 15:00:00', TRUE, 'WAIT', - 'ORD_000000004', 'MEM_000000004'), - ('PUR_000000005', 'K3 내비게이션 주문', '내비게이션 교체', '2024-01-16 15:00:00', '2024-01-16 16:00:00', TRUE, 'CANCELLED', - 'ORD_000000005', 'MEM_000000005'), - ('PUR_000000006', '모하비 오일 필터 주문', '오일 필터 10개 요청', '2024-01-17 09:00:00', '2024-01-17 10:00:00', TRUE, - 'CONFIRMED', 'ORD_000000006', 'MEM_000000006'), - ('PUR_000000007', 'K8 헤드라이트 주문', 'LED 헤드라이트 2개 요청', '2024-01-18 10:00:00', '2024-01-18 11:00:00', TRUE, 'WAIT', - 'ORD_000000007', 'MEM_000000007'), - ('PUR_000000008', '스팅어 도어 핸들 주문', '도어 핸들 4개 요청', '2024-01-19 12:00:00', '2024-01-19 13:00:00', TRUE, 'CONFIRMED', - 'ORD_000000008', 'MEM_000000008'), - ('PUR_000000009', '니로 충전 케이블 주문', '전기차 충전 케이블 요청', '2024-01-20 14:00:00', '2024-01-20 15:00:00', TRUE, - 'DELIVERED', 'ORD_000000009', 'MEM_000000009'), - ('PUR_000000010', 'K5 엔진오일 주문', '엔진오일 20L 요청', '2024-01-21 15:00:00', '2024-01-21 16:00:00', TRUE, 'WAIT', - 'ORD_000000010', 'MEM_000000010'); - -INSERT INTO tb_notice (NOT_ID, NOT_TTL, NOT_TAG, NOT_CLA, NOT_CONT, CREATED_AT, UPDATED_AT, ACTIVE, MEM_ID) -VALUES ('NOT_000000001', '신차 출시 공지', 'ALL', 'IMPORTANT', '신형 쏘렌토가 출시되었습니다.', '2024-01-10 09:00:00', - '2024-01-10 09:30:00', TRUE, 'MEM_000000001'), - ('NOT_000000002', '정기 점검 공지', 'SERVICE', 'NORMAL', '1월 정기 점검 안내입니다.', '2024-01-11 10:00:00', - '2024-01-11 10:30:00', TRUE, 'MEM_000000002'), - ('NOT_000000003', '할인 프로모션', 'SALES', 'IMPORTANT', 'K5 한정 할인 프로모션 안내', '2024-01-12 11:00:00', - '2024-01-12 11:30:00', TRUE, 'MEM_000000003'), - ('NOT_000000004', '서비스 센터 이전 안내', 'SERVICE', 'NORMAL', '서울센터가 이전되었습니다.', '2024-01-13 12:00:00', - '2024-01-13 12:30:00', TRUE, 'MEM_000000004'), - ('NOT_000000005', '부품 재고 부족', 'ALL', 'WARNING', '모하비 부품 재고가 부족합니다.', '2024-01-14 13:00:00', - '2024-01-14 13:30:00', TRUE, 'MEM_000000005'), - ('NOT_000000006', '설 연휴 휴무 안내', 'ALL', 'NORMAL', '설 연휴 기간 동안 휴무합니다.', '2024-01-15 14:00:00', - '2024-01-15 14:30:00', TRUE, 'MEM_000000006'), - ('NOT_000000007', 'K8 시승 이벤트', 'EVENT', 'NORMAL', 'K8 시승 이벤트에 참여하세요.', '2024-01-16 15:00:00', - '2024-01-16 15:30:00', TRUE, 'MEM_000000007'), - ('NOT_000000008', '스포티지 리콜 안내', 'SERVICE', 'CRITICAL', '스포티지 일부 모델 리콜 안내', '2024-01-17 16:00:00', - '2024-01-17 16:30:00', TRUE, 'MEM_000000008'), - ('NOT_000000009', '신입사원 모집', 'HR', 'NORMAL', '기아자동차 신입사원 모집 공고', '2024-01-18 17:00:00', '2024-01-18 17:30:00', - TRUE, 'MEM_000000009'), - ('NOT_000000010', '고객 감사 이벤트', 'EVENT', 'NORMAL', '고객 감사 사은품 증정 이벤트', '2024-01-19 18:00:00', - '2024-01-19 18:30:00', TRUE, 'MEM_000000010'); - -INSERT INTO tb_file (FILE_ID, FILE_NAME, FILE_URL, FILE_TYPE, ACTIVE, CREATED_AT, MEM_ID, NOT_ID) -VALUES ('FIL_000000001', '쏘렌토_계약서.pdf', '/files/contract1.pdf', 'pdf', TRUE, '2024-01-10 10:00:00', 'MEM_000000001', - 'NOT_000000001'), - ('FIL_000000002', '스포티지_메뉴얼.pdf', '/files/manual2.pdf', 'pdf', TRUE, '2024-01-11 10:30:00', 'MEM_000000002', - 'NOT_000000002'), - ('FIL_000000003', 'K7_견적서.pdf', '/files/estimate3.pdf', 'pdf', TRUE, '2024-01-12 11:00:00', 'MEM_000000003', - 'NOT_000000003'), - ('FIL_000000004', '셀토스_리콜_공지.pdf', '/files/recall4.pdf', 'pdf', TRUE, '2024-01-13 12:00:00', 'MEM_000000004', - 'NOT_000000004'), - ('FIL_000000005', 'K3_정비보고서.pdf', '/files/report5.pdf', 'pdf', TRUE, '2024-01-14 13:00:00', 'MEM_000000005', - 'NOT_000000005'), - ('FIL_000000006', '모하비_오일교환.pdf', '/files/oil6.pdf', 'pdf', TRUE, '2024-01-15 14:00:00', 'MEM_000000006', - 'NOT_000000006'), - ('FIL_000000007', 'K8_이벤트_포스터.png', '/files/event7.png', 'image', TRUE, '2024-01-16 15:00:00', 'MEM_000000007', - 'NOT_000000007'), - ('FIL_000000008', '스팅어_사용설명서.pdf', '/files/manual8.pdf', 'pdf', TRUE, '2024-01-17 16:00:00', 'MEM_000000008', - 'NOT_000000008'), - ('FIL_000000009', '니로_충전가이드.pdf', '/files/guide9.pdf', 'pdf', TRUE, '2024-01-18 17:00:00', 'MEM_000000009', - 'NOT_000000009'), - ('FIL_000000010', 'K5_고객이벤트_배너.jpg', '/files/banner10.jpg', 'image', TRUE, '2024-01-19 18:00:00', - 'MEM_000000010', 'NOT_000000010'); - -INSERT INTO tb_schedule (SCH_ID, SCH_NAME, SCH_CONT, SCH_TAG, SCH_SRT_AT, SCH_END_AT, CREATED_AT, UPDATED_AT, DELETED_AT, ACTIVE, MEM_ID) -VALUES - ('SCH_000000001', '쏘렌토 신차 발표회', '신차 발표회 준비 및 진행', 'MEETING', '2024-11-01 09:00:00', '2024-11-01 12:00:00', - '2024-01-20 10:00:00', '2024-01-20 10:30:00', NULL, TRUE, 'MEM_000000001'), - ('SCH_000000002', '스포티지 고객 상담', '신차 구매 고객 상담 일정', 'CONSULTATION', '2024-11-05 10:00:00', '2024-11-05 11:30:00', - '2024-01-21 11:00:00', '2024-01-21 11:30:00', NULL, TRUE, 'MEM_000000002'), - ('SCH_000000003', 'K7 프로모션 회의', '프로모션 기획 및 준비 회의', 'MEETING', '2024-11-10 13:00:00', '2024-11-10 15:00:00', - '2024-01-22 12:00:00', '2024-01-22 12:30:00', NULL, TRUE, 'MEM_000000003'), - ('SCH_000000004', '셀토스 시승 이벤트', '고객 대상 시승 이벤트', 'CONSULTATION', '2024-11-12 14:00:00', '2024-11-12 17:00:00', - '2024-01-23 13:00:00', '2024-01-23 13:30:00', NULL, TRUE, 'MEM_000000004'), - ('SCH_000000005', 'K3 서비스 교육', '서비스 센터 직원 교육', 'TRAINING', '2024-11-15 09:00:00', '2024-11-15 12:00:00', - '2024-01-24 14:00:00', '2024-01-24 14:30:00', NULL, TRUE, 'MEM_000000005'), - ('SCH_000000006', '모하비 유지보수 회의', '유지보수 전략 논의', 'MEETING', '2024-11-17 10:00:00', '2024-11-17 11:00:00', - '2024-01-25 15:00:00', '2024-01-25 15:30:00', NULL, TRUE, 'MEM_000000006'), - ('SCH_000000007', 'K8 내부 검토', '신차 내부 디자인 검토', 'MEETING', '2024-11-20 11:00:00', '2024-11-20 13:00:00', - '2024-01-26 16:00:00', '2024-01-26 16:30:00', NULL, TRUE, 'MEM_000000007'), - ('SCH_000000008', '스팅어 기술 세미나', '스팅어 기술 세미나 및 발표', 'TRAINING', '2024-11-22 14:00:00', '2024-11-22 16:00:00', - '2024-01-27 17:00:00', '2024-01-27 17:30:00', NULL, TRUE, 'MEM_000000008'), - ('SCH_000000009', '연말 휴가', '연말 휴가 계획', 'VACATION', '2024-11-25 09:00:00', '2024-11-25 17:00:00', - '2024-01-28 18:00:00', '2024-01-28 18:30:00', NULL, TRUE, 'MEM_000000009'), - ('SCH_000000010', 'K5 재고 점검', 'K5 재고 관리 및 점검', 'MEETING', '2024-11-28 15:00:00', '2024-11-28 17:00:00', - '2024-01-29 19:00:00', '2024-01-29 19:30:00', NULL, TRUE, 'MEM_000000010'); - - -INSERT INTO tb_alarm (ALR_ID, ALR_MSG, ALR_URL, ALR_READ_STAT, CREATED_AT, MEM_ID) -VALUES ('ALR_000000001', '신차 출시 공지 알림', '/notices/1', FALSE, '2024-01-20 12:00:00', 'MEM_000000001'), - ('ALR_000000002', '스포티지 리콜 안내', '/notices/2', FALSE, '2024-01-21 14:00:00', 'MEM_000000002'), - ('ALR_000000003', 'K7 고객 이벤트 초대', '/events/3', TRUE, '2024-01-22 09:00:00', 'MEM_000000003'), - ('ALR_000000004', '셀토스 정비 완료', '/service/4', FALSE, '2024-01-23 10:00:00', 'MEM_000000004'), - ('ALR_000000005', 'K3 테스트 드라이브 일정 변경', '/schedules/5', TRUE, '2024-01-24 11:00:00', 'MEM_000000005'), - ('ALR_000000006', '모하비 부품 입고 알림', '/inventory/6', FALSE, '2024-01-25 12:00:00', 'MEM_000000006'), - ('ALR_000000007', 'K8 디자인 변경 확정', '/design/7', TRUE, '2024-01-26 13:00:00', 'MEM_000000007'), - ('ALR_000000008', '스팅어 성능 테스트 보고서', '/reports/8', FALSE, '2024-01-27 14:00:00', 'MEM_000000008'), - ('ALR_000000009', '니로 전기차 충전소 위치', '/maps/9', TRUE, '2024-01-28 15:00:00', 'MEM_000000009'), - ('ALR_000000010', 'K5 재고 부족 경고', '/inventory/10', FALSE, '2024-01-29 16:00:00', 'MEM_000000010'); - -INSERT INTO tb_member_detail (MEM_DET_ID, MEM_DET_REL, MEM_DET_NAME, MEM_DET_BIR, MEM_DET_IDEN_NO, MEM_DET_PHO, - MEM_DET_SEX, MEM_DET_DIS, MEM_DET_DIE, MEM_DET_NOTE, MEM_DET_ENTD, MEM_DET_GRAD, - MEM_DET_FNL_EDC, MEM_DET_EDU, MEM_DET_MJR, MEM_DET_EMP_DATE, MEM_DET_RTR_DATE, CAR_INFO, - CERT_DATE, CERT_INST, CERT_NAME, CERT_SCO, MEM_ID) -VALUES ('DET_000000001', '배우자', '박미숙', '1988-05-12', '880512-1234567', '010-1111-2222', 'FEMALE', FALSE, FALSE, NULL, - '2010', 'A', '대졸', '경영학', '기아', '2020-01-01', '2025-01-01', 'K5', '2023-06-01', '기아자동차', '세일즈', '95', - 'MEM_000000001'), - ('DET_000000002', '자녀', '김철수', '2012-08-05', '120805-2345678', '010-2222-3333', 'MALE', FALSE, FALSE, NULL, - '2022', 'B+', '고졸', '정보통신', '기아', '2022-01-15', '2027-01-15', 'K3', '2023-07-15', '기아자동차', '서비스', '88', - 'MEM_000000002'), - ('DET_000000003', '배우자', '이영희', '1985-03-09', '850309-3456789', '010-3333-4444', 'FEMALE', FALSE, FALSE, - '부부 동반 여행', '2009', 'A-', '대졸', '경제학', '기아', '2019-05-12', '2024-05-12', '스포티지', '2023-04-20', '기아자동차', '기술지원', - '92', 'MEM_000000003'), - ('DET_000000004', '배우자', '정수민', '1990-11-11', '901111-4567890', '010-4444-5555', 'FEMALE', FALSE, FALSE, NULL, - '2015', 'A', '대졸', '기계공학', '기아', '2021-09-01', '2026-09-01', 'K7', '2023-02-10', '기아자동차', '품질관리', '87', - 'MEM_000000004'), - ('DET_000000005', '자녀', '한지수', '2016-01-20', '160120-5678901', '010-5555-6666', 'MALE', TRUE, FALSE, '특별 교육 필요', - '2021', 'B', '초등학교', '과학', '기아', '2023-03-05', '2028-03-05', '니로', '2023-03-01', '기아자동차', '연구개발', '85', - 'MEM_000000005'), - ('DET_000000006', '자녀', '최지우', '2005-06-15', '050615-6789012', '010-6666-7777', 'FEMALE', FALSE, FALSE, NULL, - '2023', 'A+', '대졸', '디자인', '기아', '2020-07-01', '2025-07-01', '스팅어', '2023-08-20', '기아자동차', '디자인', '90', - 'MEM_000000006'), - ('DET_000000007', '배우자', '윤미라', '1987-12-30', '871230-7890123', '010-7777-8888', 'FEMALE', FALSE, FALSE, - '해외 출장 동반', '2011', 'B+', '대졸', '마케팅', '기아', '2020-12-01', '2025-12-01', '쏘렌토', '2023-01-25', '기아자동차', '마케팅', - '93', 'MEM_000000007'), - ('DET_000000008', '자녀', '이동희', '2010-09-10', '100910-8901234', '010-8888-9999', 'MALE', FALSE, FALSE, NULL, - '2025', 'B-', '중졸', '정보기술', '기아', '2023-05-05', '2028-05-05', '모하비', '2023-12-30', '기아자동차', '엔지니어링', '89', - 'MEM_000000008'), - ('DET_000000009', '배우자', '정수영', '1991-04-17', '910417-9012345', '010-9999-0000', 'FEMALE', FALSE, FALSE, - '가족 동반 캠핑', '2018', 'A-', '대졸', '화학', '기아', '2022-06-15', '2027-06-15', '셀토스', '2023-11-22', '기아자동차', '생산', - '91', 'MEM_000000009'), - ('DET_000000010', '자녀', '김준호', '2014-07-22', '140722-0123456', '010-0000-1111', 'MALE', FALSE, FALSE, NULL, - '2027', 'B+', '고졸', '경영', '기아', '2023-08-12', '2028-08-12', 'K8', '2023-09-10', '기아자동차', '세일즈', '87', - 'MEM_000000010'); - -INSERT INTO tb_update_history (UPD_ID, UPD_IP, UPDATED_AT, UPDATED_URL, MEM_ID, CONR_ID) -VALUES ('UPD_000000001', '192.168.1.10', '2024-01-10 12:00:00', '/contracts/1', 'MEM_000000001', 'CON_000000001'), - ('UPD_000000002', '192.168.1.20', '2024-01-11 14:00:00', '/contracts/2', 'MEM_000000002', 'CON_000000002'), - ('UPD_000000003', '192.168.1.30', '2024-01-12 16:00:00', '/contracts/3', 'MEM_000000003', 'CON_000000003'), - ('UPD_000000004', '192.168.1.40', '2024-01-13 09:00:00', '/contracts/4', 'MEM_000000004', 'CON_000000004'), - ('UPD_000000005', '192.168.1.50', '2024-01-14 10:00:00', '/contracts/5', 'MEM_000000005', 'CON_000000005'), - ('UPD_000000006', '192.168.1.60', '2024-01-15 11:00:00', '/contracts/6', 'MEM_000000006', 'CON_000000006'), - ('UPD_000000007', '192.168.1.70', '2024-01-16 12:00:00', '/contracts/7', 'MEM_000000007', 'CON_000000007'), - ('UPD_000000008', '192.168.1.80', '2024-01-17 13:00:00', '/contracts/8', 'MEM_000000008', 'CON_000000008'), - ('UPD_000000009', '192.168.1.90', '2024-01-18 14:00:00', '/contracts/9', 'MEM_000000009', 'CON_000000009'), - ('UPD_000000010', '192.168.1.100', '2024-01-19 15:00:00', '/contracts/10', 'MEM_000000010', 'CON_000000010'); - -INSERT INTO tb_evaluation (EVAL_ID, EVAL_TTL, EVAL_CONT, CREATED_AT, UPDATED_AT, ACTIVE, CENT_ID, MEM_ID, WRI_ID) -VALUES ('EVAL_000000001', '쏘렌토 성능 평가', '쏘렌토의 성능에 대한 평가입니다.', '2024-01-20 10:00:00', '2024-01-21 10:30:00', TRUE, - 'CEN_000000001', 'MEM_000000001', 'MEM_000000002'), - ('EVAL_000000002', '스포티지 안전 평가', '스포티지의 안전성 평가 보고서.', '2024-01-21 11:00:00', '2024-01-22 11:30:00', TRUE, - 'CEN_000000002', 'MEM_000000002', 'MEM_000000003'), - ('EVAL_000000003', 'K7 고객 만족도 평가', '고객 만족도 조사 결과입니다.', '2024-01-23 12:00:00', '2024-01-24 12:30:00', TRUE, - 'CEN_000000003', 'MEM_000000003', 'MEM_000000004'), - ('EVAL_000000004', '셀토스 디자인 평가', '셀토스 디자인 피드백.', '2024-01-25 13:00:00', '2024-01-26 13:30:00', TRUE, - 'CEN_000000004', 'MEM_000000004', 'MEM_000000005'), - ('EVAL_000000005', 'K3 내부 공간 평가', '내부 공간의 효율성 평가.', '2024-01-27 14:00:00', '2024-01-28 14:30:00', TRUE, - 'CEN_000000005', 'MEM_000000005', 'MEM_000000006'), - ('EVAL_000000006', '모하비 유지비 평가', '유지 비용과 효율성 평가.', '2024-01-29 15:00:00', '2024-01-30 15:30:00', TRUE, - 'CEN_000000006', 'MEM_000000006', 'MEM_000000007'), - ('EVAL_000000007', 'K8 디자인 리뉴얼', '새로운 디자인에 대한 평가.', '2024-02-01 16:00:00', '2024-02-02 16:30:00', TRUE, - 'CEN_000000007', 'MEM_000000007', 'MEM_000000008'), - ('EVAL_000000008', '스팅어 출력 평가', '스팅어의 출력 성능 보고서.', '2024-02-03 17:00:00', '2024-02-04 17:30:00', TRUE, - 'CEN_000000008', 'MEM_000000008', 'MEM_000000009'), - ('EVAL_000000009', '니로 전기차 평가', '전기차 효율성 평가 결과.', '2024-02-05 18:00:00', '2024-02-06 18:30:00', TRUE, - 'CEN_000000009', 'MEM_000000009', 'MEM_000000010'), - ('EVAL_000000010', 'K5 연비 평가', 'K5의 연비 효율성 보고서.', '2024-02-07 19:00:00', '2024-02-08 19:30:00', TRUE, - 'CEN_000000010', 'MEM_000000010', 'MEM_000000001'); - -INSERT INTO tb_promotion (PROM_ID, PROM_TTL, PROM_CONT, CREATED_AT, UPDATED_AT, ACTIVE, MEM_ID) -VALUES ('PROM_000000001', '쏘렌토 겨울 특별 할인', '쏘렌토 구매 시 15% 할인 제공', '2024-01-05 09:00:00', '2024-01-06 10:00:00', TRUE, - 'MEM_000000001'), - ('PROM_000000002', '스포티지 리스 프로모션', '리스 계약 시 추가 혜택 제공', '2024-01-10 11:00:00', '2024-01-10 12:00:00', TRUE, - 'MEM_000000002'), - ('PROM_000000003', 'K7 고객 감사 이벤트', 'K7 구매 고객 대상 사은품 증정', '2024-01-15 14:00:00', '2024-01-15 15:00:00', TRUE, - 'MEM_000000003'), - ('PROM_000000004', '셀토스 한정 프로모션', '셀토스 구매 시 무료 보증 연장', '2024-01-20 09:00:00', '2024-01-20 10:00:00', TRUE, - 'MEM_000000004'), - ('PROM_000000005', 'K3 연비 보장 이벤트', 'K3 연비 테스트 이벤트 참여 시 혜택 제공', '2024-01-25 13:00:00', '2024-01-25 14:00:00', - TRUE, 'MEM_000000005'), - ('PROM_000000006', '모하비 프리미엄 서비스', '모하비 구매 고객 대상 프리미엄 서비스 제공', '2024-01-30 11:00:00', '2024-01-31 12:00:00', - TRUE, 'MEM_000000006'), - ('PROM_000000007', 'K8 럭셔리 패키지 할인', '럭셔리 패키지 선택 시 10% 할인 제공', '2024-02-01 12:00:00', '2024-02-02 13:00:00', TRUE, - 'MEM_000000007'), - ('PROM_000000008', '스팅어 시승 이벤트', '스팅어 시승 후 계약 시 추가 혜택', '2024-02-05 10:00:00', '2024-02-05 11:00:00', TRUE, - 'MEM_000000008'), - ('PROM_000000009', '니로 전기차 구매 지원', '전기차 구매 시 충전기 설치 지원', '2024-02-10 09:00:00', '2024-02-10 10:00:00', TRUE, - 'MEM_000000009'), - ('PROM_000000010', 'K5 재고 한정 특별 할인', '재고 한정 K5 모델에 대한 추가 할인 제공', '2024-02-15 15:00:00', '2024-02-16 16:00:00', - TRUE, 'MEM_000000010'); - -INSERT INTO tb_product_option (PROD_ID, PROD_SER_NO, OPT_CNTY, OPT_MNFR, OPT_VHC_TYPE, OPT_CHSS, OPT_DTIL_TYPE, - OPT_BODY_TYPE, - OPT_SFTY_DVCE, OPT_ENGN_CPCT, OPT_SCRT_CODE, OPT_PRDC_YEAR, OPT_PRDC_PLNT, OPT_ENGN, - OPT_MSSN, - OPT_TRIM, OPT_XTNL_COLR, OPT_ITNL_COLR, OPT_HUD, OPT_NAVI, OPT_DRVE_WISE, OPT_SMRT_CNCT, - OPT_STYL, OPT_MY_CFRT_PCKG, OPT_OTDR_PCKG, OPT_SUN_ROOF, OPT_SOND, ACTIVE) -VALUES --- 데이터 1 -('PRO_000000001', 'KNAHAA4AALU1A00001', 'K', 'N', 'A', 'A', 'L', '4', '2', 'A', 'P', 'A', 'U', '1', '0', '1', 'B', 'W', - '1', '1', '1', '1', '1', '0', '1', '1', '1', TRUE), --- 데이터 2 -('PRO_000000002', 'KNAHBA4BALR2Z00002', 'K', 'N', 'H', 'B', 'L', '4', '4', 'B', 'R', 'B', 'Z', '1', '1', '0', 'G', 'B', - '0', '1', '0', '1', '0', '1', '0', '0', '1', TRUE), --- 데이터 3 -('PRO_000000003', 'KMBHC64CAMJ5A00003', 'K', 'M', 'H', 'C', 'M', '6', '3', 'C', 'P', 'C', 'A', '0', '1', '1', 'R', 'G', - '1', '1', '1', '0', '1', '1', '0', '1', '0', TRUE), --- 데이터 4 -('PRO_000000004', 'KNJFA42DALU3C00004', 'K', 'N', 'J', 'D', 'L', '2', '4', 'A', 'R', 'D', 'C', '1', '0', '0', 'B', 'R', - '0', '0', '0', '1', '0', '0', '1', '0', '1', TRUE), --- 데이터 5 -('PRO_000000005', 'KFBGBM5EARP1M00005', 'K', 'B', 'F', 'B', 'M', '5', '1', 'B', 'P', 'E', 'M', '1', '1', '1', 'W', 'B', - '1', '1', '0', '0', '1', '1', '0', '1', '1', TRUE), --- 데이터 6 -('PRO_000000006', 'KNHGA6BALUP7A00006', 'K', 'N', 'H', 'G', 'L', '6', '3', 'A', 'R', 'F', 'A', '0', '0', '1', 'B', 'G', - '1', '1', '1', '1', '0', '1', '1', '0', '0', TRUE), --- 데이터 7 -('PRO_000000007', 'KNJFA34AALU4Z00007', 'K', 'N', 'J', 'A', 'M', '4', '4', 'C', 'P', 'G', 'Z', '1', '1', '0', 'R', 'W', - '0', '0', '1', '0', '1', '0', '1', '1', '1', TRUE), --- 데이터 8 -('PRO_000000008', 'KMAHDA2AAMJ3T00008', 'K', 'M', 'H', 'D', 'L', '2', '2', 'A', 'R', 'H', 'T', '0', '0', '1', 'B', 'G', - '1', '1', '0', '1', '0', '1', '1', '0', '0', TRUE), --- 데이터 9 -('PRO_000000009', 'KNAHCA5BALU5C00009', 'K', 'N', 'A', 'C', 'M', '5', '1', 'B', 'P', 'J', 'C', '1', '1', '0', 'R', 'R', - '0', '1', '1', '1', '1', '0', '1', '1', '0', TRUE), --- 데이터 10 -('PRO_000000010', 'KNFHC54CAMR1A00010', 'K', 'N', 'F', 'F', 'N', '4', '3', 'C', 'R', 'K', 'A', '0', '1', '1', 'W', 'B', - '1', '1', '0', '0', '0', '1', '0', '1', '1', TRUE); - -INSERT INTO tb_sales_history (SAL_HIST_ID, CONR_ID) -VALUES ('SAL_000000001', 'CON_000000001'), - ('SAL_000000002', 'CON_000000002'), - ('SAL_000000003', 'CON_000000003'), - ('SAL_000000004', 'CON_000000004'), - ('SAL_000000005', 'CON_000000005'), - ('SAL_000000006', 'CON_000000006'), - ('SAL_000000007', 'CON_000000007'), - ('SAL_000000008', 'CON_000000008'), - ('SAL_000000009', 'CON_000000009'), - ('SAL_000000010', 'CON_000000010'); - - From 58f9a03d6af72f35c20829c233ca82b6e1c6f1ce Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Tue, 19 Nov 2024 15:55:47 +0900 Subject: [PATCH 194/563] =?UTF-8?q?feat:=20=ED=95=B4=EB=8B=B9=20=EB=82=A0?= =?UTF-8?q?=EC=A7=9C=EC=97=90=20=EC=9D=BC=EC=A0=95=EC=9E=88=EC=9C=BC?= =?UTF-8?q?=EB=A9=B4=20=EC=95=8C=EB=A6=BC=20=EC=A0=84=EC=86=A1=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EC=99=84=EB=A3=8C=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FinalBackendApplication.java | 2 + .../domain/alarm/aggregate/dto/AlarmDTO.java | 1 + .../domain/alarm/aggregate/entity/Alarm.java | 4 +- .../alarm/controller/AlarmController.java | 19 +- .../alarm/repository/EmitterRepository.java | 22 + .../repository/EmitterRepositoryImpl.java | 74 ++ .../alarm/scheduler/AlarmScheduler.java | 63 ++ .../domain/alarm/service/AlarmService.java | 10 +- .../alarm/service/AlarmServiceImpl.java | 88 +- .../controller/ScheduleController.java | 3 +- .../service/ScheduleCommandServiceImpl.java | 1 - .../schedule/query/dto/ScheduleDayDTO.java | 19 + .../query/repository/ScheduleMapper.java | 2 + .../query/service/ScheduleQueryService.java | 2 + .../service/ScheduleQueryServiceImpl.java | 9 + src/main/resources/sql/ddl.sql | 892 +++++++++--------- .../query/repository/ScheduleMapper.xml | 85 +- .../schedule/query/repository/sample.xml | 98 ++ 18 files changed, 902 insertions(+), 492 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepository.java create mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepositoryImpl.java create mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java create mode 100644 src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDayDTO.java create mode 100644 src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/sample.xml diff --git a/src/main/java/stanl_2/final_backend/FinalBackendApplication.java b/src/main/java/stanl_2/final_backend/FinalBackendApplication.java index 5f0cb0ad..c2da41b7 100644 --- a/src/main/java/stanl_2/final_backend/FinalBackendApplication.java +++ b/src/main/java/stanl_2/final_backend/FinalBackendApplication.java @@ -2,7 +2,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; +@EnableScheduling @SpringBootApplication public class FinalBackendApplication { diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/dto/AlarmDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/dto/AlarmDTO.java index f77919dc..d5e2f253 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/dto/AlarmDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/dto/AlarmDTO.java @@ -10,6 +10,7 @@ public class AlarmDTO { private String memberId; + private String memberLoginId; private String lastEventId; } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/entity/Alarm.java b/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/entity/Alarm.java index 270468be..f3e8036a 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/entity/Alarm.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/entity/Alarm.java @@ -38,8 +38,8 @@ public class Alarm { @Column(name = "CREATED_AT", nullable = false) private String createdAt; - @Column(name = "ALR_SND", nullable = false) - private String sender; +// @Column(name = "ALR_STAT", nullable = false) +// private String status = "PENDING"; @Column(name = "MEM_ID", nullable = false) private String memberId; diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/controller/AlarmController.java b/src/main/java/stanl_2/final_backend/domain/alarm/controller/AlarmController.java index 2f0a8b62..ae0113e0 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/controller/AlarmController.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/controller/AlarmController.java @@ -1,6 +1,8 @@ package stanl_2.final_backend.domain.alarm.controller; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -8,6 +10,8 @@ import stanl_2.final_backend.domain.alarm.aggregate.dto.AlarmDTO; import stanl_2.final_backend.domain.alarm.service.AlarmService; +import java.security.Principal; + @RestController("queryAlarmController") @RequestMapping("/api/v1/alarm") public class AlarmController { @@ -19,16 +23,19 @@ public AlarmController(AlarmService alarmService) { this.alarmService = alarmService; } - @GetMapping(value= "/connect/{memberId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE) - public ResponseEntity subscribe(@PathVariable String memberId, - @RequestHeader(value = "Last-Event-ID", required = false, defaultValue = "") - String lastEventId){ + @GetMapping(value= "/connect", produces = MediaType.TEXT_EVENT_STREAM_VALUE) + public ResponseEntity subscribe(Principal principal, + @RequestHeader(value = "Last-Event-ID", required = false, + defaultValue = "") String lastEventId, + HttpServletResponse response){ + + String memberLoginId = principal.getName(); AlarmDTO alarmDTO = new AlarmDTO(); - alarmDTO.setMemberId(memberId); + alarmDTO.setMemberLoginId(memberLoginId); alarmDTO.setLastEventId(lastEventId); - return ResponseEntity.ok(alarmService.subscribe(alarmDTO)); + return ResponseEntity.ok(alarmService.subscribe(alarmDTO, response)); } } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepository.java b/src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepository.java new file mode 100644 index 00000000..a36aab22 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepository.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.alarm.repository; + +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; +import stanl_2.final_backend.domain.alarm.aggregate.entity.Alarm; + +import java.util.Map; + +public interface EmitterRepository { + SseEmitter save(String emitterId, SseEmitter sseEmitter); + + void deleteAllByEmitterId(String emitterId); + + Map findAllEmitterStartWithByMemberId(String memberId); + + Map findAllEventCacheStartWithByMemberId(String memberId); + + void saveEventCache(String eventCacheId, Object event); + + void deleteAllEmitterStartWithMemberId(String memberId); + + void deleteAllEventCacheStartWithmemberId(String memberId); +} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepositoryImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepositoryImpl.java new file mode 100644 index 00000000..e09c7f27 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepositoryImpl.java @@ -0,0 +1,74 @@ +package stanl_2.final_backend.domain.alarm.repository; + +import org.springframework.stereotype.Repository; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +@Repository +public class EmitterRepositoryImpl implements EmitterRepository{ + + private final Map emitters = new ConcurrentHashMap<>(); + private final Map eventCache = new ConcurrentHashMap<>(); + + @Override + public SseEmitter save(String emitterId, SseEmitter sseEmitter) { + + emitters.put(emitterId, sseEmitter); + return sseEmitter; + } + + @Override + public void deleteAllByEmitterId(String emitterId) { + emitters.forEach((key, emitter) -> { + if (key.startsWith(emitterId)) { + emitters.remove(key); + } + }); + } + + @Override + public Map findAllEmitterStartWithByMemberId(String memberId) { + return emitters.entrySet().stream() + .filter(entry -> entry.getKey().startsWith(memberId)) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + @Override + public Map findAllEventCacheStartWithByMemberId(String memberId) { + return eventCache.entrySet().stream() + .filter(entry -> entry.getKey().startsWith(memberId)) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + @Override + public void saveEventCache(String eventCacheId, Object event) { + eventCache.put(eventCacheId, event); + } + + @Override + public void deleteAllEmitterStartWithMemberId(String memberId) { + emitters.forEach( + (key, emitter) -> { + if (key.startsWith(memberId)){ + emitters.remove(key); + } + } + ); + } + + @Override + public void deleteAllEventCacheStartWithmemberId(String memberId) { + eventCache.forEach( + (key, emitter) -> { + if (key.startsWith(memberId)){ + eventCache.remove(key); + } + } + ); + } + + +} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java new file mode 100644 index 00000000..411e56db --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java @@ -0,0 +1,63 @@ +package stanl_2.final_backend.domain.alarm.scheduler; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; +import stanl_2.final_backend.domain.alarm.aggregate.entity.Alarm; +import stanl_2.final_backend.domain.alarm.repository.EmitterRepository; +import stanl_2.final_backend.domain.alarm.service.AlarmService; +import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; +import stanl_2.final_backend.domain.schedule.query.service.ScheduleQueryService; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Map; + + +@Service +public class AlarmScheduler { + + private final AlarmService alarmService; + private final ScheduleQueryService scheduleQueryService; + private final EmitterRepository emitterRepository; + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } + + @Autowired + public AlarmScheduler(AlarmService alarmService, ScheduleQueryService scheduleQueryService, + EmitterRepository emitterRepository) { + this.alarmService = alarmService; + this.scheduleQueryService = scheduleQueryService; + this.emitterRepository = emitterRepository; + } + + @Scheduled(cron = "0 0 2 * * *") // 매일 새벽 2시에 실행 + @Transactional + public void alarmTodaySchedule(){ + + String currentDay = getCurrentTime().substring(0,10); + + List todaySchedules = scheduleQueryService.findSchedulesByDate(currentDay); + + // 사용자 별로 알림 전송 + todaySchedules.forEach(schedule -> { + String Hour = schedule.getStartAt().substring(11,13); + String Minute = schedule.getStartAt().substring(14,16); + + String memberId = schedule.getMemberId(); + String type = schedule.getTag(); + String message = "[" + type + "] 금일 " + Hour + "시 " + Minute + "분에 '" + + schedule.getName() + "' 일정이 있습니다"; + String redirectUrl = "/api/v1/schedule"; + String createdAt = getCurrentTime(); + + alarmService.send(memberId, message, redirectUrl, type, createdAt); + }); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java index c3fee2e0..58ec195f 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java @@ -1,8 +1,16 @@ package stanl_2.final_backend.domain.alarm.service; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import stanl_2.final_backend.domain.alarm.aggregate.dto.AlarmDTO; +import stanl_2.final_backend.domain.alarm.aggregate.entity.Alarm; public interface AlarmService { - SseEmitter subscribe(AlarmDTO alarmDTO); + SseEmitter subscribe(AlarmDTO alarmDTO, HttpServletResponse response); + + void sendToClient(SseEmitter emitter, String emitterId, Object data); + + void send(String memberLoginId, String message, String redirectUrl, String type, String createdAt); + + Alarm createAlarm(String memberId, String message, String redirectUrl, String type, String createdAt); } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java index 57a9156a..f93e8687 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java @@ -1,28 +1,110 @@ package stanl_2.final_backend.domain.alarm.service; +import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import stanl_2.final_backend.domain.alarm.aggregate.dto.AlarmDTO; +import stanl_2.final_backend.domain.alarm.aggregate.entity.Alarm; import stanl_2.final_backend.domain.alarm.repository.AlarmRepository; +import stanl_2.final_backend.domain.alarm.repository.EmitterRepository; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import java.io.IOException; import java.util.Map; @Slf4j @Service public class AlarmServiceImpl implements AlarmService { + private final EmitterRepository emitterRepository; private final AlarmRepository alarmRepository; + private final AuthQueryService authQueryService; + + private static final Long DEFAULT_TIMEOUT = 60L * 1000 * 60; @Autowired - public AlarmServiceImpl(AlarmRepository alarmRepository) { + public AlarmServiceImpl(AlarmRepository alarmRepository, EmitterRepository emitterRepository, + AuthQueryService authQueryService) { this.alarmRepository = alarmRepository; + this.emitterRepository = emitterRepository; + this.authQueryService = authQueryService; } @Override - public SseEmitter subscribe(AlarmDTO alarmDTO) { + public SseEmitter subscribe(AlarmDTO alarmDTO, HttpServletResponse response) { + + String lastEventId = alarmDTO.getLastEventId(); + String memberId = authQueryService.selectMemberIdByLoginId(alarmDTO.getMemberLoginId()); + String emitterId = memberId + "_" + System.currentTimeMillis(); + + // 클라이언트의 sse 연결 요청에 응답하기 위한 SseEmitter 객체 생성 + // 유효시간 지정으로 시간이 지나면 클라이언트에서 자동으로 재연결 요청함 + SseEmitter emitter = emitterRepository.save(emitterId, new SseEmitter(DEFAULT_TIMEOUT)); + response.setHeader("X-Accel-Buffering", "no"); // NGINX PROXY 에서의 필요설정 불필요한 버퍼링방지 + + // SseEmitter의 완료/시간초과/에러로 인한 전송 불가 시 sseEmitter 삭제 + emitter.onCompletion(() -> emitterRepository.deleteAllByEmitterId(emitterId)); + emitter.onTimeout(() -> emitterRepository.deleteAllByEmitterId(emitterId)); + emitter.onError((e) -> emitterRepository.deleteAllByEmitterId(emitterId)); + + // 연결 직후, 데이터 전송이 없을 시 503 에러 발생. 에러 방지 위한 더미데이터 전송 + sendToClient(emitter, emitterId, emitterId + "님 연결되었습니다."); + + // 클라이언트가 미수신한 Event 유실 예방, 연결이 끊켰거나 미수신된 데이터를 다 찾아서 보내줌 + if (!lastEventId.isEmpty()) { + Map events = emitterRepository.findAllEventCacheStartWithByMemberId(memberId); + events.entrySet().stream() + .filter(entry -> lastEventId.compareTo(entry.getKey()) < 0) + .forEach(entry -> sendToClient(emitter, entry.getKey(), entry.getValue())); + } - return null; + return emitter; } + + @Override + public void sendToClient(SseEmitter emitter, String emitterId, Object data) { + + try { + emitter.send(SseEmitter.event() + .id(emitterId) + .name("sse") + .data(data)); + } catch (IOException e){ + emitterRepository.deleteAllByEmitterId(emitterId); + log.error("SSE 연결 오류 발생", e); + } + } + + @Override + @Transactional + public void send(String memberId, String message, String redirectUrl, String type, String createdAt){ + + Alarm alarm = alarmRepository.save(createAlarm(memberId, message, redirectUrl, type, createdAt)); + + Map sseEmitters = emitterRepository.findAllEmitterStartWithByMemberId(memberId); + sseEmitters.forEach( + (key, emitter) -> { + emitterRepository.saveEventCache(key, alarm); + sendToClient(emitter, key, alarm); + } + ); + } + + @Override + public Alarm createAlarm(String memberId, String message, String redirectUrl, String type, String createdAt) { + + Alarm alarm = new Alarm(); + alarm.setMemberId(memberId); + alarm.setMessage(message); + alarm.setRedirectUrl(redirectUrl); + alarm.setType(type); + alarm.setReadStatus(false); + alarm.setCreatedAt(createdAt); + + return alarm; + } + } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java index d0f9088c..7aa3c733 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java @@ -8,6 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.alarm.service.AlarmService; import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleDeleteDTO; import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleModifyDTO; import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleRegistDTO; @@ -23,7 +24,7 @@ public class ScheduleController { private final ScheduleCommandService scheduleCommandService; @Autowired - public ScheduleController(ScheduleCommandService scheduleCommandService) { + public ScheduleController(ScheduleCommandService scheduleCommandService, AlarmService alarmService) { this.scheduleCommandService = scheduleCommandService; } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java index eb0d2631..1be09962 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/domain/service/ScheduleCommandServiceImpl.java @@ -44,7 +44,6 @@ private String getCurrentTime() { return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } - @Override @Transactional public Boolean registSchedule(ScheduleRegistDTO scheduleRegistDTO) { diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDayDTO.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDayDTO.java new file mode 100644 index 00000000..8b14dcf7 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/dto/ScheduleDayDTO.java @@ -0,0 +1,19 @@ +package stanl_2.final_backend.domain.schedule.query.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString +public class ScheduleDayDTO { + ; + private String name; + private String tag; + private String startAt; + private String endAt; + private String memberId; + + +} diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java index bd68f381..8f8e13cb 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java @@ -16,4 +16,6 @@ public interface ScheduleMapper { ScheduleDetailDTO findScheduleByMemberIdAndScheduleId(Map arg); List findSchedulesByMemberIdAndStartAt(Map arg); + + List findAllSchedulesByDay(String currentDay); } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryService.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryService.java index 597a0d70..ded37b15 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryService.java @@ -12,4 +12,6 @@ public interface ScheduleQueryService { List selectYearMonthSchedule(ScheduleYearMonthDTO scheduleYearMonthDTO); ScheduleDetailDTO selectDetailSchedule(ScheduleDetailDTO scheduleDetailDTO); + + List findSchedulesByDate(String currentDay); } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java index 122e3a2b..3654c134 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java @@ -101,4 +101,13 @@ public ScheduleDetailDTO selectDetailSchedule(ScheduleDetailDTO scheduleDetailDT return responseDetailSchedule; } + + @Override + @Transactional(readOnly = true) + public List findSchedulesByDate(String currentDay) { + + List todaySchedules = scheduleMapper.findAllSchedulesByDay(currentDay); + + return todaySchedules; + } } diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index 9f8a7c15..909be3ea 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -1,438 +1,438 @@ --- 외래 키 제약 조건 비활성화 -SET FOREIGN_KEY_CHECKS = 0; - --- 자식 테이블부터 삭제 -DROP TABLE IF EXISTS tb_sales_history; -DROP TABLE IF EXISTS tb_update_history; -DROP TABLE IF EXISTS tb_product_option; -DROP TABLE IF EXISTS tb_alarm; -DROP TABLE IF EXISTS tb_schedule; -DROP TABLE IF EXISTS tb_file; -DROP TABLE IF EXISTS tb_promotion; -DROP TABLE IF EXISTS tb_evaluation; -DROP TABLE IF EXISTS tb_purchase_order; -DROP TABLE IF EXISTS tb_problem; -DROP TABLE IF EXISTS tb_order; -DROP TABLE IF EXISTS tb_notice; -DROP TABLE IF EXISTS tb_contract; -DROP TABLE IF EXISTS tb_customer_info; -DROP TABLE IF EXISTS tb_member_role; -DROP TABLE IF EXISTS tb_member; -DROP TABLE IF EXISTS tb_center; -DROP TABLE IF EXISTS tb_family; -DROP TABLE IF EXISTS tb_education; -DROP TABLE IF EXISTS tb_certification; -DROP TABLE IF EXISTS tb_career; -DROP TABLE IF EXISTS tb_product; -DROP TABLE IF EXISTS tb_organization_chart; - - --- 조직 관련 테이블 생성 -CREATE TABLE tb_organization_chart -( - ORG_CHA_ID VARCHAR(255) NOT NULL, - ORG_CHA_NAME VARCHAR(255) NOT NULL, - ORG_CHA_DEPTH INT NOT NULL, - PRIMARY KEY (ORG_CHA_ID) -); - -CREATE TABLE tb_center -( - CENT_ID VARCHAR(255) NOT NULL, - CENT_NAME VARCHAR(255) NOT NULL, - CENT_ADR VARCHAR(255) NOT NULL, - CENT_PHO VARCHAR(255) NOT NULL, - CENT_MEM_CNT INT NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL DEFAULT 'CURRENT_TIMESTAMP', - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL, - CENT_OPR_AT VARCHAR(255) NOT NULL, - PRIMARY KEY (CENT_ID) -); - -CREATE TABLE tb_member -( - MEM_ID VARCHAR(255) NOT NULL, - MEM_LOGIN_ID VARCHAR(255) NOT NULL, - MEM_PWD VARCHAR(255) NOT NULL, - MEM_NAME VARCHAR(255) NOT NULL, - MEM_EMA VARCHAR(255) NOT NULL, - MEM_AGE INT NOT NULL, - MEM_SEX VARCHAR(255) NOT NULL DEFAULT 'FEMALE', - MEM_IDEN_NO VARCHAR(255) NOT NULL, - MEM_PHO VARCHAR(255) NOT NULL, - MEM_EMER_PHO VARCHAR(255) NULL, - MEM_ADR VARCHAR(255) NOT NULL, - MEM_NOTE VARCHAR(255) NULL, - MEM_POS VARCHAR(255) NOT NULL DEFAULT 'INTERN', - MEM_GRD VARCHAR(255) NOT NULL DEFAULT 'High School', - MEM_JOB_TYPE VARCHAR(255) NOT NULL, - MEM_MIL VARCHAR(255) NOT NULL DEFAULT 'exemption', - MEM_BANK_NAME VARCHAR(255) NULL, - MEM_ACC VARCHAR(255) NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - CENTER_ID VARCHAR(255) NOT NULL, - ORG_CHA_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (MEM_ID), - FOREIGN KEY (CENTER_ID) REFERENCES tb_center (CENT_ID), - FOREIGN KEY (ORG_CHA_ID) REFERENCES tb_organization_chart (ORG_CHA_ID) -); - -CREATE TABLE tb_member_role -( - MEM_ROL_ID VARCHAR(255) NOT NULL, - MEM_ROL_NAME VARCHAR(255) NOT NULL DEFAULT '영업 사원', - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (MEM_ROL_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_customer_info -( - CUST_ID VARCHAR(255) NOT NULL, - CUST_NAME VARCHAR(255) NOT NULL, - CUST_AGE INT NOT NULL, - CUST_SEX VARCHAR(255) NOT NULL DEFAULT 'FEMALE', - CUST_PHO VARCHAR(255) NOT NULL, - CUST_EMER_PHO VARCHAR(255) NOT NULL, - CUST_EMA VARCHAR(255) NOT NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (CUST_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - --- 부모 테이블 생성 -CREATE TABLE tb_product -( - PROD_ID VARCHAR(255) NOT NULL, - PROD_SER_NO VARCHAR(255) NOT NULL, - PROD_COST INT NOT NULL, - PROD_NAME VARCHAR(255) NOT NULL, - PROD_STCK INT NOT NULL DEFAULT 0, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL DEFAULT 'CURRENT_TIMESTAMP', - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - PRIMARY KEY (PROD_ID, PROD_SER_NO) -); - -CREATE TABLE tb_contract -( - CONR_ID VARCHAR(255) NOT NULL, - CONR_NAME VARCHAR(255) NOT NULL, - CONR_CUST_NAME VARCHAR(255) NOT NULL, - CONR_CUST_IDEN_NO VARCHAR(255) NOT NULL, - CONR_CUST_ADR VARCHAR(255) NOT NULL, - CONR_CUST_EMA VARCHAR(255) NOT NULL, - CONR_CUST_PHO VARCHAR(255) NOT NULL, - CONR_COMP_NAME VARCHAR(255) NULL, - CONR_CUST_CLA VARCHAR(255) NOT NULL DEFAULT 'PERSONAL', - CONR_CUST_PUR_COND VARCHAR(255) NOT NULL DEFAULT 'CASH', - CONR_SERI_NUM VARCHAR(255) NOT NULL, - CONR_SELE_OPTI VARCHAR(255) NOT NULL, - CONR_DOWN_PAY INT NOT NULL, - CONR_INTE_PAY INT NOT NULL, - CONR_REM_PAY INT NOT NULL, - CONR_CONS_PAY INT NOT NULL, - CONR_DELV_DATE VARCHAR(255) NULL, - CONR_DELV_LOC VARCHAR(255) NULL, - CONR_STAT VARCHAR(255) NULL DEFAULT 'WAIT', - CONR_NO_OF_VEH INT NOT NULL DEFAULT 1, - CREATED_URL TEXT NOT NULL, - DELETED_URL TEXT NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - MEM_ID VARCHAR(255) NOT NULL, - CENT_ID VARCHAR(255) NOT NULL, - CUST_ID VARCHAR(255) NULL, - PROD_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (CONR_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE, - FOREIGN KEY (CUST_ID) REFERENCES tb_customer_info (CUST_ID) ON DELETE CASCADE, - FOREIGN KEY (PROD_ID) REFERENCES tb_product (PROD_ID) ON DELETE CASCADE, - FOREIGN KEY (CENT_ID) REFERENCES tb_center (CENT_ID) ON DELETE CASCADE -); - -CREATE TABLE tb_notice -( - NOT_ID VARCHAR(255) NOT NULL, - NOT_TTL VARCHAR(255) NOT NULL, - NOT_TAG VARCHAR(255) NOT NULL DEFAULT 'ALL', - NOT_CLA VARCHAR(255) NOT NULL DEFAULT 'NORMAL', - NOT_CONT TEXT NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (NOT_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_order -( - ORD_ID VARCHAR(255) NOT NULL, - ORD_TTL VARCHAR(255) NOT NULL, - ORD_CONT TEXT NOT NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ORD_STAT VARCHAR(255) NOT NULL DEFAULT 'WAIT', - CONR_ID VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - ADMIN_ID VARCHAR(255) NULL, - PRIMARY KEY (ORD_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE, - FOREIGN KEY (ADMIN_ID) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE, - FOREIGN KEY (CONR_ID) REFERENCES tb_contract (CONR_ID) ON DELETE CASCADE -); - -CREATE TABLE tb_problem -( - PROB_ID VARCHAR(255) NOT NULL, - PROB_TTL VARCHAR(255) NOT NULL, - PROB_CONT VARCHAR(255) NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - DELETED_AT CHAR(19) NULL, - CUST_ID VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PROD_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (PROB_ID), - FOREIGN KEY (CUST_ID) REFERENCES tb_customer_info (CUST_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), - FOREIGN KEY (PROD_ID) REFERENCES tb_product (PROD_ID) -); - -CREATE TABLE tb_purchase_order -( - PUR_ORD_ID VARCHAR(255) NOT NULL, - PUR_ORD_TTL VARCHAR(255) NOT NULL, - PUR_CONT TEXT NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - PUR_ORD_STAT VARCHAR(255) NOT NULL DEFAULT 'WAIT', - ORD_ID VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (PUR_ORD_ID), - FOREIGN KEY (ORD_ID) REFERENCES tb_order (ORD_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_EVALUATION -( - EVAL_ID VARCHAR(255) NOT NULL, - EVAL_TTL VARCHAR(255) NOT NULL, - EVAL_CONT VARCHAR(255) NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - CENT_ID VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - WRI_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (EVAL_ID), - FOREIGN KEY (CENT_ID) REFERENCES tb_center (CENT_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), - FOREIGN KEY (WRI_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_promotion -( - PROM_ID VARCHAR(255) NOT NULL, - PROM_TTL VARCHAR(255) NOT NULL, - PROM_CONT VARCHAR(255) NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (PROM_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_file -( - FILE_ID VARCHAR(255) NOT NULL, - FILE_NAME VARCHAR(255) NOT NULL, - FILE_URL VARCHAR(255) NOT NULL, - FILE_TYPE VARCHAR(255) NOT NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - CREATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - MEM_ID VARCHAR(255) NULL, - NOT_ID VARCHAR(255) NULL, - CONR_ID VARCHAR(255) NULL, - PROB_ID VARCHAR(255) NULL, - CENT_ID VARCHAR(255) NULL, - PROM_ID VARCHAR(255) NULL, - EVAL_ID VARCHAR(255) NULL, - CONR_ID2 VARCHAR(255) NULL, - PRIMARY KEY (FILE_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), - FOREIGN KEY (NOT_ID) REFERENCES tb_notice (NOT_ID), - FOREIGN KEY (CONR_ID) REFERENCES tb_contract (CONR_ID), - FOREIGN KEY (PROB_ID) REFERENCES tb_problem (PROB_ID), - FOREIGN KEY (CENT_ID) REFERENCES tb_center (CENT_ID), - FOREIGN KEY (PROM_ID) REFERENCES tb_promotion (PROM_ID), - FOREIGN KEY (EVAL_ID) REFERENCES tb_evaluation (EVAL_ID), - FOREIGN KEY (CONR_ID2) REFERENCES tb_contract (CONR_ID) -); - -CREATE TABLE tb_schedule -( - SCH_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', - SCH_NAME VARCHAR(255) NOT NULL, - SCH_CONT VARCHAR(255) NOT NULL, - SCH_TAG VARCHAR(255) NOT NULL, - SCH_SRT_AT CHAR(19) NOT NULL, - SCH_END_AT CHAR(19) NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL COMMENT 'TRUE/FALSE', - MEM_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', - PRIMARY KEY (SCH_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_alarm -( - ALR_ID VARCHAR(255) NOT NULL, - ALR_MSG VARCHAR(255) NOT NULL, - ALR_URL VARCHAR(255) NOT NULL, - ALR_READ_STAT BOOLEAN NOT NULL DEFAULT FALSE, - CREATED_AT CHAR(19) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (ALR_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_family -( - FAM_ID VARCHAR(255) NOT NULL, - FAM_NAME VARCHAR(255) NOT NULL, - FAM_REL VARCHAR(255) NOT NULL, - FAM_BIR VARCHAR(255) NOT NULL, - FAM_IDEN_NO VARCHAR(255) NOT NULL, - FAM_PHO VARCHAR(255) NOT NULL, - FAM_SEX VARCHAR(255) NOT NULL, - FAM_DIS BOOLEAN NOT NULL, - FAM_DIE BOOLEAN NOT NULL, - FAM_NOTE VARCHAR(255) NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (FAM_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) -); - -CREATE TABLE tb_education -( - EDU_ID VARCHAR(255) NOT NULL, - EDU_ENTD VARCHAR(255) NOT NULL, - EDU_GRAD VARCHAR(255) NOT NULL, - EDU_NAME VARCHAR(255) NOT NULL, - EDU_MJR VARCHAR(255) NOT NULL, - EDU_SCO VARCHAR(255) NULL, - EDU_NOTE VARCHAR(255) NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (EDU_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) -); - -CREATE TABLE tb_certification -( - CER_ID VARCHAR(255) NOT NULL, - CER_DATE VARCHAR(255) NOT NULL, - CER_INST VARCHAR(255) NOT NULL, - CER_NAME VARCHAR(255) NOT NULL, - CER_SCO VARCHAR(255) NOT NULL, - CER_NOTE VARCHAR(255) NULL, - CREATED_AT CHAR(19) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (CER_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) -); - -CREATE TABLE tb_career -( - CAR_ID VARCHAR(255) NOT NULL, - CAR_EMP_DATE VARCHAR(255) NOT NULL, - CAR_RTR_DATE VARCHAR(255) NULL, - CAR_NAME VARCHAR(255) NOT NULL, - CAR_NOTE VARCHAR(255) NULL, - CREATED_AT CHAR(19) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (CAR_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) -); - -CREATE TABLE tb_UPDATE_HISTORY -( - UPD_ID VARCHAR(255) NOT NULL, - UPD_IP VARCHAR(255) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - UPDATED_URL VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - CONR_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (UPD_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), - FOREIGN KEY (CONR_ID) REFERENCES tb_contract (CONR_ID) -); - -CREATE TABLE tb_PRODUCT_OPTION -( - PROD_ID VARCHAR(255) NOT NULL, - PROD_SER_NO VARCHAR(255) NOT NULL, - OPT_CNTY CHAR(1) NOT NULL DEFAULT 'K', - OPT_MNFR CHAR(1) NOT NULL DEFAULT 'N', - OPT_VHC_TYPE CHAR(1) NOT NULL, - OPT_CHSS CHAR(1) NOT NULL, - OPT_DTIL_TYPE CHAR(1) NOT NULL, - OPT_BODY_TYPE CHAR(1) NOT NULL, - OPT_SFTY_DVCE CHAR(1) NOT NULL, - OPT_ENGN_CPCT CHAR(1) NOT NULL, - OPT_SCRT_CODE CHAR(1) NOT NULL, - OPT_PRDC_YEAR CHAR(1) NOT NULL, - OPT_PRDC_PLNT CHAR(1) NOT NULL, - OPT_ENGN CHAR(1) NOT NULL DEFAULT '0', - OPT_MSSN CHAR(1) NOT NULL DEFAULT '0', - OPT_TRIM CHAR(1) NOT NULL DEFAULT '0', - OPT_XTNL_COLR CHAR(1) NOT NULL, - OPT_ITNL_COLR CHAR(1) NOT NULL, - OPT_HUD CHAR(1) NOT NULL DEFAULT '0', - OPT_NAVI CHAR(1) NOT NULL DEFAULT '0', - OPT_DRVE_WISE CHAR(1) NOT NULL DEFAULT '0', - OPT_SMRT_CNCT CHAR(1) NOT NULL DEFAULT '0', - OPT_STYL CHAR(1) NOT NULL DEFAULT '0', - OPT_MY_CFRT_PCKG CHAR(1) NOT NULL DEFAULT '0', - OPT_OTDR_PCKG CHAR(1) NOT NULL DEFAULT '0', - OPT_SUN_ROOF CHAR(1) NOT NULL DEFAULT '0', - OPT_SOND CHAR(1) NOT NULL DEFAULT '0', - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - PRIMARY KEY (PROD_ID, PROD_SER_NO), - FOREIGN KEY (PROD_ID, PROD_SER_NO) REFERENCES tb_product (PROD_ID, PROD_SER_NO) ON DELETE CASCADE -); - -CREATE TABLE tb_sales_history -( - SAL_HIST_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', - CONR_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고' -); +# -- 외래 키 제약 조건 비활성화 +# SET FOREIGN_KEY_CHECKS = 0; +# +# -- 자식 테이블부터 삭제 +# DROP TABLE IF EXISTS tb_sales_history; +# DROP TABLE IF EXISTS tb_update_history; +# DROP TABLE IF EXISTS tb_product_option; +# DROP TABLE IF EXISTS tb_alarm; +# DROP TABLE IF EXISTS tb_schedule; +# DROP TABLE IF EXISTS tb_file; +# DROP TABLE IF EXISTS tb_promotion; +# DROP TABLE IF EXISTS tb_evaluation; +# DROP TABLE IF EXISTS tb_purchase_order; +# DROP TABLE IF EXISTS tb_problem; +# DROP TABLE IF EXISTS tb_order; +# DROP TABLE IF EXISTS tb_notice; +# DROP TABLE IF EXISTS tb_contract; +# DROP TABLE IF EXISTS tb_customer_info; +# DROP TABLE IF EXISTS tb_member_role; +# DROP TABLE IF EXISTS tb_member; +# DROP TABLE IF EXISTS tb_center; +# DROP TABLE IF EXISTS tb_family; +# DROP TABLE IF EXISTS tb_education; +# DROP TABLE IF EXISTS tb_certification; +# DROP TABLE IF EXISTS tb_career; +# DROP TABLE IF EXISTS tb_product; +# DROP TABLE IF EXISTS tb_organization_chart; + +# -- 조직 관련 테이블 생성 +# CREATE TABLE tb_organization_chart +# ( +# ORG_CHA_ID VARCHAR(255) NOT NULL, +# ORG_CHA_NAME VARCHAR(255) NOT NULL, +# ORG_CHA_DEPTH INT NOT NULL, +# PRIMARY KEY (ORG_CHA_ID) +# ); +# +# CREATE TABLE tb_center +# ( +# CENT_ID VARCHAR(255) NOT NULL, +# CENT_NAME VARCHAR(255) NOT NULL, +# CENT_ADR VARCHAR(255) NOT NULL, +# CENT_PHO VARCHAR(255) NOT NULL, +# CENT_MEM_CNT INT NOT NULL, +# CREATED_AT CHAR(19) NOT NULL, +# UPDATED_AT CHAR(19) NOT NULL DEFAULT 'CURRENT_TIMESTAMP', +# DELETED_AT CHAR(19) NULL, +# ACTIVE BOOLEAN NOT NULL, +# CENT_OPR_AT VARCHAR(255) NOT NULL, +# PRIMARY KEY (CENT_ID) +# ); +# +# CREATE TABLE tb_member +# ( +# MEM_ID VARCHAR(255) NOT NULL, +# MEM_LOGIN_ID VARCHAR(255) NOT NULL, +# MEM_PWD VARCHAR(255) NOT NULL, +# MEM_NAME VARCHAR(255) NOT NULL, +# MEM_EMA VARCHAR(255) NOT NULL, +# MEM_AGE INT NOT NULL, +# MEM_SEX VARCHAR(255) NOT NULL DEFAULT 'FEMALE', +# MEM_IDEN_NO VARCHAR(255) NOT NULL, +# MEM_PHO VARCHAR(255) NOT NULL, +# MEM_EMER_PHO VARCHAR(255) NULL, +# MEM_ADR VARCHAR(255) NOT NULL, +# MEM_NOTE VARCHAR(255) NULL, +# MEM_POS VARCHAR(255) NOT NULL DEFAULT 'INTERN', +# MEM_GRD VARCHAR(255) NOT NULL DEFAULT 'High School', +# MEM_JOB_TYPE VARCHAR(255) NOT NULL, +# MEM_MIL VARCHAR(255) NOT NULL DEFAULT 'exemption', +# MEM_BANK_NAME VARCHAR(255) NULL, +# MEM_ACC VARCHAR(255) NULL, +# CREATED_AT CHAR(19) NOT NULL, +# UPDATED_AT CHAR(19) NOT NULL, +# DELETED_AT CHAR(19) NULL, +# ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, +# CENTER_ID VARCHAR(255) NOT NULL, +# ORG_CHA_ID VARCHAR(255) NOT NULL, +# PRIMARY KEY (MEM_ID), +# FOREIGN KEY (CENTER_ID) REFERENCES tb_center (CENT_ID), +# FOREIGN KEY (ORG_CHA_ID) REFERENCES tb_organization_chart (ORG_CHA_ID) +# ); +# +# CREATE TABLE tb_member_role +# ( +# MEM_ROL_ID VARCHAR(255) NOT NULL, +# MEM_ROL_NAME VARCHAR(255) NOT NULL DEFAULT '영업 사원', +# MEM_ID VARCHAR(255) NOT NULL, +# PRIMARY KEY (MEM_ROL_ID), +# FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) +# ); +# +# CREATE TABLE tb_customer_info +# ( +# CUST_ID VARCHAR(255) NOT NULL, +# CUST_NAME VARCHAR(255) NOT NULL, +# CUST_AGE INT NOT NULL, +# CUST_SEX VARCHAR(255) NOT NULL DEFAULT 'FEMALE', +# CUST_PHO VARCHAR(255) NOT NULL, +# CUST_EMER_PHO VARCHAR(255) NOT NULL, +# CUST_EMA VARCHAR(255) NOT NULL, +# ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, +# MEM_ID VARCHAR(255) NOT NULL, +# PRIMARY KEY (CUST_ID), +# FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) +# ); +# +# -- 부모 테이블 생성 +# CREATE TABLE tb_product +# ( +# PROD_ID VARCHAR(255) NOT NULL, +# PROD_SER_NO VARCHAR(255) NOT NULL, +# PROD_COST INT NOT NULL, +# PROD_NAME VARCHAR(255) NOT NULL, +# PROD_STCK INT NOT NULL DEFAULT 0, +# CREATED_AT CHAR(19) NOT NULL, +# UPDATED_AT CHAR(19) NOT NULL DEFAULT 'CURRENT_TIMESTAMP', +# DELETED_AT CHAR(19) NULL, +# ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, +# PRIMARY KEY (PROD_ID, PROD_SER_NO) +# ); +# +# CREATE TABLE tb_contract +# ( +# CONR_ID VARCHAR(255) NOT NULL, +# CONR_NAME VARCHAR(255) NOT NULL, +# CONR_CUST_NAME VARCHAR(255) NOT NULL, +# CONR_CUST_IDEN_NO VARCHAR(255) NOT NULL, +# CONR_CUST_ADR VARCHAR(255) NOT NULL, +# CONR_CUST_EMA VARCHAR(255) NOT NULL, +# CONR_CUST_PHO VARCHAR(255) NOT NULL, +# CONR_COMP_NAME VARCHAR(255) NULL, +# CONR_CUST_CLA VARCHAR(255) NOT NULL DEFAULT 'PERSONAL', +# CONR_CUST_PUR_COND VARCHAR(255) NOT NULL DEFAULT 'CASH', +# CONR_SERI_NUM VARCHAR(255) NOT NULL, +# CONR_SELE_OPTI VARCHAR(255) NOT NULL, +# CONR_DOWN_PAY INT NOT NULL, +# CONR_INTE_PAY INT NOT NULL, +# CONR_REM_PAY INT NOT NULL, +# CONR_CONS_PAY INT NOT NULL, +# CONR_DELV_DATE VARCHAR(255) NULL, +# CONR_DELV_LOC VARCHAR(255) NULL, +# CONR_STAT VARCHAR(255) NULL DEFAULT 'WAIT', +# CONR_NO_OF_VEH INT NOT NULL DEFAULT 1, +# CREATED_URL TEXT NOT NULL, +# DELETED_URL TEXT NULL, +# ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, +# CREATED_AT CHAR(19) NOT NULL, +# UPDATED_AT CHAR(19) NOT NULL, +# DELETED_AT CHAR(19) NULL, +# MEM_ID VARCHAR(255) NOT NULL, +# CENT_ID VARCHAR(255) NOT NULL, +# CUST_ID VARCHAR(255) NULL, +# PROD_ID VARCHAR(255) NOT NULL, +# PRIMARY KEY (CONR_ID), +# FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE, +# FOREIGN KEY (CUST_ID) REFERENCES tb_customer_info (CUST_ID) ON DELETE CASCADE, +# FOREIGN KEY (PROD_ID) REFERENCES tb_product (PROD_ID) ON DELETE CASCADE, +# FOREIGN KEY (CENT_ID) REFERENCES tb_center (CENT_ID) ON DELETE CASCADE +# ); +# +# CREATE TABLE tb_notice +# ( +# NOT_ID VARCHAR(255) NOT NULL, +# NOT_TTL VARCHAR(255) NOT NULL, +# NOT_TAG VARCHAR(255) NOT NULL DEFAULT 'ALL', +# NOT_CLA VARCHAR(255) NOT NULL DEFAULT 'NORMAL', +# NOT_CONT TEXT NOT NULL, +# CREATED_AT CHAR(19) NOT NULL, +# UPDATED_AT CHAR(19) NOT NULL, +# DELETED_AT CHAR(19) NULL, +# ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, +# MEM_ID VARCHAR(255) NOT NULL, +# PRIMARY KEY (NOT_ID), +# FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) +# ); +# +# CREATE TABLE tb_order +# ( +# ORD_ID VARCHAR(255) NOT NULL, +# ORD_TTL VARCHAR(255) NOT NULL, +# ORD_CONT TEXT NOT NULL, +# ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, +# CREATED_AT CHAR(19) NOT NULL, +# UPDATED_AT CHAR(19) NOT NULL, +# DELETED_AT CHAR(19) NULL, +# ORD_STAT VARCHAR(255) NOT NULL DEFAULT 'WAIT', +# CONR_ID VARCHAR(255) NOT NULL, +# MEM_ID VARCHAR(255) NOT NULL, +# ADMIN_ID VARCHAR(255) NULL, +# PRIMARY KEY (ORD_ID), +# FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE, +# FOREIGN KEY (ADMIN_ID) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE, +# FOREIGN KEY (CONR_ID) REFERENCES tb_contract (CONR_ID) ON DELETE CASCADE +# ); +# +# CREATE TABLE tb_problem +# ( +# PROB_ID VARCHAR(255) NOT NULL, +# PROB_TTL VARCHAR(255) NOT NULL, +# PROB_CONT VARCHAR(255) NOT NULL, +# CREATED_AT CHAR(19) NOT NULL, +# UPDATED_AT CHAR(19) NOT NULL, +# ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, +# DELETED_AT CHAR(19) NULL, +# CUST_ID VARCHAR(255) NOT NULL, +# MEM_ID VARCHAR(255) NOT NULL, +# PROD_ID VARCHAR(255) NOT NULL, +# PRIMARY KEY (PROB_ID), +# FOREIGN KEY (CUST_ID) REFERENCES tb_customer_info (CUST_ID), +# FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), +# FOREIGN KEY (PROD_ID) REFERENCES tb_product (PROD_ID) +# ); +# +# CREATE TABLE tb_purchase_order +# ( +# PUR_ORD_ID VARCHAR(255) NOT NULL, +# PUR_ORD_TTL VARCHAR(255) NOT NULL, +# PUR_CONT TEXT NOT NULL, +# CREATED_AT CHAR(19) NOT NULL, +# UPDATED_AT CHAR(19) NOT NULL, +# DELETED_AT CHAR(19) NULL, +# ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, +# PUR_ORD_STAT VARCHAR(255) NOT NULL DEFAULT 'WAIT', +# ORD_ID VARCHAR(255) NOT NULL, +# MEM_ID VARCHAR(255) NOT NULL, +# PRIMARY KEY (PUR_ORD_ID), +# FOREIGN KEY (ORD_ID) REFERENCES tb_order (ORD_ID), +# FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) +# ); +# +# CREATE TABLE tb_EVALUATION +# ( +# EVAL_ID VARCHAR(255) NOT NULL, +# EVAL_TTL VARCHAR(255) NOT NULL, +# EVAL_CONT VARCHAR(255) NOT NULL, +# CREATED_AT CHAR(19) NOT NULL, +# UPDATED_AT CHAR(19) NOT NULL, +# DELETED_AT CHAR(19) NULL, +# ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, +# CENT_ID VARCHAR(255) NOT NULL, +# MEM_ID VARCHAR(255) NOT NULL, +# WRI_ID VARCHAR(255) NOT NULL, +# PRIMARY KEY (EVAL_ID), +# FOREIGN KEY (CENT_ID) REFERENCES tb_center (CENT_ID), +# FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), +# FOREIGN KEY (WRI_ID) REFERENCES tb_member (MEM_ID) +# ); +# +# CREATE TABLE tb_promotion +# ( +# PROM_ID VARCHAR(255) NOT NULL, +# PROM_TTL VARCHAR(255) NOT NULL, +# PROM_CONT VARCHAR(255) NOT NULL, +# CREATED_AT CHAR(19) NOT NULL, +# UPDATED_AT CHAR(19) NOT NULL, +# DELETED_AT CHAR(19) NULL, +# ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, +# MEM_ID VARCHAR(255) NOT NULL, +# PRIMARY KEY (PROM_ID), +# FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) +# ); +# +# CREATE TABLE tb_file +# ( +# FILE_ID VARCHAR(255) NOT NULL, +# FILE_NAME VARCHAR(255) NOT NULL, +# FILE_URL VARCHAR(255) NOT NULL, +# FILE_TYPE VARCHAR(255) NOT NULL, +# ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, +# CREATED_AT CHAR(19) NOT NULL, +# DELETED_AT CHAR(19) NULL, +# MEM_ID VARCHAR(255) NULL, +# NOT_ID VARCHAR(255) NULL, +# CONR_ID VARCHAR(255) NULL, +# PROB_ID VARCHAR(255) NULL, +# CENT_ID VARCHAR(255) NULL, +# PROM_ID VARCHAR(255) NULL, +# EVAL_ID VARCHAR(255) NULL, +# CONR_ID2 VARCHAR(255) NULL, +# PRIMARY KEY (FILE_ID), +# FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), +# FOREIGN KEY (NOT_ID) REFERENCES tb_notice (NOT_ID), +# FOREIGN KEY (CONR_ID) REFERENCES tb_contract (CONR_ID), +# FOREIGN KEY (PROB_ID) REFERENCES tb_problem (PROB_ID), +# FOREIGN KEY (CENT_ID) REFERENCES tb_center (CENT_ID), +# FOREIGN KEY (PROM_ID) REFERENCES tb_promotion (PROM_ID), +# FOREIGN KEY (EVAL_ID) REFERENCES tb_evaluation (EVAL_ID), +# FOREIGN KEY (CONR_ID2) REFERENCES tb_contract (CONR_ID) +# ); +# +# CREATE TABLE tb_schedule +# ( +# SCH_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', +# SCH_NAME VARCHAR(255) NOT NULL, +# SCH_CONT VARCHAR(255) NOT NULL, +# SCH_TAG VARCHAR(255) NOT NULL, +# SCH_SRT_AT CHAR(19) NOT NULL, +# SCH_END_AT CHAR(19) NOT NULL, +# CREATED_AT CHAR(19) NOT NULL, +# UPDATED_AT CHAR(19) NOT NULL, +# DELETED_AT CHAR(19) NULL, +# ACTIVE BOOLEAN NOT NULL COMMENT 'TRUE/FALSE', +# MEM_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', +# PRIMARY KEY (SCH_ID), +# FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) +# ); +# +# CREATE TABLE tb_alarm +# ( +# ALR_ID VARCHAR(255) NOT NULL, +# ALR_MSG VARCHAR(255) NOT NULL, +# ALR_URL VARCHAR(255) NOT NULL, +# ALR_READ_STAT BOOLEAN NOT NULL DEFAULT FALSE, +# CREATED_AT CHAR(19) NOT NULL, +# # ALR_STAT VARCHAR(255) NOT NULL DEFAULT 'PENDING', +# MEM_ID VARCHAR(255) NOT NULL, +# PRIMARY KEY (ALR_ID), +# FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) +# ); +# +# CREATE TABLE tb_family +# ( +# FAM_ID VARCHAR(255) NOT NULL, +# FAM_NAME VARCHAR(255) NOT NULL, +# FAM_REL VARCHAR(255) NOT NULL, +# FAM_BIR VARCHAR(255) NOT NULL, +# FAM_IDEN_NO VARCHAR(255) NOT NULL, +# FAM_PHO VARCHAR(255) NOT NULL, +# FAM_SEX VARCHAR(255) NOT NULL, +# FAM_DIS BOOLEAN NOT NULL, +# FAM_DIE BOOLEAN NOT NULL, +# FAM_NOTE VARCHAR(255) NULL, +# CREATED_AT CHAR(19) NOT NULL, +# UPDATED_AT CHAR(19) NOT NULL, +# MEM_ID VARCHAR(255) NOT NULL, +# PRIMARY KEY (FAM_ID), +# FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) +# ); +# +# CREATE TABLE tb_education +# ( +# EDU_ID VARCHAR(255) NOT NULL, +# EDU_ENTD VARCHAR(255) NOT NULL, +# EDU_GRAD VARCHAR(255) NOT NULL, +# EDU_NAME VARCHAR(255) NOT NULL, +# EDU_MJR VARCHAR(255) NOT NULL, +# EDU_SCO VARCHAR(255) NULL, +# EDU_NOTE VARCHAR(255) NULL, +# CREATED_AT CHAR(19) NOT NULL, +# UPDATED_AT CHAR(19) NOT NULL, +# MEM_ID VARCHAR(255) NOT NULL, +# PRIMARY KEY (EDU_ID), +# FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) +# ); +# +# CREATE TABLE tb_certification +# ( +# CER_ID VARCHAR(255) NOT NULL, +# CER_DATE VARCHAR(255) NOT NULL, +# CER_INST VARCHAR(255) NOT NULL, +# CER_NAME VARCHAR(255) NOT NULL, +# CER_SCO VARCHAR(255) NOT NULL, +# CER_NOTE VARCHAR(255) NULL, +# CREATED_AT CHAR(19) NOT NULL, +# MEM_ID VARCHAR(255) NOT NULL, +# PRIMARY KEY (CER_ID), +# FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) +# ); +# +# CREATE TABLE tb_career +# ( +# CAR_ID VARCHAR(255) NOT NULL, +# CAR_EMP_DATE VARCHAR(255) NOT NULL, +# CAR_RTR_DATE VARCHAR(255) NULL, +# CAR_NAME VARCHAR(255) NOT NULL, +# CAR_NOTE VARCHAR(255) NULL, +# CREATED_AT CHAR(19) NOT NULL, +# MEM_ID VARCHAR(255) NOT NULL, +# PRIMARY KEY (CAR_ID), +# FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) +# ); +# +# CREATE TABLE tb_UPDATE_HISTORY +# ( +# UPD_ID VARCHAR(255) NOT NULL, +# UPD_IP VARCHAR(255) NOT NULL, +# UPDATED_AT CHAR(19) NOT NULL, +# UPDATED_URL VARCHAR(255) NOT NULL, +# MEM_ID VARCHAR(255) NOT NULL, +# CONR_ID VARCHAR(255) NOT NULL, +# PRIMARY KEY (UPD_ID), +# FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), +# FOREIGN KEY (CONR_ID) REFERENCES tb_contract (CONR_ID) +# ); +# +# CREATE TABLE tb_PRODUCT_OPTION +# ( +# PROD_ID VARCHAR(255) NOT NULL, +# PROD_SER_NO VARCHAR(255) NOT NULL, +# OPT_CNTY CHAR(1) NOT NULL DEFAULT 'K', +# OPT_MNFR CHAR(1) NOT NULL DEFAULT 'N', +# OPT_VHC_TYPE CHAR(1) NOT NULL, +# OPT_CHSS CHAR(1) NOT NULL, +# OPT_DTIL_TYPE CHAR(1) NOT NULL, +# OPT_BODY_TYPE CHAR(1) NOT NULL, +# OPT_SFTY_DVCE CHAR(1) NOT NULL, +# OPT_ENGN_CPCT CHAR(1) NOT NULL, +# OPT_SCRT_CODE CHAR(1) NOT NULL, +# OPT_PRDC_YEAR CHAR(1) NOT NULL, +# OPT_PRDC_PLNT CHAR(1) NOT NULL, +# OPT_ENGN CHAR(1) NOT NULL DEFAULT '0', +# OPT_MSSN CHAR(1) NOT NULL DEFAULT '0', +# OPT_TRIM CHAR(1) NOT NULL DEFAULT '0', +# OPT_XTNL_COLR CHAR(1) NOT NULL, +# OPT_ITNL_COLR CHAR(1) NOT NULL, +# OPT_HUD CHAR(1) NOT NULL DEFAULT '0', +# OPT_NAVI CHAR(1) NOT NULL DEFAULT '0', +# OPT_DRVE_WISE CHAR(1) NOT NULL DEFAULT '0', +# OPT_SMRT_CNCT CHAR(1) NOT NULL DEFAULT '0', +# OPT_STYL CHAR(1) NOT NULL DEFAULT '0', +# OPT_MY_CFRT_PCKG CHAR(1) NOT NULL DEFAULT '0', +# OPT_OTDR_PCKG CHAR(1) NOT NULL DEFAULT '0', +# OPT_SUN_ROOF CHAR(1) NOT NULL DEFAULT '0', +# OPT_SOND CHAR(1) NOT NULL DEFAULT '0', +# ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, +# PRIMARY KEY (PROD_ID, PROD_SER_NO), +# FOREIGN KEY (PROD_ID, PROD_SER_NO) REFERENCES tb_product (PROD_ID, PROD_SER_NO) ON DELETE CASCADE +# ); +# +# CREATE TABLE tb_sales_history +# ( +# SAL_HIST_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', +# CONR_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고' +# ); INSERT INTO tb_organization_chart (ORG_CHA_ID, ORG_CHA_NAME, ORG_CHA_DEPTH) VALUES ('ORG_000000001', '본사', 1), @@ -768,17 +768,17 @@ VALUES ('SCH_000000010', 'K5 재고 점검', 'K5 재고 관리 및 점검', 'MEETING', '2024-11-28 15:00:00', '2024-11-28 17:00:00', '2024-01-29 19:00:00', '2024-01-29 19:30:00', NULL, TRUE, 'MEM_000000010'); -INSERT INTO tb_alarm (ALR_ID, ALR_MSG, ALR_URL, ALR_READ_STAT, CREATED_AT, MEM_ID) -VALUES ('ALR_000000001', '신차 출시 공지 알림', '/notices/1', FALSE, '2024-01-20 12:00:00', 'MEM_000000001'), - ('ALR_000000002', '스포티지 리콜 안내', '/notices/2', FALSE, '2024-01-21 14:00:00', 'MEM_000000002'), - ('ALR_000000003', 'K7 고객 이벤트 초대', '/events/3', TRUE, '2024-01-22 09:00:00', 'MEM_000000003'), - ('ALR_000000004', '셀토스 정비 완료', '/service/4', FALSE, '2024-01-23 10:00:00', 'MEM_000000004'), - ('ALR_000000005', 'K3 테스트 드라이브 일정 변경', '/schedules/5', TRUE, '2024-01-24 11:00:00', 'MEM_000000005'), - ('ALR_000000006', '모하비 부품 입고 알림', '/inventory/6', FALSE, '2024-01-25 12:00:00', 'MEM_000000006'), - ('ALR_000000007', 'K8 디자인 변경 확정', '/design/7', TRUE, '2024-01-26 13:00:00', 'MEM_000000007'), - ('ALR_000000008', '스팅어 성능 테스트 보고서', '/reports/8', FALSE, '2024-01-27 14:00:00', 'MEM_000000008'), - ('ALR_000000009', '니로 전기차 충전소 위치', '/maps/9', TRUE, '2024-01-28 15:00:00', 'MEM_000000009'), - ('ALR_000000010', 'K5 재고 부족 경고', '/inventory/10', FALSE, '2024-01-29 16:00:00', 'MEM_000000010'); +# INSERT INTO tb_alarm (ALR_ID, ALR_MSG, ALR_URL, ALR_TYPE, ALR_READ_STAT, CREATED_AT, ALR_STAT, MEM_ID) +# VALUES ('ALR_000000001', '신차 출시 공지 알림', '/notices/1', FALSE, '2024-01-20 12:00:00', 'MEM_000000001'), +# ('ALR_000000002', '스포티지 리콜 안내', '/notices/2', FALSE, '2024-01-21 14:00:00', 'MEM_000000002'), +# ('ALR_000000003', 'K7 고객 이벤트 초대', '/events/3', TRUE, '2024-01-22 09:00:00', 'MEM_000000003'), +# ('ALR_000000004', '셀토스 정비 완료', '/service/4', FALSE, '2024-01-23 10:00:00', 'MEM_000000004'), +# ('ALR_000000005', 'K3 테스트 드라이브 일정 변경', '/schedules/5', TRUE, '2024-01-24 11:00:00', 'MEM_000000005'), +# ('ALR_000000006', '모하비 부품 입고 알림', '/inventory/6', FALSE, '2024-01-25 12:00:00', 'MEM_000000006'), +# ('ALR_000000007', 'K8 디자인 변경 확정', '/design/7', TRUE, '2024-01-26 13:00:00', 'MEM_000000007'), +# ('ALR_000000008', '스팅어 성능 테스트 보고서', '/reports/8', FALSE, '2024-01-27 14:00:00', 'MEM_000000008'), +# ('ALR_000000009', '니로 전기차 충전소 위치', '/maps/9', TRUE, '2024-01-28 15:00:00', 'MEM_000000009'), +# ('ALR_000000010', 'K5 재고 부족 경고', '/inventory/10', FALSE, '2024-01-29 16:00:00', 'MEM_000000010'); INSERT INTO tb_family (FAM_ID, FAM_NAME, FAM_REL, FAM_BIR, FAM_IDEN_NO, FAM_PHO, FAM_SEX, FAM_DIS, FAM_DIE, FAM_NOTE, CREATED_AT, UPDATED_AT, MEM_ID) VALUES diff --git a/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml b/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml index 7207d923..6e12d1f5 100644 --- a/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.xml @@ -14,16 +14,16 @@ @@ -37,16 +37,16 @@ @@ -61,16 +61,37 @@ - \ No newline at end of file + + + + + + + + + + + + diff --git a/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/sample.xml b/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/sample.xml new file mode 100644 index 00000000..2037307e --- /dev/null +++ b/src/main/resources/stanl_2/final_backend/domain/schedule/query/repository/sample.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From bc16b9b4c9507a92d76985ec67f4801aead0bbbc Mon Sep 17 00:00:00 2001 From: giuseog Date: Tue, 19 Nov 2024 16:15:06 +0900 Subject: [PATCH 195/563] =?UTF-8?q?fix:=20mapper=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD(#88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../center/query/repository/CenterMapper.xml | 103 +++++------ .../query/repository/EvaluationMapper.xml | 174 +++++++++--------- .../query/repository/ProductMapper.xml | 125 +++++++------ 3 files changed, 199 insertions(+), 203 deletions(-) diff --git a/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml b/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml index aa7f2c04..68269c22 100644 --- a/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml @@ -30,89 +30,88 @@ - - \ No newline at end of file + diff --git a/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml b/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml index ce699c09..7b57b109 100644 --- a/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml @@ -8,8 +8,8 @@ - - + + @@ -17,176 +17,174 @@ - - SELECT - A.EVAL_ID - , A.EVAL_TTL - , A.CENT_ID - , A.MEM_ID - , A.WRI_ID - , A.CREATED_AT - FROM TB_EVALUATION A + a.eval_id, + a.eval_ttl, + a.cent_id, + a.mem_id, + a.wri_id, + a.created_at + FROM tb_evaluation a - A.ACTIVE = TRUE AND A.CENT_ID = #{centerId} + a.active = TRUE AND a.cent_id = #{centerId} - AND A.EVAL_ID = #{evaluationSearchDTO.evalId} + AND a.eval_id = #{evaluationSearchDTO.evalId} - AND A.WRI_ID = #{evaluationSearchDTO.writerName} + AND a.wri_id = #{evaluationSearchDTO.writerName} - AND A.MEM_ID = #{evaluationSearchDTO.memberName} + AND a.mem_id = #{evaluationSearchDTO.memberName} - AND A.EVAL_TTL LIKE CONCAT('%', #{evaluationSearchDTO.title}, '%') + AND a.eval_ttl LIKE CONCAT('%', #{evaluationSearchDTO.title}, '%') - AND A.CREATED_AT BETWEEN #{evaluationSearchDTO.startDate} AND #{evaluationSearchDTO.endDate} + AND a.created_at BETWEEN #{evaluationSearchDTO.startDate} AND #{evaluationSearchDTO.endDate} - ORDER BY A.CREATED_AT DESC + ORDER BY a.created_at DESC LIMIT #{size} OFFSET #{offset} - SELECT - A.EVAL_ID - , A.EVAL_TTL - , A.CENT_ID - , A.MEM_ID - , A.WRI_ID - , A.CREATED_AT - FROM TB_EVALUATION A + a.eval_id, + a.eval_ttl, + a.cent_id, + a.mem_id, + a.wri_id, + a.created_at + FROM tb_evaluation a - A.ACTIVE = TRUE + a.active = TRUE - AND A.EVAL_ID = #{evaluationSearchDTO.evalId} + AND a.eval_id = #{evaluationSearchDTO.evalId} - AND A.WRI_ID = #{evaluationSearchDTO.writerName} + AND a.wri_id = #{evaluationSearchDTO.writerName} - AND A.MEM_ID = #{evaluationSearchDTO.memberName} + AND a.mem_id = #{evaluationSearchDTO.memberName} - AND A.CENT_ID = #{evaluationSearchDTO.centerId} + AND a.cent_id = #{evaluationSearchDTO.centerId} - AND A.EVAL_TTL LIKE CONCAT('%', #{evaluationSearchDTO.title}, '%') + AND a.eval_ttl LIKE CONCAT('%', #{evaluationSearchDTO.title}, '%') - AND A.CREATED_AT BETWEEN #{evaluationSearchDTO.startDate} AND #{evaluationSearchDTO.endDate} + AND a.created_at BETWEEN #{evaluationSearchDTO.startDate} AND #{evaluationSearchDTO.endDate} - ORDER BY A.CREATED_AT DESC + ORDER BY a.created_at DESC LIMIT #{size} OFFSET #{offset} - - \ No newline at end of file + diff --git a/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml b/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml index 3d6901b1..95e3f102 100644 --- a/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml @@ -45,100 +45,99 @@ - - \ No newline at end of file + From 7bc2565e6d838acec98b0e4c847ddfa191d7fd5b Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Tue, 19 Nov 2024 16:22:28 +0900 Subject: [PATCH 196/563] =?UTF-8?q?feat:=20dev=EC=97=90=EC=84=9C=20merge?= =?UTF-8?q?=20=EC=A0=84=20commit=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/alarm/service/AlarmService.java | 3 +++ .../alarm/service/AlarmServiceImpl.java | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java index 58ec195f..7e774686 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java @@ -4,6 +4,7 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import stanl_2.final_backend.domain.alarm.aggregate.dto.AlarmDTO; import stanl_2.final_backend.domain.alarm.aggregate.entity.Alarm; +import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; public interface AlarmService { SseEmitter subscribe(AlarmDTO alarmDTO, HttpServletResponse response); @@ -13,4 +14,6 @@ public interface AlarmService { void send(String memberLoginId, String message, String redirectUrl, String type, String createdAt); Alarm createAlarm(String memberId, String message, String redirectUrl, String type, String createdAt); + + void sendNoticeAlarm(NoticeRegistDTO noticeRegistDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java index f93e8687..a6d2fd8e 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java @@ -11,8 +11,13 @@ import stanl_2.final_backend.domain.alarm.repository.AlarmRepository; import stanl_2.final_backend.domain.alarm.repository.EmitterRepository; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; +import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; import java.io.IOException; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.Map; @Slf4j @@ -22,15 +27,21 @@ public class AlarmServiceImpl implements AlarmService { private final EmitterRepository emitterRepository; private final AlarmRepository alarmRepository; private final AuthQueryService authQueryService; + private final MemberQueryService memberQueryService; + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } private static final Long DEFAULT_TIMEOUT = 60L * 1000 * 60; @Autowired public AlarmServiceImpl(AlarmRepository alarmRepository, EmitterRepository emitterRepository, - AuthQueryService authQueryService) { + AuthQueryService authQueryService, MemberQueryService memberQueryService) { this.alarmRepository = alarmRepository; this.emitterRepository = emitterRepository; this.authQueryService = authQueryService; + this.memberQueryService = memberQueryService; } @Override @@ -107,4 +118,10 @@ public Alarm createAlarm(String memberId, String message, String redirectUrl, St return alarm; } + @Override + public void sendNoticeAlarm(NoticeRegistDTO noticeRegistDTO){ + + String currentTime = getCurrentTime(); + } + } From f65bd7f9c2b49dc46463b4dec2204a9bc12ec08c Mon Sep 17 00:00:00 2001 From: giuseog Date: Tue, 19 Nov 2024 16:26:15 +0900 Subject: [PATCH 197/563] =?UTF-8?q?fix:=20=EC=98=A4=EB=A5=98=20=EB=B0=A9?= =?UTF-8?q?=EC=A7=80=20=EC=9C=84=ED=95=B4=20=EB=8D=94=EB=AF=B8=20=EC=86=8D?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80(#88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/sales_history/query/dto/SalesHistorySelectAllDTO.java | 1 + .../domain/sales_history/query/dto/SalesHistorySelectIdDTO.java | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectAllDTO.java index 1861b964..7fd3f689 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectAllDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectAllDTO.java @@ -10,4 +10,5 @@ @Getter @Setter public class SalesHistorySelectAllDTO { + private String temp1; } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectIdDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectIdDTO.java index 18d2408e..c025ceda 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectIdDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectIdDTO.java @@ -10,4 +10,5 @@ @Getter @Setter public class SalesHistorySelectIdDTO { + private String temp2; } From 87bff9725e16786bd7fe56bba8f78f3de79c9d0b Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Tue, 19 Nov 2024 16:36:26 +0900 Subject: [PATCH 198/563] =?UTF-8?q?fix:=20xml=20=EC=88=98=EC=A0=95(#88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/service/AuthQueryServiceImpl.java | 2 +- src/main/resources/application.yml | 9 +- src/main/resources/sql/ddl.sql | 946 ------------------ .../career/query/repository/CareerMapper.xml | 14 +- .../query/repository/CertificationMapper.xml | 16 +- .../query/repository/CustomerMapper.xml | 91 +- .../query/repository/EducationMapper.xml | 18 +- .../family/query/repository/FamilyMapper.xml | 24 +- .../member/query/repository/AuthMapper.xml | 6 +- .../member/query/repository/MemberMapper.xml | 36 +- 10 files changed, 109 insertions(+), 1053 deletions(-) delete mode 100644 src/main/resources/sql/ddl.sql diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java index 413f579d..e54a72c3 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java @@ -22,7 +22,7 @@ public AuthQueryServiceImpl(AuthMapper authMapper) { @Override @Transactional public String selectMemberIdByLoginId(String loginId){ - + String id = authMapper.selectIdByMemberName(loginId); if(id == null){ diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index eee7170c..8028244e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -7,9 +7,12 @@ spring: datasource: driver-class-name: org.mariadb.jdbc.Driver - url: jdbc:mariadb://${DATABASE_HOST}:${MARIA_DATABASE_PORT}/${MARIA_DATABASE_NAME} - username: ${DB_USERNAME} - password: ${DB_PASSWORD} + # url: jdbc:mariadb://${DATABASE_URL}:${MARIA_DATABASE_PORT}/${MARIA_DATABASE_NAME} + url: jdbc:mariadb://motive.cdw8kw8go27z.ap-northeast-2.rds.amazonaws.com:3306/motive + username: admin + password: motivepassword + # username: ${DB_USERNAME} + # password: ${DB_PASSWORD} profiles: active: ${SPRING_PROFILES_ACTIVE} diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql deleted file mode 100644 index 2213cfed..00000000 --- a/src/main/resources/sql/ddl.sql +++ /dev/null @@ -1,946 +0,0 @@ --- 외래 키 제약 조건 비활성화 -SET FOREIGN_KEY_CHECKS = 0; - --- 자식 테이블부터 삭제 -DROP TABLE IF EXISTS sample; -DROP TABLE IF EXISTS tb_sales_history; -DROP TABLE IF EXISTS tb_update_history; -DROP TABLE IF EXISTS tb_product_option; -DROP TABLE IF EXISTS tb_alarm; -DROP TABLE IF EXISTS tb_schedule; -DROP TABLE IF EXISTS tb_file; -DROP TABLE IF EXISTS tb_promotion; -DROP TABLE IF EXISTS tb_evaluation; -DROP TABLE IF EXISTS tb_purchase_order; -DROP TABLE IF EXISTS tb_problem; -DROP TABLE IF EXISTS tb_order; -DROP TABLE IF EXISTS tb_notice; -DROP TABLE IF EXISTS tb_contract; -DROP TABLE IF EXISTS tb_customer_info; -DROP TABLE IF EXISTS tb_member_role; -DROP TABLE IF EXISTS tb_member; -DROP TABLE IF EXISTS tb_center; -DROP TABLE IF EXISTS tb_family; -DROP TABLE IF EXISTS tb_education; -DROP TABLE IF EXISTS tb_certification; -DROP TABLE IF EXISTS tb_career; -DROP TABLE IF EXISTS tb_product; -DROP TABLE IF EXISTS tb_organization_chart; - - --- 조직 관련 테이블 생성 -CREATE TABLE tb_organization_chart -( - ORG_CHA_ID VARCHAR(255) NOT NULL, - ORG_CHA_NAME VARCHAR(255) NOT NULL, - ORG_CHA_DEPTH INT NOT NULL, - PRIMARY KEY (ORG_CHA_ID) -); - -CREATE TABLE tb_center -( - CENT_ID VARCHAR(255) NOT NULL, - CENT_NAME VARCHAR(255) NOT NULL, - CENT_ADR VARCHAR(255) NOT NULL, - CENT_PHO VARCHAR(255) NOT NULL, - CENT_MEM_CNT INT NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL DEFAULT 'CURRENT_TIMESTAMP', - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL, - CENT_OPR_AT VARCHAR(255) NOT NULL, - PRIMARY KEY (CENT_ID) -); - -CREATE TABLE tb_member -( - MEM_ID VARCHAR(255) NOT NULL, - MEM_LOGIN_ID VARCHAR(255) NOT NULL, - MEM_PWD VARCHAR(255) NOT NULL, - MEM_NAME VARCHAR(255) NOT NULL, - MEM_EMA VARCHAR(255) NOT NULL, - MEM_AGE INT NOT NULL, - MEM_SEX VARCHAR(255) NOT NULL DEFAULT 'FEMALE', - MEM_IDEN_NO VARCHAR(255) NOT NULL, - MEM_PHO VARCHAR(255) NOT NULL, - MEM_EMER_PHO VARCHAR(255) NULL, - MEM_ADR VARCHAR(255) NOT NULL, - MEM_NOTE VARCHAR(255) NULL, - MEM_POS VARCHAR(255) NOT NULL DEFAULT 'INTERN', - MEM_GRD VARCHAR(255) NOT NULL DEFAULT 'High School', - MEM_JOB_TYPE VARCHAR(255) NOT NULL, - MEM_MIL VARCHAR(255) NOT NULL DEFAULT 'exemption', - MEM_BANK_NAME VARCHAR(255) NULL, - MEM_ACC VARCHAR(255) NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - CENTER_ID VARCHAR(255) NOT NULL, - ORG_CHA_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (MEM_ID), - FOREIGN KEY (CENTER_ID) REFERENCES tb_center (CENT_ID), - FOREIGN KEY (ORG_CHA_ID) REFERENCES tb_organization_chart (ORG_CHA_ID) -); - -CREATE TABLE tb_member_role -( - MEM_ROL_ID VARCHAR(255) NOT NULL, - MEM_ROL_NAME VARCHAR(255) NOT NULL DEFAULT '영업 사원', - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (MEM_ROL_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_customer_info -( - CUST_ID VARCHAR(255) NOT NULL, - CUST_NAME VARCHAR(255) NOT NULL, - CUST_AGE INT NOT NULL, - CUST_SEX VARCHAR(255) NOT NULL DEFAULT 'FEMALE', - CUST_PHO VARCHAR(255) NOT NULL, - CUST_EMER_PHO VARCHAR(255) NOT NULL, - CUST_EMA VARCHAR(255) NOT NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - MEM_ID VARCHAR(255) NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - PRIMARY KEY (CUST_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - --- 부모 테이블 생성 -CREATE TABLE tb_product -( - PROD_ID VARCHAR(255) NOT NULL, - PROD_SER_NO VARCHAR(255) NOT NULL, - PROD_COST INT NOT NULL, - PROD_NAME VARCHAR(255) NOT NULL, - PROD_STCK INT NOT NULL DEFAULT 0, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL DEFAULT 'CURRENT_TIMESTAMP', - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - PRIMARY KEY (PROD_ID, PROD_SER_NO) -); - -CREATE TABLE tb_contract -( - CONR_ID VARCHAR(255) NOT NULL, - CONR_NAME VARCHAR(255) NOT NULL, - CONR_CUST_NAME VARCHAR(255) NOT NULL, - CONR_CUST_IDEN_NO VARCHAR(255) NOT NULL, - CONR_CUST_ADR VARCHAR(255) NOT NULL, - CONR_CUST_EMA VARCHAR(255) NOT NULL, - CONR_CUST_PHO VARCHAR(255) NOT NULL, - CONR_COMP_NAME VARCHAR(255) NULL, - CONR_CUST_CLA VARCHAR(255) NOT NULL DEFAULT 'PERSONAL', - CONR_CUST_PUR_COND VARCHAR(255) NOT NULL DEFAULT 'CASH', - CONR_SERI_NUM VARCHAR(255) NOT NULL, - CONR_SELE_OPTI VARCHAR(255) NOT NULL, - CONR_DOWN_PAY INT NOT NULL, - CONR_INTE_PAY INT NOT NULL, - CONR_REM_PAY INT NOT NULL, - CONR_CONS_PAY INT NOT NULL, - CONR_DELV_DATE VARCHAR(255) NULL, - CONR_DELV_LOC VARCHAR(255) NULL, - CONR_STAT VARCHAR(255) NULL DEFAULT 'WAIT', - CONR_NO_OF_VEH INT NOT NULL DEFAULT 1, - CREATED_URL TEXT NOT NULL, - DELETED_URL TEXT NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - MEM_ID VARCHAR(255) NOT NULL, - CENT_ID VARCHAR(255) NOT NULL, - CUST_ID VARCHAR(255) NULL, - PROD_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (CONR_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE, - FOREIGN KEY (CUST_ID) REFERENCES tb_customer_info (CUST_ID) ON DELETE CASCADE, - FOREIGN KEY (PROD_ID) REFERENCES tb_product (PROD_ID) ON DELETE CASCADE, - FOREIGN KEY (CENT_ID) REFERENCES tb_center (CENT_ID) ON DELETE CASCADE -); - -CREATE TABLE tb_notice -( - NOT_ID VARCHAR(255) NOT NULL, - NOT_TTL VARCHAR(255) NOT NULL, - NOT_TAG VARCHAR(255) NOT NULL DEFAULT 'ALL', - NOT_CLA VARCHAR(255) NOT NULL DEFAULT 'NORMAL', - NOT_CONT TEXT NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (NOT_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_order -( - ORD_ID VARCHAR(255) NOT NULL, - ORD_TTL VARCHAR(255) NOT NULL, - ORD_CONT TEXT NOT NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ORD_STAT VARCHAR(255) NOT NULL DEFAULT 'WAIT', - CONR_ID VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - ADMIN_ID VARCHAR(255) NULL, - PRIMARY KEY (ORD_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE, - FOREIGN KEY (ADMIN_ID) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE, - FOREIGN KEY (CONR_ID) REFERENCES tb_contract (CONR_ID) ON DELETE CASCADE -); - -CREATE TABLE tb_problem -( - PROB_ID VARCHAR(255) NOT NULL, - PROB_TTL VARCHAR(255) NOT NULL, - PROB_CONT VARCHAR(255) NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - DELETED_AT CHAR(19) NULL, - CUST_ID VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PROD_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (PROB_ID), - FOREIGN KEY (CUST_ID) REFERENCES tb_customer_info (CUST_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), - FOREIGN KEY (PROD_ID) REFERENCES tb_product (PROD_ID) -); - -CREATE TABLE tb_purchase_order -( - PUR_ORD_ID VARCHAR(255) NOT NULL, - PUR_ORD_TTL VARCHAR(255) NOT NULL, - PUR_CONT TEXT NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - PUR_ORD_STAT VARCHAR(255) NOT NULL DEFAULT 'WAIT', - ORD_ID VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (PUR_ORD_ID), - FOREIGN KEY (ORD_ID) REFERENCES tb_order (ORD_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_EVALUATION -( - EVAL_ID VARCHAR(255) NOT NULL, - EVAL_TTL VARCHAR(255) NOT NULL, - EVAL_CONT VARCHAR(255) NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - CENT_ID VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - WRI_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (EVAL_ID), - FOREIGN KEY (CENT_ID) REFERENCES tb_center (CENT_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), - FOREIGN KEY (WRI_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_promotion -( - PROM_ID VARCHAR(255) NOT NULL, - PROM_TTL VARCHAR(255) NOT NULL, - PROM_CONT VARCHAR(255) NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (PROM_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_file -( - FILE_ID VARCHAR(255) NOT NULL, - FILE_NAME VARCHAR(255) NOT NULL, - FILE_URL VARCHAR(255) NOT NULL, - FILE_TYPE VARCHAR(255) NOT NULL, - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - CREATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - MEM_ID VARCHAR(255) NULL, - NOT_ID VARCHAR(255) NULL, - CONR_ID VARCHAR(255) NULL, - PROB_ID VARCHAR(255) NULL, - CENT_ID VARCHAR(255) NULL, - PROM_ID VARCHAR(255) NULL, - EVAL_ID VARCHAR(255) NULL, - CONR_ID2 VARCHAR(255) NULL, - PRIMARY KEY (FILE_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), - FOREIGN KEY (NOT_ID) REFERENCES tb_notice (NOT_ID), - FOREIGN KEY (CONR_ID) REFERENCES tb_contract (CONR_ID), - FOREIGN KEY (PROB_ID) REFERENCES tb_problem (PROB_ID), - FOREIGN KEY (CENT_ID) REFERENCES tb_center (CENT_ID), - FOREIGN KEY (PROM_ID) REFERENCES tb_promotion (PROM_ID), - FOREIGN KEY (EVAL_ID) REFERENCES tb_evaluation (EVAL_ID), - FOREIGN KEY (CONR_ID2) REFERENCES tb_contract (CONR_ID) -); - -CREATE TABLE tb_schedule -( - SCH_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', - SCH_NAME VARCHAR(255) NOT NULL, - SCH_CONT VARCHAR(255) NOT NULL, - SCH_TAG VARCHAR(255) NOT NULL, - SCH_SRT_AT CHAR(19) NOT NULL, - SCH_END_AT CHAR(19) NOT NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - DELETED_AT CHAR(19) NULL, - ACTIVE BOOLEAN NOT NULL COMMENT 'TRUE/FALSE', - MEM_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', - PRIMARY KEY (SCH_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_alarm -( - ALR_ID VARCHAR(255) NOT NULL, - ALR_MSG VARCHAR(255) NOT NULL, - ALR_URL VARCHAR(255) NOT NULL, - ALR_READ_STAT BOOLEAN NOT NULL DEFAULT FALSE, - CREATED_AT CHAR(19) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (ALR_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) -); - -CREATE TABLE tb_family -( - FAM_ID VARCHAR(255) NOT NULL, - FAM_NAME VARCHAR(255) NOT NULL, - FAM_REL VARCHAR(255) NOT NULL, - FAM_BIR VARCHAR(255) NOT NULL, - FAM_IDEN_NO VARCHAR(255) NOT NULL, - FAM_PHO VARCHAR(255) NOT NULL, - FAM_SEX VARCHAR(255) NOT NULL, - FAM_DIS BOOLEAN NOT NULL, - FAM_DIE BOOLEAN NOT NULL, - FAM_NOTE VARCHAR(255) NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (FAM_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) -); - -CREATE TABLE tb_education -( - EDU_ID VARCHAR(255) NOT NULL, - EDU_ENTD VARCHAR(255) NOT NULL, - EDU_GRAD VARCHAR(255) NOT NULL, - EDU_NAME VARCHAR(255) NOT NULL, - EDU_MJR VARCHAR(255) NOT NULL, - EDU_SCO VARCHAR(255) NULL, - EDU_NOTE VARCHAR(255) NULL, - CREATED_AT CHAR(19) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (EDU_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) -); - -CREATE TABLE tb_certification -( - CER_ID VARCHAR(255) NOT NULL, - CER_DATE VARCHAR(255) NOT NULL, - CER_INST VARCHAR(255) NOT NULL, - CER_NAME VARCHAR(255) NOT NULL, - CER_SCO VARCHAR(255) NOT NULL, - CER_NOTE VARCHAR(255) NULL, - CREATED_AT CHAR(19) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (CER_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) -); - -CREATE TABLE tb_career -( - CAR_ID VARCHAR(255) NOT NULL, - CAR_EMP_DATE VARCHAR(255) NOT NULL, - CAR_RTR_DATE VARCHAR(255) NULL, - CAR_NAME VARCHAR(255) NOT NULL, - CAR_NOTE VARCHAR(255) NULL, - CREATED_AT CHAR(19) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (CAR_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member(MEM_ID) -); - -CREATE TABLE tb_UPDATE_HISTORY -( - UPD_ID VARCHAR(255) NOT NULL, - UPD_IP VARCHAR(255) NOT NULL, - UPDATED_AT CHAR(19) NOT NULL, - UPDATED_URL VARCHAR(255) NOT NULL, - MEM_ID VARCHAR(255) NOT NULL, - CONR_ID VARCHAR(255) NOT NULL, - PRIMARY KEY (UPD_ID), - FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID), - FOREIGN KEY (CONR_ID) REFERENCES tb_contract (CONR_ID) -); - -CREATE TABLE tb_PRODUCT_OPTION -( - PROD_ID VARCHAR(255) NOT NULL, - PROD_SER_NO VARCHAR(255) NOT NULL, - OPT_CNTY CHAR(1) NOT NULL DEFAULT 'K', - OPT_MNFR CHAR(1) NOT NULL DEFAULT 'N', - OPT_VHC_TYPE CHAR(1) NOT NULL, - OPT_CHSS CHAR(1) NOT NULL, - OPT_DTIL_TYPE CHAR(1) NOT NULL, - OPT_BODY_TYPE CHAR(1) NOT NULL, - OPT_SFTY_DVCE CHAR(1) NOT NULL, - OPT_ENGN_CPCT CHAR(1) NOT NULL, - OPT_SCRT_CODE CHAR(1) NOT NULL, - OPT_PRDC_YEAR CHAR(1) NOT NULL, - OPT_PRDC_PLNT CHAR(1) NOT NULL, - OPT_ENGN CHAR(1) NOT NULL DEFAULT '0', - OPT_MSSN CHAR(1) NOT NULL DEFAULT '0', - OPT_TRIM CHAR(1) NOT NULL DEFAULT '0', - OPT_XTNL_COLR CHAR(1) NOT NULL, - OPT_ITNL_COLR CHAR(1) NOT NULL, - OPT_HUD CHAR(1) NOT NULL DEFAULT '0', - OPT_NAVI CHAR(1) NOT NULL DEFAULT '0', - OPT_DRVE_WISE CHAR(1) NOT NULL DEFAULT '0', - OPT_SMRT_CNCT CHAR(1) NOT NULL DEFAULT '0', - OPT_STYL CHAR(1) NOT NULL DEFAULT '0', - OPT_MY_CFRT_PCKG CHAR(1) NOT NULL DEFAULT '0', - OPT_OTDR_PCKG CHAR(1) NOT NULL DEFAULT '0', - OPT_SUN_ROOF CHAR(1) NOT NULL DEFAULT '0', - OPT_SOND CHAR(1) NOT NULL DEFAULT '0', - ACTIVE BOOLEAN NOT NULL DEFAULT TRUE, - PRIMARY KEY (PROD_ID, PROD_SER_NO), - FOREIGN KEY (PROD_ID, PROD_SER_NO) REFERENCES tb_product (PROD_ID, PROD_SER_NO) ON DELETE CASCADE -); - -CREATE TABLE tb_sales_history -( - SAL_HIST_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고', - CONR_ID VARCHAR(255) NOT NULL COMMENT 'Comment 1번 참고' -); - -INSERT INTO tb_organization_chart (ORG_CHA_ID, ORG_CHA_NAME, ORG_CHA_DEPTH) -VALUES ('ORG_000000001', '본사', 1), - ('ORG_000000002', '서울지사', 2), - ('ORG_000000003', '부산지사', 2), - ('ORG_000000004', '인천지사', 2), - ('ORG_000000005', '대전지사', 2), - ('ORG_000000006', '영업부', 3), - ('ORG_000000007', '기술부', 3), - ('ORG_000000008', '고객지원부', 3), - ('ORG_000000009', '마케팅부', 3), - ('ORG_000000010', '재무부', 3); - -INSERT INTO tb_center (CENT_ID, CENT_NAME, CENT_ADR, CENT_PHO, CENT_MEM_CNT, CREATED_AT, UPDATED_AT, ACTIVE, - CENT_OPR_AT) -VALUES ('CEN_000000001', '서울센터', '서울특별시 중구', '010-1234-5678', 50, '2024-01-01 12:00:00', '2024-01-01 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000002', '부산센터', '부산광역시 해운대구', '010-2345-6789', 40, '2024-01-02 12:00:00', '2024-01-02 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000003', '대구센터', '대구광역시', '010-3456-7890', 30, '2024-01-03 12:00:00', '2024-01-03 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000004', '인천센터', '인천광역시', '010-4567-8901', 20, '2024-01-04 12:00:00', '2024-01-04 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000005', '울산센터', '울산광역시', '010-5678-9012', 25, '2024-01-05 12:00:00', '2024-01-05 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000006', '대전센터', '대전광역시', '010-6789-0123', 35, '2024-01-06 12:00:00', '2024-01-06 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000007', '광주센터', '광주광역시', '010-7890-1234', 45, '2024-01-07 12:00:00', '2024-01-07 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000008', '경기센터', '경기도 수원시', '010-8901-2345', 20, '2024-01-08 12:00:00', '2024-01-08 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000009', '청주센터', '충청북도 청주시', '010-9012-3456', 10, '2024-01-09 12:00:00', '2024-01-09 12:00:00', TRUE, - '09:00-18:00'), - ('CEN_000000010', '전주센터', '전라북도 전주시', '010-0123-4567', 15, '2024-01-10 12:00:00', '2024-01-10 12:00:00', TRUE, - '09:00-18:00'); - -INSERT INTO tb_member (MEM_ID, MEM_LOGIN_ID, MEM_PWD, MEM_NAME, MEM_EMA, MEM_AGE, MEM_SEX, MEM_IDEN_NO, MEM_PHO, MEM_ADR, - MEM_POS, MEM_GRD, MEM_JOB_TYPE, CENTER_ID, ORG_CHA_ID, CREATED_AT, UPDATED_AT, ACTIVE) -VALUES ('MEM_000000001', 101, 'pwd1234', '김철수', 'chulsoo@example.com', 35, 'MALE', '123456-7890123', '010-9876-5432', - '서울특별시', 'Manager', 'Bachelor', 'Full-time', 'CEN_000000001', 'ORG_000000006', '2024-01-01 12:00:00', - '2024-01-01 12:00:00', TRUE), - ('MEM_000000002', 102, 'pwd2345', '이영희', 'younghee@example.com', 28, 'FEMALE', '234567-8901234', '010-8765-4321', - '부산광역시', 'Staff', 'Master', 'Full-time', 'CEN_000000002', 'ORG_000000006', '2024-01-02 12:00:00', - '2024-01-02 12:00:00', TRUE), - ('MEM_000000003', 103, 'pwd3456', '박지훈', 'jihun@example.com', 42, 'MALE', '345678-9012345', '010-7654-3210', - '대구광역시', 'Engineer', 'Doctorate', 'Contract', 'CEN_000000003', 'ORG_000000007', '2024-01-03 12:00:00', - '2024-01-03 12:00:00', TRUE), - ('MEM_000000004', 104, 'pwd4567', '최민수', 'minsoo@example.com', 38, 'MALE', '456789-0123456', '010-6543-2109', - '인천광역시', 'Manager', 'Bachelor', 'Part-time', 'CEN_000000004', 'ORG_000000007', '2024-01-04 12:00:00', - '2024-01-04 12:00:00', TRUE), - ('MEM_000000005', 105, 'pwd5678', '박미영', 'miyoung@example.com', 29, 'FEMALE', '567890-1234567', '010-5432-1098', - '울산광역시', 'Intern', 'High School', 'Full-time', 'CEN_000000005', 'ORG_000000008', '2024-01-05 12:00:00', - '2024-01-05 12:00:00', TRUE), - ('MEM_000000006', 106, 'pwd6789', '정수현', 'soohyun@example.com', 31, 'FEMALE', '678901-2345678', '010-4321-0987', - '대전광역시', 'Senior Engineer', 'Master', 'Full-time', 'CEN_000000006', 'ORG_000000007', '2024-01-06 12:00:00', - '2024-01-06 12:00:00', TRUE), - ('MEM_000000007', 107, 'pwd7890', '한지민', 'jimin@example.com', 45, 'FEMALE', '789012-3456789', '010-3210-9876', - '광주광역시', 'Sales Rep', 'Bachelor', 'Part-time', 'CEN_000000007', 'ORG_000000006', '2024-01-07 12:00:00', - '2024-01-07 12:00:00', TRUE), - ('MEM_000000008', 108, 'pwd8901', '이준호', 'junho@example.com', 36, 'MALE', '890123-4567890', '010-2109-8765', - '경기도 수원시', 'Technician', 'Bachelor', 'Full-time', 'CEN_000000008', 'ORG_000000008', '2024-01-08 12:00:00', - '2024-01-08 12:00:00', TRUE); - -INSERT INTO tb_member_role (MEM_ROL_ID, MEM_ROL_NAME, MEM_ID) -VALUES ('MEM_ROL_000000001', '영업 사원', 'MEM_000000001'), - ('MEM_ROL_000000002', '영업 사원', 'MEM_000000002'), - ('MEM_ROL_000000003', '영업 사원', 'MEM_000000003'), - ('MEM_ROL_000000004', '영업 사원', 'MEM_000000004'), - ('MEM_ROL_000000005', '영업 관리자', 'MEM_000000005'), - ('MEM_ROL_000000006', '영업 관리자', 'MEM_000000009'), - ('MEM_ROL_000000007', '영업 관리자', 'MEM_000000007'), - ('MEM_ROL_000000008', '영업 담당자', 'MEM_000000008'), - ('MEM_ROL_000000009', '영업 담당자', 'MEM_000000010'), - ('MEM_ROL_000000010', '영업 담당자', 'MEM_000000006'); - -INSERT INTO tb_customer_info (CUST_ID, CUST_NAME, CUST_AGE, CUST_SEX, CUST_PHO, CUST_EMER_PHO, CUST_EMA, ACTIVE, MEM_ID, CREATED_AT, UPDATED_AT, DELETED_AT) -VALUES ('CUS_000000001', '홍길동', 45, 'MALE', '010-1111-2222', '010-2222-3333', 'gildong@example.com', TRUE, - 'MEM_000000001', '2024-01-01 10:00:00', '2024-01-01 10:00:00', NULL), - ('CUS_000000002', '김민수', 38, 'MALE', '010-3333-4444', '010-4444-5555', 'minsoo@example.com', TRUE, - 'MEM_000000002', '2024-01-02 10:00:00', '2024-01-02 10:00:00', NULL), - ('CUS_000000003', '이영희', 32, 'FEMALE', '010-5555-6666', '010-6666-7777', 'younghee@example.com', TRUE, - 'MEM_000000003', '2024-01-03 10:00:00', '2024-01-03 10:00:00', NULL), - ('CUS_000000004', '박지훈', 27, 'MALE', '010-7777-8888', '010-8888-9999', 'jihun@example.com', TRUE, - 'MEM_000000004', '2024-01-04 10:00:00', '2024-01-04 10:00:00', NULL), - ('CUS_000000005', '최정민', 22, 'FEMALE', '010-9999-0000', '010-0000-1111', 'jungmin@example.com', TRUE, - 'MEM_000000005', '2024-01-05 10:00:00', '2024-01-05 10:00:00', NULL), - ('CUS_000000006', '정수민', 31, 'MALE', '010-1212-3434', '010-2323-4545', 'suming@example.com', TRUE, - 'MEM_000000006', '2024-01-06 10:00:00', '2024-01-06 10:00:00', NULL), - ('CUS_000000007', '한민정', 29, 'FEMALE', '010-4545-5656', '010-5656-6767', 'hanmj@example.com', TRUE, - 'MEM_000000007', '2024-01-07 10:00:00', '2024-01-07 10:00:00', NULL), - ('CUS_000000008', '이동수', 41, 'MALE', '010-6767-7878', '010-7878-8989', 'dongsu@example.com', TRUE, - 'MEM_000000008', '2024-01-08 10:00:00', '2024-01-08 10:00:00', NULL), - ('CUS_000000009', '윤소라', 33, 'FEMALE', '010-8989-9090', '010-9090-1010', 'sora@example.com', TRUE, - 'MEM_000000009', '2024-01-09 10:00:00', '2024-01-09 10:00:00', NULL), - ('CUS_000000010', '박성훈', 36, 'MALE', '010-1010-1112', '010-1212-1313', 'seonghun@example.com', TRUE, - 'MEM_000000010', '2024-01-10 10:00:00', '2024-01-10 10:00:00', NULL); - - -INSERT INTO tb_product (PROD_ID, PROD_SER_NO, PROD_COST, PROD_NAME, PROD_STCK, CREATED_AT, UPDATED_AT, ACTIVE) -VALUES ('PRO_000000001', 'KNAHAA4AALU1A00001', 25000000, '쏘렌토', 10, '2024-01-10 10:00:00', '2024-01-10 11:00:00', TRUE), - ('PRO_000000002', 'KNAHBA4BALR2Z00002', 22000000, '스포티지', 15, '2024-01-11 11:00:00', '2024-01-11 12:00:00', - TRUE), - ('PRO_000000003', 'KMBHC64CAMJ5A00003', 28000000, 'K7', 8, '2024-01-12 12:00:00', '2024-01-12 13:00:00', TRUE), - ('PRO_000000004', 'KNJFA42DALU3C00004', 19000000, '셀토스', 12, '2024-01-13 13:00:00', '2024-01-13 14:00:00', TRUE), - ('PRO_000000005', 'KFBGBM5EARP1M00005', 18000000, 'K3', 20, '2024-01-14 14:00:00', '2024-01-14 15:00:00', TRUE), - ('PRO_000000006', 'KNHGA6BALUP7A00006', 34000000, '모하비', 7, '2024-01-15 15:00:00', '2024-01-15 16:00:00', TRUE), - ('PRO_000000007', 'KNJFA34AALU4Z00007', 32000000, 'K8', 5, '2024-01-16 16:00:00', '2024-01-16 17:00:00', TRUE), - ('PRO_000000008', 'KMAHDA2AAMJ3T00008', 27000000, '스팅어', 9, '2024-01-17 17:00:00', '2024-01-17 18:00:00', TRUE), - ('PRO_000000009', 'KNAHCA5BALU5C00009', 23000000, '니로', 14, '2024-01-18 18:00:00', '2024-01-18 19:00:00', TRUE), - ('PRO_000000010', 'KNFHC54CAMR1A00010', 28000000, 'K5', 6, '2024-01-19 19:00:00', '2024-01-19 20:00:00', TRUE); - - -INSERT INTO tb_contract (CONR_ID, CONR_NAME, CONR_CUST_NAME, CONR_CUST_IDEN_NO, CONR_CUST_ADR, - CONR_CUST_EMA, CONR_CUST_PHO, CONR_COMP_NAME, CONR_CUST_CLA, CONR_CUST_PUR_COND, - CONR_SERI_NUM, CONR_SELE_OPTI, CONR_DOWN_PAY, CONR_INTE_PAY, CONR_REM_PAY, - CONR_CONS_PAY, CONR_DELV_DATE, CONR_DELV_LOC, CONR_STAT, CONR_NO_OF_VEH, - CREATED_URL, DELETED_URL, ACTIVE, CREATED_AT, UPDATED_AT, DELETED_AT, - MEM_ID, CENT_ID, CUST_ID, PROD_ID) -VALUES --- 계약 1 -('CON_000000001', '쏘렌토 계약', '박지훈', '880512-1234567', '서울특별시 강남구', - 'jihun@example.com', '010-1234-5678', '기아자동차', 'PERSONAL', 'CASH', - 'SER_001', '풀옵션', 5000000, 300000, 25000000, 30000000, - '2024-02-20', '서울특별시 강남구 배송센터', 'WAIT', 1, - '/contracts/1', NULL, TRUE, '2024-01-10 12:00:00', '2024-01-11 13:00:00', NULL, - 'MEM_000000001', 'CEN_000000001', 'CUS_000000001', 'PRO_000000001'), - --- 계약 2 -('CON_000000002', '스포티지 계약', '김영희', '900123-2345678', '부산광역시 해운대구', - 'younghee@example.com', '010-2345-6789', '기아자동차', 'PERSONAL', 'CREDIT', - 'SER_002', '기본옵션', 3000000, 200000, 17000000, 25000000, - '2024-03-05', '부산광역시 해운대구 배송센터', 'CONFIRMED', 1, - '/contracts/2', NULL, TRUE, '2024-01-12 14:00:00', '2024-01-13 15:00:00', NULL, - 'MEM_000000002', 'CEN_000000002', 'CUS_000000002', 'PRO_000000002'), - --- 계약 3 -('CON_000000003', 'K7 계약', '이철수', '950405-3456789', '대구광역시 수성구', - 'chulsoo@example.com', '010-3456-7890', '기아자동차', 'BUSINESS', 'LEASE', - 'SER_003', '풀옵션', 8000000, 400000, 28000000, 35000000, - '2024-03-10', '대구광역시 배송센터', 'WAIT', 2, - '/contracts/3', NULL, TRUE, '2024-01-14 09:00:00', '2024-01-15 10:00:00', NULL, - 'MEM_000000003', 'CEN_000000003', 'CUS_000000003', 'PRO_000000003'), - --- 계약 4 -('CON_000000004', '셀토스 계약', '박민준', '970512-4567890', '인천광역시 미추홀구', - 'minjoon@example.com', '010-4567-8901', '기아자동차', 'PERSONAL', 'CASH', - 'SER_004', '기본옵션', 4000000, 250000, 12000000, 20000000, - '2024-04-01', '인천광역시 배송센터', 'DELIVERED', 1, - '/contracts/4', NULL, TRUE, '2024-01-16 11:00:00', '2024-01-17 12:00:00', NULL, - 'MEM_000000004', 'CEN_000000004', 'CUS_000000004', 'PRO_000000004'), - --- 계약 5 -('CON_000000005', 'K3 계약', '최민수', '980618-5678901', '울산광역시 중구', - 'minsoo@example.com', '010-5678-9012', '기아자동차', 'BUSINESS', 'INSTALLMENT', - 'SER_005', '기본옵션', 2000000, 100000, 13000000, 18000000, - '2024-04-15', '울산광역시 배송센터', 'CANCELLED', 1, - '/contracts/5', NULL, TRUE, '2024-01-18 13:00:00', '2024-01-19 14:00:00', NULL, - 'MEM_000000005', 'CEN_000000005', 'CUS_000000005', 'PRO_000000005'), - --- 계약 6 -('CON_000000006', '모하비 계약', '김민주', '850824-6789012', '대전광역시 서구', - 'minju@example.com', '010-6789-0123', '기아자동차', 'PERSONAL', 'CASH', - 'SER_006', '풀옵션', 6000000, 500000, 34000000, 40000000, - '2024-05-01', '대전광역시 배송센터', 'CONFIRMED', 1, - '/contracts/6', NULL, TRUE, '2024-01-20 15:00:00', '2024-01-21 16:00:00', NULL, - 'MEM_000000006', 'CEN_000000006', 'CUS_000000006', 'PRO_000000006'), - --- 계약 7 -('CON_000000007', 'K8 계약', '정수영', '940705-7890123', '광주광역시 북구', - 'suyoung@example.com', '010-7890-1234', '기아자동차', 'LEASE', 'LEASE', - 'SER_007', '럭셔리 패키지', 7000000, 350000, 25000000, 32000000, - '2024-05-20', '광주광역시 배송센터', 'WAIT', 1, - '/contracts/7', NULL, TRUE, '2024-01-22 17:00:00', '2024-01-23 18:00:00', NULL, - 'MEM_000000007', 'CEN_000000007', 'CUS_000000007', 'PRO_000000007'), - --- 계약 8 -('CON_000000008', '스팅어 계약', '이준호', '930910-8901234', '경기도 수원시', - 'junho@example.com', '010-8901-2345', '기아자동차', 'PERSONAL', 'CREDIT', - 'SER_008', '풀옵션', 5000000, 300000, 22000000, 27000000, - '2024-06-01', '수원시 배송센터', 'DELIVERED', 1, - '/contracts/8', NULL, TRUE, '2024-01-24 19:00:00', '2024-01-25 20:00:00', NULL, - 'MEM_000000008', 'CEN_000000008', 'CUS_000000008', 'PRO_000000008'), - --- 계약 9 -('CON_000000009', '니로 계약', '박소영', '890321-9012345', '충청북도 청주시', - 'soyoung@example.com', '010-9012-3456', '기아자동차', 'PERSONAL', 'CASH', - 'SER_009', '기본옵션', 3500000, 150000, 17000000, 23000000, - '2024-06-15', '청주시 배송센터', 'CONFIRMED', 1, - '/contracts/9', NULL, TRUE, '2024-01-26 21:00:00', '2024-01-27 22:00:00', NULL, - 'MEM_000000009', 'CEN_000000009', 'CUS_000000009', 'PRO_000000009'), - --- 계약 10 -('CON_000000010', 'K5 계약', '김정훈', '900815-0123456', '전라북도 전주시', - 'junghoon@example.com', '010-0123-4567', '기아자동차', 'BUSINESS', 'LEASE', - 'SER_010', '풀옵션', 6000000, 250000, 20000000, 28000000, - '2024-07-01', '전주시 배송센터', 'WAIT', 1, - '/contracts/10', NULL, TRUE, '2024-01-28 23:00:00', '2024-01-29 23:59:00', NULL, - 'MEM_000000010', 'CEN_000000010', 'CUS_000000010', 'PRO_000000010'); - -INSERT INTO tb_order ( - ORD_ID, ORD_TTL, ORD_CONT, ACTIVE, CREATED_AT, UPDATED_AT, DELETED_AT, ORD_STAT, CONR_ID, MEM_ID, ADMIN_ID -) VALUES --- 주문 1 -('ORD_000000001', '쏘렌토 계약 주문', '쏘렌토 차량 계약 완료 후 주문 처리', TRUE, '2024-01-10 10:00:00', '2024-01-10 11:00:00', NULL, 'CONFIRMED', 'CON_000000001', 'MEM_000000001', 'MEM_000000005'), --- 주문 2 -('ORD_000000002', '스포티지 구매 주문', '스포티지 고객 상담 후 주문 확정', TRUE, '2024-01-11 12:00:00', '2024-01-11 13:00:00', NULL, 'WAIT', 'CON_000000002', 'MEM_000000002', 'MEM_000000005'), --- 주문 3 -('ORD_000000003', 'K7 리스 주문', 'K7 리스 계약 완료 후 주문 생성', TRUE, '2024-01-12 09:00:00', '2024-01-12 10:00:00', NULL, 'CONFIRMED', 'CON_000000003', 'MEM_000000003', 'MEM_000000005'), --- 주문 4 -('ORD_000000004', '셀토스 추가 주문', '셀토스 고객 추가 옵션 주문', TRUE, '2024-01-13 10:30:00', '2024-01-13 11:30:00', NULL, 'CANCELLED', 'CON_000000004', 'MEM_000000004', 'MEM_000000005'), --- 주문 5 -('ORD_000000005', 'K3 계약 주문', 'K3 차량 계약 후 최종 주문', TRUE, '2024-01-14 11:00:00', '2024-01-14 12:00:00', NULL, 'DELIVERED', 'CON_000000005', 'MEM_000000001', 'MEM_000000006'), --- 주문 6 -('ORD_000000006', '모하비 계약 수정 주문', '모하비 계약 변경에 따른 주문 수정', TRUE, '2024-01-15 13:00:00', '2024-01-15 14:00:00', NULL, 'WAIT', 'CON_000000006', 'MEM_000000002', 'MEM_000000006'), --- 주문 7 -('ORD_000000007', 'K8 리스 주문', 'K8 차량 리스 계약 주문', TRUE, '2024-01-16 14:30:00', '2024-01-16 15:30:00', NULL, 'CONFIRMED', 'CON_000000007', 'MEM_000000003', 'MEM_000000006'), --- 주문 8 -('ORD_000000008', '스팅어 구매 주문', '스팅어 차량 구매 계약 후 주문 확정', TRUE, '2024-01-17 16:00:00', '2024-01-17 17:00:00', NULL, 'DELIVERED', 'CON_000000008', 'MEM_000000004', 'MEM_000000006'), --- 주문 9 -('ORD_000000009', '니로 전기차 주문', '니로 전기차 계약 주문', TRUE, '2024-01-18 17:30:00', '2024-01-18 18:30:00', NULL, 'WAIT', 'CON_000000009', 'MEM_000000001', 'MEM_000000010'), --- 주문 10 -('ORD_000000010', 'K5 재고 주문', 'K5 재고 차량 추가 주문', TRUE, '2024-01-19 09:00:00', '2024-01-19 10:00:00', NULL, 'CONFIRMED', 'CON_000000010', 'MEM_000000001', 'MEM_00000009'); - - -INSERT INTO tb_problem (PROB_ID, PROB_TTL, PROB_CONT, CREATED_AT, UPDATED_AT, ACTIVE, CUST_ID, MEM_ID, PROD_ID) -VALUES ('PROB_000000001', '쏘렌토 엔진 문제', '엔진에서 소음 발생', '2024-01-12 14:00:00', '2024-01-12 15:00:00', TRUE, - 'CUS_000000001', 'MEM_000000001', 'PRO_000000001'), - ('PROB_000000002', '스포티지 브레이크 문제', '브레이크 페달 작동 불량', '2024-01-13 10:00:00', '2024-01-13 11:00:00', TRUE, - 'CUS_000000002', 'MEM_000000002', 'PRO_000000002'), - ('PROB_000000003', 'K7 전자 장비 문제', '내비게이션 오류', '2024-01-14 09:00:00', '2024-01-14 10:00:00', TRUE, - 'CUS_000000003', 'MEM_000000003', 'PRO_000000003'), - ('PROB_000000004', '셀토스 에어컨 문제', '에어컨 작동 안됨', '2024-01-15 11:00:00', '2024-01-15 12:00:00', TRUE, - 'CUS_000000004', 'MEM_000000004', 'PRO_000000004'), - ('PROB_000000005', 'K3 연비 문제', '연비가 예상보다 낮음', '2024-01-16 12:00:00', '2024-01-16 13:00:00', TRUE, - 'CUS_000000005', 'MEM_000000005', 'PRO_000000005'), - ('PROB_000000006', '모하비 변속기 문제', '변속기 오류 발생', '2024-01-17 13:00:00', '2024-01-17 14:00:00', TRUE, - 'CUS_000000006', 'MEM_000000006', 'PRO_000000006'), - ('PROB_000000007', 'K8 배터리 문제', '배터리 수명 짧음', '2024-01-18 14:00:00', '2024-01-18 15:00:00', TRUE, 'CUS_000000007', - 'MEM_000000007', 'PRO_000000007'), - ('PROB_000000008', '스팅어 오디오 문제', '오디오 스피커 잡음', '2024-01-19 15:00:00', '2024-01-19 16:00:00', TRUE, - 'CUS_000000008', 'MEM_000000008', 'PRO_000000008'), - ('PROB_000000009', '니로 전기 문제', '충전 불량', '2024-01-20 16:00:00', '2024-01-20 17:00:00', TRUE, 'CUS_000000009', - 'MEM_000000009', 'PRO_000000009'), - ('PROB_000000010', 'K5 시동 문제', '시동이 걸리지 않음', '2024-01-21 17:00:00', '2024-01-21 18:00:00', TRUE, 'CUS_000000010', - 'MEM_000000010', 'PRO_000000010'); - -INSERT INTO tb_purchase_order (PUR_ORD_ID, PUR_ORD_TTL, PUR_CONT, CREATED_AT, UPDATED_AT, ACTIVE, PUR_ORD_STAT, ORD_ID, - MEM_ID) -VALUES ('PUR_000000001', '쏘렌토 부품 주문', '엔진 부품 요청', '2024-01-12 10:00:00', '2024-01-12 11:00:00', TRUE, 'WAIT', - 'ORD_000000001', 'MEM_000000001'), - ('PUR_000000002', '스포티지 타이어 주문', '타이어 4개 요청', '2024-01-13 12:00:00', '2024-01-13 13:00:00', TRUE, 'CONFIRMED', - 'ORD_000000002', 'MEM_000000002'), - ('PUR_000000003', 'K7 배터리 주문', '배터리 교체 요청', '2024-01-14 13:00:00', '2024-01-14 14:00:00', TRUE, 'DELIVERED', - 'ORD_000000003', 'MEM_000000003'), - ('PUR_000000004', '셀토스 브레이크 주문', '브레이크 패드 요청', '2024-01-15 14:00:00', '2024-01-15 15:00:00', TRUE, 'WAIT', - 'ORD_000000004', 'MEM_000000004'), - ('PUR_000000005', 'K3 내비게이션 주문', '내비게이션 교체', '2024-01-16 15:00:00', '2024-01-16 16:00:00', TRUE, 'CANCELLED', - 'ORD_000000005', 'MEM_000000005'), - ('PUR_000000006', '모하비 오일 필터 주문', '오일 필터 10개 요청', '2024-01-17 09:00:00', '2024-01-17 10:00:00', TRUE, - 'CONFIRMED', 'ORD_000000006', 'MEM_000000006'), - ('PUR_000000007', 'K8 헤드라이트 주문', 'LED 헤드라이트 2개 요청', '2024-01-18 10:00:00', '2024-01-18 11:00:00', TRUE, 'WAIT', - 'ORD_000000007', 'MEM_000000007'), - ('PUR_000000008', '스팅어 도어 핸들 주문', '도어 핸들 4개 요청', '2024-01-19 12:00:00', '2024-01-19 13:00:00', TRUE, 'CONFIRMED', - 'ORD_000000008', 'MEM_000000008'), - ('PUR_000000009', '니로 충전 케이블 주문', '전기차 충전 케이블 요청', '2024-01-20 14:00:00', '2024-01-20 15:00:00', TRUE, - 'DELIVERED', 'ORD_000000009', 'MEM_000000009'), - ('PUR_000000010', 'K5 엔진오일 주문', '엔진오일 20L 요청', '2024-01-21 15:00:00', '2024-01-21 16:00:00', TRUE, 'WAIT', - 'ORD_000000010', 'MEM_000000010'); - -INSERT INTO tb_notice (NOT_ID, NOT_TTL, NOT_TAG, NOT_CLA, NOT_CONT, CREATED_AT, UPDATED_AT, ACTIVE, MEM_ID) -VALUES ('NOT_000000001', '신차 출시 공지', 'ALL', 'IMPORTANT', '신형 쏘렌토가 출시되었습니다.', '2024-01-10 09:00:00', - '2024-01-10 09:30:00', TRUE, 'MEM_000000001'), - ('NOT_000000002', '정기 점검 공지', 'SERVICE', 'NORMAL', '1월 정기 점검 안내입니다.', '2024-01-11 10:00:00', - '2024-01-11 10:30:00', TRUE, 'MEM_000000002'), - ('NOT_000000003', '할인 프로모션', 'SALES', 'IMPORTANT', 'K5 한정 할인 프로모션 안내', '2024-01-12 11:00:00', - '2024-01-12 11:30:00', TRUE, 'MEM_000000003'), - ('NOT_000000004', '서비스 센터 이전 안내', 'SERVICE', 'NORMAL', '서울센터가 이전되었습니다.', '2024-01-13 12:00:00', - '2024-01-13 12:30:00', TRUE, 'MEM_000000004'), - ('NOT_000000005', '부품 재고 부족', 'ALL', 'WARNING', '모하비 부품 재고가 부족합니다.', '2024-01-14 13:00:00', - '2024-01-14 13:30:00', TRUE, 'MEM_000000005'), - ('NOT_000000006', '설 연휴 휴무 안내', 'ALL', 'NORMAL', '설 연휴 기간 동안 휴무합니다.', '2024-01-15 14:00:00', - '2024-01-15 14:30:00', TRUE, 'MEM_000000006'), - ('NOT_000000007', 'K8 시승 이벤트', 'EVENT', 'NORMAL', 'K8 시승 이벤트에 참여하세요.', '2024-01-16 15:00:00', - '2024-01-16 15:30:00', TRUE, 'MEM_000000007'), - ('NOT_000000008', '스포티지 리콜 안내', 'SERVICE', 'CRITICAL', '스포티지 일부 모델 리콜 안내', '2024-01-17 16:00:00', - '2024-01-17 16:30:00', TRUE, 'MEM_000000008'), - ('NOT_000000009', '신입사원 모집', 'HR', 'NORMAL', '기아자동차 신입사원 모집 공고', '2024-01-18 17:00:00', '2024-01-18 17:30:00', - TRUE, 'MEM_000000009'), - ('NOT_000000010', '고객 감사 이벤트', 'EVENT', 'NORMAL', '고객 감사 사은품 증정 이벤트', '2024-01-19 18:00:00', - '2024-01-19 18:30:00', TRUE, 'MEM_000000010'); - -INSERT INTO tb_file (FILE_ID, FILE_NAME, FILE_URL, FILE_TYPE, ACTIVE, CREATED_AT, MEM_ID, NOT_ID) -VALUES ('FIL_000000001', '쏘렌토_계약서.pdf', '/files/contract1.pdf', 'pdf', TRUE, '2024-01-10 10:00:00', 'MEM_000000001', - 'NOT_000000001'), - ('FIL_000000002', '스포티지_메뉴얼.pdf', '/files/manual2.pdf', 'pdf', TRUE, '2024-01-11 10:30:00', 'MEM_000000002', - 'NOT_000000002'), - ('FIL_000000003', 'K7_견적서.pdf', '/files/estimate3.pdf', 'pdf', TRUE, '2024-01-12 11:00:00', 'MEM_000000003', - 'NOT_000000003'), - ('FIL_000000004', '셀토스_리콜_공지.pdf', '/files/recall4.pdf', 'pdf', TRUE, '2024-01-13 12:00:00', 'MEM_000000004', - 'NOT_000000004'), - ('FIL_000000005', 'K3_정비보고서.pdf', '/files/report5.pdf', 'pdf', TRUE, '2024-01-14 13:00:00', 'MEM_000000005', - 'NOT_000000005'), - ('FIL_000000006', '모하비_오일교환.pdf', '/files/oil6.pdf', 'pdf', TRUE, '2024-01-15 14:00:00', 'MEM_000000006', - 'NOT_000000006'), - ('FIL_000000007', 'K8_이벤트_포스터.png', '/files/event7.png', 'image', TRUE, '2024-01-16 15:00:00', 'MEM_000000007', - 'NOT_000000007'), - ('FIL_000000008', '스팅어_사용설명서.pdf', '/files/manual8.pdf', 'pdf', TRUE, '2024-01-17 16:00:00', 'MEM_000000008', - 'NOT_000000008'), - ('FIL_000000009', '니로_충전가이드.pdf', '/files/guide9.pdf', 'pdf', TRUE, '2024-01-18 17:00:00', 'MEM_000000009', - 'NOT_000000009'), - ('FIL_000000010', 'K5_고객이벤트_배너.jpg', '/files/banner10.jpg', 'image', TRUE, '2024-01-19 18:00:00', - 'MEM_000000010', 'NOT_000000010'); - -INSERT INTO tb_schedule (SCH_ID, SCH_NAME, SCH_CONT, SCH_TAG, SCH_SRT_AT, SCH_END_AT, CREATED_AT, UPDATED_AT, DELETED_AT, ACTIVE, MEM_ID) -VALUES - ('SCH_000000001', '쏘렌토 신차 발표회', '신차 발표회 준비 및 진행', 'MEETING', '2024-11-01 09:00:00', '2024-11-01 12:00:00', - '2024-01-20 10:00:00', '2024-01-20 10:30:00', NULL, TRUE, 'MEM_000000001'), - ('SCH_000000002', '스포티지 고객 상담', '신차 구매 고객 상담 일정', 'CONSULTATION', '2024-11-05 10:00:00', '2024-11-05 11:30:00', - '2024-01-21 11:00:00', '2024-01-21 11:30:00', NULL, TRUE, 'MEM_000000002'), - ('SCH_000000003', 'K7 프로모션 회의', '프로모션 기획 및 준비 회의', 'MEETING', '2024-11-10 13:00:00', '2024-11-10 15:00:00', - '2024-01-22 12:00:00', '2024-01-22 12:30:00', NULL, TRUE, 'MEM_000000003'), - ('SCH_000000004', '셀토스 시승 이벤트', '고객 대상 시승 이벤트', 'CONSULTATION', '2024-11-12 14:00:00', '2024-11-12 17:00:00', - '2024-01-23 13:00:00', '2024-01-23 13:30:00', NULL, TRUE, 'MEM_000000004'), - ('SCH_000000005', 'K3 서비스 교육', '서비스 센터 직원 교육', 'TRAINING', '2024-11-15 09:00:00', '2024-11-15 12:00:00', - '2024-01-24 14:00:00', '2024-01-24 14:30:00', NULL, TRUE, 'MEM_000000005'), - ('SCH_000000006', '모하비 유지보수 회의', '유지보수 전략 논의', 'MEETING', '2024-11-17 10:00:00', '2024-11-17 11:00:00', - '2024-01-25 15:00:00', '2024-01-25 15:30:00', NULL, TRUE, 'MEM_000000006'), - ('SCH_000000007', 'K8 내부 검토', '신차 내부 디자인 검토', 'MEETING', '2024-11-20 11:00:00', '2024-11-20 13:00:00', - '2024-01-26 16:00:00', '2024-01-26 16:30:00', NULL, TRUE, 'MEM_000000007'), - ('SCH_000000008', '스팅어 기술 세미나', '스팅어 기술 세미나 및 발표', 'TRAINING', '2024-11-22 14:00:00', '2024-11-22 16:00:00', - '2024-01-27 17:00:00', '2024-01-27 17:30:00', NULL, TRUE, 'MEM_000000008'), - ('SCH_000000009', '연말 휴가', '연말 휴가 계획', 'VACATION', '2024-11-25 09:00:00', '2024-11-25 17:00:00', - '2024-01-28 18:00:00', '2024-01-28 18:30:00', NULL, TRUE, 'MEM_000000009'), - ('SCH_000000010', 'K5 재고 점검', 'K5 재고 관리 및 점검', 'MEETING', '2024-11-28 15:00:00', '2024-11-28 17:00:00', - '2024-01-29 19:00:00', '2024-01-29 19:30:00', NULL, TRUE, 'MEM_000000010'); - -INSERT INTO tb_alarm (ALR_ID, ALR_MSG, ALR_URL, ALR_READ_STAT, CREATED_AT, MEM_ID) -VALUES ('ALR_000000001', '신차 출시 공지 알림', '/notices/1', FALSE, '2024-01-20 12:00:00', 'MEM_000000001'), - ('ALR_000000002', '스포티지 리콜 안내', '/notices/2', FALSE, '2024-01-21 14:00:00', 'MEM_000000002'), - ('ALR_000000003', 'K7 고객 이벤트 초대', '/events/3', TRUE, '2024-01-22 09:00:00', 'MEM_000000003'), - ('ALR_000000004', '셀토스 정비 완료', '/service/4', FALSE, '2024-01-23 10:00:00', 'MEM_000000004'), - ('ALR_000000005', 'K3 테스트 드라이브 일정 변경', '/schedules/5', TRUE, '2024-01-24 11:00:00', 'MEM_000000005'), - ('ALR_000000006', '모하비 부품 입고 알림', '/inventory/6', FALSE, '2024-01-25 12:00:00', 'MEM_000000006'), - ('ALR_000000007', 'K8 디자인 변경 확정', '/design/7', TRUE, '2024-01-26 13:00:00', 'MEM_000000007'), - ('ALR_000000008', '스팅어 성능 테스트 보고서', '/reports/8', FALSE, '2024-01-27 14:00:00', 'MEM_000000008'), - ('ALR_000000009', '니로 전기차 충전소 위치', '/maps/9', TRUE, '2024-01-28 15:00:00', 'MEM_000000009'), - ('ALR_000000010', 'K5 재고 부족 경고', '/inventory/10', FALSE, '2024-01-29 16:00:00', 'MEM_000000010'); - -INSERT INTO tb_family (FAM_ID, FAM_NAME, FAM_REL, FAM_BIR, FAM_IDEN_NO, FAM_PHO, FAM_SEX, FAM_DIS, FAM_DIE, FAM_NOTE, CREATED_AT, UPDATED_AT, MEM_ID) -VALUES - ('FAM_000000001', '김영희', '배우자', '1988-03-25', '880325-1234567', '010-1234-5678', 'FEMALE', FALSE, FALSE, NULL, '2024-01-10 10:00:00', '2024-01-10 11:00:00', 'MEM_000000001'), - ('FAM_000000002', '이수진', '자녀', '2015-05-12', '150512-2345678', '010-2345-6789', 'FEMALE', FALSE, FALSE, '초등학교 3학년', '2024-01-11 12:00:00', '2024-01-11 13:00:00', 'MEM_000000002'), - ('FAM_000000003', '박철수', '배우자', '1985-07-11', '850711-3456789', '010-3456-7890', 'MALE', FALSE, FALSE, NULL, '2024-01-12 14:00:00', '2024-01-12 15:00:00', 'MEM_000000003'), - ('FAM_000000004', '최민수', '자녀', '2012-11-03', '121103-4567890', '010-4567-8901', 'MALE', TRUE, FALSE, '특수 교육 필요', '2024-01-13 09:00:00', '2024-01-13 10:00:00', 'MEM_000000004'), - ('FAM_000000005', '정수정', '배우자', '1990-09-20', '900920-5678901', '010-5678-9012', 'FEMALE', FALSE, FALSE, NULL, '2024-01-14 11:00:00', '2024-01-14 12:00:00', 'MEM_000000005'), - ('FAM_000000006', '김지훈', '자녀', '2010-06-15', '100615-6789012', '010-6789-0123', 'MALE', FALSE, FALSE, '중학교 1학년', '2024-01-15 13:00:00', '2024-01-15 14:00:00', 'MEM_000000006'), - ('FAM_000000007', '한지민', '배우자', '1987-02-28', '870228-7890123', '010-7890-1234', 'FEMALE', FALSE, FALSE, NULL, '2024-01-16 15:00:00', '2024-01-16 16:00:00', 'MEM_000000007'), - ('FAM_000000008', '이동수', '자녀', '2017-12-10', '171210-8901234', '010-8901-2345', 'MALE', FALSE, FALSE, '유치원생', '2024-01-17 17:00:00', '2024-01-17 18:00:00', 'MEM_000000008'), - ('FAM_000000009', '윤소희', '배우자', '1989-04-03', '890403-9012345', '010-9012-3456', 'FEMALE', FALSE, FALSE, NULL, '2024-01-18 19:00:00', '2024-01-18 20:00:00', 'MEM_000000009'), - ('FAM_000000010', '박성준', '자녀', '2014-08-25', '140825-0123456', '010-0123-4567', 'MALE', FALSE, FALSE, '초등학교 5학년', '2024-01-19 09:00:00', '2024-01-19 10:00:00', 'MEM_000000010'); - -INSERT INTO tb_education (EDU_ID, EDU_ENTD, EDU_GRAD, EDU_NAME, EDU_MJR, EDU_SCO, EDU_NOTE, CREATED_AT, UPDATED_AT, MEM_ID) -VALUES - ('EDU_000000001', '2006-03-01', '2010-02-28', '서울대학교', '경영학', '3.8', NULL, '2024-01-10 10:00:00', '2024-01-10 11:00:00', 'MEM_000000001'), - ('EDU_000000002', '2008-03-01', '2012-02-28', '연세대학교', '경제학', '3.5', '졸업 논문 우수상', '2024-01-11 12:00:00', '2024-01-11 13:00:00', 'MEM_000000002'), - ('EDU_000000003', '2005-03-01', '2009-02-28', '고려대학교', '컴퓨터공학', '3.9', NULL, '2024-01-12 14:00:00', '2024-01-12 15:00:00', 'MEM_000000003'), - ('EDU_000000004', '2010-03-01', '2014-02-28', '서강대학교', '심리학', '3.6', '사회봉사 활동 참여', '2024-01-13 09:00:00', '2024-01-13 10:00:00', 'MEM_000000004'), - ('EDU_000000005', '2007-03-01', '2011-02-28', '성균관대학교', '화학공학', '3.7', NULL, '2024-01-14 11:00:00', '2024-01-14 12:00:00', 'MEM_000000005'), - ('EDU_000000006', '2009-03-01', '2013-02-28', '한양대학교', '전자공학', '4.0', '학과 대표', '2024-01-15 13:00:00', '2024-01-15 14:00:00', 'MEM_000000006'), - ('EDU_000000007', '2004-03-01', '2008-02-28', '이화여자대학교', '디자인', '3.8', '졸업 작품전 참여', '2024-01-16 15:00:00', '2024-01-16 16:00:00', 'MEM_000000007'), - ('EDU_000000008', '2011-03-01', '2015-02-28', '중앙대학교', '물리학', '3.4', NULL, '2024-01-17 17:00:00', '2024-01-17 18:00:00', 'MEM_000000008'), - ('EDU_000000009', '2003-03-01', '2007-02-28', '부산대학교', '수학', '3.9', '우수 장학금 수상', '2024-01-18 19:00:00', '2024-01-18 20:00:00', 'MEM_000000009'), - ('EDU_000000010', '2012-03-01', '2016-02-28', '경북대학교', '환경공학', '3.6', NULL, '2024-01-19 09:00:00', '2024-01-19 10:00:00', 'MEM_000000010'); - -INSERT INTO tb_certification (CER_ID, CER_DATE, CER_INST, CER_NAME, CER_SCO, CER_NOTE, CREATED_AT, MEM_ID) -VALUES - ('CER_000000001', '2018-06-20', '한국무역협회', '무역영어 1급', 'PASS', NULL, '2024-01-10 10:00:00', 'MEM_000000001'), - ('CER_000000002', '2019-03-15', '한국산업인력공단', '정보처리기사', 'PASS', '필기 및 실기 합격', '2024-01-11 12:00:00', 'MEM_000000002'), - ('CER_000000003', '2017-11-05', '대한상공회의소', '전산회계 2급', '90', NULL, '2024-01-12 14:00:00', 'MEM_000000003'), - ('CER_000000004', '2020-08-10', '국제공인회계사협회', 'CPA', 'PASS', '국제 회계 자격증', '2024-01-13 09:00:00', 'MEM_000000004'), - ('CER_000000005', '2016-02-22', '대한건설협회', '건축기사', 'PASS', NULL, '2024-01-14 11:00:00', 'MEM_000000005'), - ('CER_000000006', '2021-05-30', '한국능률협회', 'PMP', 'PASS', '프로젝트 관리 자격증', '2024-01-15 13:00:00', 'MEM_000000006'), - ('CER_000000007', '2015-12-12', '한국관광공사', '국내여행안내사', '85', NULL, '2024-01-16 15:00:00', 'MEM_000000007'), - ('CER_000000008', '2018-09-18', '한국소방안전협회', '소방안전관리자 1급', 'PASS', NULL, '2024-01-17 17:00:00', 'MEM_000000008'), - ('CER_000000009', '2022-04-25', '대한적십자사', '응급처치 강사', 'PASS', '응급처치 교육 수료', '2024-01-18 19:00:00', 'MEM_000000009'), - ('CER_000000010', '2019-11-11', '한국정보통신기술협회', '정보보안기사', 'PASS', NULL, '2024-01-19 09:00:00', 'MEM_000000010'); - -INSERT INTO tb_career (CAR_ID, CAR_EMP_DATE, CAR_RTR_DATE, CAR_NAME, CAR_NOTE, CREATED_AT, MEM_ID) -VALUES - ('CAR_000000001', '2012-03-01', '2016-12-31', '삼성전자', '마케팅팀 근무', '2024-01-10 10:00:00', 'MEM_000000001'), - ('CAR_000000002', '2015-05-01', '2019-08-31', 'LG화학', '연구개발팀 근무', '2024-01-11 12:00:00', 'MEM_000000002'), - ('CAR_000000003', '2010-09-01', '2015-02-28', '포스코', '공정관리팀', '2024-01-12 14:00:00', 'MEM_000000003'), - ('CAR_000000004', '2013-01-01', '2018-03-31', '현대자동차', '생산기술팀', '2024-01-13 09:00:00', 'MEM_000000004'), - ('CAR_000000005', '2017-06-01', '2021-11-30', 'SK텔레콤', '네트워크 운영팀', '2024-01-14 11:00:00', 'MEM_000000005'), - ('CAR_000000006', '2011-02-01', '2016-07-31', '네이버', '프론트엔드 개발자', '2024-01-15 13:00:00', 'MEM_000000006'), - ('CAR_000000007', '2014-04-01', '2019-12-31', '카카오', '백엔드 개발자', '2024-01-16 15:00:00', 'MEM_000000007'), - ('CAR_000000008', '2018-01-01', '2022-06-30', 'CJ제일제당', '품질관리팀', '2024-01-17 17:00:00', 'MEM_000000008'), - ('CAR_000000009', '2010-07-01', '2014-09-30', '롯데케미칼', '안전관리팀', '2024-01-18 19:00:00', 'MEM_000000009'), - ('CAR_000000010', '2016-03-01', '2020-10-31', '한화생명', '재무팀', '2024-01-19 09:00:00', 'MEM_000000010'); - -INSERT INTO tb_update_history (UPD_ID, UPD_IP, UPDATED_AT, UPDATED_URL, MEM_ID, CONR_ID) -VALUES ('UPD_000000001', '192.168.1.10', '2024-01-10 12:00:00', '/contracts/1', 'MEM_000000001', 'CON_000000001'), - ('UPD_000000002', '192.168.1.20', '2024-01-11 14:00:00', '/contracts/2', 'MEM_000000002', 'CON_000000002'), - ('UPD_000000003', '192.168.1.30', '2024-01-12 16:00:00', '/contracts/3', 'MEM_000000003', 'CON_000000003'), - ('UPD_000000004', '192.168.1.40', '2024-01-13 09:00:00', '/contracts/4', 'MEM_000000004', 'CON_000000004'), - ('UPD_000000005', '192.168.1.50', '2024-01-14 10:00:00', '/contracts/5', 'MEM_000000005', 'CON_000000005'), - ('UPD_000000006', '192.168.1.60', '2024-01-15 11:00:00', '/contracts/6', 'MEM_000000006', 'CON_000000006'), - ('UPD_000000007', '192.168.1.70', '2024-01-16 12:00:00', '/contracts/7', 'MEM_000000007', 'CON_000000007'), - ('UPD_000000008', '192.168.1.80', '2024-01-17 13:00:00', '/contracts/8', 'MEM_000000008', 'CON_000000008'), - ('UPD_000000009', '192.168.1.90', '2024-01-18 14:00:00', '/contracts/9', 'MEM_000000009', 'CON_000000009'), - ('UPD_000000010', '192.168.1.100', '2024-01-19 15:00:00', '/contracts/10', 'MEM_000000010', 'CON_000000010'); - -INSERT INTO tb_evaluation (EVAL_ID, EVAL_TTL, EVAL_CONT, CREATED_AT, UPDATED_AT, ACTIVE, CENT_ID, MEM_ID, WRI_ID) -VALUES ('EVAL_000000001', '쏘렌토 성능 평가', '쏘렌토의 성능에 대한 평가입니다.', '2024-01-20 10:00:00', '2024-01-21 10:30:00', TRUE, - 'CEN_000000001', 'MEM_000000001', 'MEM_000000002'), - ('EVAL_000000002', '스포티지 안전 평가', '스포티지의 안전성 평가 보고서.', '2024-01-21 11:00:00', '2024-01-22 11:30:00', TRUE, - 'CEN_000000002', 'MEM_000000002', 'MEM_000000003'), - ('EVAL_000000003', 'K7 고객 만족도 평가', '고객 만족도 조사 결과입니다.', '2024-01-23 12:00:00', '2024-01-24 12:30:00', TRUE, - 'CEN_000000003', 'MEM_000000003', 'MEM_000000004'), - ('EVAL_000000004', '셀토스 디자인 평가', '셀토스 디자인 피드백.', '2024-01-25 13:00:00', '2024-01-26 13:30:00', TRUE, - 'CEN_000000004', 'MEM_000000004', 'MEM_000000005'), - ('EVAL_000000005', 'K3 내부 공간 평가', '내부 공간의 효율성 평가.', '2024-01-27 14:00:00', '2024-01-28 14:30:00', TRUE, - 'CEN_000000005', 'MEM_000000005', 'MEM_000000006'), - ('EVAL_000000006', '모하비 유지비 평가', '유지 비용과 효율성 평가.', '2024-01-29 15:00:00', '2024-01-30 15:30:00', TRUE, - 'CEN_000000006', 'MEM_000000006', 'MEM_000000007'), - ('EVAL_000000007', 'K8 디자인 리뉴얼', '새로운 디자인에 대한 평가.', '2024-02-01 16:00:00', '2024-02-02 16:30:00', TRUE, - 'CEN_000000007', 'MEM_000000007', 'MEM_000000008'), - ('EVAL_000000008', '스팅어 출력 평가', '스팅어의 출력 성능 보고서.', '2024-02-03 17:00:00', '2024-02-04 17:30:00', TRUE, - 'CEN_000000008', 'MEM_000000008', 'MEM_000000009'), - ('EVAL_000000009', '니로 전기차 평가', '전기차 효율성 평가 결과.', '2024-02-05 18:00:00', '2024-02-06 18:30:00', TRUE, - 'CEN_000000009', 'MEM_000000009', 'MEM_000000010'), - ('EVAL_000000010', 'K5 연비 평가', 'K5의 연비 효율성 보고서.', '2024-02-07 19:00:00', '2024-02-08 19:30:00', TRUE, - 'CEN_000000010', 'MEM_000000010', 'MEM_000000001'); - -INSERT INTO tb_promotion (PROM_ID, PROM_TTL, PROM_CONT, CREATED_AT, UPDATED_AT, ACTIVE, MEM_ID) -VALUES ('PROM_000000001', '쏘렌토 겨울 특별 할인', '쏘렌토 구매 시 15% 할인 제공', '2024-01-05 09:00:00', '2024-01-06 10:00:00', TRUE, - 'MEM_000000001'), - ('PROM_000000002', '스포티지 리스 프로모션', '리스 계약 시 추가 혜택 제공', '2024-01-10 11:00:00', '2024-01-10 12:00:00', TRUE, - 'MEM_000000002'), - ('PROM_000000003', 'K7 고객 감사 이벤트', 'K7 구매 고객 대상 사은품 증정', '2024-01-15 14:00:00', '2024-01-15 15:00:00', TRUE, - 'MEM_000000003'), - ('PROM_000000004', '셀토스 한정 프로모션', '셀토스 구매 시 무료 보증 연장', '2024-01-20 09:00:00', '2024-01-20 10:00:00', TRUE, - 'MEM_000000004'), - ('PROM_000000005', 'K3 연비 보장 이벤트', 'K3 연비 테스트 이벤트 참여 시 혜택 제공', '2024-01-25 13:00:00', '2024-01-25 14:00:00', - TRUE, 'MEM_000000005'), - ('PROM_000000006', '모하비 프리미엄 서비스', '모하비 구매 고객 대상 프리미엄 서비스 제공', '2024-01-30 11:00:00', '2024-01-31 12:00:00', - TRUE, 'MEM_000000006'), - ('PROM_000000007', 'K8 럭셔리 패키지 할인', '럭셔리 패키지 선택 시 10% 할인 제공', '2024-02-01 12:00:00', '2024-02-02 13:00:00', TRUE, - 'MEM_000000007'), - ('PROM_000000008', '스팅어 시승 이벤트', '스팅어 시승 후 계약 시 추가 혜택', '2024-02-05 10:00:00', '2024-02-05 11:00:00', TRUE, - 'MEM_000000008'), - ('PROM_000000009', '니로 전기차 구매 지원', '전기차 구매 시 충전기 설치 지원', '2024-02-10 09:00:00', '2024-02-10 10:00:00', TRUE, - 'MEM_000000009'), - ('PROM_000000010', 'K5 재고 한정 특별 할인', '재고 한정 K5 모델에 대한 추가 할인 제공', '2024-02-15 15:00:00', '2024-02-16 16:00:00', - TRUE, 'MEM_000000010'); - -INSERT INTO tb_product_option (PROD_ID, PROD_SER_NO, OPT_CNTY, OPT_MNFR, OPT_VHC_TYPE, OPT_CHSS, OPT_DTIL_TYPE, - OPT_BODY_TYPE, - OPT_SFTY_DVCE, OPT_ENGN_CPCT, OPT_SCRT_CODE, OPT_PRDC_YEAR, OPT_PRDC_PLNT, OPT_ENGN, - OPT_MSSN, - OPT_TRIM, OPT_XTNL_COLR, OPT_ITNL_COLR, OPT_HUD, OPT_NAVI, OPT_DRVE_WISE, OPT_SMRT_CNCT, - OPT_STYL, OPT_MY_CFRT_PCKG, OPT_OTDR_PCKG, OPT_SUN_ROOF, OPT_SOND, ACTIVE) -VALUES --- 데이터 1 -('PRO_000000001', 'KNAHAA4AALU1A00001', 'K', 'N', 'A', 'A', 'L', '4', '2', 'A', 'P', 'A', 'U', '1', '0', '1', 'B', 'W', - '1', '1', '1', '1', '1', '0', '1', '1', '1', TRUE), --- 데이터 2 -('PRO_000000002', 'KNAHBA4BALR2Z00002', 'K', 'N', 'H', 'B', 'L', '4', '4', 'B', 'R', 'B', 'Z', '1', '1', '0', 'G', 'B', - '0', '1', '0', '1', '0', '1', '0', '0', '1', TRUE), --- 데이터 3 -('PRO_000000003', 'KMBHC64CAMJ5A00003', 'K', 'M', 'H', 'C', 'M', '6', '3', 'C', 'P', 'C', 'A', '0', '1', '1', 'R', 'G', - '1', '1', '1', '0', '1', '1', '0', '1', '0', TRUE), --- 데이터 4 -('PRO_000000004', 'KNJFA42DALU3C00004', 'K', 'N', 'J', 'D', 'L', '2', '4', 'A', 'R', 'D', 'C', '1', '0', '0', 'B', 'R', - '0', '0', '0', '1', '0', '0', '1', '0', '1', TRUE), --- 데이터 5 -('PRO_000000005', 'KFBGBM5EARP1M00005', 'K', 'B', 'F', 'B', 'M', '5', '1', 'B', 'P', 'E', 'M', '1', '1', '1', 'W', 'B', - '1', '1', '0', '0', '1', '1', '0', '1', '1', TRUE), --- 데이터 6 -('PRO_000000006', 'KNHGA6BALUP7A00006', 'K', 'N', 'H', 'G', 'L', '6', '3', 'A', 'R', 'F', 'A', '0', '0', '1', 'B', 'G', - '1', '1', '1', '1', '0', '1', '1', '0', '0', TRUE), --- 데이터 7 -('PRO_000000007', 'KNJFA34AALU4Z00007', 'K', 'N', 'J', 'A', 'M', '4', '4', 'C', 'P', 'G', 'Z', '1', '1', '0', 'R', 'W', - '0', '0', '1', '0', '1', '0', '1', '1', '1', TRUE), --- 데이터 8 -('PRO_000000008', 'KMAHDA2AAMJ3T00008', 'K', 'M', 'H', 'D', 'L', '2', '2', 'A', 'R', 'H', 'T', '0', '0', '1', 'B', 'G', - '1', '1', '0', '1', '0', '1', '1', '0', '0', TRUE), --- 데이터 9 -('PRO_000000009', 'KNAHCA5BALU5C00009', 'K', 'N', 'A', 'C', 'M', '5', '1', 'B', 'P', 'J', 'C', '1', '1', '0', 'R', 'R', - '0', '1', '1', '1', '1', '0', '1', '1', '0', TRUE), --- 데이터 10 -('PRO_000000010', 'KNFHC54CAMR1A00010', 'K', 'N', 'F', 'F', 'N', '4', '3', 'C', 'R', 'K', 'A', '0', '1', '1', 'W', 'B', - '1', '1', '0', '0', '0', '1', '0', '1', '1', TRUE); - -INSERT INTO tb_sales_history (SAL_HIST_ID, CONR_ID) -VALUES ('SAL_000000001', 'CON_000000001'), - ('SAL_000000002', 'CON_000000002'), - ('SAL_000000003', 'CON_000000003'), - ('SAL_000000004', 'CON_000000004'), - ('SAL_000000005', 'CON_000000005'), - ('SAL_000000006', 'CON_000000006'), - ('SAL_000000007', 'CON_000000007'), - ('SAL_000000008', 'CON_000000008'), - ('SAL_000000009', 'CON_000000009'), - ('SAL_000000010', 'CON_000000010'); - - diff --git a/src/main/resources/stanl_2/final_backend/domain/career/query/repository/CareerMapper.xml b/src/main/resources/stanl_2/final_backend/domain/career/query/repository/CareerMapper.xml index 247a90fa..8f4e08f5 100644 --- a/src/main/resources/stanl_2/final_backend/domain/career/query/repository/CareerMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/career/query/repository/CareerMapper.xml @@ -12,12 +12,12 @@ \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.xml b/src/main/resources/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.xml index 71f155d0..361542c9 100644 --- a/src/main/resources/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.xml @@ -13,13 +13,13 @@ \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml b/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml index 65d1432e..17f0c25f 100644 --- a/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml @@ -28,87 +28,86 @@ - \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/education/query/repository/EducationMapper.xml b/src/main/resources/stanl_2/final_backend/domain/education/query/repository/EducationMapper.xml index 6e9b3812..67598006 100644 --- a/src/main/resources/stanl_2/final_backend/domain/education/query/repository/EducationMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/education/query/repository/EducationMapper.xml @@ -13,14 +13,14 @@ \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/family/query/repository/FamilyMapper.xml b/src/main/resources/stanl_2/final_backend/domain/family/query/repository/FamilyMapper.xml index 3790ebdf..a45cca05 100644 --- a/src/main/resources/stanl_2/final_backend/domain/family/query/repository/FamilyMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/family/query/repository/FamilyMapper.xml @@ -16,18 +16,18 @@ \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/AuthMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/AuthMapper.xml index cecda79d..eca37ff2 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/AuthMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/AuthMapper.xml @@ -6,8 +6,8 @@ \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml index 8b2632d0..20ad9185 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml @@ -24,24 +24,24 @@ \ No newline at end of file From d0fa2fdebd13bce80b74cf417b88d0e7a44bee3b Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Tue, 19 Nov 2024 16:43:09 +0900 Subject: [PATCH 199/563] =?UTF-8?q?fix:=20yml=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/query/service/AuthQueryServiceImpl.java | 2 +- src/main/resources/application.yml | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java index e54a72c3..413f579d 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java @@ -22,7 +22,7 @@ public AuthQueryServiceImpl(AuthMapper authMapper) { @Override @Transactional public String selectMemberIdByLoginId(String loginId){ - + String id = authMapper.selectIdByMemberName(loginId); if(id == null){ diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8028244e..eee7170c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -7,12 +7,9 @@ spring: datasource: driver-class-name: org.mariadb.jdbc.Driver - # url: jdbc:mariadb://${DATABASE_URL}:${MARIA_DATABASE_PORT}/${MARIA_DATABASE_NAME} - url: jdbc:mariadb://motive.cdw8kw8go27z.ap-northeast-2.rds.amazonaws.com:3306/motive - username: admin - password: motivepassword - # username: ${DB_USERNAME} - # password: ${DB_PASSWORD} + url: jdbc:mariadb://${DATABASE_HOST}:${MARIA_DATABASE_PORT}/${MARIA_DATABASE_NAME} + username: ${DB_USERNAME} + password: ${DB_PASSWORD} profiles: active: ${SPRING_PROFILES_ACTIVE} From 6d914c2823fce2d6ed9c679795acec537b0b25b3 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 16:53:10 +0900 Subject: [PATCH 200/563] =?UTF-8?q?fix:=20Notice=20=EA=B6=8C=ED=95=9C=20?= =?UTF-8?q?=EB=B6=80=EC=97=AC(#92)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/NoticeController.java | 24 +++-- .../application/dto/NoticeDeleteDTO.java | 15 ++++ .../application/dto/NoticeModifyDTO.java | 6 +- .../application/dto/NoticeRegistDTO.java | 2 + .../service/NoticeCommandService.java | 4 +- .../domain/repository/NoticeRepository.java | 4 + .../service/NoticeCommandServiceImpl.java | 90 +++++++++++++++---- .../common/exception/NoticeErrorCode.java | 5 ++ 8 files changed, 122 insertions(+), 28 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeDeleteDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java index de142480..9ac74191 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java @@ -9,10 +9,12 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.notices.command.application.dto.NoticeDeleteDTO; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeModifyDTO; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; import stanl_2.final_backend.domain.notices.command.application.service.NoticeCommandService; import stanl_2.final_backend.domain.notices.common.response.NoticeResponseMessage; +import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleDeleteDTO; import java.security.Principal; @@ -51,11 +53,15 @@ public ResponseEntity postNotice(@RequestBody NoticeRegis @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = NoticeResponseMessage.class))}) }) - @PutMapping("{id}") - public ResponseEntity modifyNotice(@PathVariable String id, + @PutMapping("{noticeId}") + public ResponseEntity modifyNotice(Principal principal, + @PathVariable String noticeId, @RequestBody NoticeModifyDTO noticeModifyRequestDTO){ + String memberLoginId = principal.getName(); + noticeModifyRequestDTO.setMemberLoginId(memberLoginId); + noticeModifyRequestDTO.setNoticeId(noticeId); - NoticeModifyDTO noticeModifyDTO = noticeCommandService.modifyNotice(id,noticeModifyRequestDTO); + NoticeModifyDTO noticeModifyDTO = noticeCommandService.modifyNotice(noticeId,noticeModifyRequestDTO); return ResponseEntity.ok(NoticeResponseMessage.builder() .httpStatus(200) @@ -69,10 +75,16 @@ public ResponseEntity modifyNotice(@PathVariable String i @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = NoticeResponseMessage.class))}) }) - @DeleteMapping("{id}") - public ResponseEntity deleteNotice(@PathVariable String id) { + @DeleteMapping("{noticeId}") + public ResponseEntity deleteNotice(Principal principal, + @PathVariable String noticeId) { - noticeCommandService.deleteNotice(id); + String memberLoginId = principal.getName(); + NoticeDeleteDTO noticeDeleteDTO = new NoticeDeleteDTO(); + noticeDeleteDTO.setMemberLoginId(memberLoginId); + noticeDeleteDTO.setNoticeId(noticeId); + + noticeCommandService.deleteNotice(noticeDeleteDTO); return ResponseEntity.ok(NoticeResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeDeleteDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeDeleteDTO.java new file mode 100644 index 00000000..6ad98a1d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeDeleteDTO.java @@ -0,0 +1,15 @@ +package stanl_2.final_backend.domain.notices.command.application.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class NoticeDeleteDTO { + private String NoticeId; + private String memberLoginId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java index e4695591..8c860781 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java @@ -10,11 +10,11 @@ @Setter @Getter public class NoticeModifyDTO { + private String noticeId; private String title; - private String tag; - + private String memberId; private String classification; - private String content; + private String memberLoginId; } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java index 766989d3..1c50cc0e 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java @@ -18,5 +18,7 @@ public class NoticeRegistDTO { private String content; + private String memberLoginId; + private String memberId; } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/service/NoticeCommandService.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/service/NoticeCommandService.java index f0b51c66..cec6dad6 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/service/NoticeCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/service/NoticeCommandService.java @@ -1,12 +1,14 @@ package stanl_2.final_backend.domain.notices.command.application.service; +import stanl_2.final_backend.domain.notices.command.application.dto.NoticeDeleteDTO; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeModifyDTO; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; +import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleDeleteDTO; public interface NoticeCommandService { void registerNotice(NoticeRegistDTO noticeRegistDTO); NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO); - void deleteNotice(String id); + void deleteNotice(NoticeDeleteDTO noticeDeleteDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/repository/NoticeRepository.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/repository/NoticeRepository.java index 4c96292b..e856b47f 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/repository/NoticeRepository.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/repository/NoticeRepository.java @@ -5,6 +5,10 @@ import org.springframework.data.jpa.repository.JpaRepository; import stanl_2.final_backend.domain.notices.command.domain.aggragate.entity.Notice; +import java.util.Optional; + public interface NoticeRepository extends JpaRepository { Page findAll(Pageable pageable); + + Optional findByNoticeId(String noticeId); } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java index fa3c3b32..4eefe494 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java @@ -2,8 +2,11 @@ import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.notices.command.application.dto.NoticeDeleteDTO; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeModifyDTO; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; import stanl_2.final_backend.domain.notices.command.application.service.NoticeCommandService; @@ -11,6 +14,10 @@ import stanl_2.final_backend.domain.notices.command.domain.repository.NoticeRepository; import stanl_2.final_backend.domain.notices.common.exception.NoticeCommonException; import stanl_2.final_backend.domain.notices.common.exception.NoticeErrorCode; +import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleDeleteDTO; +import stanl_2.final_backend.domain.schedule.command.domain.aggregate.entity.Schedule; +import stanl_2.final_backend.domain.schedule.common.exception.ScheduleCommonException; +import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -21,12 +28,15 @@ public class NoticeCommandServiceImpl implements NoticeCommandService { private final NoticeRepository noticeRepository; + private final AuthQueryService authQueryService; + private final ModelMapper modelMapper; @Autowired - public NoticeCommandServiceImpl(NoticeRepository noticeRepository, ModelMapper modelMapper) { + public NoticeCommandServiceImpl(NoticeRepository noticeRepository, ModelMapper modelMapper, AuthQueryService authQueryService) { this.noticeRepository = noticeRepository; this.modelMapper = modelMapper; + this.authQueryService =authQueryService; } private String getCurrentTimestamp() { @@ -37,39 +47,83 @@ private String getCurrentTimestamp() { @Override @Transactional public void registerNotice(NoticeRegistDTO noticeRegistDTO) { - Notice notice =modelMapper.map(noticeRegistDTO,Notice.class); - noticeRepository.save(notice); + String memberId = authQueryService.selectMemberIdByLoginId(noticeRegistDTO.getMemberLoginId()); + noticeRegistDTO.setMemberId(memberId); + + try { + Notice notice = modelMapper.map(noticeRegistDTO, Notice.class); + + noticeRepository.save(notice); + + } catch (DataIntegrityViolationException e){ + // DB 무결정 제약 조건 (NOT NULL, UNIQUE) 위반 + throw new NoticeCommonException(NoticeErrorCode.DATA_INTEGRITY_VIOLATION); + } catch (Exception e) { + // 서버 오류 + throw new NoticeCommonException(NoticeErrorCode.INTERNAL_SERVER_ERROR); + } } @Override @Transactional public NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO) { + String memberId = authQueryService.selectMemberIdByLoginId(noticeModifyDTO.getMemberLoginId()); + noticeModifyDTO.setMemberId(memberId); + Notice notice = noticeRepository.findById(id) .orElseThrow(() -> new NoticeCommonException(NoticeErrorCode.NOTICE_NOT_FOUND)); + if(!noticeModifyDTO.getMemberId().equals(notice.getMemberId())){ + // 권한 오류 + throw new ScheduleCommonException(ScheduleErrorCode.AUTHORIZATION_VIOLATION); + } + try { + Notice updateNotice = modelMapper.map(noticeModifyDTO, Notice.class); + updateNotice.setNoticeId(notice.getNoticeId()); + updateNotice.setMemberId(notice.getMemberId()); + updateNotice.setCreatedAt(notice.getCreatedAt()); + updateNotice.setActive(notice.getActive()); + + noticeRepository.save(updateNotice); + + NoticeModifyDTO noticeModify = modelMapper.map(updateNotice,NoticeModifyDTO.class); + + return noticeModify; + } catch (DataIntegrityViolationException e) { + // 데이터 무결성 위반 예외 처리 + throw new NoticeCommonException(NoticeErrorCode.DATA_INTEGRITY_VIOLATION); + } catch (Exception e) { + // 서버 오류 + throw new NoticeCommonException(NoticeErrorCode.INTERNAL_SERVER_ERROR); + } + } - Notice updateNotice = modelMapper.map(noticeModifyDTO, Notice.class); - updateNotice.setNoticeId(notice.getNoticeId()); - updateNotice.setMemberId(notice.getMemberId()); - updateNotice.setCreatedAt(notice.getCreatedAt()); - updateNotice.setActive(notice.getActive()); - noticeRepository.save(updateNotice); + @Override + @Transactional + public void deleteNotice(NoticeDeleteDTO noticeDeleteDTO) { - NoticeModifyDTO noticeModify = modelMapper.map(updateNotice,NoticeModifyDTO.class); + String memberId = authQueryService.selectMemberIdByLoginId(noticeDeleteDTO.getMemberLoginId()); - return noticeModify; - } + Notice notice = noticeRepository.findByNoticeId(noticeDeleteDTO.getNoticeId()) + .orElseThrow(() -> new NoticeCommonException(NoticeErrorCode.NOTICE_NOT_FOUND)); - @Override - @Transactional - public void deleteNotice(String id) { - Notice notice = noticeRepository.findById(id) - .orElseThrow(()-> new NoticeCommonException(NoticeErrorCode.NOTICE_NOT_FOUND)); + if(!memberId.equals(notice.getMemberId())){ + // 권한 오류 + throw new NoticeCommonException(NoticeErrorCode.AUTHORIZATION_VIOLATION); + } notice.setActive(false); notice.setDeletedAt(getCurrentTimestamp()); - noticeRepository.save(notice); + try { + noticeRepository.save(notice); + } catch (DataIntegrityViolationException e) { + // 데이터 무결성 위반 예외 처리 + throw new ScheduleCommonException(ScheduleErrorCode.DATA_INTEGRITY_VIOLATION); + } catch (Exception e) { + // 서버 오류 + throw new ScheduleCommonException(ScheduleErrorCode.INTERNAL_SERVER_ERROR); + } } } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/common/exception/NoticeErrorCode.java b/src/main/java/stanl_2/final_backend/domain/notices/common/exception/NoticeErrorCode.java index f693efe3..6f90d956 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/common/exception/NoticeErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/common/exception/NoticeErrorCode.java @@ -39,6 +39,11 @@ public enum NoticeErrorCode { * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. */ NOTICE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "notice 데이터를 찾지 못했습니다"), + + DATA_INTEGRITY_VIOLATION(40002, HttpStatus.BAD_REQUEST, "데이터 무결 위반하였습니다."), + + AUTHORIZATION_VIOLATION(40002, HttpStatus.BAD_REQUEST, "접근 권한이 없습니다."), + /** * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. */ From fa54d84e4005cb7788a5d732eaa165491805447d Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Tue, 19 Nov 2024 17:10:57 +0900 Subject: [PATCH 201/563] =?UTF-8?q?feat:=20=EA=B3=84=EC=95=BD=EC=84=9C=20?= =?UTF-8?q?=EA=B6=8C=ED=95=9C=EC=84=A4=EC=A0=95,=20=EA=B3=84=EC=95=BD?= =?UTF-8?q?=EC=84=9C=20=EC=8A=B9=EC=9D=B8=20=EC=83=81=ED=83=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20(#85)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ContractController.java | 56 ++- .../application/dto/ContractModifyDTO.java | 4 +- .../application/dto/ContractRegistDTO.java | 4 +- .../dto/ContractStatusModifyDTO.java | 5 + .../service/ContractCommandService.java | 9 +- .../domain/aggregate/entity/Contract.java | 14 +- .../domain/repository/ContractRepository.java | 2 + .../service/ContractCommandServiceImpl.java | 148 ++++++-- .../query/controller/ContractController.java | 40 ++- .../contract/query/dto/ContractSearchDTO.java | 20 +- .../query/dto/ContractSelectAllDTO.java | 8 +- .../query/dto/ContractSeletIdDTO.java | 8 +- .../query/repository/ContractMapper.java | 14 +- .../query/service/ContractQueryService.java | 2 +- .../service/ContractQueryServiceImpl.java | 185 +++++++--- .../query/repository/ProductMapper.java | 2 + .../product/query/service/ProductService.java | 4 + .../query/service/ProductServiceImpl.java | 11 + .../controller/PurchaseOrderController.java | 6 +- .../dto/PurchaseOrderStatusModifyDTO.java | 5 +- .../PurchaseOrderCommandServiceImpl.java | 2 +- src/main/resources/sql/ddl.sql | 30 +- .../query/repository/ContractMapper.xml | 340 +++++++++++++----- .../order/query/repository/OrderMapper.xml | 146 ++++---- .../query/repository/ProductMapper.xml | 11 + .../query/repository/PurchaseOrderMapper.xml | 146 ++++---- 26 files changed, 834 insertions(+), 388 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java index 77b657b4..590fa3ad 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java @@ -12,9 +12,16 @@ import stanl_2.final_backend.domain.contract.command.application.dto.ContractDeleteDTO; import stanl_2.final_backend.domain.contract.command.application.dto.ContractModifyDTO; import stanl_2.final_backend.domain.contract.command.application.dto.ContractRegistDTO; +import stanl_2.final_backend.domain.contract.command.application.dto.ContractStatusModifyDTO; import stanl_2.final_backend.domain.contract.command.application.service.ContractCommandService; import stanl_2.final_backend.domain.contract.common.response.ContractResponseMessage; +import org.springframework.security.core.GrantedAuthority; + +import java.security.GeneralSecurityException; +import java.util.List; +import java.util.stream.Collectors; + @RestController("contractController") @RequestMapping("/api/v1/contract") public class ContractController { @@ -60,17 +67,17 @@ public ContractController(ContractCommandService contractCommandService) { }) @PostMapping("") public ResponseEntity postTest(@RequestBody ContractRegistDTO contractRegistRequestDTO, - Authentication authentication) { + Authentication authentication) throws GeneralSecurityException { contractRegistRequestDTO.setMemberId(authentication.getName()); contractRegistRequestDTO.setRoles(authentication.getAuthorities()); contractCommandService.registerContract(contractRegistRequestDTO); return ResponseEntity.ok(ContractResponseMessage.builder() - .httpStatus(200) - .msg("계약서가 성공적으로 등록되었습니다.") - .result(null) - .build()); + .httpStatus(200) + .msg("계약서가 성공적으로 등록되었습니다.") + .result(null) + .build()); } /** @@ -107,7 +114,7 @@ public ResponseEntity postTest(@RequestBody ContractReg @PutMapping("{id}") public ResponseEntity putContract(@PathVariable String id, @RequestBody ContractModifyDTO contractModifyRequestDTO, - Authentication authentication) { + Authentication authentication) throws GeneralSecurityException { contractModifyRequestDTO.setContractId(id); contractModifyRequestDTO.setMemberId(authentication.getName()); @@ -124,9 +131,9 @@ public ResponseEntity putContract(@PathVariable String /** * [DELETE] http://localhost:8080/api/v1/contract/CON_000000011 * */ - @Operation(summary = "샘플 삭제") + @Operation(summary = "계약서 삭제") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "샘플 삭제 성공", + @ApiResponse(responseCode = "200", description = "계약서 삭제 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) @DeleteMapping("{id}") @@ -145,4 +152,37 @@ public ResponseEntity deleteContract(@PathVariable Stri .result(null) .build()); } + + @Operation(summary = "계약서 승인상태 수정(관리자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "계약서 승인상태 수정 성공", + content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) + }) + @PutMapping("/status/{id}") + public ResponseEntity putContractStatus( + @PathVariable String id, + @RequestBody ContractStatusModifyDTO contractStatusModifyDTO, + Authentication authentication) { + + // 권한 정보를 String 리스트로 변환 + List roles = authentication.getAuthorities().stream() + .map(role -> role.getAuthority()) + .toList(); + + // DTO에 설정 + contractStatusModifyDTO.setRoles(roles); + contractStatusModifyDTO.setContractId(id); + contractStatusModifyDTO.setAdminId(authentication.getName()); + + // 서비스 호출 + contractCommandService.modifyContractStatus(contractStatusModifyDTO); + + return ResponseEntity.ok(ContractResponseMessage.builder() + .httpStatus(200) + .msg("계약서 승인 상태가 성공적으로 변경되었습니다.") + .result(null) + .build()); + } + + } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java index 8ef0fe92..bcb1cb4a 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java @@ -12,13 +12,14 @@ public class ContractModifyDTO { private String contractId; - private String name; + private String title; private String customerName; private String customerIdentifiNo; private String customerAddrress; private String customerEmail; private String customerPhone; private String companyName; + private String carName; private String customerClassifcation; private String customerPurchaseCondition; private String serialNum; @@ -27,6 +28,7 @@ public class ContractModifyDTO { private Integer intermediatePayment; private Integer remainderPayment; private Integer consignmentPayment; + private Integer totalSales; private String delveryDate; private String delveryLocation; private String status; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java index 5e1134c9..3f5b3990 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java @@ -11,7 +11,7 @@ @Getter public class ContractRegistDTO { - private String name; + private String title; private String customerName; private String customerIdentifiNo; private String customerAddrress; @@ -26,6 +26,8 @@ public class ContractRegistDTO { private Integer intermediatePayment; private Integer remainderPayment; private Integer consignmentPayment; + private Integer totalSales; + private String carName; private String delveryDate; private String delveryLocation; private String status; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractStatusModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractStatusModifyDTO.java index 57d547c3..67a67af5 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractStatusModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractStatusModifyDTO.java @@ -4,10 +4,15 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import java.util.List; @AllArgsConstructor @NoArgsConstructor @Setter @Getter public class ContractStatusModifyDTO { + private String contractId; + private String status; + private String adminId; + private List roles; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java index 042daf55..2b32ac17 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java @@ -3,11 +3,16 @@ import stanl_2.final_backend.domain.contract.command.application.dto.ContractDeleteDTO; import stanl_2.final_backend.domain.contract.command.application.dto.ContractModifyDTO; import stanl_2.final_backend.domain.contract.command.application.dto.ContractRegistDTO; +import stanl_2.final_backend.domain.contract.command.application.dto.ContractStatusModifyDTO; + +import java.security.GeneralSecurityException; public interface ContractCommandService { - void registerContract(ContractRegistDTO contractRegistRequestDTO); + void registerContract(ContractRegistDTO contractRegistRequestDTO) throws GeneralSecurityException; - ContractModifyDTO modifyContract(ContractModifyDTO contractModifyRequestDTO); + ContractModifyDTO modifyContract(ContractModifyDTO contractModifyRequestDTO) throws GeneralSecurityException; void deleteContract(ContractDeleteDTO contractDeleteDTO); + + void modifyContractStatus(ContractStatusModifyDTO contractStatusModifyDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java index 450cedad..354e3e16 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java @@ -31,8 +31,8 @@ public class Contract { @Column(name = "CONR_ID") private String contractId; - @Column(name = "CONR_NAME", nullable = false) - private String name; + @Column(name = "CONR_TTL", nullable = false) + private String title; @Column(name = "CONR_CUST_NAME", nullable = false) private String customerName; @@ -84,6 +84,9 @@ public class Contract { @Column(name = "CONR_DELV_LOC") private String delveryLocation; + @Column(name = "CONR_CAR_NAME") + private String carName; + @Column(name = "CONR_STAT", nullable = false) @ColumnDefault("'WAIT'") private String status; @@ -92,6 +95,10 @@ public class Contract { @ColumnDefault("1") private String numberOfVehicles; + @Column(name = "CONR_TOTA_SALE", nullable = false) + @ColumnDefault("0") + private String totalSales; + @Lob @Column(name = "CREATED_URL", nullable = false, columnDefinition = "TEXT") private String createdUrl; @@ -115,6 +122,9 @@ public class Contract { @Column(name = "MEM_ID", nullable = false) private String memberId; + @Column(name = "ADMI_ID", nullable = false) + private String adminId; + @Column(name = "CENT_ID", nullable = false) private String centerId; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/repository/ContractRepository.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/repository/ContractRepository.java index 5d7c202f..e6ba9d8c 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/repository/ContractRepository.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/repository/ContractRepository.java @@ -7,4 +7,6 @@ public interface ContractRepository extends JpaRepository { Optional findByContractIdAndMemberId(String contractId, String memberId); + + Contract findByContractId(String contractId); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index 674d939d..35e1c04f 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -6,13 +6,21 @@ import stanl_2.final_backend.domain.contract.command.application.dto.ContractDeleteDTO; import stanl_2.final_backend.domain.contract.command.application.dto.ContractModifyDTO; import stanl_2.final_backend.domain.contract.command.application.dto.ContractRegistDTO; +import stanl_2.final_backend.domain.contract.command.application.dto.ContractStatusModifyDTO; import stanl_2.final_backend.domain.contract.command.application.service.ContractCommandService; import stanl_2.final_backend.domain.contract.command.domain.aggregate.entity.Contract; import stanl_2.final_backend.domain.contract.command.domain.repository.ContractRepository; import stanl_2.final_backend.domain.contract.common.exception.ContractCommonException; import stanl_2.final_backend.domain.contract.common.exception.ContractErrorCode; +import stanl_2.final_backend.domain.member.query.dto.MemberDTO; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; +import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; +import stanl_2.final_backend.domain.product.query.service.ProductService; +import stanl_2.final_backend.global.exception.GlobalCommonException; +import stanl_2.final_backend.global.exception.GlobalErrorCode; +import java.security.GeneralSecurityException; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -22,11 +30,15 @@ public class ContractCommandServiceImpl implements ContractCommandService { private final ContractRepository contractRepository; private final AuthQueryService authQueryService; + private final MemberQueryService memberQueryService; + private final ProductService productService; private final ModelMapper modelMapper; - public ContractCommandServiceImpl(ContractRepository contractRepository, AuthQueryService authQueryService, ModelMapper modelMapper) { + public ContractCommandServiceImpl(ContractRepository contractRepository, AuthQueryService authQueryService, MemberQueryService memberQueryService, ProductService productService, ModelMapper modelMapper) { this.contractRepository = contractRepository; this.authQueryService = authQueryService; + this.memberQueryService = memberQueryService; + this.productService = productService; this.modelMapper = modelMapper; } @@ -37,66 +49,134 @@ private String getCurrentTime() { @Override @Transactional - public void registerContract(ContractRegistDTO contractRegistRequestDTO) { - // 회원인지 확인여부 및 값 가져오기 - - // 일련번호로 제품테이블의 총식별번호 찾아서 제품 가져오기 + public void registerContract(ContractRegistDTO contractRegistRequestDTO) throws GeneralSecurityException { - // 고객전화번호로 고객테이블 찾아서 고객이 있으면 넘어가고, 고객이 없으면 고객테이블에 고객 정보 넣기 + if(contractRegistRequestDTO.getRoles().stream() + .anyMatch(role -> "ROLE_EMPLOYEE".equals(role.getAuthority()))){ - Contract contract = modelMapper.map(contractRegistRequestDTO, Contract.class); + // 영업사원 번호 + String memberId = authQueryService.selectMemberIdByLoginId(contractRegistRequestDTO.getMemberId()); - contract.setCenterId("CEN_000000001"); // 회원의 매장번호 넣기 - contract.setProductId("PRO_000000001"); // 제품 번호 넣기 - contract.setCustomerId("CUS_000000001"); // 제품 번호 넣기 + // 일련번호로 제품테이블의 총식별번호 찾아서 제품 가져오기 +// ProductSelectIdDTO productSelectIdDTO = productService.selectByProductSerialNumber(contractRegistRequestDTO.getSerialNum()); +// +// System.out.println("제품!!!!!!!!!!: " + productSelectIdDTO); +// String productId = productSelectIdDTO.getId(); - contractRepository.save(contract); + // 판매내역 업로드 + + // 제품 재고 수 줄이기 + + // 고객전화번호로 고객테이블 찾아서 고객이 있으면 넘어가고, 고객이 없으면 고객테이블에 고객 정보 넣기 + + // 회원의 영업 매장번호 +// MemberDTO memberDTO = memberQueryService.selectMemberInfo(contractRegistRequestDTO.getMemberId()); +// String centerId = memberDTO.getCenterId(); + + Contract contract = modelMapper.map(contractRegistRequestDTO, Contract.class); + + contract.setMemberId(memberId); + contract.setCenterId("CEN_000000001"); // 회원의 매장번호 넣기 + contract.setProductId("PRO_000000001"); // 제품 번호 넣기 + contract.setCustomerId("CUS_000000001"); + + contractRepository.save(contract); + } else { + throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + } } @Override @Transactional public ContractModifyDTO modifyContract(ContractModifyDTO contractModifyRequestDTO) { - String memberId = authQueryService.selectMemberIdByLoginId(contractModifyRequestDTO.getMemberId()); + if(contractModifyRequestDTO.getRoles().stream() + .anyMatch(role -> "ROLE_EMPLOYEE".equals(role.getAuthority()))) { + + String memberId = authQueryService.selectMemberIdByLoginId(contractModifyRequestDTO.getMemberId()); + + // 일련번호로 제품테이블의 총식별번호 찾아서 제품 가져오기 +// ProductSelectIdDTO productSelectIdDTO = productService.selectByProductSerialNumber(contractModifyRequestDTO.getSerialNum()); +// String productId = productSelectIdDTO.getId(); + contractModifyRequestDTO.setProductId("PRO_000000001"); - // 일련번호로 제품테이블의 총식별번호 찾아서 제품 가져오기 + // 판매내역 수정 - // 가져온 제품 정보에 수정된 값 넣기 + // 고객전화번호로 고객테이블 찾아서 가져오기 - // 고객전화번호로 고객테이블 찾아서 가져오기 + // 가져온 고객 정보에 수정된 값 넣기 - // 가져온 고객 정보에 수정된 값 넣기 + Contract contract = (Contract) contractRepository.findByContractIdAndMemberId(contractModifyRequestDTO.getContractId(), memberId) + .orElseThrow(() -> new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND)); - Contract contract = (Contract) contractRepository.findByContractIdAndMemberId(contractModifyRequestDTO.getContractId(), memberId) - .orElseThrow(() -> new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND)); + Contract updateContract = modelMapper.map(contractModifyRequestDTO, Contract.class); + updateContract.setCreatedAt(contract.getCreatedAt()); + updateContract.setUpdatedAt(contract.getUpdatedAt()); + updateContract.setActive(contract.isActive()); + updateContract.setCenterId(contract.getCenterId()); + updateContract.setCreatedUrl(contract.getCreatedUrl()); - Contract updateContract = modelMapper.map(contractModifyRequestDTO, Contract.class); - updateContract.setCreatedAt(contract.getCreatedAt()); - updateContract.setUpdatedAt(contract.getUpdatedAt()); - updateContract.setActive(contract.isActive()); - updateContract.setCenterId(contract.getCenterId()); - updateContract.setProductId(contract.getProductId()); - updateContract.setCreatedUrl(contract.getCreatedUrl()); + contractRepository.save(updateContract); - contractRepository.save(updateContract); + ContractModifyDTO contractModifyDTO = modelMapper.map(updateContract, ContractModifyDTO.class); + + return contractModifyDTO; + } else { + throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + } - ContractModifyDTO contractModifyDTO = modelMapper.map(updateContract, ContractModifyDTO.class); - return contractModifyDTO; } @Override @Transactional public void deleteContract(ContractDeleteDTO contractDeleteDTO) { - String memberId = authQueryService.selectMemberIdByLoginId(contractDeleteDTO.getMemberId()); + if(contractDeleteDTO.getRoles().stream() + .anyMatch(role -> "ROLE_EMPLOYEE".equals(role.getAuthority()))) { + + String memberId = authQueryService.selectMemberIdByLoginId(contractDeleteDTO.getMemberId()); - Contract contract = (Contract) contractRepository.findByContractIdAndMemberId(contractDeleteDTO.getContractId(), memberId) - .orElseThrow(() -> new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND)); + Contract contract = (Contract) contractRepository.findByContractIdAndMemberId(contractDeleteDTO.getContractId(), memberId) + .orElseThrow(() -> new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND)); - contract.setActive(false); - contract.setDeletedAt(getCurrentTime()); + contract.setActive(false); + contract.setDeletedAt(getCurrentTime()); - contractRepository.save(contract); + // 고객정보도 false + + // 판매내역도 false 해서 -1 + + contractRepository.save(contract); + + } else { + throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + } } + + @Override + @Transactional + public void modifyContractStatus(ContractStatusModifyDTO contractStatusModifyDTO) { + // 역할 확인 + if (contractStatusModifyDTO.getRoles().stream() + .anyMatch(role -> "ROLE_MANAGER".equals(role) || "ROLE_REPRESENTATIVE".equals(role))) { + + // 관리자 ID 조회 + String adminId = authQueryService.selectMemberIdByLoginId(contractStatusModifyDTO.getAdminId()); + + // 계약 조회 및 수정 + Contract contract = contractRepository.findByContractId(contractStatusModifyDTO.getContractId()); + + if (contract != null) { + contract.setStatus(contractStatusModifyDTO.getStatus()); + contract.setAdminId(adminId); + + contractRepository.save(contract); + } + } else { + throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + } + } + + } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java index 2692af46..fb7cc9af 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java @@ -10,6 +10,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.contract.common.response.ContractResponseMessage; import stanl_2.final_backend.domain.contract.query.dto.ContractSearchDTO; @@ -33,16 +34,18 @@ public ContractController(ContractQueryService contractQueryService) { * */ @Operation(summary = "계약서 전체 조회") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "계약서 전채 조회 성공", + @ApiResponse(responseCode = "200", description = "계약서 전체 조회 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) - @GetMapping("{memberId}") - public ResponseEntity getAllContract(@PathVariable("memberId") String memberId, - @PageableDefault(size = 10) Pageable pageable) { + @GetMapping("") + public ResponseEntity getAllContract(@PageableDefault(size = 10) Pageable pageable, + Authentication authentication) { - // 회원 아이디 받아 오는건 나중에 수정할 예정 + ContractSelectAllDTO contractSelectAllDTO = new ContractSelectAllDTO(); + contractSelectAllDTO.setMemberId(authentication.getName()); + contractSelectAllDTO.setRoles(authentication.getAuthorities()); - Page responseContracts = contractQueryService.selectAll(memberId, pageable); + Page responseContracts = contractQueryService.selectAll(contractSelectAllDTO, pageable); return ResponseEntity.ok(ContractResponseMessage.builder() .httpStatus(200) @@ -59,15 +62,16 @@ public ResponseEntity getAllContract(@PathVariable("mem @ApiResponse(responseCode = "200", description = "계약서 상세 조회 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) - @GetMapping("{id}/{memberId}") + @GetMapping("{id}") public ResponseEntity getDetailContract(@PathVariable("id") String id, - @PathVariable("memberId") String memberId) { + Authentication authentication) { - ContractSeletIdDTO contractDTO = new ContractSeletIdDTO(); - contractDTO.setContractId(id); - contractDTO.setMemberId(memberId); + ContractSeletIdDTO contractSeletIdDTO = new ContractSeletIdDTO(); + contractSeletIdDTO.setContractId(id); + contractSeletIdDTO.setMemberId(authentication.getName()); + contractSeletIdDTO.setRoles(authentication.getAuthorities()); - ContractSeletIdDTO responseContract = contractQueryService.selectDetailContract(contractDTO); + ContractSeletIdDTO responseContract = contractQueryService.selectDetailContract(contractSeletIdDTO); return ResponseEntity.ok(ContractResponseMessage.builder() .httpStatus(200) @@ -85,10 +89,10 @@ public ResponseEntity getDetailContract(@PathVariable(" content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) @GetMapping("/search") - public ResponseEntity getContractBySearch(@RequestParam(required = false) String memberId, - @RequestParam(required = false) String memId, + public ResponseEntity getContractBySearch(Authentication authentication, + @RequestParam(required = false) String searchMemberId, @RequestParam(required = false) String centerId, - @RequestParam(required = false) String name, + @RequestParam(required = false) String title, @RequestParam(required = false) String startAt, @RequestParam(required = false) String endAt, @RequestParam(required = false) String customerName, @@ -99,9 +103,9 @@ public ResponseEntity getContractBySearch(@RequestParam @RequestParam(required = false) String customerPurchaseCondition, @PageableDefault(size = 10) Pageable pageable) { - - ContractSearchDTO contractSearchDTO = new ContractSearchDTO(memberId, memId, centerId, name, startAt, endAt, - customerName, customerClassifcation, productId, status, companyName, customerPurchaseCondition); + String memberId = authentication.getName(); + ContractSearchDTO contractSearchDTO = new ContractSearchDTO(memberId, searchMemberId, centerId, title, startAt, endAt, + customerName, customerClassifcation, productId, status, companyName, customerPurchaseCondition, authentication.getAuthorities()); Page responseContracts = contractQueryService.selectBySearch(contractSearchDTO, pageable); return ResponseEntity.ok(ContractResponseMessage.builder() diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java index 3a936f3d..20d295d8 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java @@ -1,6 +1,9 @@ package stanl_2.final_backend.domain.contract.query.dto; import lombok.*; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; @AllArgsConstructor @NoArgsConstructor @@ -10,7 +13,7 @@ public class ContractSearchDTO { private String contractId; - private String name; + private String title; private String customerName; private String customerIdentifiNo; private String customerAddrress; @@ -29,6 +32,7 @@ public class ContractSearchDTO { private String delveryLocationLoc; private String status; private String numberOfVehicles; + private String totalSales; private String createdUrl; private String updatedUrl; private boolean active; @@ -36,19 +40,22 @@ public class ContractSearchDTO { private String updatedAt; private String deletedAt; private String memberId; - private String memId; + private String searchMemberId; private String centerId; private String customerId; private String productId; private String startAt; private String endAt; - private String productName; + private String carName; + private Collection roles; - public ContractSearchDTO(String memberId, String memId, String centerId, String name, String startAt, String endAt, String customerName, String customerClassifcation, String productId, String status, String companyName, String customerPurchaseCondition) { + public ContractSearchDTO(String memberId, String searchMemberId, String centerId, String title, String startAt, String endAt, + String customerName, String customerClassifcation, String productId, String status, + String companyName, String customerPurchaseCondition, Collection roles) { this.memberId = memberId; - this.memId = memId; + this.searchMemberId = searchMemberId; this.centerId = centerId; - this.name = name; + this.title = title; this.startAt = startAt; this.endAt = endAt; this.customerName = customerName; @@ -57,5 +64,6 @@ public ContractSearchDTO(String memberId, String memId, String centerId, String this.status = status; this.companyName = companyName; this.customerPurchaseCondition = customerPurchaseCondition; + this.roles = roles; } } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java index fb8f34cb..3aff0a4d 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java @@ -1,6 +1,9 @@ package stanl_2.final_backend.domain.contract.query.dto; import lombok.*; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; @AllArgsConstructor @NoArgsConstructor @@ -10,7 +13,7 @@ public class ContractSelectAllDTO { private String contractId; - private String name; + private String title; private String customerName; private String companyName; private String status; @@ -21,5 +24,6 @@ public class ContractSelectAllDTO { private String centerId; private String customerId; private String productId; - private String productName; + private String carName; + private Collection roles; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java index d40cd9a6..89056236 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java @@ -1,6 +1,9 @@ package stanl_2.final_backend.domain.contract.query.dto; import lombok.*; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; @AllArgsConstructor @NoArgsConstructor @@ -10,7 +13,7 @@ public class ContractSeletIdDTO { private String contractId; - private String name; + private String title; private String customerName; private String customerIdentifiNo; private String customerAddrress; @@ -29,6 +32,8 @@ public class ContractSeletIdDTO { private String delveryLocation; private String status; private String NumberOfVehicles; + private String totalSales; + private String carName; private String createdUrl; private String updatedUrl; private boolean active; @@ -39,4 +44,5 @@ public class ContractSeletIdDTO { private String centerId; private String customerId; private String productId; + private Collection roles; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java index f3857623..7ad187f8 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java @@ -12,11 +12,21 @@ public interface ContractMapper { ContractSeletIdDTO findContractByIdAndMemId(ContractSeletIdDTO contractDTO); - List findContractBySearch(Map map); + List findContractBySearchAndMemberId(Map map); - int findContractBySearchCount(Map map); + int findContractBySearchAndMemberIdCount(Map map); List findContractAllByMemId(Map map); int findContractCountByMemId(String memId); + + List findContractAll(Map params); + + int findContractCount(); + + ContractSeletIdDTO findContractById(String contractId); + + List findContractBySearch(Map map); + + int findContractBySearchCount(Map map); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java index ccc7a126..8cff949d 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java @@ -13,5 +13,5 @@ public interface ContractQueryService { Page selectBySearch(ContractSearchDTO contractSearchDTO, Pageable pageable); - Page selectAll(String memId, Pageable pageable); + Page selectAll(ContractSelectAllDTO contractSelectAllDTO, Pageable pageable); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java index 9470dfe9..8b67fc8f 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java @@ -13,6 +13,11 @@ import stanl_2.final_backend.domain.contract.query.dto.ContractSelectAllDTO; import stanl_2.final_backend.domain.contract.query.dto.ContractSeletIdDTO; import stanl_2.final_backend.domain.contract.query.repository.ContractMapper; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; +import stanl_2.final_backend.domain.product.query.service.ProductService; +import stanl_2.final_backend.global.exception.GlobalCommonException; +import stanl_2.final_backend.global.exception.GlobalErrorCode; import java.util.HashMap; import java.util.List; @@ -23,78 +28,164 @@ public class ContractQueryServiceImpl implements ContractQueryService { private final ContractMapper contractMapper; + private final AuthQueryService authQueryService; @Autowired - public ContractQueryServiceImpl(ContractMapper contractMapper) { + public ContractQueryServiceImpl(ContractMapper contractMapper, AuthQueryService authQueryService) { this.contractMapper = contractMapper; + this.authQueryService = authQueryService; } // 계약서 전체조회 @Override - public Page selectAll(String memId, Pageable pageable) { + public Page selectAll(ContractSelectAllDTO contractSelectAllDTO, Pageable pageable) { - Map params = new HashMap<>(); - params.put("memId", memId); - params.put("offset", pageable.getOffset()); - params.put("pageSize", pageable.getPageSize()); + if(contractSelectAllDTO.getRoles().stream() + .anyMatch(role -> "ROLE_EMPLOYEE".equals(role.getAuthority()))){ - List contractList = contractMapper.findContractAllByMemId(params); + String memberId = authQueryService.selectMemberIdByLoginId(contractSelectAllDTO.getMemberId()); - if (contractList == null || contractList.isEmpty()) { - throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); - } + Map params = new HashMap<>(); + params.put("memId", memberId); + params.put("offset", pageable.getOffset()); + params.put("pageSize", pageable.getPageSize()); + + List contractList = contractMapper.findContractAllByMemId(params); + + if (contractList == null || contractList.isEmpty()) { + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); + } + + int total = contractMapper.findContractCountByMemId(memberId); + + return new PageImpl<>(contractList, pageable, total); + + } else if (contractSelectAllDTO.getRoles().stream() + .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()) || "ROLE_REPRESENTATIVE".equals(role.getAuthority()))) { - int total = contractMapper.findContractCountByMemId(memId); + Map params = new HashMap<>(); + params.put("offset", pageable.getOffset()); + params.put("pageSize", pageable.getPageSize()); - return new PageImpl<>(contractList, pageable, total); + List contractList = contractMapper.findContractAll(params); + + if (contractList == null || contractList.isEmpty()) { + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); + } + + int total = contractMapper.findContractCount(); + + return new PageImpl<>(contractList, pageable, total); + } else { + throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + } } // 계약서 상세조회 @Override - public ContractSeletIdDTO selectDetailContract(ContractSeletIdDTO contractDTO) { + public ContractSeletIdDTO selectDetailContract(ContractSeletIdDTO contractSeletIdDTO) { - ContractSeletIdDTO responseContract = contractMapper.findContractByIdAndMemId(contractDTO); + if(contractSeletIdDTO.getRoles().stream() + .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()))){ - if (responseContract == null) { - throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); - } + String memberId = authQueryService.selectMemberIdByLoginId(contractSeletIdDTO.getMemberId()); + contractSeletIdDTO.setMemberId(memberId); - // 이스케이프된 HTML 제거 - String unescapedHtml = StringEscapeUtils.unescapeJson(responseContract.getCreatedUrl()); - responseContract.setCreatedUrl(unescapedHtml); + ContractSeletIdDTO responseContract = contractMapper.findContractByIdAndMemId(contractSeletIdDTO); - return responseContract; - } + if (responseContract == null) { + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); + } - @Override - public Page selectBySearch(ContractSearchDTO contractSearchDTO, Pageable pageable) { + // 이스케이프된 HTML 제거 + String unescapedHtml = StringEscapeUtils.unescapeJson(responseContract.getCreatedUrl()); + responseContract.setCreatedUrl(unescapedHtml); - Map map = new HashMap<>(); - map.put("memberId", contractSearchDTO.getMemberId()); - map.put("memId", contractSearchDTO.getMemId()); - map.put("centerId", contractSearchDTO.getCenterId()); - map.put("name", contractSearchDTO.getName()); - map.put("startAt", contractSearchDTO.getStartAt()); - map.put("endAt", contractSearchDTO.getEndAt()); - map.put("customerName", contractSearchDTO.getCustomerName()); - map.put("customerClassifcation", contractSearchDTO.getCustomerClassifcation()); - map.put("productId", contractSearchDTO.getProductId()); - map.put("status", contractSearchDTO.getStatus()); - map.put("companyName", contractSearchDTO.getCompanyName()); - map.put("customerPurchaseCondition", contractSearchDTO.getCustomerPurchaseCondition()); - map.put("pageSize", pageable.getPageSize()); - map.put("offset", pageable.getOffset()); - - List contracts = contractMapper.findContractBySearch(map); - - if (contracts == null || contracts.isEmpty()) { - throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); - } + return responseContract; + + } else if (contractSeletIdDTO.getRoles().stream() + .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()) || "ROLE_REPRESENTATIVE".equals(role.getAuthority()))) { + + ContractSeletIdDTO responseContract = contractMapper.findContractById(contractSeletIdDTO.getContractId()); + + if (responseContract == null) { + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); + } - int total = contractMapper.findContractBySearchCount(map); + // 이스케이프된 HTML 제거 + String unescapedHtml = StringEscapeUtils.unescapeJson(responseContract.getCreatedUrl()); + responseContract.setCreatedUrl(unescapedHtml); - return new PageImpl<>(contracts, pageable, total); + return responseContract; + + } else { + throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + } } + @Override + public Page selectBySearch(ContractSearchDTO contractSearchDTO, Pageable pageable) { + if (contractSearchDTO.getRoles().stream() + .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()))) { + + String memberId = authQueryService.selectMemberIdByLoginId(contractSearchDTO.getMemberId()); + + Map map = new HashMap<>(); + map.put("memberId", memberId); + map.put("centerId", contractSearchDTO.getCenterId()); + map.put("title", contractSearchDTO.getTitle()); + map.put("startAt", contractSearchDTO.getStartAt()); + map.put("endAt", contractSearchDTO.getEndAt()); + map.put("customerName", contractSearchDTO.getCustomerName()); + map.put("customerClassifcation", contractSearchDTO.getCustomerClassifcation()); + map.put("productId", contractSearchDTO.getProductId()); + map.put("status", contractSearchDTO.getStatus()); + map.put("companyName", contractSearchDTO.getCompanyName()); + map.put("customerPurchaseCondition", contractSearchDTO.getCustomerPurchaseCondition()); + map.put("pageSize", pageable.getPageSize()); + map.put("offset", pageable.getOffset()); + + List contracts = contractMapper.findContractBySearchAndMemberId(map); + + if (contracts == null || contracts.isEmpty()) { + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); + } + + int total = contractMapper.findContractBySearchAndMemberIdCount(map); + + return new PageImpl<>(contracts, pageable, total); + + } else if (contractSearchDTO.getRoles().stream() + .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()) || "ROLE_REPRESENTATIVE".equals(role.getAuthority()))) { + + Map map = new HashMap<>(); + map.put("searchMemberId", contractSearchDTO.getSearchMemberId()); + map.put("centerId", contractSearchDTO.getCenterId()); + map.put("title", contractSearchDTO.getTitle()); + map.put("startAt", contractSearchDTO.getStartAt()); + map.put("endAt", contractSearchDTO.getEndAt()); + map.put("customerName", contractSearchDTO.getCustomerName()); + map.put("customerClassifcation", contractSearchDTO.getCustomerClassifcation()); + map.put("productId", contractSearchDTO.getProductId()); + map.put("status", contractSearchDTO.getStatus()); + map.put("companyName", contractSearchDTO.getCompanyName()); + map.put("customerPurchaseCondition", contractSearchDTO.getCustomerPurchaseCondition()); + map.put("pageSize", pageable.getPageSize()); + map.put("offset", pageable.getOffset()); + + List contracts = contractMapper.findContractBySearch(map); + + if (contracts == null || contracts.isEmpty()) { + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); + } + + int total = contractMapper.findContractBySearchCount(map); + + return new PageImpl<>(contracts, pageable, total); + + } else { + throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + } + } } diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java b/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java index 6ace42e4..8e0ac637 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java @@ -18,4 +18,6 @@ public interface ProductMapper { List> findProductBySearch(Map paramMap); int findProductBySearchCount(Map paramMap); + + ProductSelectIdDTO findProductBySerialNumber(String serialNumber); } diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductService.java b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductService.java index 74e876f5..634f51b8 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductService.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductService.java @@ -3,6 +3,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; import java.util.Map; @@ -14,4 +15,7 @@ public interface ProductService { ProductSelectIdDTO selectByProductId(String id); Page> selectProductBySearch(Map paramMap); + + @Transactional + ProductSelectIdDTO selectByProductSerialNumber(String id); } diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductServiceImpl.java index ced78f15..8eeeb36b 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductServiceImpl.java @@ -60,4 +60,15 @@ public Page> selectProductBySearch(Map param return new PageImpl<>(productList, pageable, total); } + + @Override + @Transactional + public ProductSelectIdDTO selectByProductSerialNumber(String id) { + + /* 설명. 상세 조회 시, Mapper에서 product와 productOption join해서 보여줄 것 */ + ProductSelectIdDTO productSelectIdDTO = productMapper.findProductBySerialNumber(id); + + return productSelectIdDTO; + } + } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java index 25ab672d..91b36162 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java @@ -96,12 +96,10 @@ public ResponseEntity deletePurchaseOrder(@PathVar public ResponseEntity putPurchaseOrderStatus(@PathVariable String id, PurchaseOrderStatusModifyDTO purchaseOrderStatusModifyDTO, Authentication authentication) { - String role = authentication.getAuthorities().toString(); - String adminId = authentication.getName(); purchaseOrderStatusModifyDTO.setPurchaseOrderId(id); - purchaseOrderStatusModifyDTO.setRole(role); - purchaseOrderStatusModifyDTO.setAdminId(adminId); + purchaseOrderStatusModifyDTO.setRoles(authentication.getAuthorities()); + purchaseOrderStatusModifyDTO.setAdminId(authentication.getName()); purchaseOrderCommandService.modifyPurchaseOrderStatus(purchaseOrderStatusModifyDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderStatusModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderStatusModifyDTO.java index 9f1af2c1..d8896798 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderStatusModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderStatusModifyDTO.java @@ -1,6 +1,9 @@ package stanl_2.final_backend.domain.purchase_order.command.application.dto; import lombok.*; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; @AllArgsConstructor @NoArgsConstructor @@ -10,5 +13,5 @@ public class PurchaseOrderStatusModifyDTO { private String purchaseOrderId; private String status; private String adminId; - private String role; + private Collection roles; } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java index c3c05ee4..90a96c59 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java @@ -127,7 +127,7 @@ public void modifyPurchaseOrderStatus(PurchaseOrderStatusModifyDTO purchaseOrder String adminId = authQueryService.selectMemberIdByLoginId(purchaseOrderStatusModifyDTO.getAdminId()); - if ("[ROLE_REPRESENTATIVE]".equals(purchaseOrderStatusModifyDTO.getRole())) { + if ("[ROLE_REPRESENTATIVE]".equals(purchaseOrderStatusModifyDTO.getRoles())) { PurchaseOrder purchaseOrder = purchaseOrderRepository.findByPurchaseOrderId(purchaseOrderStatusModifyDTO.getPurchaseOrderId()) .orElseThrow(() -> new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND)); diff --git a/src/main/resources/sql/ddl.sql b/src/main/resources/sql/ddl.sql index 06cb3f61..10db9bf8 100644 --- a/src/main/resources/sql/ddl.sql +++ b/src/main/resources/sql/ddl.sql @@ -125,7 +125,7 @@ CREATE TABLE tb_product CREATE TABLE tb_contract ( CONR_ID VARCHAR(255) NOT NULL, - CONR_NAME VARCHAR(255) NOT NULL, + CONR_TTL VARCHAR(255) NOT NULL, CONR_CUST_NAME VARCHAR(255) NOT NULL, CONR_CUST_IDEN_NO VARCHAR(255) NOT NULL, CONR_CUST_ADR VARCHAR(255) NOT NULL, @@ -154,11 +154,13 @@ CREATE TABLE tb_contract CENT_ID VARCHAR(255) NOT NULL, CUST_ID VARCHAR(255) NULL, PROD_ID VARCHAR(255) NOT NULL, + ADMI_ID VARCHAR(255) NULL, PRIMARY KEY (CONR_ID), FOREIGN KEY (MEM_ID) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE, FOREIGN KEY (CUST_ID) REFERENCES tb_customer_info (CUST_ID) ON DELETE CASCADE, FOREIGN KEY (PROD_ID) REFERENCES tb_product (PROD_ID) ON DELETE CASCADE, - FOREIGN KEY (CENT_ID) REFERENCES tb_center (CENT_ID) ON DELETE CASCADE + FOREIGN KEY (CENT_ID) REFERENCES tb_center (CENT_ID) ON DELETE CASCADE, + FOREIGN KEY (ADMI_ID) REFERENCES tb_member (MEM_ID) ON DELETE CASCADE ); CREATE TABLE tb_notice @@ -547,12 +549,12 @@ VALUES ('PRO_000000001', 'KNAHAA4AALU1A00001', 25000000, '쏘렌토', 10, '2024- ('PRO_000000010', 'KNFHC54CAMR1A00010', 28000000, 'K5', 6, '2024-01-19 19:00:00', '2024-01-19 20:00:00', TRUE); -INSERT INTO tb_contract (CONR_ID, CONR_NAME, CONR_CUST_NAME, CONR_CUST_IDEN_NO, CONR_CUST_ADR, +INSERT INTO tb_contract (CONR_ID, CONR_TTL, CONR_CUST_NAME, CONR_CUST_IDEN_NO, CONR_CUST_ADR, CONR_CUST_EMA, CONR_CUST_PHO, CONR_COMP_NAME, CONR_CUST_CLA, CONR_CUST_PUR_COND, CONR_SERI_NUM, CONR_SELE_OPTI, CONR_DOWN_PAY, CONR_INTE_PAY, CONR_REM_PAY, CONR_CONS_PAY, CONR_DELV_DATE, CONR_DELV_LOC, CONR_STAT, CONR_NO_OF_VEH, CREATED_URL, DELETED_URL, ACTIVE, CREATED_AT, UPDATED_AT, DELETED_AT, - MEM_ID, CENT_ID, CUST_ID, PROD_ID) + MEM_ID, CENT_ID, CUST_ID, PROD_ID, ADMI_ID) VALUES -- 계약 1 ('CON_000000001', '쏘렌토 계약', '박지훈', '880512-1234567', '서울특별시 강남구', @@ -560,7 +562,7 @@ VALUES 'SER_001', '풀옵션', 5000000, 300000, 25000000, 30000000, '2024-02-20', '서울특별시 강남구 배송센터', 'WAIT', 1, '/contracts/1', NULL, TRUE, '2024-01-10 12:00:00', '2024-01-11 13:00:00', NULL, - 'MEM_000000001', 'CEN_000000001', 'CUS_000000001', 'PRO_000000001'), + 'MEM_000000001', 'CEN_000000001', 'CUS_000000001', 'PRO_000000001', NULL), -- 계약 2 ('CON_000000002', '스포티지 계약', '김영희', '900123-2345678', '부산광역시 해운대구', @@ -568,7 +570,7 @@ VALUES 'SER_002', '기본옵션', 3000000, 200000, 17000000, 25000000, '2024-03-05', '부산광역시 해운대구 배송센터', 'CONFIRMED', 1, '/contracts/2', NULL, TRUE, '2024-01-12 14:00:00', '2024-01-13 15:00:00', NULL, - 'MEM_000000002', 'CEN_000000002', 'CUS_000000002', 'PRO_000000002'), + 'MEM_000000002', 'CEN_000000002', 'CUS_000000002', 'PRO_000000002', NULL), -- 계약 3 ('CON_000000003', 'K7 계약', '이철수', '950405-3456789', '대구광역시 수성구', @@ -576,7 +578,7 @@ VALUES 'SER_003', '풀옵션', 8000000, 400000, 28000000, 35000000, '2024-03-10', '대구광역시 배송센터', 'WAIT', 2, '/contracts/3', NULL, TRUE, '2024-01-14 09:00:00', '2024-01-15 10:00:00', NULL, - 'MEM_000000003', 'CEN_000000003', 'CUS_000000003', 'PRO_000000003'), + 'MEM_000000003', 'CEN_000000003', 'CUS_000000003', 'PRO_000000003', NULL), -- 계약 4 ('CON_000000004', '셀토스 계약', '박민준', '970512-4567890', '인천광역시 미추홀구', @@ -584,7 +586,7 @@ VALUES 'SER_004', '기본옵션', 4000000, 250000, 12000000, 20000000, '2024-04-01', '인천광역시 배송센터', 'DELIVERED', 1, '/contracts/4', NULL, TRUE, '2024-01-16 11:00:00', '2024-01-17 12:00:00', NULL, - 'MEM_000000004', 'CEN_000000004', 'CUS_000000004', 'PRO_000000004'), + 'MEM_000000004', 'CEN_000000004', 'CUS_000000004', 'PRO_000000004', NULL), -- 계약 5 ('CON_000000005', 'K3 계약', '최민수', '980618-5678901', '울산광역시 중구', @@ -592,7 +594,7 @@ VALUES 'SER_005', '기본옵션', 2000000, 100000, 13000000, 18000000, '2024-04-15', '울산광역시 배송센터', 'CANCELLED', 1, '/contracts/5', NULL, TRUE, '2024-01-18 13:00:00', '2024-01-19 14:00:00', NULL, - 'MEM_000000005', 'CEN_000000005', 'CUS_000000005', 'PRO_000000005'), + 'MEM_000000005', 'CEN_000000005', 'CUS_000000005', 'PRO_000000005', NULL), -- 계약 6 ('CON_000000006', '모하비 계약', '김민주', '850824-6789012', '대전광역시 서구', @@ -600,7 +602,7 @@ VALUES 'SER_006', '풀옵션', 6000000, 500000, 34000000, 40000000, '2024-05-01', '대전광역시 배송센터', 'CONFIRMED', 1, '/contracts/6', NULL, TRUE, '2024-01-20 15:00:00', '2024-01-21 16:00:00', NULL, - 'MEM_000000006', 'CEN_000000006', 'CUS_000000006', 'PRO_000000006'), + 'MEM_000000006', 'CEN_000000006', 'CUS_000000006', 'PRO_000000006', NULL), -- 계약 7 ('CON_000000007', 'K8 계약', '정수영', '940705-7890123', '광주광역시 북구', @@ -608,7 +610,7 @@ VALUES 'SER_007', '럭셔리 패키지', 7000000, 350000, 25000000, 32000000, '2024-05-20', '광주광역시 배송센터', 'WAIT', 1, '/contracts/7', NULL, TRUE, '2024-01-22 17:00:00', '2024-01-23 18:00:00', NULL, - 'MEM_000000007', 'CEN_000000007', 'CUS_000000007', 'PRO_000000007'), + 'MEM_000000007', 'CEN_000000007', 'CUS_000000007', 'PRO_000000007', NULL), -- 계약 8 ('CON_000000008', '스팅어 계약', '이준호', '930910-8901234', '경기도 수원시', @@ -616,7 +618,7 @@ VALUES 'SER_008', '풀옵션', 5000000, 300000, 22000000, 27000000, '2024-06-01', '수원시 배송센터', 'DELIVERED', 1, '/contracts/8', NULL, TRUE, '2024-01-24 19:00:00', '2024-01-25 20:00:00', NULL, - 'MEM_000000008', 'CEN_000000008', 'CUS_000000008', 'PRO_000000008'), + 'MEM_000000008', 'CEN_000000008', 'CUS_000000008', 'PRO_000000008', NULL), -- 계약 9 ('CON_000000009', '니로 계약', '박소영', '890321-9012345', '충청북도 청주시', @@ -624,7 +626,7 @@ VALUES 'SER_009', '기본옵션', 3500000, 150000, 17000000, 23000000, '2024-06-15', '청주시 배송센터', 'CONFIRMED', 1, '/contracts/9', NULL, TRUE, '2024-01-26 21:00:00', '2024-01-27 22:00:00', NULL, - 'MEM_000000009', 'CEN_000000009', 'CUS_000000009', 'PRO_000000009'), + 'MEM_000000009', 'CEN_000000009', 'CUS_000000009', 'PRO_000000009', NULL), -- 계약 10 ('CON_000000010', 'K5 계약', '김정훈', '900815-0123456', '전라북도 전주시', @@ -632,7 +634,7 @@ VALUES 'SER_010', '풀옵션', 6000000, 250000, 20000000, 28000000, '2024-07-01', '전주시 배송센터', 'WAIT', 1, '/contracts/10', NULL, TRUE, '2024-01-28 23:00:00', '2024-01-29 23:59:00', NULL, - 'MEM_000000010', 'CEN_000000010', 'CUS_000000010', 'PRO_000000010'); + 'MEM_000000010', 'CEN_000000010', 'CUS_000000010', 'PRO_000000010', NULL); INSERT INTO tb_order ( ORD_ID, ORD_TTL, ORD_CONT, ACTIVE, CREATED_AT, UPDATED_AT, DELETED_AT, ORD_STAT, CONR_ID, MEM_ID, ADMIN_ID diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index ba8c2ebb..4d733dac 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -6,7 +6,7 @@ - + @@ -25,6 +25,7 @@ + @@ -35,11 +36,12 @@ + - + @@ -55,11 +57,12 @@ + - + @@ -71,155 +74,300 @@ + + + + + + - + SELECT + COUNT(*) AS cnt + FROM tb_contract a + WHERE a.active = TRUE; + + + + \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml index eb371a51..136bb510 100644 --- a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml @@ -30,117 +30,117 @@ diff --git a/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml b/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml index 3d6901b1..31f67349 100644 --- a/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml @@ -83,6 +83,17 @@ WHERE A.PROD_ID = #{id} AND A.ACTIVE = TRUE + + SELECT - A.PUR_ORD_ID, - A.PUR_ORD_TTL, - A.PUR_ORD_CONT, - A.ACTIVE, - A.CREATED_AT, - A.UPDATED_AT, - A.DELETED_AT, - A.PUR_ORD_STAT, - A.ORD_ID, - A.ADMIN_ID, - A.MEM_ID - FROM TB_PURCHASE_ORDER A - WHERE A.ACTIVE = TRUE - AND A.PUR_ORD_ID = #{purchaseOrderId} + a.pur_ord_id, + a.pur_ord_ttl, + a.pur_ord_cont, + a.active, + a.created_at, + a.updated_at, + a.deleted_at, + a.pur_ord_stat, + a.ord_id, + a.admin_id, + a.mem_id + FROM tb_purchase_order a + WHERE a.active = TRUE + AND a.pur_ord_id = #{purchaseOrderId}; - - \ No newline at end of file From f82bdc8c943e8893812567b47afb0cbc5cc9ca5f Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 17:24:30 +0900 Subject: [PATCH 202/563] =?UTF-8?q?fix:=20Notice=20=EA=B6=8C=ED=95=9C=20?= =?UTF-8?q?=EB=B6=80=EC=97=AC(#92)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/NoticeController.java | 9 +++++--- .../service/NoticeCommandService.java | 8 ++++--- .../service/NoticeCommandServiceImpl.java | 21 ++++++++++--------- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java index 9ac74191..4b72813a 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java @@ -39,8 +39,11 @@ public NoticeController(NoticeCommandService noticeCommandService, AuthQueryServ @PostMapping("") public ResponseEntity postNotice(@RequestBody NoticeRegistDTO noticeRegistDTO, Principal principal){ String memberId =authQueryService.selectMemberIdByLoginId(principal.getName()); + System.out.println("memberId"+memberId); noticeRegistDTO.setMemberId(memberId); - noticeCommandService.registerNotice(noticeRegistDTO); + System.out.println("1.========================="); + noticeCommandService.registerNotice(noticeRegistDTO, principal); + System.out.println("2.========================="); return ResponseEntity.ok(NoticeResponseMessage.builder() .httpStatus(200) .msg("성공") @@ -61,7 +64,7 @@ public ResponseEntity modifyNotice(Principal principal, noticeModifyRequestDTO.setMemberLoginId(memberLoginId); noticeModifyRequestDTO.setNoticeId(noticeId); - NoticeModifyDTO noticeModifyDTO = noticeCommandService.modifyNotice(noticeId,noticeModifyRequestDTO); + NoticeModifyDTO noticeModifyDTO = noticeCommandService.modifyNotice(noticeId,noticeModifyRequestDTO,principal); return ResponseEntity.ok(NoticeResponseMessage.builder() .httpStatus(200) @@ -84,7 +87,7 @@ public ResponseEntity deleteNotice(Principal principal, noticeDeleteDTO.setMemberLoginId(memberLoginId); noticeDeleteDTO.setNoticeId(noticeId); - noticeCommandService.deleteNotice(noticeDeleteDTO); + noticeCommandService.deleteNotice(noticeDeleteDTO,principal); return ResponseEntity.ok(NoticeResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/service/NoticeCommandService.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/service/NoticeCommandService.java index cec6dad6..61fe84e2 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/service/NoticeCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/service/NoticeCommandService.java @@ -5,10 +5,12 @@ import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleDeleteDTO; +import java.security.Principal; + public interface NoticeCommandService { - void registerNotice(NoticeRegistDTO noticeRegistDTO); + void registerNotice(NoticeRegistDTO noticeRegistDTO, Principal principal); - NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO); + NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO,Principal principal); - void deleteNotice(NoticeDeleteDTO noticeDeleteDTO); + void deleteNotice(NoticeDeleteDTO noticeDeleteDTO,Principal principal); } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java index 4eefe494..a24f3cef 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java @@ -19,6 +19,7 @@ import stanl_2.final_backend.domain.schedule.common.exception.ScheduleCommonException; import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; +import java.security.Principal; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -46,13 +47,13 @@ private String getCurrentTimestamp() { @Override @Transactional - public void registerNotice(NoticeRegistDTO noticeRegistDTO) { - String memberId = authQueryService.selectMemberIdByLoginId(noticeRegistDTO.getMemberLoginId()); + public void registerNotice(NoticeRegistDTO noticeRegistDTO, + Principal principal) { + String memberId= principal.getName(); noticeRegistDTO.setMemberId(memberId); try { Notice notice = modelMapper.map(noticeRegistDTO, Notice.class); - noticeRepository.save(notice); } catch (DataIntegrityViolationException e){ @@ -66,16 +67,16 @@ public void registerNotice(NoticeRegistDTO noticeRegistDTO) { @Override @Transactional - public NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO) { - String memberId = authQueryService.selectMemberIdByLoginId(noticeModifyDTO.getMemberLoginId()); + public NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO,Principal principal) { + String memberId= principal.getName(); noticeModifyDTO.setMemberId(memberId); Notice notice = noticeRepository.findById(id) .orElseThrow(() -> new NoticeCommonException(NoticeErrorCode.NOTICE_NOT_FOUND)); - if(!noticeModifyDTO.getMemberId().equals(notice.getMemberId())){ + if(!noticeModifyDTO.getMemberId().equals(memberId)){ // 권한 오류 - throw new ScheduleCommonException(ScheduleErrorCode.AUTHORIZATION_VIOLATION); + throw new NoticeCommonException(NoticeErrorCode.AUTHORIZATION_VIOLATION); } try { Notice updateNotice = modelMapper.map(noticeModifyDTO, Notice.class); @@ -101,14 +102,14 @@ public NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO) @Override @Transactional - public void deleteNotice(NoticeDeleteDTO noticeDeleteDTO) { + public void deleteNotice(NoticeDeleteDTO noticeDeleteDTO, Principal principal) { - String memberId = authQueryService.selectMemberIdByLoginId(noticeDeleteDTO.getMemberLoginId()); + String memberId = principal.getName(); Notice notice = noticeRepository.findByNoticeId(noticeDeleteDTO.getNoticeId()) .orElseThrow(() -> new NoticeCommonException(NoticeErrorCode.NOTICE_NOT_FOUND)); - if(!memberId.equals(notice.getMemberId())){ + if(!memberId.equals(memberId)){ // 권한 오류 throw new NoticeCommonException(NoticeErrorCode.AUTHORIZATION_VIOLATION); } From 1c297a1d71e7eb5e6a2c4d5eeb9dd1ccdbd4d0fb Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 17:28:07 +0900 Subject: [PATCH 203/563] =?UTF-8?q?fix:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=20(#92)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/application/controller/NoticeController.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java index 4b72813a..6102ff4e 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java @@ -41,9 +41,7 @@ public ResponseEntity postNotice(@RequestBody NoticeRegis String memberId =authQueryService.selectMemberIdByLoginId(principal.getName()); System.out.println("memberId"+memberId); noticeRegistDTO.setMemberId(memberId); - System.out.println("1.========================="); noticeCommandService.registerNotice(noticeRegistDTO, principal); - System.out.println("2.========================="); return ResponseEntity.ok(NoticeResponseMessage.builder() .httpStatus(200) .msg("성공") From e14e18f91bc14b3a15c6c082e530922fe4745637 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 17:29:28 +0900 Subject: [PATCH 204/563] =?UTF-8?q?fix:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=20(#92)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notices/command/application/controller/NoticeController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java index 6102ff4e..b8f78c99 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java @@ -39,7 +39,6 @@ public NoticeController(NoticeCommandService noticeCommandService, AuthQueryServ @PostMapping("") public ResponseEntity postNotice(@RequestBody NoticeRegistDTO noticeRegistDTO, Principal principal){ String memberId =authQueryService.selectMemberIdByLoginId(principal.getName()); - System.out.println("memberId"+memberId); noticeRegistDTO.setMemberId(memberId); noticeCommandService.registerNotice(noticeRegistDTO, principal); return ResponseEntity.ok(NoticeResponseMessage.builder() From 31eb7c94b4afd44f6f48d72bc5317e10db4efba1 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Tue, 19 Nov 2024 18:01:14 +0900 Subject: [PATCH 205/563] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=95=8C=EB=A6=BC=20=EC=84=A4=EC=A0=95=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EC=A4=91=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../alarm/service/AlarmServiceImpl.java | 24 +++++++++++++++++-- .../member/query/dto/MemberRoleDTO.java | 13 ++++++++++ .../query/repository/MemberRoleMapper.java | 10 ++++++++ .../query/service/MemberQueryService.java | 4 ++++ .../query/service/MemberQueryServiceImpl.java | 20 ++++++++++++++-- .../query/repository/MemberRoleMapper.xml | 17 +++++++++++++ 6 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberRoleDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberRoleMapper.java create mode 100644 src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberRoleMapper.xml diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java index a6d2fd8e..4499908a 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java @@ -3,6 +3,7 @@ import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; @@ -13,11 +14,13 @@ import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; +import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; import java.io.IOException; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.util.List; import java.util.Map; @Slf4j @@ -28,6 +31,8 @@ public class AlarmServiceImpl implements AlarmService { private final AlarmRepository alarmRepository; private final AuthQueryService authQueryService; private final MemberQueryService memberQueryService; + private final AlarmService alarmService; + private String getCurrentTime() { ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); @@ -37,11 +42,12 @@ private String getCurrentTime() { @Autowired public AlarmServiceImpl(AlarmRepository alarmRepository, EmitterRepository emitterRepository, - AuthQueryService authQueryService, MemberQueryService memberQueryService) { + AuthQueryService authQueryService, MemberQueryService memberQueryService, @Qualifier("alarmService") AlarmService alarmService) { this.alarmRepository = alarmRepository; this.emitterRepository = emitterRepository; this.authQueryService = authQueryService; this.memberQueryService = memberQueryService; + this.alarmService = alarmService; } @Override @@ -121,7 +127,21 @@ public Alarm createAlarm(String memberId, String message, String redirectUrl, St @Override public void sendNoticeAlarm(NoticeRegistDTO noticeRegistDTO){ - String currentTime = getCurrentTime(); + List memberIdList = memberQueryService.selectMemberByRole(noticeRegistDTO.getTag()); + + memberIdList.forEach(member -> { + String memberId = member; + String type = "NOTICE"; + String tag = noticeRegistDTO.getClassification(); + String message = "[" + tag + "] 영업 관리자 대상 공지사항이 등록되었습니다."; +// String redirectUrl = "/api/v1/notice" + noticeRegistDTO.getNoteiceId(); + String redirectUrl = "/api/v1/notice"; + String createdAt = getCurrentTime(); + + alarmService.send(memberId, message, redirectUrl, type, createdAt); + }); + + } } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberRoleDTO.java b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberRoleDTO.java new file mode 100644 index 00000000..0646569b --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberRoleDTO.java @@ -0,0 +1,13 @@ +package stanl_2.final_backend.domain.member.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class MemberRoleDTO { +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberRoleMapper.java b/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberRoleMapper.java new file mode 100644 index 00000000..0ab792fe --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberRoleMapper.java @@ -0,0 +1,10 @@ +package stanl_2.final_backend.domain.member.query.repository; + +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface MemberRoleMapper { + List findMembersbyRole(String role); +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java index 26ddc73f..83f28f3c 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java @@ -1,11 +1,15 @@ package stanl_2.final_backend.domain.member.query.service; +import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; import java.security.GeneralSecurityException; +import java.util.List; public interface MemberQueryService { MemberDTO selectMemberInfo(String name) throws GeneralSecurityException; + List selectMemberByRole(String role); + // MemberDetailDTO selectMemberDetail(String name) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index b35f448b..b1281412 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -3,25 +3,32 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.alarm.service.AlarmService; import stanl_2.final_backend.domain.member.common.exception.MemberCommonException; import stanl_2.final_backend.domain.member.common.exception.MemberErrorCode; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; import stanl_2.final_backend.domain.member.query.repository.MemberMapper; +import stanl_2.final_backend.domain.member.query.repository.MemberRoleMapper; import stanl_2.final_backend.global.utils.AESUtils; import java.security.GeneralSecurityException; +import java.util.List; @Service(value = "queryMemberService") public class MemberQueryServiceImpl implements MemberQueryService { private final MemberMapper memberMapper; private final AESUtils aesUtils; + private final MemberRoleMapper memberRoleMapper; + private final AlarmService alarmService; @Autowired - public MemberQueryServiceImpl(MemberMapper memberMapper, - AESUtils aesUtils) { + public MemberQueryServiceImpl(MemberMapper memberMapper, AESUtils aesUtils, + MemberRoleMapper memberRoleMapper, AlarmService alarmService) { this.memberMapper = memberMapper; this.aesUtils = aesUtils; + this.memberRoleMapper = memberRoleMapper; + this.alarmService = alarmService; } @Override @@ -46,4 +53,13 @@ public MemberDTO selectMemberInfo(String name) throws GeneralSecurityException { return memberInfo; } + + @Override + @Transactional(readOnly = true) + public List selectMemberByRole(String role){ + + List memberList = memberRoleMapper.findMembersbyRole(role); + + return memberList; + } } diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberRoleMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberRoleMapper.xml new file mode 100644 index 00000000..f2aae3a6 --- /dev/null +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberRoleMapper.xml @@ -0,0 +1,17 @@ + + + + + + + + + + \ No newline at end of file From a368649ec2482388f9a2e0041ec74fd4f697c19f Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 18:24:47 +0900 Subject: [PATCH 206/563] =?UTF-8?q?fix:=204=EC=B0=A8=20=EB=B0=98=EC=98=81?= =?UTF-8?q?=20=EB=B0=8F=20=EC=BF=BC=EB=A6=AC=EB=AC=B8=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20=ED=9B=84=20=EC=9E=AC=EB=B0=B0=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/alarm/aggregate/dto/AlarmDTO.java | 16 -- .../domain/alarm/aggregate/entity/Alarm.java | 48 ------ .../exception/AlarmCommonException.java | 17 -- .../common/exception/AlarmErrorCode.java | 53 ------- .../exception/AlarmExceptionResponse.java | 22 --- .../common/response/AlarmResponseMessage.java | 14 -- .../alarm/controller/AlarmController.java | 41 ----- .../alarm/repository/AlarmRepository.java | 9 -- .../alarm/repository/EmitterRepository.java | 22 --- .../repository/EmitterRepositoryImpl.java | 74 --------- .../alarm/scheduler/AlarmScheduler.java | 63 -------- .../domain/alarm/service/AlarmService.java | 19 --- .../alarm/service/AlarmServiceImpl.java | 147 ------------------ .../member/query/dto/MemberRoleDTO.java | 13 -- .../query/service/MemberQueryServiceImpl.java | 5 +- .../controller/ScheduleController.java | 3 +- 16 files changed, 2 insertions(+), 564 deletions(-) delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/aggregate/dto/AlarmDTO.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/aggregate/entity/Alarm.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmCommonException.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmErrorCode.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmExceptionResponse.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/common/response/AlarmResponseMessage.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/controller/AlarmController.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/repository/AlarmRepository.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepository.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepositoryImpl.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberRoleDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/dto/AlarmDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/dto/AlarmDTO.java deleted file mode 100644 index d5e2f253..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/dto/AlarmDTO.java +++ /dev/null @@ -1,16 +0,0 @@ -package stanl_2.final_backend.domain.alarm.aggregate.dto; - -import lombok.*; - -@AllArgsConstructor -@NoArgsConstructor -@Getter -@Setter -@ToString -public class AlarmDTO { - - private String memberId; - private String memberLoginId; - private String lastEventId; - -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/entity/Alarm.java b/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/entity/Alarm.java deleted file mode 100644 index f3e8036a..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/entity/Alarm.java +++ /dev/null @@ -1,48 +0,0 @@ -package stanl_2.final_backend.domain.alarm.aggregate.entity; - -import jakarta.persistence.*; -import lombok.*; -import org.hibernate.annotations.GenericGenerator; -import org.hibernate.annotations.Parameter; -import stanl_2.final_backend.global.config.PrefixGeneratorConfig; - -@Entity -@Table(name = "TB_ALARM") -@Getter -@Setter -@AllArgsConstructor -@NoArgsConstructor -public class Alarm { - - @Id - @GeneratedValue(generator = "PrefixGeneratorConfig") - @GenericGenerator(name = "PrefixGeneratorConfig", - type = PrefixGeneratorConfig.class, - parameters = @Parameter(name = "prefix", value = "ALR") - ) - @Column(name = "ALR_ID", nullable = false) - private String alarmId; - - @Column(name = "ALR_MSG", nullable = false) - private String message; - - @Column(name = "ALR_URL", nullable = false) - private String redirectUrl; - - @Column(name = "ALR_TYPE", nullable = false) - private String type = "NOTICE"; - - @Column(name = "ALR_READ_STAT", nullable = false) - private Boolean readStatus; - - @Column(name = "CREATED_AT", nullable = false) - private String createdAt; - -// @Column(name = "ALR_STAT", nullable = false) -// private String status = "PENDING"; - - @Column(name = "MEM_ID", nullable = false) - private String memberId; - - -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmCommonException.java b/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmCommonException.java deleted file mode 100644 index a86154c2..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmCommonException.java +++ /dev/null @@ -1,17 +0,0 @@ -package stanl_2.final_backend.domain.alarm.common.exception; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; - -@Getter -@RequiredArgsConstructor -public class AlarmCommonException extends RuntimeException { - private final AlarmErrorCode alarmErrorCode; - - // 에러 발생시 ErroCode 별 메시지 - @Override - public String getMessage() { - return this.alarmErrorCode.getMsg(); - } -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmErrorCode.java b/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmErrorCode.java deleted file mode 100644 index b0a92704..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmErrorCode.java +++ /dev/null @@ -1,53 +0,0 @@ -package stanl_2.final_backend.domain.alarm.common.exception; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.springframework.http.HttpStatus; - -@Getter -@AllArgsConstructor -public enum AlarmErrorCode { - - /** - * 400(Bad Request) - * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. - */ - - - - /** - * 401(Unauthorized) - * 비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만, - * 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다. - * 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다. - */ - - - /** - * 403(Forbidden) - * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. - * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다. - */ - - - /** - * 404(Not Found) - * 서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다. - * 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있습니다. - * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. - * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. - */ - - - /** - * 500(Internal Server Error) - * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. - */ - INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); - - - - private final Integer code; - private final HttpStatus httpStatus; - private final String msg; -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmExceptionResponse.java deleted file mode 100644 index 075a2bbd..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmExceptionResponse.java +++ /dev/null @@ -1,22 +0,0 @@ -package stanl_2.final_backend.domain.alarm.common.exception; - -import lombok.Getter; -import org.springframework.http.HttpStatus; - -@Getter -public class AlarmExceptionResponse { - private final Integer code; - private final String msg; - private final HttpStatus httpStatus; - - public AlarmExceptionResponse(AlarmErrorCode alarmErrorCode) { - this.code = alarmErrorCode.getCode(); - this.msg = alarmErrorCode.getMsg(); - this.httpStatus = alarmErrorCode.getHttpStatus(); - } - - public static AlarmExceptionResponse of(AlarmErrorCode alarmErrorCode) { - return new AlarmExceptionResponse(alarmErrorCode); - } - -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/common/response/AlarmResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/alarm/common/response/AlarmResponseMessage.java deleted file mode 100644 index b5f9d40d..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/common/response/AlarmResponseMessage.java +++ /dev/null @@ -1,14 +0,0 @@ -package stanl_2.final_backend.domain.alarm.common.response; - -import lombok.*; - -@NoArgsConstructor -@AllArgsConstructor -@Builder -@Getter -@Setter -public class AlarmResponseMessage { - private Integer httpStatus; - private String msg; - private Object result; -} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/controller/AlarmController.java b/src/main/java/stanl_2/final_backend/domain/alarm/controller/AlarmController.java deleted file mode 100644 index ae0113e0..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/controller/AlarmController.java +++ /dev/null @@ -1,41 +0,0 @@ -package stanl_2.final_backend.domain.alarm.controller; - -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import stanl_2.final_backend.domain.alarm.aggregate.dto.AlarmDTO; -import stanl_2.final_backend.domain.alarm.service.AlarmService; - -import java.security.Principal; - -@RestController("queryAlarmController") -@RequestMapping("/api/v1/alarm") -public class AlarmController { - - private final AlarmService alarmService; - - @Autowired - public AlarmController(AlarmService alarmService) { - this.alarmService = alarmService; - } - - @GetMapping(value= "/connect", produces = MediaType.TEXT_EVENT_STREAM_VALUE) - public ResponseEntity subscribe(Principal principal, - @RequestHeader(value = "Last-Event-ID", required = false, - defaultValue = "") String lastEventId, - HttpServletResponse response){ - - String memberLoginId = principal.getName(); - - AlarmDTO alarmDTO = new AlarmDTO(); - alarmDTO.setMemberLoginId(memberLoginId); - alarmDTO.setLastEventId(lastEventId); - - return ResponseEntity.ok(alarmService.subscribe(alarmDTO, response)); - } - -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/repository/AlarmRepository.java b/src/main/java/stanl_2/final_backend/domain/alarm/repository/AlarmRepository.java deleted file mode 100644 index 7392412b..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/repository/AlarmRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package stanl_2.final_backend.domain.alarm.repository; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; -import stanl_2.final_backend.domain.alarm.aggregate.entity.Alarm; - -@Repository -public interface AlarmRepository extends JpaRepository { -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepository.java b/src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepository.java deleted file mode 100644 index a36aab22..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepository.java +++ /dev/null @@ -1,22 +0,0 @@ -package stanl_2.final_backend.domain.alarm.repository; - -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import stanl_2.final_backend.domain.alarm.aggregate.entity.Alarm; - -import java.util.Map; - -public interface EmitterRepository { - SseEmitter save(String emitterId, SseEmitter sseEmitter); - - void deleteAllByEmitterId(String emitterId); - - Map findAllEmitterStartWithByMemberId(String memberId); - - Map findAllEventCacheStartWithByMemberId(String memberId); - - void saveEventCache(String eventCacheId, Object event); - - void deleteAllEmitterStartWithMemberId(String memberId); - - void deleteAllEventCacheStartWithmemberId(String memberId); -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepositoryImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepositoryImpl.java deleted file mode 100644 index e09c7f27..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepositoryImpl.java +++ /dev/null @@ -1,74 +0,0 @@ -package stanl_2.final_backend.domain.alarm.repository; - -import org.springframework.stereotype.Repository; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -@Repository -public class EmitterRepositoryImpl implements EmitterRepository{ - - private final Map emitters = new ConcurrentHashMap<>(); - private final Map eventCache = new ConcurrentHashMap<>(); - - @Override - public SseEmitter save(String emitterId, SseEmitter sseEmitter) { - - emitters.put(emitterId, sseEmitter); - return sseEmitter; - } - - @Override - public void deleteAllByEmitterId(String emitterId) { - emitters.forEach((key, emitter) -> { - if (key.startsWith(emitterId)) { - emitters.remove(key); - } - }); - } - - @Override - public Map findAllEmitterStartWithByMemberId(String memberId) { - return emitters.entrySet().stream() - .filter(entry -> entry.getKey().startsWith(memberId)) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - } - - @Override - public Map findAllEventCacheStartWithByMemberId(String memberId) { - return eventCache.entrySet().stream() - .filter(entry -> entry.getKey().startsWith(memberId)) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - } - - @Override - public void saveEventCache(String eventCacheId, Object event) { - eventCache.put(eventCacheId, event); - } - - @Override - public void deleteAllEmitterStartWithMemberId(String memberId) { - emitters.forEach( - (key, emitter) -> { - if (key.startsWith(memberId)){ - emitters.remove(key); - } - } - ); - } - - @Override - public void deleteAllEventCacheStartWithmemberId(String memberId) { - eventCache.forEach( - (key, emitter) -> { - if (key.startsWith(memberId)){ - eventCache.remove(key); - } - } - ); - } - - -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java deleted file mode 100644 index 411e56db..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java +++ /dev/null @@ -1,63 +0,0 @@ -package stanl_2.final_backend.domain.alarm.scheduler; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import stanl_2.final_backend.domain.alarm.aggregate.entity.Alarm; -import stanl_2.final_backend.domain.alarm.repository.EmitterRepository; -import stanl_2.final_backend.domain.alarm.service.AlarmService; -import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; -import stanl_2.final_backend.domain.schedule.query.service.ScheduleQueryService; - -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.List; -import java.util.Map; - - -@Service -public class AlarmScheduler { - - private final AlarmService alarmService; - private final ScheduleQueryService scheduleQueryService; - private final EmitterRepository emitterRepository; - private String getCurrentTime() { - ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); - return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); - } - - @Autowired - public AlarmScheduler(AlarmService alarmService, ScheduleQueryService scheduleQueryService, - EmitterRepository emitterRepository) { - this.alarmService = alarmService; - this.scheduleQueryService = scheduleQueryService; - this.emitterRepository = emitterRepository; - } - - @Scheduled(cron = "0 0 2 * * *") // 매일 새벽 2시에 실행 - @Transactional - public void alarmTodaySchedule(){ - - String currentDay = getCurrentTime().substring(0,10); - - List todaySchedules = scheduleQueryService.findSchedulesByDate(currentDay); - - // 사용자 별로 알림 전송 - todaySchedules.forEach(schedule -> { - String Hour = schedule.getStartAt().substring(11,13); - String Minute = schedule.getStartAt().substring(14,16); - - String memberId = schedule.getMemberId(); - String type = schedule.getTag(); - String message = "[" + type + "] 금일 " + Hour + "시 " + Minute + "분에 '" - + schedule.getName() + "' 일정이 있습니다"; - String redirectUrl = "/api/v1/schedule"; - String createdAt = getCurrentTime(); - - alarmService.send(memberId, message, redirectUrl, type, createdAt); - }); - } -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java deleted file mode 100644 index 7e774686..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java +++ /dev/null @@ -1,19 +0,0 @@ -package stanl_2.final_backend.domain.alarm.service; - -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import stanl_2.final_backend.domain.alarm.aggregate.dto.AlarmDTO; -import stanl_2.final_backend.domain.alarm.aggregate.entity.Alarm; -import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; - -public interface AlarmService { - SseEmitter subscribe(AlarmDTO alarmDTO, HttpServletResponse response); - - void sendToClient(SseEmitter emitter, String emitterId, Object data); - - void send(String memberLoginId, String message, String redirectUrl, String type, String createdAt); - - Alarm createAlarm(String memberId, String message, String redirectUrl, String type, String createdAt); - - void sendNoticeAlarm(NoticeRegistDTO noticeRegistDTO); -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java deleted file mode 100644 index 4499908a..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java +++ /dev/null @@ -1,147 +0,0 @@ -package stanl_2.final_backend.domain.alarm.service; - -import jakarta.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import stanl_2.final_backend.domain.alarm.aggregate.dto.AlarmDTO; -import stanl_2.final_backend.domain.alarm.aggregate.entity.Alarm; -import stanl_2.final_backend.domain.alarm.repository.AlarmRepository; -import stanl_2.final_backend.domain.alarm.repository.EmitterRepository; -import stanl_2.final_backend.domain.member.query.service.AuthQueryService; -import stanl_2.final_backend.domain.member.query.service.MemberQueryService; -import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; -import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; - -import java.io.IOException; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.List; -import java.util.Map; - -@Slf4j -@Service -public class AlarmServiceImpl implements AlarmService { - - private final EmitterRepository emitterRepository; - private final AlarmRepository alarmRepository; - private final AuthQueryService authQueryService; - private final MemberQueryService memberQueryService; - private final AlarmService alarmService; - - private String getCurrentTime() { - ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); - return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); - } - - private static final Long DEFAULT_TIMEOUT = 60L * 1000 * 60; - - @Autowired - public AlarmServiceImpl(AlarmRepository alarmRepository, EmitterRepository emitterRepository, - AuthQueryService authQueryService, MemberQueryService memberQueryService, @Qualifier("alarmService") AlarmService alarmService) { - this.alarmRepository = alarmRepository; - this.emitterRepository = emitterRepository; - this.authQueryService = authQueryService; - this.memberQueryService = memberQueryService; - this.alarmService = alarmService; - } - - @Override - public SseEmitter subscribe(AlarmDTO alarmDTO, HttpServletResponse response) { - - String lastEventId = alarmDTO.getLastEventId(); - String memberId = authQueryService.selectMemberIdByLoginId(alarmDTO.getMemberLoginId()); - String emitterId = memberId + "_" + System.currentTimeMillis(); - - // 클라이언트의 sse 연결 요청에 응답하기 위한 SseEmitter 객체 생성 - // 유효시간 지정으로 시간이 지나면 클라이언트에서 자동으로 재연결 요청함 - SseEmitter emitter = emitterRepository.save(emitterId, new SseEmitter(DEFAULT_TIMEOUT)); - response.setHeader("X-Accel-Buffering", "no"); // NGINX PROXY 에서의 필요설정 불필요한 버퍼링방지 - - // SseEmitter의 완료/시간초과/에러로 인한 전송 불가 시 sseEmitter 삭제 - emitter.onCompletion(() -> emitterRepository.deleteAllByEmitterId(emitterId)); - emitter.onTimeout(() -> emitterRepository.deleteAllByEmitterId(emitterId)); - emitter.onError((e) -> emitterRepository.deleteAllByEmitterId(emitterId)); - - // 연결 직후, 데이터 전송이 없을 시 503 에러 발생. 에러 방지 위한 더미데이터 전송 - sendToClient(emitter, emitterId, emitterId + "님 연결되었습니다."); - - // 클라이언트가 미수신한 Event 유실 예방, 연결이 끊켰거나 미수신된 데이터를 다 찾아서 보내줌 - if (!lastEventId.isEmpty()) { - Map events = emitterRepository.findAllEventCacheStartWithByMemberId(memberId); - events.entrySet().stream() - .filter(entry -> lastEventId.compareTo(entry.getKey()) < 0) - .forEach(entry -> sendToClient(emitter, entry.getKey(), entry.getValue())); - } - - return emitter; - } - - @Override - public void sendToClient(SseEmitter emitter, String emitterId, Object data) { - - try { - emitter.send(SseEmitter.event() - .id(emitterId) - .name("sse") - .data(data)); - } catch (IOException e){ - emitterRepository.deleteAllByEmitterId(emitterId); - log.error("SSE 연결 오류 발생", e); - } - } - - @Override - @Transactional - public void send(String memberId, String message, String redirectUrl, String type, String createdAt){ - - Alarm alarm = alarmRepository.save(createAlarm(memberId, message, redirectUrl, type, createdAt)); - - Map sseEmitters = emitterRepository.findAllEmitterStartWithByMemberId(memberId); - sseEmitters.forEach( - (key, emitter) -> { - emitterRepository.saveEventCache(key, alarm); - sendToClient(emitter, key, alarm); - } - ); - } - - @Override - public Alarm createAlarm(String memberId, String message, String redirectUrl, String type, String createdAt) { - - Alarm alarm = new Alarm(); - alarm.setMemberId(memberId); - alarm.setMessage(message); - alarm.setRedirectUrl(redirectUrl); - alarm.setType(type); - alarm.setReadStatus(false); - alarm.setCreatedAt(createdAt); - - return alarm; - } - - @Override - public void sendNoticeAlarm(NoticeRegistDTO noticeRegistDTO){ - - List memberIdList = memberQueryService.selectMemberByRole(noticeRegistDTO.getTag()); - - memberIdList.forEach(member -> { - String memberId = member; - String type = "NOTICE"; - String tag = noticeRegistDTO.getClassification(); - String message = "[" + tag + "] 영업 관리자 대상 공지사항이 등록되었습니다."; -// String redirectUrl = "/api/v1/notice" + noticeRegistDTO.getNoteiceId(); - String redirectUrl = "/api/v1/notice"; - String createdAt = getCurrentTime(); - - alarmService.send(memberId, message, redirectUrl, type, createdAt); - }); - - - } - -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberRoleDTO.java b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberRoleDTO.java deleted file mode 100644 index 0646569b..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberRoleDTO.java +++ /dev/null @@ -1,13 +0,0 @@ -package stanl_2.final_backend.domain.member.query.dto; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@AllArgsConstructor -@NoArgsConstructor -@Setter -@Getter -public class MemberRoleDTO { -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index b1281412..fb9e9e28 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -3,7 +3,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.alarm.service.AlarmService; import stanl_2.final_backend.domain.member.common.exception.MemberCommonException; import stanl_2.final_backend.domain.member.common.exception.MemberErrorCode; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; @@ -20,15 +19,13 @@ public class MemberQueryServiceImpl implements MemberQueryService { private final MemberMapper memberMapper; private final AESUtils aesUtils; private final MemberRoleMapper memberRoleMapper; - private final AlarmService alarmService; @Autowired public MemberQueryServiceImpl(MemberMapper memberMapper, AESUtils aesUtils, - MemberRoleMapper memberRoleMapper, AlarmService alarmService) { + MemberRoleMapper memberRoleMapper) { this.memberMapper = memberMapper; this.aesUtils = aesUtils; this.memberRoleMapper = memberRoleMapper; - this.alarmService = alarmService; } @Override diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java index 7aa3c733..d0f9088c 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java @@ -8,7 +8,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.alarm.service.AlarmService; import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleDeleteDTO; import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleModifyDTO; import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleRegistDTO; @@ -24,7 +23,7 @@ public class ScheduleController { private final ScheduleCommandService scheduleCommandService; @Autowired - public ScheduleController(ScheduleCommandService scheduleCommandService, AlarmService alarmService) { + public ScheduleController(ScheduleCommandService scheduleCommandService) { this.scheduleCommandService = scheduleCommandService; } From 66290e50061c608abd4b20b4ba023784311f82fc Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 18:43:25 +0900 Subject: [PATCH 207/563] =?UTF-8?q?fix:=20MemberRoleMapper=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/query/repository/MemberRoleMapper.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberRoleMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberRoleMapper.xml index f2aae3a6..c510ab3c 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberRoleMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberRoleMapper.xml @@ -8,7 +8,7 @@ - SELECT a.mem_id FROM tb_mem_role a From 1cfabb4dd39a784ce91f1fc883d7ecbcc4dbaaee Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Tue, 19 Nov 2024 19:51:43 +0900 Subject: [PATCH 208/563] =?UTF-8?q?feat:=20=EC=A1=B0=EC=A7=81=EB=8F=84=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C(#94)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../response/SampleResponseMessage.java | 2 +- .../domain/alarm/aggregate/dto/AlarmDTO.java | 16 -- .../domain/alarm/aggregate/entity/Alarm.java | 48 ------ .../exception/AlarmCommonException.java | 17 -- .../exception/AlarmExceptionResponse.java | 22 --- .../common/response/AlarmResponseMessage.java | 14 -- .../alarm/controller/AlarmController.java | 41 ----- .../alarm/repository/AlarmRepository.java | 9 -- .../alarm/repository/EmitterRepository.java | 22 --- .../repository/EmitterRepositoryImpl.java | 74 --------- .../alarm/scheduler/AlarmScheduler.java | 63 -------- .../domain/alarm/service/AlarmService.java | 19 --- .../alarm/service/AlarmServiceImpl.java | 147 ------------------ .../query/service/CareerQueryServiceImpl.java | 2 +- .../CertificationQueryServiceImpl.java | 2 +- .../controller/CustomerController.java | 1 + .../service/CustomerQueryServiceImpl.java | 6 +- .../service/EducationQueryServiceImpl.java | 2 +- .../query/service/FamilyQueryServiceImpl.java | 2 +- .../member/query/dto/MemberRoleDTO.java | 2 + .../query/service/AuthQueryServiceImpl.java | 2 +- .../query/service/MemberQueryServiceImpl.java | 11 +- .../controller/OrganizationController.java | 18 +++ .../dto/OrganizationRegisterDTO.java | 15 ++ .../service/OrganizationCommandService.java | 4 + .../domain/aggregate/entity/Organization.java | 32 ++++ .../repository/OrganizationRepository.java | 9 ++ .../OrganizationCommandServiceImpl.java | 23 +++ .../OrganizationCommonException.java | 17 ++ .../exception/OrganizationErrorCode.java} | 11 +- .../OrganizationExceptionResponse.java | 23 +++ .../response/OrganizationResponseMessage.java | 14 ++ .../controller/OrganizationController.java | 50 ++++++ .../query/dto/OrganizationDTO.java | 16 ++ .../query/repository/OrganizationMapper.java | 11 ++ .../service/OrganizationQueryService.java | 10 ++ .../service/OrganizationQueryServiceImpl.java | 29 ++++ .../controller/ScheduleController.java | 6 +- .../query/repository/CustomerMapper.xml | 2 +- .../query/repository/MemberRoleMapper.xml | 2 +- .../query/repository/OrganizationMapper.xml | 18 +++ 41 files changed, 318 insertions(+), 516 deletions(-) delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/aggregate/dto/AlarmDTO.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/aggregate/entity/Alarm.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmCommonException.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmExceptionResponse.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/common/response/AlarmResponseMessage.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/controller/AlarmController.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/repository/AlarmRepository.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepository.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepositoryImpl.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java create mode 100644 src/main/java/stanl_2/final_backend/domain/organization/command/application/controller/OrganizationController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/organization/command/application/dto/OrganizationRegisterDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/organization/command/application/service/OrganizationCommandService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/organization/command/domain/aggregate/entity/Organization.java create mode 100644 src/main/java/stanl_2/final_backend/domain/organization/command/domain/repository/OrganizationRepository.java create mode 100644 src/main/java/stanl_2/final_backend/domain/organization/command/domain/service/OrganizationCommandServiceImpl.java create mode 100644 src/main/java/stanl_2/final_backend/domain/organization/common/exception/OrganizationCommonException.java rename src/main/java/stanl_2/final_backend/domain/{alarm/common/exception/AlarmErrorCode.java => organization/common/exception/OrganizationErrorCode.java} (86%) create mode 100644 src/main/java/stanl_2/final_backend/domain/organization/common/exception/OrganizationExceptionResponse.java create mode 100644 src/main/java/stanl_2/final_backend/domain/organization/common/response/OrganizationResponseMessage.java create mode 100644 src/main/java/stanl_2/final_backend/domain/organization/query/controller/OrganizationController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/organization/query/dto/OrganizationDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/organization/query/repository/OrganizationMapper.java create mode 100644 src/main/java/stanl_2/final_backend/domain/organization/query/service/OrganizationQueryService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/organization/query/service/OrganizationQueryServiceImpl.java create mode 100644 src/main/resources/stanl_2/final_backend/domain/organization/query/repository/OrganizationMapper.xml diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/common/response/SampleResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/A_sample/common/response/SampleResponseMessage.java index 92a165f9..f1fb01b7 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/common/response/SampleResponseMessage.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/common/response/SampleResponseMessage.java @@ -8,7 +8,7 @@ @Getter @Setter public class SampleResponseMessage { - private int httpStatus; + private Integer httpStatus; private String msg; private Object result; } \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/dto/AlarmDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/dto/AlarmDTO.java deleted file mode 100644 index d5e2f253..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/dto/AlarmDTO.java +++ /dev/null @@ -1,16 +0,0 @@ -package stanl_2.final_backend.domain.alarm.aggregate.dto; - -import lombok.*; - -@AllArgsConstructor -@NoArgsConstructor -@Getter -@Setter -@ToString -public class AlarmDTO { - - private String memberId; - private String memberLoginId; - private String lastEventId; - -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/entity/Alarm.java b/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/entity/Alarm.java deleted file mode 100644 index f3e8036a..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/aggregate/entity/Alarm.java +++ /dev/null @@ -1,48 +0,0 @@ -package stanl_2.final_backend.domain.alarm.aggregate.entity; - -import jakarta.persistence.*; -import lombok.*; -import org.hibernate.annotations.GenericGenerator; -import org.hibernate.annotations.Parameter; -import stanl_2.final_backend.global.config.PrefixGeneratorConfig; - -@Entity -@Table(name = "TB_ALARM") -@Getter -@Setter -@AllArgsConstructor -@NoArgsConstructor -public class Alarm { - - @Id - @GeneratedValue(generator = "PrefixGeneratorConfig") - @GenericGenerator(name = "PrefixGeneratorConfig", - type = PrefixGeneratorConfig.class, - parameters = @Parameter(name = "prefix", value = "ALR") - ) - @Column(name = "ALR_ID", nullable = false) - private String alarmId; - - @Column(name = "ALR_MSG", nullable = false) - private String message; - - @Column(name = "ALR_URL", nullable = false) - private String redirectUrl; - - @Column(name = "ALR_TYPE", nullable = false) - private String type = "NOTICE"; - - @Column(name = "ALR_READ_STAT", nullable = false) - private Boolean readStatus; - - @Column(name = "CREATED_AT", nullable = false) - private String createdAt; - -// @Column(name = "ALR_STAT", nullable = false) -// private String status = "PENDING"; - - @Column(name = "MEM_ID", nullable = false) - private String memberId; - - -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmCommonException.java b/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmCommonException.java deleted file mode 100644 index a86154c2..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmCommonException.java +++ /dev/null @@ -1,17 +0,0 @@ -package stanl_2.final_backend.domain.alarm.common.exception; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; - -@Getter -@RequiredArgsConstructor -public class AlarmCommonException extends RuntimeException { - private final AlarmErrorCode alarmErrorCode; - - // 에러 발생시 ErroCode 별 메시지 - @Override - public String getMessage() { - return this.alarmErrorCode.getMsg(); - } -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmExceptionResponse.java deleted file mode 100644 index 075a2bbd..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmExceptionResponse.java +++ /dev/null @@ -1,22 +0,0 @@ -package stanl_2.final_backend.domain.alarm.common.exception; - -import lombok.Getter; -import org.springframework.http.HttpStatus; - -@Getter -public class AlarmExceptionResponse { - private final Integer code; - private final String msg; - private final HttpStatus httpStatus; - - public AlarmExceptionResponse(AlarmErrorCode alarmErrorCode) { - this.code = alarmErrorCode.getCode(); - this.msg = alarmErrorCode.getMsg(); - this.httpStatus = alarmErrorCode.getHttpStatus(); - } - - public static AlarmExceptionResponse of(AlarmErrorCode alarmErrorCode) { - return new AlarmExceptionResponse(alarmErrorCode); - } - -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/common/response/AlarmResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/alarm/common/response/AlarmResponseMessage.java deleted file mode 100644 index b5f9d40d..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/common/response/AlarmResponseMessage.java +++ /dev/null @@ -1,14 +0,0 @@ -package stanl_2.final_backend.domain.alarm.common.response; - -import lombok.*; - -@NoArgsConstructor -@AllArgsConstructor -@Builder -@Getter -@Setter -public class AlarmResponseMessage { - private Integer httpStatus; - private String msg; - private Object result; -} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/controller/AlarmController.java b/src/main/java/stanl_2/final_backend/domain/alarm/controller/AlarmController.java deleted file mode 100644 index ae0113e0..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/controller/AlarmController.java +++ /dev/null @@ -1,41 +0,0 @@ -package stanl_2.final_backend.domain.alarm.controller; - -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import stanl_2.final_backend.domain.alarm.aggregate.dto.AlarmDTO; -import stanl_2.final_backend.domain.alarm.service.AlarmService; - -import java.security.Principal; - -@RestController("queryAlarmController") -@RequestMapping("/api/v1/alarm") -public class AlarmController { - - private final AlarmService alarmService; - - @Autowired - public AlarmController(AlarmService alarmService) { - this.alarmService = alarmService; - } - - @GetMapping(value= "/connect", produces = MediaType.TEXT_EVENT_STREAM_VALUE) - public ResponseEntity subscribe(Principal principal, - @RequestHeader(value = "Last-Event-ID", required = false, - defaultValue = "") String lastEventId, - HttpServletResponse response){ - - String memberLoginId = principal.getName(); - - AlarmDTO alarmDTO = new AlarmDTO(); - alarmDTO.setMemberLoginId(memberLoginId); - alarmDTO.setLastEventId(lastEventId); - - return ResponseEntity.ok(alarmService.subscribe(alarmDTO, response)); - } - -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/repository/AlarmRepository.java b/src/main/java/stanl_2/final_backend/domain/alarm/repository/AlarmRepository.java deleted file mode 100644 index 7392412b..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/repository/AlarmRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package stanl_2.final_backend.domain.alarm.repository; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; -import stanl_2.final_backend.domain.alarm.aggregate.entity.Alarm; - -@Repository -public interface AlarmRepository extends JpaRepository { -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepository.java b/src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepository.java deleted file mode 100644 index a36aab22..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepository.java +++ /dev/null @@ -1,22 +0,0 @@ -package stanl_2.final_backend.domain.alarm.repository; - -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import stanl_2.final_backend.domain.alarm.aggregate.entity.Alarm; - -import java.util.Map; - -public interface EmitterRepository { - SseEmitter save(String emitterId, SseEmitter sseEmitter); - - void deleteAllByEmitterId(String emitterId); - - Map findAllEmitterStartWithByMemberId(String memberId); - - Map findAllEventCacheStartWithByMemberId(String memberId); - - void saveEventCache(String eventCacheId, Object event); - - void deleteAllEmitterStartWithMemberId(String memberId); - - void deleteAllEventCacheStartWithmemberId(String memberId); -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepositoryImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepositoryImpl.java deleted file mode 100644 index e09c7f27..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/repository/EmitterRepositoryImpl.java +++ /dev/null @@ -1,74 +0,0 @@ -package stanl_2.final_backend.domain.alarm.repository; - -import org.springframework.stereotype.Repository; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -@Repository -public class EmitterRepositoryImpl implements EmitterRepository{ - - private final Map emitters = new ConcurrentHashMap<>(); - private final Map eventCache = new ConcurrentHashMap<>(); - - @Override - public SseEmitter save(String emitterId, SseEmitter sseEmitter) { - - emitters.put(emitterId, sseEmitter); - return sseEmitter; - } - - @Override - public void deleteAllByEmitterId(String emitterId) { - emitters.forEach((key, emitter) -> { - if (key.startsWith(emitterId)) { - emitters.remove(key); - } - }); - } - - @Override - public Map findAllEmitterStartWithByMemberId(String memberId) { - return emitters.entrySet().stream() - .filter(entry -> entry.getKey().startsWith(memberId)) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - } - - @Override - public Map findAllEventCacheStartWithByMemberId(String memberId) { - return eventCache.entrySet().stream() - .filter(entry -> entry.getKey().startsWith(memberId)) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - } - - @Override - public void saveEventCache(String eventCacheId, Object event) { - eventCache.put(eventCacheId, event); - } - - @Override - public void deleteAllEmitterStartWithMemberId(String memberId) { - emitters.forEach( - (key, emitter) -> { - if (key.startsWith(memberId)){ - emitters.remove(key); - } - } - ); - } - - @Override - public void deleteAllEventCacheStartWithmemberId(String memberId) { - eventCache.forEach( - (key, emitter) -> { - if (key.startsWith(memberId)){ - eventCache.remove(key); - } - } - ); - } - - -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java deleted file mode 100644 index 411e56db..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java +++ /dev/null @@ -1,63 +0,0 @@ -package stanl_2.final_backend.domain.alarm.scheduler; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import stanl_2.final_backend.domain.alarm.aggregate.entity.Alarm; -import stanl_2.final_backend.domain.alarm.repository.EmitterRepository; -import stanl_2.final_backend.domain.alarm.service.AlarmService; -import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; -import stanl_2.final_backend.domain.schedule.query.service.ScheduleQueryService; - -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.List; -import java.util.Map; - - -@Service -public class AlarmScheduler { - - private final AlarmService alarmService; - private final ScheduleQueryService scheduleQueryService; - private final EmitterRepository emitterRepository; - private String getCurrentTime() { - ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); - return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); - } - - @Autowired - public AlarmScheduler(AlarmService alarmService, ScheduleQueryService scheduleQueryService, - EmitterRepository emitterRepository) { - this.alarmService = alarmService; - this.scheduleQueryService = scheduleQueryService; - this.emitterRepository = emitterRepository; - } - - @Scheduled(cron = "0 0 2 * * *") // 매일 새벽 2시에 실행 - @Transactional - public void alarmTodaySchedule(){ - - String currentDay = getCurrentTime().substring(0,10); - - List todaySchedules = scheduleQueryService.findSchedulesByDate(currentDay); - - // 사용자 별로 알림 전송 - todaySchedules.forEach(schedule -> { - String Hour = schedule.getStartAt().substring(11,13); - String Minute = schedule.getStartAt().substring(14,16); - - String memberId = schedule.getMemberId(); - String type = schedule.getTag(); - String message = "[" + type + "] 금일 " + Hour + "시 " + Minute + "분에 '" - + schedule.getName() + "' 일정이 있습니다"; - String redirectUrl = "/api/v1/schedule"; - String createdAt = getCurrentTime(); - - alarmService.send(memberId, message, redirectUrl, type, createdAt); - }); - } -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java deleted file mode 100644 index 7e774686..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java +++ /dev/null @@ -1,19 +0,0 @@ -package stanl_2.final_backend.domain.alarm.service; - -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import stanl_2.final_backend.domain.alarm.aggregate.dto.AlarmDTO; -import stanl_2.final_backend.domain.alarm.aggregate.entity.Alarm; -import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; - -public interface AlarmService { - SseEmitter subscribe(AlarmDTO alarmDTO, HttpServletResponse response); - - void sendToClient(SseEmitter emitter, String emitterId, Object data); - - void send(String memberLoginId, String message, String redirectUrl, String type, String createdAt); - - Alarm createAlarm(String memberId, String message, String redirectUrl, String type, String createdAt); - - void sendNoticeAlarm(NoticeRegistDTO noticeRegistDTO); -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java deleted file mode 100644 index 4499908a..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java +++ /dev/null @@ -1,147 +0,0 @@ -package stanl_2.final_backend.domain.alarm.service; - -import jakarta.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import stanl_2.final_backend.domain.alarm.aggregate.dto.AlarmDTO; -import stanl_2.final_backend.domain.alarm.aggregate.entity.Alarm; -import stanl_2.final_backend.domain.alarm.repository.AlarmRepository; -import stanl_2.final_backend.domain.alarm.repository.EmitterRepository; -import stanl_2.final_backend.domain.member.query.service.AuthQueryService; -import stanl_2.final_backend.domain.member.query.service.MemberQueryService; -import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; -import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; - -import java.io.IOException; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.List; -import java.util.Map; - -@Slf4j -@Service -public class AlarmServiceImpl implements AlarmService { - - private final EmitterRepository emitterRepository; - private final AlarmRepository alarmRepository; - private final AuthQueryService authQueryService; - private final MemberQueryService memberQueryService; - private final AlarmService alarmService; - - private String getCurrentTime() { - ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); - return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); - } - - private static final Long DEFAULT_TIMEOUT = 60L * 1000 * 60; - - @Autowired - public AlarmServiceImpl(AlarmRepository alarmRepository, EmitterRepository emitterRepository, - AuthQueryService authQueryService, MemberQueryService memberQueryService, @Qualifier("alarmService") AlarmService alarmService) { - this.alarmRepository = alarmRepository; - this.emitterRepository = emitterRepository; - this.authQueryService = authQueryService; - this.memberQueryService = memberQueryService; - this.alarmService = alarmService; - } - - @Override - public SseEmitter subscribe(AlarmDTO alarmDTO, HttpServletResponse response) { - - String lastEventId = alarmDTO.getLastEventId(); - String memberId = authQueryService.selectMemberIdByLoginId(alarmDTO.getMemberLoginId()); - String emitterId = memberId + "_" + System.currentTimeMillis(); - - // 클라이언트의 sse 연결 요청에 응답하기 위한 SseEmitter 객체 생성 - // 유효시간 지정으로 시간이 지나면 클라이언트에서 자동으로 재연결 요청함 - SseEmitter emitter = emitterRepository.save(emitterId, new SseEmitter(DEFAULT_TIMEOUT)); - response.setHeader("X-Accel-Buffering", "no"); // NGINX PROXY 에서의 필요설정 불필요한 버퍼링방지 - - // SseEmitter의 완료/시간초과/에러로 인한 전송 불가 시 sseEmitter 삭제 - emitter.onCompletion(() -> emitterRepository.deleteAllByEmitterId(emitterId)); - emitter.onTimeout(() -> emitterRepository.deleteAllByEmitterId(emitterId)); - emitter.onError((e) -> emitterRepository.deleteAllByEmitterId(emitterId)); - - // 연결 직후, 데이터 전송이 없을 시 503 에러 발생. 에러 방지 위한 더미데이터 전송 - sendToClient(emitter, emitterId, emitterId + "님 연결되었습니다."); - - // 클라이언트가 미수신한 Event 유실 예방, 연결이 끊켰거나 미수신된 데이터를 다 찾아서 보내줌 - if (!lastEventId.isEmpty()) { - Map events = emitterRepository.findAllEventCacheStartWithByMemberId(memberId); - events.entrySet().stream() - .filter(entry -> lastEventId.compareTo(entry.getKey()) < 0) - .forEach(entry -> sendToClient(emitter, entry.getKey(), entry.getValue())); - } - - return emitter; - } - - @Override - public void sendToClient(SseEmitter emitter, String emitterId, Object data) { - - try { - emitter.send(SseEmitter.event() - .id(emitterId) - .name("sse") - .data(data)); - } catch (IOException e){ - emitterRepository.deleteAllByEmitterId(emitterId); - log.error("SSE 연결 오류 발생", e); - } - } - - @Override - @Transactional - public void send(String memberId, String message, String redirectUrl, String type, String createdAt){ - - Alarm alarm = alarmRepository.save(createAlarm(memberId, message, redirectUrl, type, createdAt)); - - Map sseEmitters = emitterRepository.findAllEmitterStartWithByMemberId(memberId); - sseEmitters.forEach( - (key, emitter) -> { - emitterRepository.saveEventCache(key, alarm); - sendToClient(emitter, key, alarm); - } - ); - } - - @Override - public Alarm createAlarm(String memberId, String message, String redirectUrl, String type, String createdAt) { - - Alarm alarm = new Alarm(); - alarm.setMemberId(memberId); - alarm.setMessage(message); - alarm.setRedirectUrl(redirectUrl); - alarm.setType(type); - alarm.setReadStatus(false); - alarm.setCreatedAt(createdAt); - - return alarm; - } - - @Override - public void sendNoticeAlarm(NoticeRegistDTO noticeRegistDTO){ - - List memberIdList = memberQueryService.selectMemberByRole(noticeRegistDTO.getTag()); - - memberIdList.forEach(member -> { - String memberId = member; - String type = "NOTICE"; - String tag = noticeRegistDTO.getClassification(); - String message = "[" + tag + "] 영업 관리자 대상 공지사항이 등록되었습니다."; -// String redirectUrl = "/api/v1/notice" + noticeRegistDTO.getNoteiceId(); - String redirectUrl = "/api/v1/notice"; - String createdAt = getCurrentTime(); - - alarmService.send(memberId, message, redirectUrl, type, createdAt); - }); - - - } - -} diff --git a/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryServiceImpl.java index dbd067dc..81b08c01 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryServiceImpl.java @@ -25,7 +25,7 @@ public CareerQueryServiceImpl(CareerMapper careerMapper, } @Override - @Transactional + @Transactional(readOnly = true) public List selectCareerList(String loginId) { String memberId = authQueryService.selectMemberIdByLoginId(loginId); diff --git a/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryServiceImpl.java index 67eea073..21ad4b13 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryServiceImpl.java @@ -23,7 +23,7 @@ public CertificationQueryServiceImpl(CertificationMapper certificationMapper, } @Override - @Transactional + @Transactional(readOnly = true) public List selectCertificationList(String loginId) { String memberId = authQueryService.selectMemberIdByLoginId(loginId); diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java b/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java index 9fe3d31f..d8582dd4 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java index 066dc4da..74308c9a 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java @@ -32,7 +32,7 @@ public CustomerQueryServiceImpl(CustomerMapper customerMapper, } @Override - @Transactional + @Transactional(readOnly = true) public CustomerDTO selectCustomerInfo(String customerId) throws GeneralSecurityException { CustomerDTO customerInfoDTO = customerMapper.selectCustomerInfoById(customerId); @@ -45,7 +45,7 @@ public CustomerDTO selectCustomerInfo(String customerId) throws GeneralSecurityE } @Override - @Transactional + @Transactional(readOnly = true) public Page selectCustomerList(Pageable pageable) throws GeneralSecurityException { int page = pageable.getPageNumber(); int size = pageable.getPageSize(); @@ -64,7 +64,7 @@ public Page selectCustomerList(Pageable pageable) throws GeneralSec } @Override - @Transactional + @Transactional(readOnly = true) public Page findCustomerByCondition(Pageable pageable, CustomerSearchDTO customerSearchDTO) throws GeneralSecurityException { int page = pageable.getPageNumber(); int size = pageable.getPageSize(); diff --git a/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryServiceImpl.java index d9133650..8a51b2a1 100644 --- a/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/education/query/service/EducationQueryServiceImpl.java @@ -24,7 +24,7 @@ public EducationQueryServiceImpl(EducationMapper educationMapper, } @Override - @Transactional + @Transactional(readOnly = true) public List selectEducationList(String loginId) { String memberId = authQueryService.selectMemberIdByLoginId(loginId); diff --git a/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryServiceImpl.java index 4a1c04e3..9b9be9e9 100644 --- a/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/family/query/service/FamilyQueryServiceImpl.java @@ -27,7 +27,7 @@ public FamilyQueryServiceImpl(FamilyMapper familyMapper, } @Override - @Transactional + @Transactional(readOnly = true) public List selectFamilyList(String loginId) { String memberId = authQueryService.selectMemberIdByLoginId(loginId); diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberRoleDTO.java b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberRoleDTO.java index 0646569b..814caa12 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberRoleDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberRoleDTO.java @@ -10,4 +10,6 @@ @Setter @Getter public class MemberRoleDTO { + private String memberRoleId; + private String role; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java index 413f579d..5991a042 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java @@ -20,7 +20,7 @@ public AuthQueryServiceImpl(AuthMapper authMapper) { } @Override - @Transactional + @Transactional(readOnly = true) public String selectMemberIdByLoginId(String loginId){ String id = authMapper.selectIdByMemberName(loginId); diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index b1281412..581cfe54 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -3,7 +3,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.alarm.service.AlarmService; import stanl_2.final_backend.domain.member.common.exception.MemberCommonException; import stanl_2.final_backend.domain.member.common.exception.MemberErrorCode; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; @@ -20,19 +19,21 @@ public class MemberQueryServiceImpl implements MemberQueryService { private final MemberMapper memberMapper; private final AESUtils aesUtils; private final MemberRoleMapper memberRoleMapper; - private final AlarmService alarmService; +// private final AlarmService alarmService; @Autowired public MemberQueryServiceImpl(MemberMapper memberMapper, AESUtils aesUtils, - MemberRoleMapper memberRoleMapper, AlarmService alarmService) { + MemberRoleMapper memberRoleMapper +// , AlarmService alarmService + ) { this.memberMapper = memberMapper; this.aesUtils = aesUtils; this.memberRoleMapper = memberRoleMapper; - this.alarmService = alarmService; +// this.alarmService = alarmService; } @Override - @Transactional + @Transactional(readOnly = true) public MemberDTO selectMemberInfo(String name) throws GeneralSecurityException { MemberDTO memberInfo = memberMapper.findMemberInfoById(name); diff --git a/src/main/java/stanl_2/final_backend/domain/organization/command/application/controller/OrganizationController.java b/src/main/java/stanl_2/final_backend/domain/organization/command/application/controller/OrganizationController.java new file mode 100644 index 00000000..87855ae9 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/organization/command/application/controller/OrganizationController.java @@ -0,0 +1,18 @@ +package stanl_2.final_backend.domain.organization.command.application.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.organization.command.application.service.OrganizationCommandService; + +@RestController("commandOrganizationController") +@RequestMapping("/api/v1/organization") +public class OrganizationController { + + private final OrganizationCommandService organizationCommandService; + + @Autowired + public OrganizationController(OrganizationCommandService organizationCommandService) { + this.organizationCommandService = organizationCommandService; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/organization/command/application/dto/OrganizationRegisterDTO.java b/src/main/java/stanl_2/final_backend/domain/organization/command/application/dto/OrganizationRegisterDTO.java new file mode 100644 index 00000000..d404b79f --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/organization/command/application/dto/OrganizationRegisterDTO.java @@ -0,0 +1,15 @@ +package stanl_2.final_backend.domain.organization.command.application.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class OrganizationRegisterDTO { + private String name; + private String depth; +} diff --git a/src/main/java/stanl_2/final_backend/domain/organization/command/application/service/OrganizationCommandService.java b/src/main/java/stanl_2/final_backend/domain/organization/command/application/service/OrganizationCommandService.java new file mode 100644 index 00000000..d13400e8 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/organization/command/application/service/OrganizationCommandService.java @@ -0,0 +1,4 @@ +package stanl_2.final_backend.domain.organization.command.application.service; + +public interface OrganizationCommandService { +} diff --git a/src/main/java/stanl_2/final_backend/domain/organization/command/domain/aggregate/entity/Organization.java b/src/main/java/stanl_2/final_backend/domain/organization/command/domain/aggregate/entity/Organization.java new file mode 100644 index 00000000..ff1bcf6f --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/organization/command/domain/aggregate/entity/Organization.java @@ -0,0 +1,32 @@ +package stanl_2.final_backend.domain.organization.command.domain.aggregate.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "TB_organization_chart") +public class Organization { + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "ORG") + ) + @Column(name = "ORG_ID") + private String organizationId; + + @Column(name = "ORG_CHA_NAME") + private String name; + + @Column(name = "ORG_CHA_DEPTH") + private Integer depth; +} diff --git a/src/main/java/stanl_2/final_backend/domain/organization/command/domain/repository/OrganizationRepository.java b/src/main/java/stanl_2/final_backend/domain/organization/command/domain/repository/OrganizationRepository.java new file mode 100644 index 00000000..fa94a423 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/organization/command/domain/repository/OrganizationRepository.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.organization.command.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import stanl_2.final_backend.domain.organization.command.domain.aggregate.entity.Organization; + +@Repository +public interface OrganizationRepository extends JpaRepository { +} diff --git a/src/main/java/stanl_2/final_backend/domain/organization/command/domain/service/OrganizationCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/organization/command/domain/service/OrganizationCommandServiceImpl.java new file mode 100644 index 00000000..a3d1dfdd --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/organization/command/domain/service/OrganizationCommandServiceImpl.java @@ -0,0 +1,23 @@ +package stanl_2.final_backend.domain.organization.command.domain.service; + +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.organization.command.application.service.OrganizationCommandService; +import stanl_2.final_backend.domain.organization.command.domain.repository.OrganizationRepository; + +@Service("commandOrganizationService") +public class OrganizationCommandServiceImpl implements OrganizationCommandService { + + private final OrganizationRepository organizationRepository; + private final ModelMapper modelMapper; + + @Autowired + public OrganizationCommandServiceImpl(OrganizationRepository organizationRepository, + ModelMapper modelMapper) { + this.organizationRepository = organizationRepository; + this.modelMapper = modelMapper; + } + + +} diff --git a/src/main/java/stanl_2/final_backend/domain/organization/common/exception/OrganizationCommonException.java b/src/main/java/stanl_2/final_backend/domain/organization/common/exception/OrganizationCommonException.java new file mode 100644 index 00000000..73f7143d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/organization/common/exception/OrganizationCommonException.java @@ -0,0 +1,17 @@ +package stanl_2.final_backend.domain.organization.common.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import stanl_2.final_backend.domain.A_sample.common.exception.SampleErrorCode; + +@Getter +@RequiredArgsConstructor +public class OrganizationCommonException extends RuntimeException { + private final SampleErrorCode sampleErrorCode; + + // 에러 발생시 ErroCode 별 메시지 + @Override + public String getMessage() { + return this.sampleErrorCode.getMsg(); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmErrorCode.java b/src/main/java/stanl_2/final_backend/domain/organization/common/exception/OrganizationErrorCode.java similarity index 86% rename from src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmErrorCode.java rename to src/main/java/stanl_2/final_backend/domain/organization/common/exception/OrganizationErrorCode.java index b0a92704..42ed83b9 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/organization/common/exception/OrganizationErrorCode.java @@ -1,4 +1,4 @@ -package stanl_2.final_backend.domain.alarm.common.exception; +package stanl_2.final_backend.domain.organization.common.exception; import lombok.AllArgsConstructor; import lombok.Getter; @@ -6,7 +6,7 @@ @Getter @AllArgsConstructor -public enum AlarmErrorCode { +public enum OrganizationErrorCode { /** * 400(Bad Request) @@ -30,6 +30,7 @@ public enum AlarmErrorCode { */ + /** * 404(Not Found) * 서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다. @@ -37,16 +38,14 @@ public enum AlarmErrorCode { * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. */ - - + SAMPLE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "sample 데이터를 찾지 못했습니다"), + CENTER_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "center 데이터를 찾지 못했습니다."), /** - * 500(Internal Server Error) * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. */ INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); - private final Integer code; private final HttpStatus httpStatus; private final String msg; diff --git a/src/main/java/stanl_2/final_backend/domain/organization/common/exception/OrganizationExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/organization/common/exception/OrganizationExceptionResponse.java new file mode 100644 index 00000000..7c6d6720 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/organization/common/exception/OrganizationExceptionResponse.java @@ -0,0 +1,23 @@ +package stanl_2.final_backend.domain.organization.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; +import stanl_2.final_backend.domain.A_sample.common.exception.SampleErrorCode; + +@Getter +public class OrganizationExceptionResponse { + private final Integer code; + private final String msg; + private final HttpStatus httpStatus; + + public OrganizationExceptionResponse(stanl_2.final_backend.domain.A_sample.common.exception.SampleErrorCode sampleErrorCode) { + this.code = sampleErrorCode.getCode(); + this.msg = sampleErrorCode.getMsg(); + this.httpStatus = sampleErrorCode.getHttpStatus(); + } + + public static OrganizationExceptionResponse of(SampleErrorCode sampleErrorCode) { + return new OrganizationExceptionResponse(sampleErrorCode); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/organization/common/response/OrganizationResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/organization/common/response/OrganizationResponseMessage.java new file mode 100644 index 00000000..be617d66 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/organization/common/response/OrganizationResponseMessage.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.organization.common.response; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class OrganizationResponseMessage { + private int httpStatus; + private String msg; + private Object result; +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/organization/query/controller/OrganizationController.java b/src/main/java/stanl_2/final_backend/domain/organization/query/controller/OrganizationController.java new file mode 100644 index 00000000..cc25c963 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/organization/query/controller/OrganizationController.java @@ -0,0 +1,50 @@ +package stanl_2.final_backend.domain.organization.query.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.organization.common.response.OrganizationResponseMessage; +import stanl_2.final_backend.domain.organization.query.dto.OrganizationDTO; +import stanl_2.final_backend.domain.organization.query.service.OrganizationQueryService; + +import java.util.List; + +@RestController(value = "queryOrganizationController") +@RequestMapping("/api/v1/organization") +public class OrganizationController { + + private final OrganizationQueryService organizationQueryService; + + @Autowired + public OrganizationController(OrganizationQueryService organizationQueryService) { + this.organizationQueryService = organizationQueryService; + } + + + @Operation(summary = "사이드바 메뉴 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = OrganizationResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("") + public ResponseEntity getOrganizationById() { + + List organizationDTO = organizationQueryService.selectOrganizationChart(); + + return ResponseEntity.ok(OrganizationResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(organizationDTO) + .build()); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/organization/query/dto/OrganizationDTO.java b/src/main/java/stanl_2/final_backend/domain/organization/query/dto/OrganizationDTO.java new file mode 100644 index 00000000..48853d47 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/organization/query/dto/OrganizationDTO.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.organization.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +public class OrganizationDTO { + private String organizationId; + private String name; + private Integer depth; +} diff --git a/src/main/java/stanl_2/final_backend/domain/organization/query/repository/OrganizationMapper.java b/src/main/java/stanl_2/final_backend/domain/organization/query/repository/OrganizationMapper.java new file mode 100644 index 00000000..a35df811 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/organization/query/repository/OrganizationMapper.java @@ -0,0 +1,11 @@ +package stanl_2.final_backend.domain.organization.query.repository; + +import org.apache.ibatis.annotations.Mapper; +import stanl_2.final_backend.domain.organization.query.dto.OrganizationDTO; + +import java.util.List; + +@Mapper +public interface OrganizationMapper { + List selectOrganizationChart(); +} diff --git a/src/main/java/stanl_2/final_backend/domain/organization/query/service/OrganizationQueryService.java b/src/main/java/stanl_2/final_backend/domain/organization/query/service/OrganizationQueryService.java new file mode 100644 index 00000000..47874a8c --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/organization/query/service/OrganizationQueryService.java @@ -0,0 +1,10 @@ +package stanl_2.final_backend.domain.organization.query.service; + +import stanl_2.final_backend.domain.organization.query.dto.OrganizationDTO; + +import java.util.List; + +public interface OrganizationQueryService { + List selectOrganizationChart(); + +} diff --git a/src/main/java/stanl_2/final_backend/domain/organization/query/service/OrganizationQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/organization/query/service/OrganizationQueryServiceImpl.java new file mode 100644 index 00000000..0450edd5 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/organization/query/service/OrganizationQueryServiceImpl.java @@ -0,0 +1,29 @@ +package stanl_2.final_backend.domain.organization.query.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.organization.query.dto.OrganizationDTO; +import stanl_2.final_backend.domain.organization.query.repository.OrganizationMapper; + +import java.util.List; + +@Service("queryOrganizationService") +public class OrganizationQueryServiceImpl implements OrganizationQueryService{ + + private final OrganizationMapper organizationMapper; + + @Autowired + public OrganizationQueryServiceImpl(OrganizationMapper organizationMapper) { + this.organizationMapper = organizationMapper; + } + + @Override + @Transactional(readOnly = true) + public List selectOrganizationChart() { + + List organizationDTO = organizationMapper.selectOrganizationChart(); + + return organizationDTO; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java index 7aa3c733..074c461e 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/command/application/controller/ScheduleController.java @@ -8,7 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.alarm.service.AlarmService; +//import stanl_2.final_backend.domain.alarm.service.AlarmService; import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleDeleteDTO; import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleModifyDTO; import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleRegistDTO; @@ -24,7 +24,9 @@ public class ScheduleController { private final ScheduleCommandService scheduleCommandService; @Autowired - public ScheduleController(ScheduleCommandService scheduleCommandService, AlarmService alarmService) { + public ScheduleController(ScheduleCommandService scheduleCommandService +// , AlarmService alarmService + ) { this.scheduleCommandService = scheduleCommandService; } diff --git a/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml b/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml index 17f0c25f..3592a2eb 100644 --- a/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml @@ -58,7 +58,7 @@ a.created_at, a.updated_at, a.deleted_at - FROM tb_cutomer_info a + FROM tb_customer_info a LIMIT #{ size } OFFSET #{ offset } diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberRoleMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberRoleMapper.xml index f2aae3a6..c510ab3c 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberRoleMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberRoleMapper.xml @@ -8,7 +8,7 @@ - SELECT a.mem_id FROM tb_mem_role a diff --git a/src/main/resources/stanl_2/final_backend/domain/organization/query/repository/OrganizationMapper.xml b/src/main/resources/stanl_2/final_backend/domain/organization/query/repository/OrganizationMapper.xml new file mode 100644 index 00000000..c2d755d1 --- /dev/null +++ b/src/main/resources/stanl_2/final_backend/domain/organization/query/repository/OrganizationMapper.xml @@ -0,0 +1,18 @@ + + + + + + + + + + \ No newline at end of file From 52a46632e3e4611f3354f0c0c1b45f4b3462afe0 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 19 Nov 2024 21:04:15 +0900 Subject: [PATCH 209/563] =?UTF-8?q?feat=20:=20=EB=A0=88=EB=94=94=EC=8A=A4?= =?UTF-8?q?=20=EC=84=B8=ED=8C=85=20=EB=B0=8F=20=EA=B5=AC=ED=98=84(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 ++ .../query/service/NoticeServiceImpl.java | 20 +++++++-- .../global/config/CacheConfig.java | 9 ++++ .../global/config/RedisConfig.java | 44 +++++++++++++++++++ 4 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/global/config/CacheConfig.java create mode 100644 src/main/java/stanl_2/final_backend/global/config/RedisConfig.java diff --git a/build.gradle b/build.gradle index 1f9b7eb1..6ab564e7 100644 --- a/build.gradle +++ b/build.gradle @@ -55,6 +55,9 @@ dependencies { // redis implementation 'org.springframework.boot:spring-boot-starter-data-redis' + //cache + implementation 'org.springframework.boot:spring-boot-starter-cache' + //hibernate core implementation 'org.hibernate.orm:hibernate-core:6.5.2.Final' diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java index 1bca9daf..379bf290 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java @@ -4,9 +4,9 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.notices.command.domain.aggragate.entity.Notice; import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; import stanl_2.final_backend.domain.notices.query.dto.SearchDTO; import stanl_2.final_backend.domain.notices.query.repository.NoticeMapper; @@ -17,10 +17,12 @@ @Service("queryNoticeServiceImpl") public class NoticeServiceImpl implements NoticeService{ private final NoticeMapper noticeMapper; + private final RedisTemplate redisTemplate; @Autowired - public NoticeServiceImpl(NoticeMapper noticeMapper) { + public NoticeServiceImpl(NoticeMapper noticeMapper, RedisTemplate redisTemplate) { this.noticeMapper = noticeMapper; + this.redisTemplate = redisTemplate; } @Transactional @@ -28,9 +30,19 @@ public NoticeServiceImpl(NoticeMapper noticeMapper) { public Page findAllNotices(Pageable pageable) { int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); - List notices = noticeMapper.findAllNotices(offset,size); - int totalElements = noticeMapper.findNoticeCount(); // 총 개수 조회 + String cacheKey = "myCache::notices::offset=" + offset + "::size=" + size; + + List notices = (List) redisTemplate.opsForValue().get(cacheKey); + // 캐시에 데이터가 없다면 DB에서 조회하고 캐시에 저장 + if (notices == null) { + System.out.println("데이터베이스에서 데이터 조회 중..."); + notices = noticeMapper.findAllNotices(offset, size); + redisTemplate.opsForValue().set(cacheKey, notices); + } else { + System.out.println("캐시에서 데이터 조회 중..."); + } + int totalElements = noticeMapper.findNoticeCount(); // 총 개수 조회 return new PageImpl<>(notices, pageable, totalElements); } diff --git a/src/main/java/stanl_2/final_backend/global/config/CacheConfig.java b/src/main/java/stanl_2/final_backend/global/config/CacheConfig.java new file mode 100644 index 00000000..6165a1a4 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/config/CacheConfig.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.global.config; + +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableCaching +public class CacheConfig { +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/global/config/RedisConfig.java b/src/main/java/stanl_2/final_backend/global/config/RedisConfig.java new file mode 100644 index 00000000..705c5488 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/config/RedisConfig.java @@ -0,0 +1,44 @@ +package stanl_2.final_backend.global.config; + +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.EventListener; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.StringRedisSerializer; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; + +@Configuration +public class RedisConfig { + + private final RedisConnectionFactory redisConnectionFactory; + + public RedisConfig(RedisConnectionFactory redisConnectionFactory) { + this.redisConnectionFactory = redisConnectionFactory; + } + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + // Key는 String으로 직렬화 + template.setKeySerializer(new StringRedisSerializer()); + // Value는 JSON으로 직렬화 + template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); + + template.afterPropertiesSet(); + return template; + } + + @EventListener(ApplicationReadyEvent.class) + public void testRedisConnection() { + try { + redisConnectionFactory.getConnection().ping(); + System.out.println("✅ Successfully connected to Redis"); + } catch (Exception e) { + System.err.println("❌ Failed to connect to Redis: " + e.getMessage()); + } + } +} From 79629872e129159a770f6dd0fd23c73f6cd2d958 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Tue, 19 Nov 2024 21:44:02 +0900 Subject: [PATCH 210/563] =?UTF-8?q?feat:=20logstash=20=EC=84=B8=ED=8C=85?= =?UTF-8?q?=EC=A4=91(#99)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 ++ src/main/resources/application-prod.yml | 3 +-- src/main/resources/logback-spring.xml | 25 +++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 src/main/resources/logback-spring.xml diff --git a/build.gradle b/build.gradle index 1f9b7eb1..9e848586 100644 --- a/build.gradle +++ b/build.gradle @@ -58,6 +58,8 @@ dependencies { //hibernate core implementation 'org.hibernate.orm:hibernate-core:6.5.2.Final' + // Logback Logstash Encoder + implementation 'net.logstash.logback:logstash-logback-encoder:7.4' } tasks.named('test') { diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 60bf5a48..520817c3 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -5,5 +5,4 @@ spring: logging: level: -# org.springframework.security: DEBUG - org.springframework.security: TRACE \ No newline at end of file + org.springframework.security: DEBUG \ No newline at end of file diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 00000000..1e95594e --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,25 @@ + + + + + %d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} - %msg%n + + + + + + logs/app-logs.json + + logs/app-logs-%d{yyyy-MM-dd}.json + 30 + + + %msg%n + + + + + + + + From c999ca833a989378c245ae5af05e5d7cec29243a Mon Sep 17 00:00:00 2001 From: woosuk1 Date: Tue, 19 Nov 2024 22:10:59 +0900 Subject: [PATCH 211/563] =?UTF-8?q?fix:=20center=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EB=8F=99=EC=9E=91=EC=97=AC=EB=B6=80=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8=20&=20pk=20=EC=83=9D=EC=84=B1=EA=B0=92=20=EC=88=98?= =?UTF-8?q?=EC=A0=95(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/center/command/domain/aggregate/entity/Center.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java b/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java index 0f82cbbd..70daaae1 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java @@ -25,7 +25,7 @@ public class Center { @GeneratedValue(generator = "PrefixGeneratorConfig") @GenericGenerator(name = "PrefixGeneratorConfig", type = PrefixGeneratorConfig.class, - parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "CENT") + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "CEN") ) @Column(name ="CENT_ID") private String id; From e6b8e0f454be1372e924cb5fde966b69235af7c2 Mon Sep 17 00:00:00 2001 From: giuseog Date: Tue, 19 Nov 2024 22:15:55 +0900 Subject: [PATCH 212/563] =?UTF-8?q?feat:=20=ED=8C=90=EB=A7=A4=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84=EC=A4=91=20?= =?UTF-8?q?(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SalesHistoryController.java | 16 +- .../query/dto/SalesHistorySelectAllDTO.java | 14 -- .../query/dto/SalesHistorySelectDTO.java | 30 +++ .../query/repository/SalesHistoryMapper.java | 14 ++ .../service/SalesHistoryQueryService.java | 8 +- .../service/SalesHistoryQueryServiceImpl.java | 73 ++++++- .../query/repository/SalesHistoryMapper.xml | 192 ++++++++++++++++++ 7 files changed, 324 insertions(+), 23 deletions(-) delete mode 100644 src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectAllDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectDTO.java create mode 100644 src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java index 8cf96941..464caf69 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java @@ -6,12 +6,15 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import stanl_2.final_backend.domain.sales_history.common.response.SalesHistoryResponseMessage; -import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectAllDTO; +import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectDTO; import stanl_2.final_backend.domain.sales_history.query.service.SalesHistoryQueryService; @RestController(value = "querySalesHistoryController") @@ -32,14 +35,19 @@ public SalesHistoryController(SalesHistoryQueryService salesHistoryQueryService) content = @Content(mediaType = "application/json")) }) @GetMapping("") - public ResponseEntity getAllSalesHistory(){ + public ResponseEntity getAllSalesHistory(Authentication authentication, + Pageable pageable){ + SalesHistorySelectDTO salesHistorySelectDTO = new SalesHistorySelectDTO(); - SalesHistorySelectAllDTO salesHistorySelectAllDTO = salesHistoryQueryService.selectAllSalesHistory(); + salesHistorySelectDTO.setRoles(authentication.getAuthorities()); + salesHistorySelectDTO.setSearcherName(authentication.getName()); + + Page responseSalesHistory = salesHistoryQueryService.selectAllSalesHistory(salesHistorySelectDTO, pageable); return ResponseEntity.ok(SalesHistoryResponseMessage.builder() .httpStatus(200) .msg("성공") - .result(salesHistorySelectAllDTO) + .result(responseSalesHistory) .build()); } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectAllDTO.java deleted file mode 100644 index 7fd3f689..00000000 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectAllDTO.java +++ /dev/null @@ -1,14 +0,0 @@ -package stanl_2.final_backend.domain.sales_history.query.dto; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@AllArgsConstructor -@NoArgsConstructor -@Getter -@Setter -public class SalesHistorySelectAllDTO { - private String temp1; -} diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectDTO.java new file mode 100644 index 00000000..4e628315 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectDTO.java @@ -0,0 +1,30 @@ +package stanl_2.final_backend.domain.sales_history.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class SalesHistorySelectDTO { + private String salesHistoryId; + private String salesHistoryNumberOfVehicles; + private String salesHistoryTotalSales; + private String salesHistoryIncentive; + private String createdAt; + private String deletedAt; + private String active; + private String contractId; + private String customerId; + private String productId; + private String memberId; + private String centerId; + private Collection roles; + private String searcherName; +} diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java index 5fb7e575..b96394e2 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java @@ -1,7 +1,21 @@ package stanl_2.final_backend.domain.sales_history.query.repository; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectDTO; + +import java.util.List; @Mapper public interface SalesHistoryMapper { + List findAllSalesHistory(@Param("size") int size + ,@Param("offset") int offset); + + int findSalesHistoryCount(); + + List findSalesHistoryByMember(@Param("size") int size + ,@Param("offset") int offset + ,@Param("searcherId") String searcherId); + + int findSalesHistoryCountByMember(@Param("searcherId") String searcherId); } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java index e5ff25f5..d821d33f 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java @@ -1,7 +1,11 @@ package stanl_2.final_backend.domain.sales_history.query.service; -import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectAllDTO; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectDTO; + +import java.security.GeneralSecurityException; public interface SalesHistoryQueryService { - SalesHistorySelectAllDTO selectAllSalesHistory(); + Page selectAllSalesHistory(SalesHistorySelectDTO salesHistorySelectDTO, Pageable pageable) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java index 044ce47b..01c9de96 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java @@ -1,12 +1,79 @@ package stanl_2.final_backend.domain.sales_history.query.service; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; -import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectAllDTO; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; +import stanl_2.final_backend.domain.sales_history.common.exception.SalesHistoryCommonException; +import stanl_2.final_backend.domain.sales_history.common.exception.SalesHistoryErrorCode; +import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectDTO; +import stanl_2.final_backend.domain.sales_history.query.repository.SalesHistoryMapper; +import stanl_2.final_backend.global.exception.GlobalCommonException; +import stanl_2.final_backend.global.exception.GlobalErrorCode; +import stanl_2.final_backend.global.utils.AESUtils; + +import java.security.GeneralSecurityException; +import java.util.List; @Service public class SalesHistoryQueryServiceImpl implements SalesHistoryQueryService { + + private final SalesHistoryMapper salesHistoryMapper; + private final AESUtils aesUtils; + private final MemberQueryService memberQueryService; + private final AuthQueryService authQueryService; + + @Autowired + public SalesHistoryQueryServiceImpl(SalesHistoryMapper salesHistoryMapper, AESUtils aesUtils, MemberQueryService memberQueryService, @Qualifier("authQueryService") AuthQueryService authQueryService) { + this.salesHistoryMapper = salesHistoryMapper; + this.aesUtils = aesUtils; + this.memberQueryService = memberQueryService; + this.authQueryService = authQueryService; + } + @Override - public SalesHistorySelectAllDTO selectAllSalesHistory() { - return null; + @Transactional(readOnly = true) + public Page selectAllSalesHistory(SalesHistorySelectDTO salesHistorySelectDTO, Pageable pageable) throws GeneralSecurityException { + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); + + if(salesHistorySelectDTO.getRoles().stream() + .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()) + || "ROLE_REPRESENTATIVE".equals(role.getAuthority())) + ){ + + List salesHistoryList = salesHistoryMapper.findAllSalesHistory(size,offset); + + int total = salesHistoryMapper.findSalesHistoryCount(); + + if(salesHistoryList.isEmpty() || total == 0){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } + return new PageImpl<>(salesHistoryList, pageable, total); + }else if(salesHistorySelectDTO.getRoles().stream() + .anyMatch(role -> "ROLE_MEMBER".equals(role.getAuthority()))) { + + String searcherId = authQueryService.selectMemberIdByLoginId(salesHistorySelectDTO.getSearcherName()); + + List salesHistoryList = salesHistoryMapper.findSalesHistoryByMember(size,offset, searcherId); + + int total = salesHistoryMapper.findSalesHistoryCountByMember(searcherId); + + if(salesHistoryList.isEmpty() || total == 0){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } + return new PageImpl<>(salesHistoryList, pageable, total); + + }else{ + throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + } + } + + } diff --git a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml new file mode 100644 index 00000000..f91b1bcf --- /dev/null +++ b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From c3802dda38d6e12dda9e68b7c0b6fdd3866bb840 Mon Sep 17 00:00:00 2001 From: woosuk1 Date: Tue, 19 Nov 2024 22:22:14 +0900 Subject: [PATCH 213/563] =?UTF-8?q?fix:=20=EC=A3=BC=EC=84=9D=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/product/query/service/ProductServiceImpl.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductServiceImpl.java index ced78f15..886e715d 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductServiceImpl.java @@ -41,8 +41,6 @@ public Page> selectAll(Pageable pageable) { @Override @Transactional public ProductSelectIdDTO selectByProductId(String id) { - - /* 설명. 상세 조회 시, Mapper에서 product와 productOption join해서 보여줄 것 */ ProductSelectIdDTO productSelectIdDTO = productMapper.findProductById(id); return productSelectIdDTO; From a743c23b7179d5ed969e639a48d73f3b1dd16caf Mon Sep 17 00:00:00 2001 From: woosuk1 Date: Tue, 19 Nov 2024 23:10:15 +0900 Subject: [PATCH 214/563] =?UTF-8?q?feat:=20product=20=EC=9E=91=EB=8F=99=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=20=EB=B0=8F=20entity=20=EC=B6=94=EA=B0=80(#3?= =?UTF-8?q?1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/aggregate/entity/Center.java | 3 - .../domain/aggregate/entity/Product.java | 52 +++++++++ .../aggregate/entity/ProductOption.java | 104 ++++++++++++++++++ src/main/resources/application-dev.yml | 2 +- src/main/resources/application-prod.yml | 2 +- src/main/resources/application.yml | 2 +- 6 files changed, 159 insertions(+), 6 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/product/command/application/domain/aggregate/entity/Product.java create mode 100644 src/main/java/stanl_2/final_backend/domain/product/command/application/domain/aggregate/entity/ProductOption.java diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java b/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java index 0f82cbbd..f77872d6 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java @@ -10,9 +10,6 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; -/* 설명. 테스트를 위한 어노테이션(나중에 삭제 예정)*/ -@ToString - @Entity @Table(name="TB_CENTER") @AllArgsConstructor diff --git a/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/aggregate/entity/Product.java b/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/aggregate/entity/Product.java new file mode 100644 index 00000000..3ad6ed93 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/aggregate/entity/Product.java @@ -0,0 +1,52 @@ +package stanl_2.final_backend.domain.product.command.application.domain.aggregate.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +@Entity +@Table(name="TB_PRODUCT") +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class Product { + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "PRO") + ) + @Column(name ="PROD_ID") + private String productId; + + @Column(name ="PROD_SER_NO", nullable = false) + private String serialNumber; + + @Column(name ="PROD_COST", nullable = false) + private Integer cost; + + @Column(name ="PROD_NAME", nullable = false) + private String name; + + @Column(name ="PROD_STCK", nullable = false) + private Integer stock; + + @Column(name ="CREATED_AT", nullable = false) + private String createdAt; + + @Column(name ="UPDATED_AT", nullable = false) + private String updatedAt; + + @Column(name ="DELETED_AT") + private String deletedAt; + + @Column(name ="ACTIVE", nullable = false) + private Boolean active = true; + + +} diff --git a/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/aggregate/entity/ProductOption.java b/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/aggregate/entity/ProductOption.java new file mode 100644 index 00000000..4f2c3d6e --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/aggregate/entity/ProductOption.java @@ -0,0 +1,104 @@ +package stanl_2.final_backend.domain.product.command.application.domain.aggregate.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +@Entity +@Table(name="TB_PRODUCT_OPTION") +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class ProductOption { + + @Id + private String productId; + + @Column(name ="OPT_CNTY", nullable = false) + private Character country; + + @Column(name ="OPT_MNFR", nullable = false) + private Character manufacturer; + + @Column(name ="OPT_VHC_TYPE", nullable = false) + private Character vehicleType; + + @Column(name ="OPT_CHSS", nullable = false) + private Character chassis; + + @Column(name ="OPT_DTIL_TYPE", nullable = false) + private Character detailType; + + @Column(name ="OPT_BODY_TYPE", nullable = false) + private Character bodyType; + + @Column(name ="OPT_SFTY_DVCE", nullable = false) + private Character safetyDevice; + + @Column(name ="OPT_ENGN_CPCT", nullable = false) + private Character engineCapacity; + + @Column(name ="OPT_SCRT_CODE", nullable = false) + private Character secretCode; + + @Column(name ="OPT_PRDC_YEAR", nullable = false) + private Character productYear; + + @Column(name ="OPT_PRDC_PLNT", nullable = false) + private Character productPlant; + + @Column(name ="OPT_ENGN", nullable = false) + private Character engine; + + @Column(name ="OPT_MSSN", nullable = false) + private Character mission; + + @Column(name ="OPT_TRIM", nullable = false) + private Character trim; + + @Column(name ="OPT_XTNL_COLR", nullable = false) + private Character externalColor; + + @Column(name ="OPT_ITNL_COLR", nullable = false) + private Character internalColor; + + @Column(name ="OPT_HUD", nullable = false) + private Character headUpDisplay; + + @Column(name ="OPT_NAVI", nullable = false) + private Character navigation; + + @Column(name ="OPT_DRVE_WISE", nullable = false) + private Character driveWise; + + @Column(name ="OPT_SMRT_CNCT", nullable = false) + private Character smartConnect; + + @Column(name ="OPT_STYL", nullable = false) + private Character style; + + @Column(name ="OPT_MY_CFRT_PCKG", nullable = false) + private Character myComfortPackage; + + @Column(name ="OPT_OTDR_PCKG", nullable = false) + private Character outdoorPackage; + + @Column(name ="OPT_SUN_ROOF", nullable = false) + private Character sunRoof; + + @Column(name ="OPT_SOND", nullable = false) + private Character sound; + + @Column(name ="Active") + private Boolean active = true; + + @MapsId + @OneToOne + @JoinColumn(name="PROD_ID") + private Product product; +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index aabe59aa..e9390662 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: update + ddl-auto: create logging: level: diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 60bf5a48..23faa6a8 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: update + ddl-auto: create logging: level: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index eee7170c..6618043e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -23,7 +23,7 @@ spring: jpa: show-sql: true hibernate: - ddl-auto: update + ddl-auto: create properties: hibernate.format_sql: true From 8af9ffe3d9f1ac0b170c695d9fb9a31b0cfefbf5 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Wed, 20 Nov 2024 00:11:44 +0900 Subject: [PATCH 215/563] =?UTF-8?q?feat:=20=EC=9D=BC=EC=A0=95=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20&=20=EA=B3=B5=EC=A7=80=EC=82=AC=ED=95=AD=20?= =?UTF-8?q?=EC=95=8C=EB=A6=BC=20=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C=20?= =?UTF-8?q?(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../alarm/controller/AlarmController.java | 2 +- .../alarm/scheduler/AlarmScheduler.java | 3 ++- .../domain/alarm/service/AlarmService.java | 3 ++- .../alarm/service/AlarmServiceImpl.java | 21 ++++++---------- .../member/query/dto/MemberRoleDTO.java | 13 ---------- .../query/service/MemberQueryServiceImpl.java | 9 ++++--- .../application/dto/NoticeAlarmDTO.java | 25 +++++++++++++++++++ .../service/NoticeCommandServiceImpl.java | 17 ++++++++++--- .../query/repository/ScheduleMapper.java | 3 ++- .../query/service/ScheduleQueryService.java | 3 ++- .../service/ScheduleQueryServiceImpl.java | 5 ++-- .../query/repository/MemberRoleMapper.xml | 11 +++----- 12 files changed, 65 insertions(+), 50 deletions(-) delete mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberRoleDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeAlarmDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/controller/AlarmController.java b/src/main/java/stanl_2/final_backend/domain/alarm/controller/AlarmController.java index ae0113e0..80e7e3e0 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/controller/AlarmController.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/controller/AlarmController.java @@ -12,7 +12,7 @@ import java.security.Principal; -@RestController("queryAlarmController") +@RestController @RequestMapping("/api/v1/alarm") public class AlarmController { diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java index 411e56db..573efb43 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java @@ -9,6 +9,7 @@ import stanl_2.final_backend.domain.alarm.repository.EmitterRepository; import stanl_2.final_backend.domain.alarm.service.AlarmService; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; +import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDayDTO; import stanl_2.final_backend.domain.schedule.query.service.ScheduleQueryService; import java.time.ZoneId; @@ -43,7 +44,7 @@ public void alarmTodaySchedule(){ String currentDay = getCurrentTime().substring(0,10); - List todaySchedules = scheduleQueryService.findSchedulesByDate(currentDay); + List todaySchedules = scheduleQueryService.findSchedulesByDate(currentDay); // 사용자 별로 알림 전송 todaySchedules.forEach(schedule -> { diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java index 7e774686..2d351e99 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmService.java @@ -4,6 +4,7 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import stanl_2.final_backend.domain.alarm.aggregate.dto.AlarmDTO; import stanl_2.final_backend.domain.alarm.aggregate.entity.Alarm; +import stanl_2.final_backend.domain.notices.command.application.dto.NoticeAlarmDTO; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; public interface AlarmService { @@ -15,5 +16,5 @@ public interface AlarmService { Alarm createAlarm(String memberId, String message, String redirectUrl, String type, String createdAt); - void sendNoticeAlarm(NoticeRegistDTO noticeRegistDTO); + void sendNoticeAlarm(NoticeAlarmDTO noticeAlarmDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java index 4499908a..55873624 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/service/AlarmServiceImpl.java @@ -3,7 +3,6 @@ import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; @@ -13,8 +12,7 @@ import stanl_2.final_backend.domain.alarm.repository.EmitterRepository; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.member.query.service.MemberQueryService; -import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; -import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; +import stanl_2.final_backend.domain.notices.command.application.dto.NoticeAlarmDTO; import java.io.IOException; import java.time.ZoneId; @@ -31,7 +29,6 @@ public class AlarmServiceImpl implements AlarmService { private final AlarmRepository alarmRepository; private final AuthQueryService authQueryService; private final MemberQueryService memberQueryService; - private final AlarmService alarmService; private String getCurrentTime() { ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); @@ -42,12 +39,11 @@ private String getCurrentTime() { @Autowired public AlarmServiceImpl(AlarmRepository alarmRepository, EmitterRepository emitterRepository, - AuthQueryService authQueryService, MemberQueryService memberQueryService, @Qualifier("alarmService") AlarmService alarmService) { + AuthQueryService authQueryService, MemberQueryService memberQueryService){ this.alarmRepository = alarmRepository; this.emitterRepository = emitterRepository; this.authQueryService = authQueryService; this.memberQueryService = memberQueryService; - this.alarmService = alarmService; } @Override @@ -125,23 +121,20 @@ public Alarm createAlarm(String memberId, String message, String redirectUrl, St } @Override - public void sendNoticeAlarm(NoticeRegistDTO noticeRegistDTO){ + public void sendNoticeAlarm(NoticeAlarmDTO noticeAlarmDTO){ - List memberIdList = memberQueryService.selectMemberByRole(noticeRegistDTO.getTag()); + List memberIdList = memberQueryService.selectMemberByRole(noticeAlarmDTO.getTag()); memberIdList.forEach(member -> { String memberId = member; String type = "NOTICE"; - String tag = noticeRegistDTO.getClassification(); + String tag = noticeAlarmDTO.getClassification(); String message = "[" + tag + "] 영업 관리자 대상 공지사항이 등록되었습니다."; -// String redirectUrl = "/api/v1/notice" + noticeRegistDTO.getNoteiceId(); - String redirectUrl = "/api/v1/notice"; + String redirectUrl = "/api/v1/notice" + noticeAlarmDTO.getNoticeId(); String createdAt = getCurrentTime(); - alarmService.send(memberId, message, redirectUrl, type, createdAt); + send(memberId, message, redirectUrl, type, createdAt); }); - - } } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberRoleDTO.java b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberRoleDTO.java deleted file mode 100644 index 0646569b..00000000 --- a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberRoleDTO.java +++ /dev/null @@ -1,13 +0,0 @@ -package stanl_2.final_backend.domain.member.query.dto; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@AllArgsConstructor -@NoArgsConstructor -@Setter -@Getter -public class MemberRoleDTO { -} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index b1281412..6c28b9cf 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -1,9 +1,9 @@ package stanl_2.final_backend.domain.member.query.service; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.alarm.service.AlarmService; import stanl_2.final_backend.domain.member.common.exception.MemberCommonException; import stanl_2.final_backend.domain.member.common.exception.MemberErrorCode; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; @@ -14,21 +14,20 @@ import java.security.GeneralSecurityException; import java.util.List; +@Slf4j @Service(value = "queryMemberService") public class MemberQueryServiceImpl implements MemberQueryService { private final MemberMapper memberMapper; private final AESUtils aesUtils; private final MemberRoleMapper memberRoleMapper; - private final AlarmService alarmService; @Autowired public MemberQueryServiceImpl(MemberMapper memberMapper, AESUtils aesUtils, - MemberRoleMapper memberRoleMapper, AlarmService alarmService) { + MemberRoleMapper memberRoleMapper) { this.memberMapper = memberMapper; this.aesUtils = aesUtils; this.memberRoleMapper = memberRoleMapper; - this.alarmService = alarmService; } @Override @@ -60,6 +59,8 @@ public List selectMemberByRole(String role){ List memberList = memberRoleMapper.findMembersbyRole(role); + log.info("값 출력: {}", memberList.toArray()); + return memberList; } } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeAlarmDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeAlarmDTO.java new file mode 100644 index 00000000..67202faa --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeAlarmDTO.java @@ -0,0 +1,25 @@ +package stanl_2.final_backend.domain.notices.command.application.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class NoticeAlarmDTO { + + private String noticeId; + + private String title; + + private String tag; + + private String classification; + + private String content; + + private String memberLoginId; + + private String memberId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java index a24f3cef..c9344c70 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java @@ -1,11 +1,14 @@ package stanl_2.final_backend.domain.notices.command.domain.service; +import lombok.extern.slf4j.Slf4j; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.alarm.service.AlarmService; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.notices.command.application.dto.NoticeAlarmDTO; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeDeleteDTO; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeModifyDTO; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; @@ -14,8 +17,6 @@ import stanl_2.final_backend.domain.notices.command.domain.repository.NoticeRepository; import stanl_2.final_backend.domain.notices.common.exception.NoticeCommonException; import stanl_2.final_backend.domain.notices.common.exception.NoticeErrorCode; -import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleDeleteDTO; -import stanl_2.final_backend.domain.schedule.command.domain.aggregate.entity.Schedule; import stanl_2.final_backend.domain.schedule.common.exception.ScheduleCommonException; import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; @@ -24,6 +25,7 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +@Slf4j @Service("commandNoticeService") public class NoticeCommandServiceImpl implements NoticeCommandService { @@ -32,12 +34,15 @@ public class NoticeCommandServiceImpl implements NoticeCommandService { private final AuthQueryService authQueryService; private final ModelMapper modelMapper; + private final AlarmService alarmService; @Autowired - public NoticeCommandServiceImpl(NoticeRepository noticeRepository, ModelMapper modelMapper, AuthQueryService authQueryService) { + public NoticeCommandServiceImpl(NoticeRepository noticeRepository, ModelMapper modelMapper, + AuthQueryService authQueryService, AlarmService alarmService) { this.noticeRepository = noticeRepository; this.modelMapper = modelMapper; this.authQueryService =authQueryService; + this.alarmService = alarmService; } private String getCurrentTimestamp() { @@ -54,7 +59,11 @@ public void registerNotice(NoticeRegistDTO noticeRegistDTO, try { Notice notice = modelMapper.map(noticeRegistDTO, Notice.class); - noticeRepository.save(notice); + Notice newNotice = noticeRepository.save(notice); + + NoticeAlarmDTO noticeAlarmDTO = modelMapper.map(newNotice, NoticeAlarmDTO.class); + + alarmService.sendNoticeAlarm(noticeAlarmDTO); } catch (DataIntegrityViolationException e){ // DB 무결정 제약 조건 (NOT NULL, UNIQUE) 위반 diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java index 8f8e13cb..c0e09889 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/repository/ScheduleMapper.java @@ -2,6 +2,7 @@ import org.apache.ibatis.annotations.Mapper; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; +import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDayDTO; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDetailDTO; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleYearMonthDTO; @@ -17,5 +18,5 @@ public interface ScheduleMapper { List findSchedulesByMemberIdAndStartAt(Map arg); - List findAllSchedulesByDay(String currentDay); + List findAllSchedulesByDay(String currentDay); } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryService.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryService.java index ded37b15..1484886c 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryService.java @@ -1,6 +1,7 @@ package stanl_2.final_backend.domain.schedule.query.service; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; +import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDayDTO; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDetailDTO; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleYearMonthDTO; @@ -13,5 +14,5 @@ public interface ScheduleQueryService { ScheduleDetailDTO selectDetailSchedule(ScheduleDetailDTO scheduleDetailDTO); - List findSchedulesByDate(String currentDay); + List findSchedulesByDate(String currentDay); } diff --git a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java index 3654c134..82bb904c 100644 --- a/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/schedule/query/service/ScheduleQueryServiceImpl.java @@ -11,6 +11,7 @@ import stanl_2.final_backend.domain.schedule.common.exception.ScheduleCommonException; import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDTO; +import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDayDTO; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDetailDTO; import stanl_2.final_backend.domain.schedule.query.dto.ScheduleYearMonthDTO; import stanl_2.final_backend.domain.schedule.query.repository.ScheduleMapper; @@ -104,9 +105,9 @@ public ScheduleDetailDTO selectDetailSchedule(ScheduleDetailDTO scheduleDetailDT @Override @Transactional(readOnly = true) - public List findSchedulesByDate(String currentDay) { + public List findSchedulesByDate(String currentDay) { - List todaySchedules = scheduleMapper.findAllSchedulesByDay(currentDay); + List todaySchedules = scheduleMapper.findAllSchedulesByDay(currentDay); return todaySchedules; } diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberRoleMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberRoleMapper.xml index f2aae3a6..ff3c6c2f 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberRoleMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberRoleMapper.xml @@ -3,15 +3,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> - - - - - - SELECT a.mem_id - FROM tb_mem_role a - WHERE a.role_name = #{ memberRole } + FROM tb_member_role a + WHERE a.mem_rol_name = #{ memberRole } \ No newline at end of file From d4177508adf4bd7d1b7b47122550cd6408ea5eb7 Mon Sep 17 00:00:00 2001 From: woosuk1 Date: Wed, 20 Nov 2024 01:11:11 +0900 Subject: [PATCH 216/563] =?UTF-8?q?feat:=20=ED=8F=89=EA=B0=80=EC=84=9C=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84(#53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/EvaluationController.java | 89 ++++++++++--- .../query/repository/EvaluationMapper.java | 3 +- .../query/service/EvaluationQueryService.java | 6 +- .../service/EvaluationQueryServiceImpl.java | 117 +++++++++++------- .../query/repository/EvaluationMapper.xml | 4 +- .../member/query/repository/MemberMapper.xml | 4 +- 6 files changed, 152 insertions(+), 71 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java index 0378ec97..9d5a6d9d 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java @@ -20,6 +20,7 @@ import stanl_2.final_backend.domain.product.query.dto.ProductSearchRequestDTO; import java.security.GeneralSecurityException; +import java.security.Principal; import java.util.HashMap; import java.util.Map; @@ -37,26 +38,51 @@ public EvaluationController(EvaluationQueryService evaluationQueryService) { /** * [GET] http://localhost:7777/api/v1/sample/SAM_000000001 * */ - @Operation(summary = "평가서 전체 조회") + @Operation(summary = "평가서 관리자 전체 조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = EvaluationResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("") - public ResponseEntity getAllEvaluations(Authentication authentication, + @GetMapping("/manager") + public ResponseEntity getAllEvaluationsByManager(Principal principal, + Pageable pageable) throws GeneralSecurityException { + EvaluationDTO evaluationDTO = new EvaluationDTO(); + + evaluationDTO.setMemberId(principal.getName()); + + Page responseEvaluations = evaluationQueryService.selectAllEvaluationsByManager(evaluationDTO, pageable); + + return ResponseEntity.ok(EvaluationResponseMessage.builder() + .httpStatus(200) + .msg("평가서 관리자 전체 조회 성공") + .result(responseEvaluations) + .build()); + } + + /** + * [GET] http://localhost:7777/api/v1/sample/SAM_000000001 + * */ + @Operation(summary = "평가서 담당자 전체 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = EvaluationResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/representative") + public ResponseEntity getAllEvaluationsByRepresentative(Principal principal, Pageable pageable) throws GeneralSecurityException { EvaluationDTO evaluationDTO = new EvaluationDTO(); - evaluationDTO.setRoles(authentication.getAuthorities()); - evaluationDTO.setMemberId(authentication.getName()); + evaluationDTO.setMemberId(principal.getName()); - Page responseEvaluations = evaluationQueryService.selectAllEvaluations(evaluationDTO, pageable); + Page responseEvaluations = evaluationQueryService.selectAllEvaluationsByRepresentative(evaluationDTO, pageable); return ResponseEntity.ok(EvaluationResponseMessage.builder() .httpStatus(200) - .msg("평가서 전체 조회 성공") + .msg("평가서 담당자 전체 조회 성공") .result(responseEvaluations) .build()); } @@ -71,7 +97,7 @@ public ResponseEntity getAllEvaluations(Authenticatio @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("/detail/{id}") + @GetMapping("{id}") public ResponseEntity getEvaluationDetail(@PathVariable String id) { EvaluationDTO evaluationDTO = evaluationQueryService.selectEvaluationById(id); @@ -83,16 +109,48 @@ public ResponseEntity getEvaluationDetail(@PathVariab .build()); } - @Operation(summary = "평가서 검색 테스트") + + @Operation(summary = "평가서 관리자 검색") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = EvaluationResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/manager/search") + public ResponseEntity getEvaluationBySearchByManager(@RequestParam Map params + ,Principal principal + , @PageableDefault(size = 20) Pageable pageable) throws GeneralSecurityException { + + EvaluationSearchDTO evaluationSearchDTO = new EvaluationSearchDTO(); + evaluationSearchDTO.setEvalId(params.get("evalId")); + evaluationSearchDTO.setTitle(params.get("title")); + evaluationSearchDTO.setWriterName(params.get("writerName")); + evaluationSearchDTO.setMemberName(params.get("memberName")); + evaluationSearchDTO.setCenterId(params.get("centerId")); + evaluationSearchDTO.setStartDate(params.get("startDate")); + evaluationSearchDTO.setEndDate(params.get("endDate")); + evaluationSearchDTO.setSearcherName(principal.getName()); + + Page responseEvaluations = evaluationQueryService.selectEvaluationBySearchByManager(pageable, evaluationSearchDTO); + + return ResponseEntity.ok(EvaluationResponseMessage.builder() + .httpStatus(200) + .msg("관리자 검색 조회 성공") + .result(responseEvaluations) + .build()); + } + + @Operation(summary = "평가서 관리자 검색") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = EvaluationResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("/search") - public ResponseEntity getEvaluationBySearch(@RequestParam Map params - ,Authentication authentication + @GetMapping("/representative/search") + public ResponseEntity getEvaluationBySearchByRepresentative(@RequestParam Map params + ,Principal principal , @PageableDefault(size = 20) Pageable pageable) throws GeneralSecurityException { EvaluationSearchDTO evaluationSearchDTO = new EvaluationSearchDTO(); @@ -103,14 +161,13 @@ public ResponseEntity getEvaluationBySearch(@RequestP evaluationSearchDTO.setCenterId(params.get("centerId")); evaluationSearchDTO.setStartDate(params.get("startDate")); evaluationSearchDTO.setEndDate(params.get("endDate")); - evaluationSearchDTO.setRoles(authentication.getAuthorities()); - evaluationSearchDTO.setSearcherName(authentication.getName()); + evaluationSearchDTO.setSearcherName(principal.getName()); - Page responseEvaluations = evaluationQueryService.selectEvaluationBySearch(pageable, evaluationSearchDTO); + Page responseEvaluations = evaluationQueryService.selectEvaluationBySearchByRepresentative(pageable, evaluationSearchDTO); return ResponseEntity.ok(EvaluationResponseMessage.builder() .httpStatus(200) - .msg("검색 조회 성공") + .msg("담당자 검색 조회 성공") .result(responseEvaluations) .build()); } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.java index ae89e38d..fa255f58 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.java @@ -30,8 +30,7 @@ List findEvaluationBySearch(@Param("size") int size List findEvaluationByCenterIdAndSearch(@Param("size") int size ,@Param("offset") int offset - ,@Param("evaluationSearchDTO") EvaluationSearchDTO evaluationSearchDTO - ,@Param("centerId") String centerId); + ,@Param("evaluationSearchDTO") EvaluationSearchDTO evaluationSearchDTO); int findEvaluationBySearchCount(@Param("evaluationSearchDTO") EvaluationSearchDTO evaluationSearchDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryService.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryService.java index 505df489..ebbf3977 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryService.java @@ -13,7 +13,9 @@ public interface EvaluationQueryService { EvaluationDTO selectEvaluationById(String id); - Page selectAllEvaluations(EvaluationDTO evaluationDTO, Pageable pageable) throws GeneralSecurityException; + Page selectAllEvaluationsByManager(EvaluationDTO evaluationDTO, Pageable pageable) throws GeneralSecurityException; + Page selectAllEvaluationsByRepresentative(EvaluationDTO evaluationDTO, Pageable pageable) throws GeneralSecurityException; - Page selectEvaluationBySearch(Pageable pageable, EvaluationSearchDTO evaluationSearchDTO) throws GeneralSecurityException; + Page selectEvaluationBySearchByManager(Pageable pageable, EvaluationSearchDTO evaluationSearchDTO) throws GeneralSecurityException; + Page selectEvaluationBySearchByRepresentative(Pageable pageable, EvaluationSearchDTO evaluationSearchDTO) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java index 21aa27ed..542de41f 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java @@ -22,6 +22,7 @@ import java.security.GeneralSecurityException; import java.util.List; import java.util.Map; +import java.util.Optional; @Service public class EvaluationQueryServiceImpl implements EvaluationQueryService { @@ -41,24 +42,25 @@ public EvaluationQueryServiceImpl(EvaluationMapper evaluationMapper, AESUtils ae @Override @Transactional(readOnly = true) - public Page selectAllEvaluations(EvaluationDTO evaluationDTO, Pageable pageable) throws GeneralSecurityException { + public Page selectAllEvaluationsByManager(EvaluationDTO evaluationDTO, Pageable pageable) throws GeneralSecurityException { int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); - if(evaluationDTO.getRoles().stream() - .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()))){ + MemberDTO memberDTO = memberQueryService.selectMemberInfo(evaluationDTO.getMemberId()); +// String centerId = aesUtils.decrypt(memberDTO.getCenterId()); + String centerId = memberDTO.getCenterId(); - MemberDTO memberDTO = memberQueryService.selectMemberInfo(evaluationDTO.getMemberId()); - String centerId = aesUtils.decrypt(memberDTO.getCenterId()); + System.out.println("centerId: " + centerId); - List evaluationList = evaluationMapper.findEvaluationByCenterId(size,offset, centerId); + List evaluationList = evaluationMapper.findEvaluationByCenterId(size,offset, centerId); - int total = evaluationMapper.findEvaluationCountByCenterId(centerId); + int total = evaluationMapper.findEvaluationCountByCenterId(centerId); - if(evaluationList.isEmpty() || total == 0){ - throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); - } -/* 설명. ID를 LOGINID로 바꿔주는 메소드 찾아서... */ + if(evaluationList.isEmpty() || total == 0) { + throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); + } + + /* 설명. ID를 LOGINID로 바꿔주는 메소드 찾아서... */ // evaluationList.forEach(evaluation -> { // try { // evaluation.setMemberId(evaluation.getMemberName())); @@ -66,22 +68,32 @@ public Page selectAllEvaluations(EvaluationDTO evaluationDTO, Pag // throw new RuntimeException(e); // } // }); - return new PageImpl<>(evaluationList, pageable, total); - } else if(evaluationDTO.getRoles().stream() - .anyMatch(role -> "ROLE_REPRESENTATIVE".equals(role.getAuthority()))) { + return new PageImpl<>(evaluationList, pageable, total); + } - List evaluationList = evaluationMapper.findAllEvaluations(size,offset); + @Override + @Transactional(readOnly = true) + public Page selectAllEvaluationsByRepresentative(EvaluationDTO evaluationDTO, Pageable pageable) throws GeneralSecurityException { + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); - int total = evaluationMapper.findEvaluationCount(); + List evaluationList = evaluationMapper.findAllEvaluations(size,offset); - if(evaluationList.isEmpty() || total == 0) { - throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); - } + int total = evaluationMapper.findEvaluationCount(); - return new PageImpl<>(evaluationList, pageable, total); - } else{ - throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + if(evaluationList.isEmpty() || total == 0) { + throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); } + + /* 설명. ID를 LOGINID로 바꿔주는 메소드 찾아서... */ +// evaluationList.forEach(evaluation -> { +// try { +// evaluation.setMemberId(evaluation.getMemberName())); +// } catch (GeneralSecurityException e) { +// throw new RuntimeException(e); +// } +// }); + return new PageImpl<>(evaluationList, pageable, total); } @Override @@ -98,30 +110,29 @@ public EvaluationDTO selectEvaluationById(String id) { @Override @Transactional(readOnly = true) - public Page selectEvaluationBySearch(Pageable pageable, EvaluationSearchDTO evaluationSearchDTO) throws GeneralSecurityException { + public Page selectEvaluationBySearchByManager(Pageable pageable, EvaluationSearchDTO evaluationSearchDTO) throws GeneralSecurityException { int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); - if(!evaluationSearchDTO.getWriterName().isEmpty()) { - evaluationSearchDTO.setWriterName(authQueryService.selectMemberIdByLoginId(evaluationSearchDTO.getWriterName())); + String writerName = Optional.ofNullable(evaluationSearchDTO.getWriterName()).orElse(""); + if (!writerName.isEmpty()) { + evaluationSearchDTO.setWriterName(authQueryService.selectMemberIdByLoginId(writerName)); } - if(!evaluationSearchDTO.getWriterName().isEmpty()) { - evaluationSearchDTO.setMemberName(authQueryService.selectMemberIdByLoginId(evaluationSearchDTO.getMemberName())); + String memberName = Optional.ofNullable(evaluationSearchDTO.getMemberName()).orElse(""); + if (!memberName.isEmpty()) { + evaluationSearchDTO.setMemberName(authQueryService.selectMemberIdByLoginId(memberName)); } - if(evaluationSearchDTO.getRoles().stream() - .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()))){ + MemberDTO memberDTO = memberQueryService.selectMemberInfo(evaluationSearchDTO.getSearcherName()); + evaluationSearchDTO.setCenterId(memberDTO.getCenterId()); - MemberDTO memberDTO = memberQueryService.selectMemberInfo(evaluationSearchDTO.getSearcherName()); - String centerId = aesUtils.decrypt(memberDTO.getCenterId()); + List evaluationList = evaluationMapper.findEvaluationByCenterIdAndSearch(size,offset, evaluationSearchDTO); - List evaluationList = evaluationMapper.findEvaluationByCenterIdAndSearch(size,offset, evaluationSearchDTO, centerId); + int total = evaluationMapper.findEvaluationByCenterIdAndSearchCount(evaluationSearchDTO); - int total = evaluationMapper.findEvaluationByCenterIdAndSearchCount(evaluationSearchDTO); - - if(evaluationList.isEmpty() || total == 0){ - throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); - } + if(evaluationList.isEmpty() || total == 0){ + throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); + } /* 설명. ID를 LOGINID로 바꿔주는 메소드 찾아서... */ // evaluationList.forEach(evaluation -> { // try { @@ -130,22 +141,32 @@ public Page selectEvaluationBySearch(Pageable pageable, Evaluatio // throw new RuntimeException(e); // } // }); - return new PageImpl<>(evaluationList, pageable, total); - } else if(evaluationSearchDTO.getRoles().stream() - .anyMatch(role -> "ROLE_REPRESENTATIVE".equals(role.getAuthority()))) { + return new PageImpl<>(evaluationList, pageable, total); + } + @Override + @Transactional(readOnly = true) + public Page selectEvaluationBySearchByRepresentative(Pageable pageable, EvaluationSearchDTO evaluationSearchDTO) throws GeneralSecurityException { + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); - List evaluationList = evaluationMapper.findEvaluationBySearch(size,offset, evaluationSearchDTO); + String writerName = Optional.ofNullable(evaluationSearchDTO.getWriterName()).orElse(""); + if (!writerName.isEmpty()) { + evaluationSearchDTO.setWriterName(authQueryService.selectMemberIdByLoginId(writerName)); + } + String memberName = Optional.ofNullable(evaluationSearchDTO.getMemberName()).orElse(""); + if (!memberName.isEmpty()) { + evaluationSearchDTO.setMemberName(authQueryService.selectMemberIdByLoginId(memberName)); + } - int total = evaluationMapper.findEvaluationBySearchCount(evaluationSearchDTO); + List evaluationList = evaluationMapper.findEvaluationBySearch(size,offset, evaluationSearchDTO); - if(evaluationList.isEmpty() || total == 0) { - throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); - } + int total = evaluationMapper.findEvaluationBySearchCount(evaluationSearchDTO); - return new PageImpl<>(evaluationList, pageable, total); - } else{ - throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + if(evaluationList.isEmpty() || total == 0) { + throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); } + return new PageImpl<>(evaluationList, pageable, total); + } } diff --git a/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml b/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml index 7b57b109..f252913f 100644 --- a/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml @@ -67,7 +67,7 @@ a.created_at FROM tb_evaluation a - a.active = TRUE AND a.cent_id = #{centerId} + a.active = TRUE AND a.cent_id = #{evaluationSearchDTO.centerId} AND a.eval_id = #{evaluationSearchDTO.evalId} @@ -168,7 +168,7 @@ COUNT(*) AS cnt FROM tb_evaluation a - a.active = TRUE AND a.cent_id = #{centerId} + a.active = TRUE AND a.cent_id = #{evaluationSearchDTO.centerId} AND a.eval_id = #{evaluationSearchDTO.evalId} diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml index 20ad9185..bdc3d46e 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml @@ -20,6 +20,7 @@ + From 3f12c15d719eeeb873e8b6916b8c2a682984fb34 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Wed, 20 Nov 2024 01:23:46 +0900 Subject: [PATCH 217/563] =?UTF-8?q?refactor:=20=EC=88=98=EC=A3=BC=EC=84=9C?= =?UTF-8?q?=20=ED=9A=8C=EC=9B=90=20id=EA=B0=92=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=B0=8F=20=EB=A0=88=EB=94=94=EC=8A=A4=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?(#101)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/aggregate/entity/Contract.java | 2 +- .../query/service/ContractQueryService.java | 4 + .../service/ContractQueryServiceImpl.java | 24 ++- .../controller/OrderController.java | 53 ++---- .../service/OrderCommandService.java | 2 +- .../domain/aggregate/entity/Order.java | 2 +- .../service/OrderCommandServiceImpl.java | 39 ++-- .../query/controller/OrderController.java | 112 ++++++++++-- .../order/query/dto/OrderSelectAllDTO.java | 2 +- .../order/query/dto/OrderSelectSearchDTO.java | 14 +- .../order/query/repository/OrderMapper.java | 10 + .../query/service/OrderQueryService.java | 8 +- .../query/service/OrderQueryServiceImpl.java | 123 ++++++++++++- .../query/repository/ContractMapper.xml | 3 +- .../order/query/repository/OrderMapper.xml | 173 +++++++++++++++--- 15 files changed, 451 insertions(+), 120 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java index 354e3e16..4f381169 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java @@ -122,7 +122,7 @@ public class Contract { @Column(name = "MEM_ID", nullable = false) private String memberId; - @Column(name = "ADMI_ID", nullable = false) + @Column(name = "ADMI_ID") private String adminId; @Column(name = "CENT_ID", nullable = false) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java index 8cff949d..adb26553 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java @@ -2,6 +2,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.contract.query.dto.ContractSearchDTO; import stanl_2.final_backend.domain.contract.query.dto.ContractSelectAllDTO; import stanl_2.final_backend.domain.contract.query.dto.ContractSeletIdDTO; @@ -14,4 +15,7 @@ public interface ContractQueryService { Page selectBySearch(ContractSearchDTO contractSearchDTO, Pageable pageable); Page selectAll(ContractSelectAllDTO contractSelectAllDTO, Pageable pageable); + + @Transactional(readOnly = true) + ContractSeletIdDTO selectContractByIdAndMemberId(String contractId, String memberId); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java index 8b67fc8f..d717eeec 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java @@ -7,6 +7,7 @@ import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.contract.common.exception.ContractCommonException; import stanl_2.final_backend.domain.contract.common.exception.ContractErrorCode; import stanl_2.final_backend.domain.contract.query.dto.ContractSearchDTO; @@ -14,8 +15,6 @@ import stanl_2.final_backend.domain.contract.query.dto.ContractSeletIdDTO; import stanl_2.final_backend.domain.contract.query.repository.ContractMapper; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; -import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; -import stanl_2.final_backend.domain.product.query.service.ProductService; import stanl_2.final_backend.global.exception.GlobalCommonException; import stanl_2.final_backend.global.exception.GlobalErrorCode; @@ -38,6 +37,7 @@ public ContractQueryServiceImpl(ContractMapper contractMapper, AuthQueryService // 계약서 전체조회 @Override + @Transactional(readOnly = true) public Page selectAll(ContractSelectAllDTO contractSelectAllDTO, Pageable pageable) { if(contractSelectAllDTO.getRoles().stream() @@ -83,6 +83,7 @@ public Page selectAll(ContractSelectAllDTO contractSelectA // 계약서 상세조회 @Override + @Transactional(readOnly = true) public ContractSeletIdDTO selectDetailContract(ContractSeletIdDTO contractSeletIdDTO) { if(contractSeletIdDTO.getRoles().stream() @@ -124,6 +125,7 @@ public ContractSeletIdDTO selectDetailContract(ContractSeletIdDTO contractSeletI } @Override + @Transactional(readOnly = true) public Page selectBySearch(ContractSearchDTO contractSearchDTO, Pageable pageable) { if (contractSearchDTO.getRoles().stream() @@ -188,4 +190,22 @@ public Page selectBySearch(ContractSearchDTO contractSearchDT throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); } } + + + @Override + @Transactional(readOnly = true) + public ContractSeletIdDTO selectContractByIdAndMemberId(String contractId, String memberId) { + + ContractSeletIdDTO contractDTO = new ContractSeletIdDTO(); + contractDTO.setContractId(contractId); + contractDTO.setMemberId(memberId); + + ContractSeletIdDTO contractSelectDto = contractMapper.findContractByIdAndMemId(contractDTO); + + if (contractSelectDto == null) { + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); + } + + return contractSelectDto; + } } diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderController.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderController.java index 3360fa30..5f12e9df 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderController.java @@ -13,6 +13,8 @@ import stanl_2.final_backend.domain.order.command.application.service.OrderCommandService; import stanl_2.final_backend.domain.order.common.response.OrderResponseMessage; +import java.security.Principal; + @RestController("OrderCommandController") @RequestMapping("/api/v1/order") public class OrderController { @@ -24,24 +26,16 @@ public OrderController(OrderCommandService orderCommandService) { this.orderCommandService = orderCommandService; } - /** - * [POST] http://localhost:8080/api/v1/order - * Request - * { - * "title": "241115 셀토스 계약 주문", - * "content": "\n\n\n \n \n 자동차 매매 계약서\n \n\n\n
\n
\n

기아 자동차 매매 계약서

\n
\n
\n

계약 정보

\n \n \n \n
계약 번호KL-JS계약일2022-11-17계약장소서울 강남구
담당자유혜진전화번호010-7158-8796
\n
\n
\n

고객사항

\n
성명홍길동상호
주민등록번호****-*******사업자등록번호
주소**********사업자등록주소OOOOOOOO
전화(휴대폰)010-****-****구분개인
E-mail****@****.com구매유형현금
\n
\n
\n

차량사항

\n
차종Q4 e-tron 40일련번호2Y2Y
선택옵션AO대수1대
인도예정일2023년 인도인도장소서울지점
특약사항- 특약사항 내용이 여기에 표시됩니다.
\n
\n
\n

금액사항

\n
차량가격66,700,000원
계약금900,000원
중도금100,000원
인도금65,700,000원
탁송료65,700,000원
\n
\n
\n

본 계약서 주요 내용을 확인하고 계약을 체결하였음을 확인합니다.

\n
\n
매수인 (서명): ****
\n
매도인 (서명): ****
\n
\n
\n
\n\n", - * "conrId": "CON_000000001", - * "memId": "MEM_000000001" - * } - * */ @Operation(summary = "수주서 등록") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "수주서 등록 성공", content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postOrder(@RequestBody OrderRegistDTO orderRegistDTO) { + public ResponseEntity postOrder(@RequestBody OrderRegistDTO orderRegistDTO, + Principal principal) { + orderRegistDTO.setMemberId(principal.getName()); orderCommandService.registerOrder(orderRegistDTO); return ResponseEntity.ok(OrderResponseMessage.builder() @@ -51,26 +45,18 @@ public ResponseEntity postOrder(@RequestBody OrderRegistDT .build()); } - - /** - * [PUT] http://localhost:8080/api/v1/order/ORD_000000011 - * Request - * { - * "title": "241115 셀토스 계약 주문 수정!!", - * "content": "\n\n\n \n \n 자동차 매매 계약서\n \n\n\n
\n
\n

기아 자동차 매매 계약서

\n
\n
\n

계약 정보

\n \n \n \n
계약 번호KL-JS계약일2022-11-17계약장소서울 강남구
담당자유혜진전화번호010-7158-8796
\n
\n
\n

고객사항

\n
성명홍길동상호
주민등록번호****-*******사업자등록번호
주소**********사업자등록주소OOOOOOOO
전화(휴대폰)010-****-****구분개인
E-mail****@****.com구매유형현금
\n
\n
\n

차량사항

\n
차종Q4 e-tron 40일련번호2Y2Y
선택옵션AO대수1대
인도예정일2023년 인도인도장소서울지점
특약사항- 특약사항 내용이 여기에 표시됩니다.
\n
\n
\n

금액사항

\n
차량가격66,700,000원
계약금900,000원
중도금100,000원
인도금65,700,000원
탁송료65,700,000원
\n
\n
\n

본 계약서 주요 내용을 확인하고 계약을 체결하였음을 확인합니다.

\n
\n
매수인 (서명): ****
\n
매도인 (서명): ****
\n
\n
\n
\n\n", - * "memId": "MEM_000000001" - * } - * */ @Operation(summary = "수주서 수정") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "수주서 수정 성공", content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) }) - @PutMapping("{id}") - public ResponseEntity putOrder(@PathVariable String id, - @RequestBody OrderModifyDTO orderModifyDTO) { + @PutMapping("{orderId}") + public ResponseEntity putOrder(@PathVariable String orderId, + @RequestBody OrderModifyDTO orderModifyDTO, + Principal principal) { - orderModifyDTO.setOrderId(id); + orderModifyDTO.setOrderId(orderId); + orderModifyDTO.setMemberId(principal.getName()); OrderModifyDTO orderModifyResponseDTO = orderCommandService.modifyOrder(orderModifyDTO); return ResponseEntity.ok(OrderResponseMessage.builder() @@ -80,24 +66,17 @@ public ResponseEntity putOrder(@PathVariable String id, .build()); } - /** - * [PUT] http://localhost:8080/api/v1/order/ORD_000000011 - * Request - * { - * "title": "241115 셀토스 계약 주문 수정!!", - * "content": "\n\n\n \n \n 자동차 매매 계약서\n \n\n\n
\n
\n

기아 자동차 매매 계약서

\n
\n
\n

계약 정보

\n \n \n \n
계약 번호KL-JS계약일2022-11-17계약장소서울 강남구
담당자유혜진전화번호010-7158-8796
\n
\n
\n

고객사항

\n
성명홍길동상호
주민등록번호****-*******사업자등록번호
주소**********사업자등록주소OOOOOOOO
전화(휴대폰)010-****-****구분개인
E-mail****@****.com구매유형현금
\n
\n
\n

차량사항

\n
차종Q4 e-tron 40일련번호2Y2Y
선택옵션AO대수1대
인도예정일2023년 인도인도장소서울지점
특약사항- 특약사항 내용이 여기에 표시됩니다.
\n
\n
\n

금액사항

\n
차량가격66,700,000원
계약금900,000원
중도금100,000원
인도금65,700,000원
탁송료65,700,000원
\n
\n
\n

본 계약서 주요 내용을 확인하고 계약을 체결하였음을 확인합니다.

\n
\n
매수인 (서명): ****
\n
매도인 (서명): ****
\n
\n
\n
\n\n", - * "memId": "MEM_000000001" - * } - * */ @Operation(summary = "수주서 삭제") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "수주서 삭제 성공", content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) }) - @DeleteMapping("/{id}") - public ResponseEntity deleteTest(@PathVariable("id") String id) { + @DeleteMapping("/{orderId}") + public ResponseEntity deleteTest(@PathVariable("orderId") String orderId, + Principal principal) { - orderCommandService.deleteOrder(id); + String loginId = principal.getName(); + orderCommandService.deleteOrder(orderId, loginId); return ResponseEntity.ok(OrderResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/service/OrderCommandService.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/service/OrderCommandService.java index 35665d2f..f76e18b2 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/application/service/OrderCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/service/OrderCommandService.java @@ -8,5 +8,5 @@ public interface OrderCommandService { OrderModifyDTO modifyOrder(OrderModifyDTO orderModifyDTO); - void deleteOrder(String id); + void deleteOrder(String orderId, String loginId); } diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/domain/aggregate/entity/Order.java b/src/main/java/stanl_2/final_backend/domain/order/command/domain/aggregate/entity/Order.java index 123163f1..012fd0be 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/domain/aggregate/entity/Order.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/domain/aggregate/entity/Order.java @@ -49,7 +49,7 @@ public class Order { private String deletedAt; @Column(name = "ORD_STAT", nullable = false) - private String status; + private String status = "WAIT"; @Column(name = "CONR_ID", nullable = false) private String contractId; diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java index 015c3b35..de6f9905 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java @@ -3,7 +3,11 @@ import org.modelmapper.ModelMapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.contract.command.domain.aggregate.entity.Contract; import stanl_2.final_backend.domain.contract.command.domain.repository.ContractRepository; +import stanl_2.final_backend.domain.contract.query.dto.ContractSeletIdDTO; +import stanl_2.final_backend.domain.contract.query.service.ContractQueryService; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.order.command.application.dto.OrderModifyDTO; import stanl_2.final_backend.domain.order.command.application.dto.OrderRegistDTO; import stanl_2.final_backend.domain.order.command.application.service.OrderCommandService; @@ -20,12 +24,12 @@ public class OrderCommandServiceImpl implements OrderCommandService { private final OrderRepository orderRepository; - private final ContractRepository contractRepository; + private final AuthQueryService authQueryService; private final ModelMapper modelMapper; - public OrderCommandServiceImpl(OrderRepository orderRepository, ContractRepository contractRepository, ModelMapper modelMapper) { + public OrderCommandServiceImpl(OrderRepository orderRepository, AuthQueryService authQueryService, ModelMapper modelMapper) { this.orderRepository = orderRepository; - this.contractRepository = contractRepository; + this.authQueryService = authQueryService; this.modelMapper = modelMapper; } @@ -37,14 +41,12 @@ private String getCurrentTime() { @Override @Transactional public void registerOrder(OrderRegistDTO orderRegistDTO) { - // 회원인지 확인여부 - // 회원의 계약서가 맞는지 확인 -// Contract contract = contractRepository.findById(orderRegistDTO.getConrId()) -// .orElseThrow(() -> new Contrac) + String memberId = authQueryService.selectMemberIdByLoginId(orderRegistDTO.getMemberId()); + + orderRegistDTO.setMemberId(memberId); Order order = modelMapper.map(orderRegistDTO, Order.class); - order.setStatus("WAIT"); orderRepository.save(order); } @@ -52,10 +54,14 @@ public void registerOrder(OrderRegistDTO orderRegistDTO) { @Override @Transactional public OrderModifyDTO modifyOrder(OrderModifyDTO orderModifyDTO) { - // 회원인지 확인여부 - Order order = orderRepository.findById(orderModifyDTO.getOrderId()) - .orElseThrow(() -> new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND)); + String memberId = authQueryService.selectMemberIdByLoginId(orderModifyDTO.getMemberId()); + + Order order = orderRepository.findByOrderIdAndMemberId(orderModifyDTO.getOrderId(), memberId); + + if (order == null) { + throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); + } Order updateOrder = modelMapper.map(orderModifyDTO, Order.class); updateOrder.setCreatedAt(order.getCreatedAt()); @@ -73,12 +79,15 @@ public OrderModifyDTO modifyOrder(OrderModifyDTO orderModifyDTO) { @Override @Transactional - public void deleteOrder(String id) { + public void deleteOrder(String id, String loginId) { + + String memberId = authQueryService.selectMemberIdByLoginId(loginId); - // 회원 확인 + Order order = orderRepository.findByOrderIdAndMemberId(id, memberId); - Order order = orderRepository.findById(id) - .orElseThrow(() -> new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND)); + if (order == null) { + throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); + } order.setActive(false); order.setDeletedAt(getCurrentTime()); diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java b/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java index edd37ba6..f48af8ff 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java @@ -17,6 +17,8 @@ import stanl_2.final_backend.domain.order.query.dto.OrderSelectSearchDTO; import stanl_2.final_backend.domain.order.query.service.OrderQueryService; +import java.security.Principal; + @RestController("queryOrderController") @RequestMapping("/api/v1/order") public class OrderController { @@ -28,18 +30,18 @@ public OrderController(OrderQueryService orderQueryService) { this.orderQueryService = orderQueryService; } - @Operation(summary = "수주서 전제 조회") + @Operation(summary = "수주서 전체 조회(영업사원)") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "수주서 전제 조회 성공", + @ApiResponse(responseCode = "200", description = "수주서 전체 조회 성공", content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) }) - @GetMapping("{memberId}") - public ResponseEntity getAllOrder(@PathVariable("memberId") String memberId, + @GetMapping("/employee") + public ResponseEntity getAllOrderEmployee(Principal principal, @PageableDefault(size = 10)Pageable pageable) { - // 회원 아이디 받아 오는건 나중에 수정할 예정 + String loginId = principal.getName(); - Page responseOrders = orderQueryService.selectAll(memberId, pageable); + Page responseOrders = orderQueryService.selectAllEmployee(loginId, pageable); return ResponseEntity.ok(OrderResponseMessage.builder() .httpStatus(200) @@ -48,20 +50,21 @@ public ResponseEntity getAllOrder(@PathVariable("memberId" .build()); } - @Operation(summary = "수주서 상세 조회") + @Operation(summary = "수주서 상세 조회(영업사원)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "수주서 상세 조회 성공", content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) }) - @GetMapping("{id}/{memberId}") - public ResponseEntity getDetailOrder(@PathVariable("id") String orderId, - @PathVariable("memberId") String memberId) { + @GetMapping("/employee/{orderId}") + public ResponseEntity getDetailOrderEmployee(@PathVariable("orderId") String orderId, + Principal principal) { + String memberId = principal.getName(); OrderSelectIdDTO orderSelectIdDTO = new OrderSelectIdDTO(); orderSelectIdDTO.setOrderId(orderId); orderSelectIdDTO.setMemberId(memberId); - OrderSelectIdDTO responseOrder = orderQueryService.selectDetailOrder(orderSelectIdDTO); + OrderSelectIdDTO responseOrder = orderQueryService.selectDetailOrderEmployee(orderSelectIdDTO); return ResponseEntity.ok(OrderResponseMessage.builder() .httpStatus(200) @@ -70,21 +73,98 @@ public ResponseEntity getDetailOrder(@PathVariable("id") S .build()); } - @Operation(summary = "수주서 검색 조회") + @Operation(summary = "수주서 검색 조회(영업사원)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "수주서 검색 조회 성공", + content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) + }) + @GetMapping("/employee/search") + public ResponseEntity getSearchOrderEmployee(@RequestParam(required = false) String title, + @RequestParam(required = false) String status, + @RequestParam(required = false) String adminId, + @RequestParam(required = false) String searchMemberId, + @RequestParam(required = false) String startDate, + @RequestParam(required = false) String endDate, + Principal principal, + @PageableDefault(size = 10) Pageable pageable) { + + OrderSelectSearchDTO orderSelectSearchDTO = new OrderSelectSearchDTO(); + orderSelectSearchDTO.setTitle(title); + orderSelectSearchDTO.setStatus(status); + orderSelectSearchDTO.setAdminId(adminId); + orderSelectSearchDTO.setSearchMemberId(searchMemberId); + orderSelectSearchDTO.setStartDate(startDate); + orderSelectSearchDTO.setEndDate(endDate); + orderSelectSearchDTO.setMemberId(principal.getName()); + + Page responseOrders = orderQueryService.selectSearchOrdersEmployee(orderSelectSearchDTO, pageable); + + return ResponseEntity.ok(OrderResponseMessage.builder() + .httpStatus(200) + .msg("수주서 검색 조회 성공") + .result(responseOrders) + .build()); + } + + // 영업관리자, 영업담당자 조회 + @Operation(summary = "수주서 전체 조회(영업관리자, 영업담당자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "수주서 전체 조회 성공", + content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) + }) + @GetMapping("") + public ResponseEntity getAllOrder(@PageableDefault(size = 10)Pageable pageable) { + + Page responseOrders = orderQueryService.selectAll(pageable); + + return ResponseEntity.ok(OrderResponseMessage.builder() + .httpStatus(200) + .msg("수주서 전체 조회 성공") + .result(responseOrders) + .build()); + } + + @Operation(summary = "수주서 상세 조회(영업관리자, 영업담당자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "수주서 상세 조회 성공", + content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) + }) + @GetMapping("{orderId}") + public ResponseEntity getDetailOrder(@PathVariable("orderId") String orderId) { + + OrderSelectIdDTO orderSelectIdDTO = new OrderSelectIdDTO(); + orderSelectIdDTO.setOrderId(orderId); + + OrderSelectIdDTO responseOrder = orderQueryService.selectDetailOrder(orderSelectIdDTO); + + return ResponseEntity.ok(OrderResponseMessage.builder() + .httpStatus(200) + .msg("수주서 상세 조회 성공") + .result(responseOrder) + .build()); + } + + @Operation(summary = "수주서 검색 조회(영업관리자, 영업담당자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "수주서 검색 조회 성공", content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) }) - @GetMapping("/search/{memId}") + @GetMapping("/search") public ResponseEntity getSearchOrder(@RequestParam(required = false) String title, @RequestParam(required = false) String status, @RequestParam(required = false) String adminId, - @RequestParam(required = false) String memberId, + @RequestParam(required = false) String searchMemberId, @RequestParam(required = false) String startDate, @RequestParam(required = false) String endDate, - @RequestParam(required = false) String memId, @PageableDefault(size = 10) Pageable pageable) { - OrderSelectSearchDTO orderSelectSearchDTO = new OrderSelectSearchDTO(title, status, adminId, memberId, memId, startDate, endDate); + + OrderSelectSearchDTO orderSelectSearchDTO = new OrderSelectSearchDTO(); + orderSelectSearchDTO.setTitle(title); + orderSelectSearchDTO.setStatus(status); + orderSelectSearchDTO.setAdminId(adminId); + orderSelectSearchDTO.setSearchMemberId(searchMemberId); + orderSelectSearchDTO.setStartDate(startDate); + orderSelectSearchDTO.setEndDate(endDate); Page responseOrders = orderQueryService.selectSearchOrders(orderSelectSearchDTO, pageable); diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectAllDTO.java index f6ac7908..a17b8908 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectAllDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectAllDTO.java @@ -13,7 +13,7 @@ public class OrderSelectAllDTO { private String orderId; private String title; private String status; - private String contractName; + private String contractTitle; private String adminName; private String memberName; private String productName; diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectSearchDTO.java index bcfd21b9..fa1487b4 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectSearchDTO.java @@ -13,23 +13,13 @@ public class OrderSelectSearchDTO { private String orderId; private String title; private String status; - private String contractName; + private String contractTitle; private String adminId; + private String searchMemberId; private String memberId; - private String memId; private String adminName; private String memberName; private String productName; private String startDate; private String endDate; - - public OrderSelectSearchDTO(String title, String status, String adminId, String memberId, String memId, String startDate, String endDate) { - this.title = title; - this.status = status; - this.adminId = adminId; - this.memId = memId; - this.memberId = memberId; - this.startDate = startDate; - this.endDate = endDate; - } } diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java b/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java index 665cf16b..6115952e 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java @@ -21,4 +21,14 @@ public interface OrderMapper { int findOrderSearchCountByMemberId(Map map); List findSearchOrderByMemberId(Map map); + + List findAllOrder(int offset, int pageSize); + + int findOrderCount(); + + OrderSelectIdDTO findOrderByOrderId(String orderId); + + List findSearchOrder(Map map); + + int findOrderSearchCount(Map map); } diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java index 4d77a61e..ae23d4aa 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java @@ -9,7 +9,13 @@ import java.util.Map; public interface OrderQueryService { - Page selectAll(String memberId, Pageable pageable); + Page selectAllEmployee(String loginId, Pageable pageable); + + OrderSelectIdDTO selectDetailOrderEmployee(OrderSelectIdDTO orderSelectIdDTO); + + Page selectSearchOrdersEmployee(OrderSelectSearchDTO orderSelectSearchDTO, Pageable pageable); + + Page selectAll(Pageable pageable); OrderSelectIdDTO selectDetailOrder(OrderSelectIdDTO orderSelectIdDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java index f1053581..a61f1c78 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java @@ -1,11 +1,14 @@ package stanl_2.final_backend.domain.order.query.service; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.order.common.exception.OrderCommonException; import stanl_2.final_backend.domain.order.common.exception.OrderErrorCode; import stanl_2.final_backend.domain.order.query.dto.OrderSelectAllDTO; @@ -18,35 +21,135 @@ import java.util.Map; @Service -@Transactional(readOnly = true) public class OrderQueryServiceImpl implements OrderQueryService { private final OrderMapper orderMapper; + private final AuthQueryService authQueryService; + private final RedisTemplate redisTemplate; @Autowired - public OrderQueryServiceImpl(OrderMapper orderMapper) { + public OrderQueryServiceImpl(OrderMapper orderMapper, AuthQueryService authQueryService, RedisTemplate redisTemplate) { this.orderMapper = orderMapper; + this.authQueryService = authQueryService; + this.redisTemplate = redisTemplate; } @Override - public Page selectAll(String memberId, Pageable pageable) { + @Transactional(readOnly = true) + public Page selectAllEmployee(String loginId, Pageable pageable) { + String memberId = authQueryService.selectMemberIdByLoginId(loginId); + int offset = Math.toIntExact(pageable.getOffset()); int pageSize = pageable.getPageSize(); - List orders = orderMapper.findAllOrderByMemberId(offset, pageSize, memberId); + + String cacheKey = "myCache::orders::offset=" + offset + "::size=" + pageSize; + + // 캐시 조회 + List orders = (List) redisTemplate.opsForValue().get(cacheKey); + + if (orders == null) { + System.out.println("데이터베이스에서 데이터 조회 중..."); + orders = orderMapper.findAllOrderByMemberId(offset, pageSize, memberId); + + // 결과가 null이거나 빈 리스트인지 확인 + if (orders == null || orders.isEmpty()) { + throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); + } + + // 캐시에 데이터 저장 (빈 리스트는 저장하지 않음) + redisTemplate.opsForValue().set(cacheKey, orders); + } else { + System.out.println("캐시에서 데이터 조회 중..."); + } + + // 전체 개수 조회 + int totalElements = orderMapper.findOrderCountByMemberId(memberId); + + return new PageImpl<>(orders, pageable, totalElements); + } + + + @Override + @Transactional(readOnly = true) + public OrderSelectIdDTO selectDetailOrderEmployee(OrderSelectIdDTO orderSelectIdDTO) { + String memberId = authQueryService.selectMemberIdByLoginId(orderSelectIdDTO.getMemberId()); + + OrderSelectIdDTO order = orderMapper.findOrderByIdAndMemberId(orderSelectIdDTO.getOrderId(), memberId); + + if (order == null) { + throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); + } + + return order; + } + + @Override + @Transactional(readOnly = true) + public Page selectSearchOrdersEmployee(OrderSelectSearchDTO orderSelectSearchDTO, Pageable pageable) { + + String memberId = authQueryService.selectMemberIdByLoginId(orderSelectSearchDTO.getMemberId()); + + Map map = new HashMap<>(); + map.put("memberId", memberId); + map.put("title", orderSelectSearchDTO.getTitle()); + map.put("status", orderSelectSearchDTO.getStatus()); + map.put("adminId", orderSelectSearchDTO.getAdminId()); + map.put("searchMemberId", orderSelectSearchDTO.getSearchMemberId()); + map.put("startDate", orderSelectSearchDTO.getStartDate()); + map.put("endDate", orderSelectSearchDTO.getEndDate()); + map.put("pageSize", pageable.getPageSize()); + map.put("offset", pageable.getOffset()); + + List orders = orderMapper.findSearchOrderByMemberId(map); if (orders == null || orders.isEmpty()) { throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); } - int totalElements = orderMapper.findOrderCountByMemberId(memberId); + int totalElements = orderMapper.findOrderSearchCountByMemberId(map); + + return new PageImpl<>(orders, pageable, totalElements); + } + + // 영업담당자, 영업관리자 조회 + @Override + @Transactional(readOnly = true) + public Page selectAll(Pageable pageable) { + + int offset = Math.toIntExact(pageable.getOffset()); + int pageSize = pageable.getPageSize(); + + String cacheKey = "myCache::orders::offset=" + offset + "::size=" + pageSize; + + // 캐시 조회 + List orders = (List) redisTemplate.opsForValue().get(cacheKey); + + if (orders == null) { + System.out.println("데이터베이스에서 데이터 조회 중..."); + orders = orderMapper.findAllOrder(offset, pageSize); + + // 결과가 null이거나 빈 리스트인지 확인 + if (orders == null || orders.isEmpty()) { + throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); + } + + // 캐시에 데이터 저장 (빈 리스트는 저장하지 않음) + redisTemplate.opsForValue().set(cacheKey, orders); + } else { + System.out.println("캐시에서 데이터 조회 중..."); + } + + // 전체 개수 조회 + int totalElements = orderMapper.findOrderCount(); return new PageImpl<>(orders, pageable, totalElements); } @Override + @Transactional(readOnly = true) public OrderSelectIdDTO selectDetailOrder(OrderSelectIdDTO orderSelectIdDTO) { - OrderSelectIdDTO order = orderMapper.findOrderByIdAndMemberId(orderSelectIdDTO.getOrderId(), orderSelectIdDTO.getMemberId()); + OrderSelectIdDTO order = orderMapper.findOrderByOrderId(orderSelectIdDTO.getOrderId()); if (order == null) { throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); @@ -56,26 +159,26 @@ public OrderSelectIdDTO selectDetailOrder(OrderSelectIdDTO orderSelectIdDTO) { } @Override + @Transactional(readOnly = true) public Page selectSearchOrders(OrderSelectSearchDTO orderSelectSearchDTO, Pageable pageable) { Map map = new HashMap<>(); - map.put("memId", orderSelectSearchDTO.getMemId()); map.put("title", orderSelectSearchDTO.getTitle()); map.put("status", orderSelectSearchDTO.getStatus()); map.put("adminId", orderSelectSearchDTO.getAdminId()); - map.put("memberId", orderSelectSearchDTO.getMemberId()); + map.put("searchMemberId", orderSelectSearchDTO.getSearchMemberId()); map.put("startDate", orderSelectSearchDTO.getStartDate()); map.put("endDate", orderSelectSearchDTO.getEndDate()); map.put("pageSize", pageable.getPageSize()); map.put("offset", pageable.getOffset()); - List orders = orderMapper.findSearchOrderByMemberId(map); + List orders = orderMapper.findSearchOrder(map); if (orders == null || orders.isEmpty()) { throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); } - int totalElements = orderMapper.findOrderSearchCountByMemberId(map); + int totalElements = orderMapper.findOrderSearchCount(map); return new PageImpl<>(orders, pageable, totalElements); } diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index 4d733dac..2a44829e 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -368,6 +368,7 @@ AND b.prod_name LIKE CONCAT('%', #{productId}, '%') -
; +
+ \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml index 136bb510..479f799f 100644 --- a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml @@ -8,7 +8,7 @@ - + @@ -29,11 +29,70 @@ + + + + + + - SELECT COUNT(*) AS cnt FROM tb_order a - WHERE a.mem_id = #{memberId} - AND a.active = TRUE; + + a.mem_id = #{memberId} + AND a.active = TRUE + + AND a.ord_ttl LIKE CONCAT('%', #{title}, '%') + + + AND a.ord_stat = #{status} + + + AND a.admin_id = #{adminId} + + + AND a.mem_id = #{searchMemberId} + + + AND a.created_at BETWEEN #{startDate} AND #{endDate} + + - + SELECT + a.ord_id, + a.ord_ttl, + a.ord_stat, + b.conr_ttl, + d.mem_name AS admin_name, + c.mem_name AS mem_name, + e.prod_name + FROM tb_order a + LEFT JOIN tb_contract b + ON a.conr_id = b.conr_id + LEFT JOIN tb_member c + ON a.mem_id = c.mem_id + LEFT JOIN tb_member d + ON a.admin_id = d.mem_id + LEFT JOIN tb_product e + ON b.prod_id = e.prod_id + WHERE AND a.active = TRUE + ORDER BY a.created_at DESC + LIMIT #{pageSize} OFFSET #{offset}; + + + + + - SELECT a.ord_id, a.ord_ttl, a.ord_stat, - b.conr_name, + b.conr_ttl, d.mem_name AS admin_name, c.mem_name AS mem_name, e.prod_name @@ -98,8 +230,7 @@ LEFT JOIN tb_product e ON b.prod_id = e.prod_id - a.mem_id = #{memId} - AND a.active = TRUE + a.active = TRUE AND a.ord_ttl LIKE CONCAT('%', #{title}, '%') @@ -109,8 +240,8 @@ AND a.admin_id = #{adminId} - - AND a.mem_id = #{memberId} + + AND a.mem_id = #{searchMemberId} AND a.created_at BETWEEN #{startDate} AND #{endDate} @@ -120,13 +251,12 @@ LIMIT #{pageSize} OFFSET #{offset}; - SELECT COUNT(*) AS cnt FROM tb_order a - a.mem_id = #{memId} - AND a.active = TRUE + a.active = TRUE AND a.ord_ttl LIKE CONCAT('%', #{title}, '%') @@ -136,8 +266,8 @@ AND a.admin_id = #{adminId} - - AND a.mem_id = #{memberId} + + AND a.mem_id = #{searchMemberId} AND a.created_at BETWEEN #{startDate} AND #{endDate} @@ -145,5 +275,4 @@ - \ No newline at end of file From 5bf590a47bea77aaf3885ae734a37f66471f5fcc Mon Sep 17 00:00:00 2001 From: giuseog Date: Wed, 20 Nov 2024 09:32:56 +0900 Subject: [PATCH 218/563] =?UTF-8?q?fix:=20=EC=95=88=EC=93=B0=EB=8A=94=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C,=20=EB=A1=9C=EA=B7=B8=20=EC=A0=9C=EA=B1=B0(#?= =?UTF-8?q?53)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/service/EvaluationQueryServiceImpl.java | 7 +------ src/main/resources/application-dev.yml | 2 +- src/main/resources/application-prod.yml | 2 +- src/main/resources/application.yml | 2 +- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java index 542de41f..0d01b9bd 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java @@ -28,14 +28,12 @@ public class EvaluationQueryServiceImpl implements EvaluationQueryService { private final EvaluationMapper evaluationMapper; - private final AESUtils aesUtils; private final MemberQueryService memberQueryService; private final AuthQueryService authQueryService; @Autowired - public EvaluationQueryServiceImpl(EvaluationMapper evaluationMapper, AESUtils aesUtils, MemberQueryService memberQueryService, AuthQueryService authQueryService) { + public EvaluationQueryServiceImpl(EvaluationMapper evaluationMapper, MemberQueryService memberQueryService, AuthQueryService authQueryService) { this.evaluationMapper = evaluationMapper; - this.aesUtils = aesUtils; this.memberQueryService = memberQueryService; this.authQueryService = authQueryService; } @@ -47,11 +45,8 @@ public Page selectAllEvaluationsByManager(EvaluationDTO evaluatio int size = pageable.getPageSize(); MemberDTO memberDTO = memberQueryService.selectMemberInfo(evaluationDTO.getMemberId()); -// String centerId = aesUtils.decrypt(memberDTO.getCenterId()); String centerId = memberDTO.getCenterId(); - System.out.println("centerId: " + centerId); - List evaluationList = evaluationMapper.findEvaluationByCenterId(size,offset, centerId); int total = evaluationMapper.findEvaluationCountByCenterId(centerId); diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index aabe59aa..e9390662 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: update + ddl-auto: create logging: level: diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 60bf5a48..23faa6a8 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: update + ddl-auto: create logging: level: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index eee7170c..6618043e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -23,7 +23,7 @@ spring: jpa: show-sql: true hibernate: - ddl-auto: update + ddl-auto: create properties: hibernate.format_sql: true From ac915deaaf009def88f2d30ef26d58531eefa431 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Wed, 20 Nov 2024 09:37:20 +0900 Subject: [PATCH 219/563] =?UTF-8?q?fix:=20log=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1(#99)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- logs/app-logs.json | 303 ++++++++++++++++++ src/logstash/conf/logstash.conf | 27 ++ .../global/logging/LoggingAspect.java | 36 +++ 3 files changed, 366 insertions(+) create mode 100644 logs/app-logs.json create mode 100644 src/logstash/conf/logstash.conf create mode 100644 src/main/java/stanl_2/final_backend/global/logging/LoggingAspect.java diff --git a/logs/app-logs.json b/logs/app-logs.json new file mode 100644 index 00000000..14dd3df2 --- /dev/null +++ b/logs/app-logs.json @@ -0,0 +1,303 @@ +HV000001: Hibernate Validator 8.0.1.Final +Starting FinalBackendApplication using Java 17.0.13 with PID 56754 (/Users/bdh/Final_Backend/build/classes/java/main started by bdh in /Users/bdh/Final_Backend) +The following 1 profile is active: "prod" +Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable +For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG' +Multiple Spring Data modules found, entering strict repository configuration mode +Bootstrapping Spring Data JPA repositories in DEFAULT mode. +Finished Spring Data repository scanning in 360 ms. Found 19 JPA repository interfaces. +Multiple Spring Data modules found, entering strict repository configuration mode +Bootstrapping Spring Data Redis repositories in DEFAULT mode. +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.A_sample.command.domain.repository.SampleRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.career.command.domain.repository.CareerRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.center.command.domain.repository.CenterRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.certification.command.domain.repository.CertificationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.contract.command.domain.repository.ContractRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.customer.command.domain.repository.CustomerRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.education.command.domain.repository.EducationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.evaluation.command.domain.repository.EvaluationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.family.command.domain.repository.FamilyRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.member.command.domain.repository.MemberRoleRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.notices.command.domain.repository.NoticeRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.order.command.domain.repository.OrderRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.organization.command.domain.repository.OrganizationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.problem.command.domain.aggregate.repository.ProblemRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.promotion.command.domain.aggregate.repository.PromotionRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.purchase_order.command.domain.repository.PurchaseOrderRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.sales_history.command.domain.repository.SalesHistoryRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.schedule.command.domain.repository.ScheduleRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Finished Spring Data repository scanning in 40 ms. Found 0 Redis repository interfaces. +Skipping MapperFactoryBean with name 'careerMapper' and 'stanl_2.final_backend.domain.career.query.repository.CareerMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'certificationMapper' and 'stanl_2.final_backend.domain.certification.query.repository.CertificationMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'customerMapper' and 'stanl_2.final_backend.domain.customer.query.repository.CustomerMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'educationMapper' and 'stanl_2.final_backend.domain.education.query.repository.EducationMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'familyMapper' and 'stanl_2.final_backend.domain.family.query.repository.FamilyMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'authMapper' and 'stanl_2.final_backend.domain.member.query.repository.AuthMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'memberMapper' and 'stanl_2.final_backend.domain.member.query.repository.MemberMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'memberRoleMapper' and 'stanl_2.final_backend.domain.member.query.repository.MemberRoleMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'noticeMapper' and 'stanl_2.final_backend.domain.notices.query.repository.NoticeMapper' mapperInterface. Bean already defined with the same name! +Tomcat initialized with port 8080 (http) +Initializing ProtocolHandler ["http-nio-8080"] +Starting service [Tomcat] +Starting Servlet engine: [Apache Tomcat/10.1.31] +Initializing Spring embedded WebApplicationContext +Root WebApplicationContext: initialization completed in 3128 ms +HHH000204: Processing PersistenceUnitInfo [name: default] +HHH000412: Hibernate ORM core version 6.5.2.Final +HHH000026: Second-level cache disabled +No LoadTimeWeaver setup: ignoring JPA class transformer +HikariPool-1 - Starting... +HikariPool-1 - Added connection org.mariadb.jdbc.Connection@573d554f +HikariPool-1 - Start completed. +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration) +Initialized JPA EntityManagerFactory for persistence unit 'default' +spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning +Global AuthenticationManager configured with AuthenticationProvider bean with name prodUsernamePwdAuthenticationProvider +Global AuthenticationManager configured with an AuthenticationProvider bean. UserDetailsService beans will not be used for username/password login. Consider removing the AuthenticationProvider bean. Alternatively, consider using the UserDetailsService in a manually instantiated DaoAuthenticationProvider. +Validated configuration attributes +Will secure any request with filters: DisableEncodeUrlFilter, ChannelProcessingFilter, WebAsyncManagerIntegrationFilter, SecurityContextHolderFilter, HeaderWriterFilter, CorsFilter, LogoutFilter, JWTTokenValidatorFilter, UsernamePasswordAuthenticationFilter, DefaultLoginPageGeneratingFilter, DefaultLogoutPageGeneratingFilter, BasicAuthenticationFilter, RequestCacheAwareFilter, SecurityContextHolderAwareRequestFilter, AnonymousAuthenticationFilter, ExceptionTranslationFilter, AuthorizationFilter +Starting ProtocolHandler ["http-nio-8080"] +Tomcat started on port 8080 (http) with context path '/' +Started FinalBackendApplication in 11.412 seconds (process running for 12.476) +Initializing Spring DispatcherServlet 'dispatcherServlet' +Initializing Servlet 'dispatcherServlet' +Completed initialization in 2 ms +Securing POST /api/v1/auth/signin +Request: filter invocation [POST /api/v1/auth/signin]; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL] +Secured POST /api/v1/auth/signin +Request Info: IP=0:0:0:0:0:0:0:1, API=/api/v1/auth/signin, Method=POST, UserPK=null, RequestID=7ee91c36-d5b9-413d-82ef-d0c656e4954c +Set SecurityContextHolder to anonymous SecurityContext +Securing GET /api/v1/certification/other/test0 +Request: filter invocation [GET /api/v1/certification/other/test0]; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL] +Secured GET /api/v1/certification/other/test0 +Request Info: IP=0:0:0:0:0:0:0:1, API=/api/v1/certification/other/test0, Method=GET, UserPK=null, RequestID=57d48e1c-c54e-49d9-b2a8-d88555951264 +Securing GET /api/v1/education/other/test0 +Request: filter invocation [GET /api/v1/education/other/test0]; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL] +Secured GET /api/v1/education/other/test0 +Request Info: IP=0:0:0:0:0:0:0:1, API=/api/v1/education/other/test0, Method=GET, UserPK=null, RequestID=0bb6121a-c778-4430-aa8f-e3ae8c3216d4 +Closing JPA EntityManagerFactory for persistence unit 'default' +HikariPool-1 - Shutdown initiated... +HikariPool-1 - Shutdown completed. +HV000001: Hibernate Validator 8.0.1.Final +Starting FinalBackendApplication using Java 17.0.13 with PID 59037 (/Users/bdh/Final_Backend/build/classes/java/main started by bdh in /Users/bdh/Final_Backend) +The following 1 profile is active: "prod" +Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable +For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG' +Multiple Spring Data modules found, entering strict repository configuration mode +Bootstrapping Spring Data JPA repositories in DEFAULT mode. +Finished Spring Data repository scanning in 360 ms. Found 19 JPA repository interfaces. +Multiple Spring Data modules found, entering strict repository configuration mode +Bootstrapping Spring Data Redis repositories in DEFAULT mode. +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.A_sample.command.domain.repository.SampleRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.career.command.domain.repository.CareerRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.center.command.domain.repository.CenterRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.certification.command.domain.repository.CertificationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.contract.command.domain.repository.ContractRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.customer.command.domain.repository.CustomerRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.education.command.domain.repository.EducationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.evaluation.command.domain.repository.EvaluationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.family.command.domain.repository.FamilyRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.member.command.domain.repository.MemberRoleRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.notices.command.domain.repository.NoticeRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.order.command.domain.repository.OrderRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.organization.command.domain.repository.OrganizationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.problem.command.domain.aggregate.repository.ProblemRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.promotion.command.domain.aggregate.repository.PromotionRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.purchase_order.command.domain.repository.PurchaseOrderRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.sales_history.command.domain.repository.SalesHistoryRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.schedule.command.domain.repository.ScheduleRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Finished Spring Data repository scanning in 42 ms. Found 0 Redis repository interfaces. +Skipping MapperFactoryBean with name 'careerMapper' and 'stanl_2.final_backend.domain.career.query.repository.CareerMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'certificationMapper' and 'stanl_2.final_backend.domain.certification.query.repository.CertificationMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'customerMapper' and 'stanl_2.final_backend.domain.customer.query.repository.CustomerMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'educationMapper' and 'stanl_2.final_backend.domain.education.query.repository.EducationMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'familyMapper' and 'stanl_2.final_backend.domain.family.query.repository.FamilyMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'authMapper' and 'stanl_2.final_backend.domain.member.query.repository.AuthMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'memberMapper' and 'stanl_2.final_backend.domain.member.query.repository.MemberMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'memberRoleMapper' and 'stanl_2.final_backend.domain.member.query.repository.MemberRoleMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'noticeMapper' and 'stanl_2.final_backend.domain.notices.query.repository.NoticeMapper' mapperInterface. Bean already defined with the same name! +Tomcat initialized with port 8080 (http) +Initializing ProtocolHandler ["http-nio-8080"] +Starting service [Tomcat] +Starting Servlet engine: [Apache Tomcat/10.1.31] +Initializing Spring embedded WebApplicationContext +Root WebApplicationContext: initialization completed in 2908 ms +HHH000204: Processing PersistenceUnitInfo [name: default] +HHH000412: Hibernate ORM core version 6.5.2.Final +HHH000026: Second-level cache disabled +No LoadTimeWeaver setup: ignoring JPA class transformer +HikariPool-1 - Starting... +HikariPool-1 - Added connection org.mariadb.jdbc.Connection@7f5f6a2e +HikariPool-1 - Start completed. +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration) +Initialized JPA EntityManagerFactory for persistence unit 'default' +spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning +Global AuthenticationManager configured with AuthenticationProvider bean with name prodUsernamePwdAuthenticationProvider +Global AuthenticationManager configured with an AuthenticationProvider bean. UserDetailsService beans will not be used for username/password login. Consider removing the AuthenticationProvider bean. Alternatively, consider using the UserDetailsService in a manually instantiated DaoAuthenticationProvider. +Validated configuration attributes +Will secure any request with filters: DisableEncodeUrlFilter, ChannelProcessingFilter, WebAsyncManagerIntegrationFilter, SecurityContextHolderFilter, HeaderWriterFilter, CorsFilter, LogoutFilter, JWTTokenValidatorFilter, UsernamePasswordAuthenticationFilter, DefaultLoginPageGeneratingFilter, DefaultLogoutPageGeneratingFilter, BasicAuthenticationFilter, RequestCacheAwareFilter, SecurityContextHolderAwareRequestFilter, AnonymousAuthenticationFilter, ExceptionTranslationFilter, AuthorizationFilter +Starting ProtocolHandler ["http-nio-8080"] +Tomcat started on port 8080 (http) with context path '/' +Started FinalBackendApplication in 9.504 seconds (process running for 10.419) +Initializing Spring DispatcherServlet 'dispatcherServlet' +Initializing Servlet 'dispatcherServlet' +Completed initialization in 1 ms +Securing POST /api/v1/auth/signin +Request: filter invocation [POST /api/v1/auth/signin]; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL] +Secured POST /api/v1/auth/signin +Request Info: IP=0:0:0:0:0:0:0:1, API=/api/v1/auth/signin, Method=POST, UserPK=null, RequestID=2bdc6b15-2b71-49e8-bec1-1e8b218c3ea3 +Set SecurityContextHolder to anonymous SecurityContext +Securing GET /api/v1/member +Request: filter invocation [GET /api/v1/member]; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL] +Secured GET /api/v1/member +Request Info: IP=0:0:0:0:0:0:0:1, API=/api/v1/member, Method=GET, UserPK=null, RequestID=496b10c9-ab8d-4775-850c-938a57709b8d +Securing POST /api/v1/career +Request: filter invocation [POST /api/v1/career]; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL] +Secured POST /api/v1/career +Request Info: IP=0:0:0:0:0:0:0:1, API=/api/v1/career, Method=POST, UserPK=null, RequestID=fa0305fe-e76c-4578-bf7b-7fa2c1618d46 +Securing GET /api/v1/career/other/test0 +Request: filter invocation [GET /api/v1/career/other/test0]; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL] +Secured GET /api/v1/career/other/test0 +Request Info: IP=0:0:0:0:0:0:0:1, API=/api/v1/career/other/test0, Method=GET, UserPK=null, RequestID=712703f2-4790-49b8-89f7-74da83c7ef40 +Securing GET /api/v1/career +Request: filter invocation [GET /api/v1/career]; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL] +Secured GET /api/v1/career +Request Info: IP=0:0:0:0:0:0:0:1, API=/api/v1/career, Method=GET, UserPK=null, RequestID=4924a332-633e-415c-a85b-109ee2a59bf6 +Securing POST /api/v1/certification +Request: filter invocation [POST /api/v1/certification]; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL] +Secured POST /api/v1/certification +Request Info: IP=0:0:0:0:0:0:0:1, API=/api/v1/certification, Method=POST, UserPK=null, RequestID=4e0773a7-69ea-4167-8a5a-62201040f2dd +Closing JPA EntityManagerFactory for persistence unit 'default' +HikariPool-1 - Shutdown initiated... +HikariPool-1 - Shutdown completed. +HV000001: Hibernate Validator 8.0.1.Final +Starting FinalBackendApplication using Java 17.0.13 with PID 61658 (/Users/bdh/Final_Backend/build/classes/java/main started by bdh in /Users/bdh/Final_Backend) +The following 1 profile is active: "prod" +Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable +For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG' +Multiple Spring Data modules found, entering strict repository configuration mode +Bootstrapping Spring Data JPA repositories in DEFAULT mode. +Finished Spring Data repository scanning in 311 ms. Found 19 JPA repository interfaces. +Multiple Spring Data modules found, entering strict repository configuration mode +Bootstrapping Spring Data Redis repositories in DEFAULT mode. +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.A_sample.command.domain.repository.SampleRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.career.command.domain.repository.CareerRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.center.command.domain.repository.CenterRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.certification.command.domain.repository.CertificationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.contract.command.domain.repository.ContractRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.customer.command.domain.repository.CustomerRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.education.command.domain.repository.EducationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.evaluation.command.domain.repository.EvaluationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.family.command.domain.repository.FamilyRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.member.command.domain.repository.MemberRoleRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.notices.command.domain.repository.NoticeRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.order.command.domain.repository.OrderRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.organization.command.domain.repository.OrganizationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.problem.command.domain.aggregate.repository.ProblemRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.promotion.command.domain.aggregate.repository.PromotionRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.purchase_order.command.domain.repository.PurchaseOrderRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.sales_history.command.domain.repository.SalesHistoryRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.schedule.command.domain.repository.ScheduleRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository +Finished Spring Data repository scanning in 41 ms. Found 0 Redis repository interfaces. +Skipping MapperFactoryBean with name 'careerMapper' and 'stanl_2.final_backend.domain.career.query.repository.CareerMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'certificationMapper' and 'stanl_2.final_backend.domain.certification.query.repository.CertificationMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'customerMapper' and 'stanl_2.final_backend.domain.customer.query.repository.CustomerMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'educationMapper' and 'stanl_2.final_backend.domain.education.query.repository.EducationMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'familyMapper' and 'stanl_2.final_backend.domain.family.query.repository.FamilyMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'authMapper' and 'stanl_2.final_backend.domain.member.query.repository.AuthMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'memberMapper' and 'stanl_2.final_backend.domain.member.query.repository.MemberMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'memberRoleMapper' and 'stanl_2.final_backend.domain.member.query.repository.MemberRoleMapper' mapperInterface. Bean already defined with the same name! +Skipping MapperFactoryBean with name 'noticeMapper' and 'stanl_2.final_backend.domain.notices.query.repository.NoticeMapper' mapperInterface. Bean already defined with the same name! +Tomcat initialized with port 8080 (http) +Initializing ProtocolHandler ["http-nio-8080"] +Starting service [Tomcat] +Starting Servlet engine: [Apache Tomcat/10.1.31] +Initializing Spring embedded WebApplicationContext +Root WebApplicationContext: initialization completed in 2775 ms +HHH000204: Processing PersistenceUnitInfo [name: default] +HHH000412: Hibernate ORM core version 6.5.2.Final +HHH000026: Second-level cache disabled +No LoadTimeWeaver setup: ignoring JPA class transformer +HikariPool-1 - Starting... +HikariPool-1 - Added connection org.mariadb.jdbc.Connection@68626282 +HikariPool-1 - Start completed. +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000069: Duplicate generator name PrefixGeneratorConfig +HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration) +Initialized JPA EntityManagerFactory for persistence unit 'default' +spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning +Global AuthenticationManager configured with AuthenticationProvider bean with name prodUsernamePwdAuthenticationProvider +Global AuthenticationManager configured with an AuthenticationProvider bean. UserDetailsService beans will not be used for username/password login. Consider removing the AuthenticationProvider bean. Alternatively, consider using the UserDetailsService in a manually instantiated DaoAuthenticationProvider. +Validated configuration attributes +Will secure any request with filters: DisableEncodeUrlFilter, ChannelProcessingFilter, WebAsyncManagerIntegrationFilter, SecurityContextHolderFilter, HeaderWriterFilter, CorsFilter, LogoutFilter, JWTTokenValidatorFilter, UsernamePasswordAuthenticationFilter, DefaultLoginPageGeneratingFilter, DefaultLogoutPageGeneratingFilter, BasicAuthenticationFilter, RequestCacheAwareFilter, SecurityContextHolderAwareRequestFilter, AnonymousAuthenticationFilter, ExceptionTranslationFilter, AuthorizationFilter +Starting ProtocolHandler ["http-nio-8080"] +Tomcat started on port 8080 (http) with context path '/' +Started FinalBackendApplication in 9.197 seconds (process running for 10.025) +Initializing Spring DispatcherServlet 'dispatcherServlet' +Initializing Servlet 'dispatcherServlet' +Completed initialization in 1 ms +Securing POST /api/v1/auth/signin +Request: filter invocation [POST /api/v1/auth/signin]; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL] +Secured POST /api/v1/auth/signin +Request Info: IP=0:0:0:0:0:0:0:1, API=/api/v1/auth/signin, Method=POST, UserPK=null, RequestID=82151ecb-f6cd-403a-9407-7f3e2f1e4bd4 +사용자 예외처리: 유저 정보가 없습니다. +Resolved [stanl_2.final_backend.global.exception.GlobalCommonException: 유저 정보가 없습니다.] +Set SecurityContextHolder to anonymous SecurityContext +Closing JPA EntityManagerFactory for persistence unit 'default' +HikariPool-1 - Shutdown initiated... +HikariPool-1 - Shutdown completed. diff --git a/src/logstash/conf/logstash.conf b/src/logstash/conf/logstash.conf new file mode 100644 index 00000000..b6b7ef3d --- /dev/null +++ b/src/logstash/conf/logstash.conf @@ -0,0 +1,27 @@ +input { + file { + path => "/src/logs/*.json" # JSON 로그 파일이 위치한 경로 + start_position => "beginning" # 로그를 처음부터 읽도록 설정 + sincedb_path => "/dev/null" # 모든 실행에서 새로 시작하도록 설정 + } +} + +filter { + json { + source => "message" # JSON 포맷의 메시지 필드를 파싱 + } +} + +output { + elasticsearch { + hosts => ["http://localhost:9200"] # Elasticsearch 서버 주소 + index => "app-logs" # Elasticsearch 인덱스 이름 + } + stdout { codec => rubydebug } # 디버깅을 위한 콘솔 출력 + + # 파일 출력 추가 + file { + path => "/output/logstash_logs.log" # 저장될 파일 경로 + codec => "json_lines" # JSON 형태로 저장 + } +} diff --git a/src/main/java/stanl_2/final_backend/global/logging/LoggingAspect.java b/src/main/java/stanl_2/final_backend/global/logging/LoggingAspect.java new file mode 100644 index 00000000..596c4338 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/logging/LoggingAspect.java @@ -0,0 +1,36 @@ +package stanl_2.final_backend.global.logging; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.util.UUID; + +@Slf4j +@Aspect +@Component +public class LoggingAspect { + + @Before("within(@org.springframework.web.bind.annotation.RestController *)") + public void logRequestInfo(JoinPoint joinPoint) { + // HttpServletRequest를 가져옵니다. + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if (attributes == null) return; + + HttpServletRequest request = attributes.getRequest(); + String ipAddress = request.getRemoteAddr(); + String apiEndpoint = request.getRequestURI(); + String method = request.getMethod(); + String userPk = request.getHeader("User-Pk"); // 예시 헤더로 사용자 PK 전달 + String requestId = UUID.randomUUID().toString(); + + // 로그 출력 + log.info("Request Info: IP={}, API={}, Method={}, UserPK={}, RequestID={}", + ipAddress, apiEndpoint, method, userPk, requestId); + } +} From 452e487d09abc59aa6e12853a52069156b8e68da Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Wed, 20 Nov 2024 09:43:41 +0900 Subject: [PATCH 220/563] =?UTF-8?q?feat:=20=EB=A9=A4=EB=B2=84=20pk?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=A6=84=20=EC=A1=B0=ED=9A=8C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1(#103)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/query/repository/MemberMapper.java | 2 ++ .../domain/member/query/service/MemberQueryService.java | 2 ++ .../member/query/service/MemberQueryServiceImpl.java | 8 ++++++++ .../domain/member/query/repository/MemberMapper.xml | 7 +++++++ 4 files changed, 19 insertions(+) diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java b/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java index 3cc8a550..aec9832d 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java @@ -7,4 +7,6 @@ @Mapper public interface MemberMapper { MemberDTO findMemberInfoById(@Param("loginId") String loginId); + + String findNameById(@Param("memberId") String memberId); } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java index 83f28f3c..12669f86 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java @@ -11,5 +11,7 @@ public interface MemberQueryService { List selectMemberByRole(String role); + String selectNameById(String memberId); + // MemberDetailDTO selectMemberDetail(String name) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index 581cfe54..ed8a7f95 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -63,4 +63,12 @@ public List selectMemberByRole(String role){ return memberList; } + + @Override + public String selectNameById(String memberId){ + + String name = memberMapper.findNameById(memberId); + + return name; + } } diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml index bdc3d46e..0a70095d 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml @@ -46,4 +46,11 @@ WHERE a.mem_login_id = #{ loginId } + + \ No newline at end of file From d43326a22d13e59bc105862ebda4e7acaebf1e86 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Wed, 20 Nov 2024 09:44:22 +0900 Subject: [PATCH 221/563] =?UTF-8?q?fix:=20=EB=A9=A4=EB=B2=84=20pk=EB=A1=9C?= =?UTF-8?q?=20=EC=9D=B4=EB=A6=84=20=EC=A1=B0=ED=9A=8C=20=EC=88=98=EC=A0=95?= =?UTF-8?q?(#103)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/query/service/MemberQueryServiceImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index ed8a7f95..8c5bc579 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -65,6 +65,7 @@ public List selectMemberByRole(String role){ } @Override + @Transactional(readOnly = true) public String selectNameById(String memberId){ String name = memberMapper.findNameById(memberId); From 02003f2fa70bcd28eb4be05235bf0cb1ee980585 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Wed, 20 Nov 2024 09:49:50 +0900 Subject: [PATCH 222/563] =?UTF-8?q?fix:=20pr=20=EB=B0=98=EC=98=81=20(#103)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/query/service/MemberQueryService.java | 2 +- .../domain/member/query/service/MemberQueryServiceImpl.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java index 12669f86..cc18a8a5 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java @@ -11,7 +11,7 @@ public interface MemberQueryService { List selectMemberByRole(String role); - String selectNameById(String memberId); + String selectNameById(String memberId) throws GeneralSecurityException; // MemberDetailDTO selectMemberDetail(String name) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index 8c5bc579..fc0001ce 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -66,9 +66,10 @@ public List selectMemberByRole(String role){ @Override @Transactional(readOnly = true) - public String selectNameById(String memberId){ + public String selectNameById(String memberId) throws GeneralSecurityException { String name = memberMapper.findNameById(memberId); + name = aesUtils.decrypt(name); return name; } From 9f6638551bb9962502f2c0f423d4794fc70df478 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Wed, 20 Nov 2024 10:21:59 +0900 Subject: [PATCH 223/563] =?UTF-8?q?refactor:=20=EC=88=98=EC=A3=BC=EC=84=9C?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= =?UTF-8?q?(#101)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../order/query/repository/OrderMapper.java | 13 +++-- .../query/service/OrderQueryServiceImpl.java | 46 +++++++---------- .../order/query/repository/OrderMapper.xml | 50 +++++++++---------- 3 files changed, 51 insertions(+), 58 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java b/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java index 6115952e..90c0a072 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java @@ -1,6 +1,7 @@ package stanl_2.final_backend.domain.order.query.repository; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; import stanl_2.final_backend.domain.order.query.dto.OrderSelectAllDTO; import stanl_2.final_backend.domain.order.query.dto.OrderSelectIdDTO; import stanl_2.final_backend.domain.order.query.dto.OrderSelectSearchDTO; @@ -10,7 +11,9 @@ @Mapper public interface OrderMapper { - List findSearchOrderByMemberId(int offset, int pageSize, OrderSelectSearchDTO orderSelectSearchDTO); + List findSearchOrderByMemberId(@Param("offset") int offset, + @Param("pageSize") int pageSize, + @Param("orderSelectSearchDTO") OrderSelectSearchDTO orderSelectSearchDTO); int findOrderCountByMemberId(String memberId); @@ -18,7 +21,7 @@ public interface OrderMapper { List findAllOrderByMemberId(int offset, int pageSize, String memberId); - int findOrderSearchCountByMemberId(Map map); + int findOrderSearchCountByMemberId(OrderSelectSearchDTO orderSelectSearchDTO); List findSearchOrderByMemberId(Map map); @@ -28,7 +31,9 @@ public interface OrderMapper { OrderSelectIdDTO findOrderByOrderId(String orderId); - List findSearchOrder(Map map); + List findSearchOrder(@Param("offset") int offset, + @Param("pageSize") int pageSize, + @Param("orderSelectSearchDTO") OrderSelectSearchDTO orderSelectSearchDTO); - int findOrderSearchCount(Map map); + int findOrderSearchCount(OrderSelectSearchDTO orderSelectSearchDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java index a61f1c78..bdb4f15b 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java @@ -34,6 +34,7 @@ public OrderQueryServiceImpl(OrderMapper orderMapper, AuthQueryService authQuery this.redisTemplate = redisTemplate; } + // 영업사원 조회 @Override @Transactional(readOnly = true) public Page selectAllEmployee(String loginId, Pageable pageable) { @@ -88,27 +89,20 @@ public OrderSelectIdDTO selectDetailOrderEmployee(OrderSelectIdDTO orderSelectId public Page selectSearchOrdersEmployee(OrderSelectSearchDTO orderSelectSearchDTO, Pageable pageable) { String memberId = authQueryService.selectMemberIdByLoginId(orderSelectSearchDTO.getMemberId()); + orderSelectSearchDTO.setMemberId(memberId); - Map map = new HashMap<>(); - map.put("memberId", memberId); - map.put("title", orderSelectSearchDTO.getTitle()); - map.put("status", orderSelectSearchDTO.getStatus()); - map.put("adminId", orderSelectSearchDTO.getAdminId()); - map.put("searchMemberId", orderSelectSearchDTO.getSearchMemberId()); - map.put("startDate", orderSelectSearchDTO.getStartDate()); - map.put("endDate", orderSelectSearchDTO.getEndDate()); - map.put("pageSize", pageable.getPageSize()); - map.put("offset", pageable.getOffset()); - - List orders = orderMapper.findSearchOrderByMemberId(map); + int offset = Math.toIntExact(pageable.getOffset()); + int pageSize = pageable.getPageSize(); + List orders = orderMapper.findSearchOrderByMemberId(offset, pageSize, orderSelectSearchDTO); if (orders == null || orders.isEmpty()) { throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); } - int totalElements = orderMapper.findOrderSearchCountByMemberId(map); + Integer count = orderMapper.findOrderSearchCountByMemberId(orderSelectSearchDTO); + int totalOrder = (count != null) ? count : 0; - return new PageImpl<>(orders, pageable, totalElements); + return new PageImpl<>(orders, pageable, totalOrder); } // 영업담당자, 영업관리자 조회 @@ -140,9 +134,10 @@ public Page selectAll(Pageable pageable) { } // 전체 개수 조회 - int totalElements = orderMapper.findOrderCount(); + Integer count = orderMapper.findOrderCount(); + int totalOrder = (count != null) ? count : 0; - return new PageImpl<>(orders, pageable, totalElements); + return new PageImpl<>(orders, pageable, totalOrder); } @Override @@ -162,24 +157,17 @@ public OrderSelectIdDTO selectDetailOrder(OrderSelectIdDTO orderSelectIdDTO) { @Transactional(readOnly = true) public Page selectSearchOrders(OrderSelectSearchDTO orderSelectSearchDTO, Pageable pageable) { - Map map = new HashMap<>(); - map.put("title", orderSelectSearchDTO.getTitle()); - map.put("status", orderSelectSearchDTO.getStatus()); - map.put("adminId", orderSelectSearchDTO.getAdminId()); - map.put("searchMemberId", orderSelectSearchDTO.getSearchMemberId()); - map.put("startDate", orderSelectSearchDTO.getStartDate()); - map.put("endDate", orderSelectSearchDTO.getEndDate()); - map.put("pageSize", pageable.getPageSize()); - map.put("offset", pageable.getOffset()); - - List orders = orderMapper.findSearchOrder(map); + int offset = Math.toIntExact(pageable.getOffset()); + int pageSize = pageable.getPageSize(); + List orders = orderMapper.findSearchOrder(offset, pageSize, orderSelectSearchDTO); if (orders == null || orders.isEmpty()) { throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); } - int totalElements = orderMapper.findOrderSearchCount(map); + Integer count = orderMapper.findOrderSearchCount(orderSelectSearchDTO); + int totalOrder = (count != null) ? count : 0; - return new PageImpl<>(orders, pageable, totalElements); + return new PageImpl<>(orders, pageable, totalOrder); } } diff --git a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml index 479f799f..51554325 100644 --- a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml @@ -87,7 +87,7 @@ AND a.ord_id = #{orderId}; - SELECT a.ord_id, a.ord_ttl, @@ -106,29 +106,29 @@ LEFT JOIN tb_product e ON b.prod_id = e.prod_id - a.mem_id = #{memberId} + a.mem_id = #{orderSelectSearchDTO.memberId} AND a.active = TRUE - - AND a.ord_ttl LIKE CONCAT('%', #{title}, '%') + + AND a.ord_ttl LIKE CONCAT('%', #{orderSelectSearchDTO.title}, '%') - - AND a.ord_stat = #{status} + + AND a.ord_stat = #{orderSelectSearchDTO.status} - - AND a.admin_id = #{adminId} + + AND a.admin_id = #{orderSelectSearchDTO.adminId} - + AND a.mem_id = #{searchMemberId} - - AND a.created_at BETWEEN #{startDate} AND #{endDate} + + AND a.created_at BETWEEN #{orderSelectSearchDTO.startDate} AND #{orderSelectSearchDTO.endDate} ORDER BY a.created_at DESC LIMIT #{pageSize} OFFSET #{offset}; - SELECT COUNT(*) AS cnt FROM tb_order a @@ -178,14 +178,14 @@ LIMIT #{pageSize} OFFSET #{offset}; - SELECT COUNT(*) AS cnt FROM tb_order a WHERE a.active = TRUE; - SELECT a.ord_id, a.ord_ttl, @@ -211,7 +211,7 @@ AND a.ord_id = #{orderId}; - SELECT a.ord_id, a.ord_ttl, @@ -231,27 +231,27 @@ ON b.prod_id = e.prod_id a.active = TRUE - - AND a.ord_ttl LIKE CONCAT('%', #{title}, '%') + + AND a.ord_ttl LIKE CONCAT('%', #{orderSelectSearchDTO.title}, '%') - - AND a.ord_stat = #{status} + + AND a.ord_stat = #{orderSelectSearchDTO.status} - - AND a.admin_id = #{adminId} + + AND a.admin_id = #{orderSelectSearchDTO.adminId} - + AND a.mem_id = #{searchMemberId} - - AND a.created_at BETWEEN #{startDate} AND #{endDate} + + AND a.created_at BETWEEN #{orderSelectSearchDTO.startDate} AND #{orderSelectSearchDTO.endDate} ORDER BY a.created_at DESC LIMIT #{pageSize} OFFSET #{offset}; - SELECT COUNT(*) AS cnt FROM tb_order a From 4b198fa9de4a37ab2eaf35fd14d0672a88594320 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Wed, 20 Nov 2024 10:35:42 +0900 Subject: [PATCH 224/563] =?UTF-8?q?feat:=20=EC=88=98=EC=A3=BC=EC=84=9C=20?= =?UTF-8?q?=EC=8A=B9=EC=9D=B8=EC=83=81=ED=83=9C=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#101)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/OrderController.java | 32 +++++++++++++++++-- .../application/dto/OrderStatusModifyDTO.java | 14 ++++++++ .../service/OrderCommandService.java | 3 ++ .../domain/repository/OrderRepository.java | 2 ++ .../service/OrderCommandServiceImpl.java | 17 ++++++++++ 5 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderStatusModifyDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderController.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderController.java index 5f12e9df..e19c55a3 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderController.java @@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.order.command.application.dto.OrderModifyDTO; import stanl_2.final_backend.domain.order.command.application.dto.OrderRegistDTO; +import stanl_2.final_backend.domain.order.command.application.dto.OrderStatusModifyDTO; import stanl_2.final_backend.domain.order.command.application.service.OrderCommandService; import stanl_2.final_backend.domain.order.common.response.OrderResponseMessage; @@ -26,7 +27,7 @@ public OrderController(OrderCommandService orderCommandService) { this.orderCommandService = orderCommandService; } - @Operation(summary = "수주서 등록") + @Operation(summary = "수주서 등록(영업사원)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "수주서 등록 성공", content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) @@ -45,7 +46,7 @@ public ResponseEntity postOrder(@RequestBody OrderRegistDT .build()); } - @Operation(summary = "수주서 수정") + @Operation(summary = "수주서 수정(영업사원)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "수주서 수정 성공", content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) @@ -66,7 +67,7 @@ public ResponseEntity putOrder(@PathVariable String orderI .build()); } - @Operation(summary = "수주서 삭제") + @Operation(summary = "수주서 삭제(영업사원)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "수주서 삭제 성공", content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) @@ -84,4 +85,29 @@ public ResponseEntity deleteTest(@PathVariable("orderId") .result(null) .build()); } + + @Operation(summary = "수주서 승인상태 변경(영업관리자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "수주서 승인상태 변경 성공", + content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) + }) + @PutMapping("/status/{orderId}") + public ResponseEntity putOrderStatus (@PathVariable String orderId, + @RequestBody OrderStatusModifyDTO orderStatusModifyDTO, + Principal principal) { + + String adminLoginId = principal.getName(); + orderStatusModifyDTO.setOrderId(orderId); + orderStatusModifyDTO.setAdminId(adminLoginId); + + System.out.println("상태: " + orderStatusModifyDTO.getStatus()); + + orderCommandService.modifyOrderStatus(orderStatusModifyDTO); + + return ResponseEntity.ok(OrderResponseMessage.builder() + .httpStatus(200) + .msg("수주서 승인 상태가 성공적으로 변경되었습니다.") + .result(null) + .build()); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderStatusModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderStatusModifyDTO.java new file mode 100644 index 00000000..76d2761c --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderStatusModifyDTO.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.order.command.application.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class OrderStatusModifyDTO { + private String orderId; + private String status; + private String adminId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/service/OrderCommandService.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/service/OrderCommandService.java index f76e18b2..b570cb6a 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/application/service/OrderCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/service/OrderCommandService.java @@ -2,6 +2,7 @@ import stanl_2.final_backend.domain.order.command.application.dto.OrderModifyDTO; import stanl_2.final_backend.domain.order.command.application.dto.OrderRegistDTO; +import stanl_2.final_backend.domain.order.command.application.dto.OrderStatusModifyDTO; public interface OrderCommandService { void registerOrder(OrderRegistDTO orderRegistDTO); @@ -9,4 +10,6 @@ public interface OrderCommandService { OrderModifyDTO modifyOrder(OrderModifyDTO orderModifyDTO); void deleteOrder(String orderId, String loginId); + + void modifyOrderStatus(OrderStatusModifyDTO orderStatusModifyDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/domain/repository/OrderRepository.java b/src/main/java/stanl_2/final_backend/domain/order/command/domain/repository/OrderRepository.java index 9bc411e2..477d2b24 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/domain/repository/OrderRepository.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/domain/repository/OrderRepository.java @@ -7,4 +7,6 @@ public interface OrderRepository extends JpaRepository { Order findByOrderIdAndMemberId(String orderId, String memberId); Order findByOrderIdAndStatus(String orderId, String approved); + + Order findByOrderId(String orderId); } diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java index de6f9905..627d24c5 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java @@ -10,6 +10,7 @@ import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.order.command.application.dto.OrderModifyDTO; import stanl_2.final_backend.domain.order.command.application.dto.OrderRegistDTO; +import stanl_2.final_backend.domain.order.command.application.dto.OrderStatusModifyDTO; import stanl_2.final_backend.domain.order.command.application.service.OrderCommandService; import stanl_2.final_backend.domain.order.command.domain.aggregate.entity.Order; import stanl_2.final_backend.domain.order.command.domain.repository.OrderRepository; @@ -94,4 +95,20 @@ public void deleteOrder(String id, String loginId) { orderRepository.save(order); } + + @Override + @Transactional + public void modifyOrderStatus(OrderStatusModifyDTO orderStatusModifyDTO) { + + String adminId = authQueryService.selectMemberIdByLoginId(orderStatusModifyDTO.getAdminId()); + Order order = orderRepository.findByOrderId(orderStatusModifyDTO.getOrderId()); + if (order == null) { + throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); + } + + order.setStatus(orderStatusModifyDTO.getStatus()); + order.setAdminId(adminId); + + orderRepository.save(order); + } } From f872ebfe2275c5749ccbfc81113955079c86c893 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Wed, 20 Nov 2024 11:09:26 +0900 Subject: [PATCH 225/563] =?UTF-8?q?fix:=20pr=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81(#101)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../contract/query/service/ContractQueryService.java | 1 - .../command/application/controller/OrderController.java | 6 ++---- .../command/application/dto/OrderStatusModifyDTO.java | 1 - .../order/command/domain/repository/OrderRepository.java | 2 -- .../command/domain/service/OrderCommandServiceImpl.java | 4 ---- .../domain/order/query/controller/OrderController.java | 8 ++++---- .../order/query/service/OrderQueryServiceImpl.java | 9 --------- 7 files changed, 6 insertions(+), 25 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java index adb26553..1fb39b8d 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java @@ -16,6 +16,5 @@ public interface ContractQueryService { Page selectAll(ContractSelectAllDTO contractSelectAllDTO, Pageable pageable); - @Transactional(readOnly = true) ContractSeletIdDTO selectContractByIdAndMemberId(String contractId, String memberId); } diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderController.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderController.java index e19c55a3..9a0cd2e4 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderController.java @@ -72,7 +72,7 @@ public ResponseEntity putOrder(@PathVariable String orderI @ApiResponse(responseCode = "200", description = "수주서 삭제 성공", content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) }) - @DeleteMapping("/{orderId}") + @DeleteMapping("{orderId}") public ResponseEntity deleteTest(@PathVariable("orderId") String orderId, Principal principal) { @@ -91,7 +91,7 @@ public ResponseEntity deleteTest(@PathVariable("orderId") @ApiResponse(responseCode = "200", description = "수주서 승인상태 변경 성공", content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) }) - @PutMapping("/status/{orderId}") + @PutMapping("status/{orderId}") public ResponseEntity putOrderStatus (@PathVariable String orderId, @RequestBody OrderStatusModifyDTO orderStatusModifyDTO, Principal principal) { @@ -100,8 +100,6 @@ public ResponseEntity putOrderStatus (@PathVariable String orderStatusModifyDTO.setOrderId(orderId); orderStatusModifyDTO.setAdminId(adminLoginId); - System.out.println("상태: " + orderStatusModifyDTO.getStatus()); - orderCommandService.modifyOrderStatus(orderStatusModifyDTO); return ResponseEntity.ok(OrderResponseMessage.builder() diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderStatusModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderStatusModifyDTO.java index 76d2761c..f3311c09 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderStatusModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderStatusModifyDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Setter @Getter -@ToString public class OrderStatusModifyDTO { private String orderId; private String status; diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/domain/repository/OrderRepository.java b/src/main/java/stanl_2/final_backend/domain/order/command/domain/repository/OrderRepository.java index 477d2b24..4430164d 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/domain/repository/OrderRepository.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/domain/repository/OrderRepository.java @@ -6,7 +6,5 @@ public interface OrderRepository extends JpaRepository { Order findByOrderIdAndMemberId(String orderId, String memberId); - Order findByOrderIdAndStatus(String orderId, String approved); - Order findByOrderId(String orderId); } diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java index 627d24c5..027670ae 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java @@ -3,10 +3,6 @@ import org.modelmapper.ModelMapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.contract.command.domain.aggregate.entity.Contract; -import stanl_2.final_backend.domain.contract.command.domain.repository.ContractRepository; -import stanl_2.final_backend.domain.contract.query.dto.ContractSeletIdDTO; -import stanl_2.final_backend.domain.contract.query.service.ContractQueryService; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.order.command.application.dto.OrderModifyDTO; import stanl_2.final_backend.domain.order.command.application.dto.OrderRegistDTO; diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java b/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java index f48af8ff..af2458c4 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java @@ -35,7 +35,7 @@ public OrderController(OrderQueryService orderQueryService) { @ApiResponse(responseCode = "200", description = "수주서 전체 조회 성공", content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) }) - @GetMapping("/employee") + @GetMapping("employee") public ResponseEntity getAllOrderEmployee(Principal principal, @PageableDefault(size = 10)Pageable pageable) { @@ -55,7 +55,7 @@ public ResponseEntity getAllOrderEmployee(Principal princi @ApiResponse(responseCode = "200", description = "수주서 상세 조회 성공", content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) }) - @GetMapping("/employee/{orderId}") + @GetMapping("employee/{orderId}") public ResponseEntity getDetailOrderEmployee(@PathVariable("orderId") String orderId, Principal principal) { @@ -78,7 +78,7 @@ public ResponseEntity getDetailOrderEmployee(@PathVariable @ApiResponse(responseCode = "200", description = "수주서 검색 조회 성공", content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) }) - @GetMapping("/employee/search") + @GetMapping("employee/search") public ResponseEntity getSearchOrderEmployee(@RequestParam(required = false) String title, @RequestParam(required = false) String status, @RequestParam(required = false) String adminId, @@ -149,7 +149,7 @@ public ResponseEntity getDetailOrder(@PathVariable("orderI @ApiResponse(responseCode = "200", description = "수주서 검색 조회 성공", content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) }) - @GetMapping("/search") + @GetMapping("search") public ResponseEntity getSearchOrder(@RequestParam(required = false) String title, @RequestParam(required = false) String status, @RequestParam(required = false) String adminId, diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java index bdb4f15b..e7373a08 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java @@ -1,7 +1,6 @@ package stanl_2.final_backend.domain.order.query.service; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; @@ -16,9 +15,7 @@ import stanl_2.final_backend.domain.order.query.dto.OrderSelectSearchDTO; import stanl_2.final_backend.domain.order.query.repository.OrderMapper; -import java.util.HashMap; import java.util.List; -import java.util.Map; @Service public class OrderQueryServiceImpl implements OrderQueryService { @@ -49,7 +46,6 @@ public Page selectAllEmployee(String loginId, Pageable pageab List orders = (List) redisTemplate.opsForValue().get(cacheKey); if (orders == null) { - System.out.println("데이터베이스에서 데이터 조회 중..."); orders = orderMapper.findAllOrderByMemberId(offset, pageSize, memberId); // 결과가 null이거나 빈 리스트인지 확인 @@ -59,8 +55,6 @@ public Page selectAllEmployee(String loginId, Pageable pageab // 캐시에 데이터 저장 (빈 리스트는 저장하지 않음) redisTemplate.opsForValue().set(cacheKey, orders); - } else { - System.out.println("캐시에서 데이터 조회 중..."); } // 전체 개수 조회 @@ -119,7 +113,6 @@ public Page selectAll(Pageable pageable) { List orders = (List) redisTemplate.opsForValue().get(cacheKey); if (orders == null) { - System.out.println("데이터베이스에서 데이터 조회 중..."); orders = orderMapper.findAllOrder(offset, pageSize); // 결과가 null이거나 빈 리스트인지 확인 @@ -129,8 +122,6 @@ public Page selectAll(Pageable pageable) { // 캐시에 데이터 저장 (빈 리스트는 저장하지 않음) redisTemplate.opsForValue().set(cacheKey, orders); - } else { - System.out.println("캐시에서 데이터 조회 중..."); } // 전체 개수 조회 From 3808336c0c976149fadd2d3ab01ef841241694d1 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 20 Nov 2024 12:04:09 +0900 Subject: [PATCH 226/563] =?UTF-8?q?=ED=94=84=EB=A1=9C=EB=AA=A8=EC=85=98=20?= =?UTF-8?q?=EC=A1=B0=EA=B1=B4=20=EC=A1=B0=ED=9A=8C(#61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/notices/query/dto/NoticeDTO.java | 1 + .../query/config/MyBatisConfiguration.java | 9 +++ .../query/controller/PromotionController.java | 55 +++++++++++++++ .../promotion/query/dto/PromotionDTO.java | 30 ++++++++ .../query/dto/PromotionSearchDTO.java | 23 +++++++ .../query/repository/PromotionMapper.java | 20 ++++++ .../query/service/PromotionService.java | 10 +++ .../query/service/PromotionServiceImpl.java | 34 +++++++++ src/main/resources/application-prod.yml | 2 +- src/main/resources/application.yml | 2 +- .../query/repository/PromotionMapper.xml | 69 +++++++++++++++++++ 11 files changed, 253 insertions(+), 2 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/promotion/query/config/MyBatisConfiguration.java create mode 100644 src/main/java/stanl_2/final_backend/domain/promotion/query/controller/PromotionController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionSearchDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.java create mode 100644 src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java create mode 100644 src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java index a7fac9ef..17b84579 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java @@ -46,4 +46,5 @@ public NoticeDTO(Notice notice) { this.active = notice.getActive(); this.memberId = notice.getMemberId(); } + } diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/config/MyBatisConfiguration.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/config/MyBatisConfiguration.java new file mode 100644 index 00000000..dffa7434 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/config/MyBatisConfiguration.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.promotion.query.config; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Configuration; + +@Configuration("promotionMybatisConfiguration") +@MapperScan(basePackages = "stanl_2.final_backend.domain.promotion.query.repository") +public class MyBatisConfiguration { +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/controller/PromotionController.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/controller/PromotionController.java new file mode 100644 index 00000000..66102095 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/controller/PromotionController.java @@ -0,0 +1,55 @@ +package stanl_2.final_backend.domain.promotion.query.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.promotion.common.response.PromotionResponseMessage; +import stanl_2.final_backend.domain.promotion.query.dto.PromotionDTO; +import stanl_2.final_backend.domain.promotion.query.dto.PromotionSearchDTO; +import stanl_2.final_backend.domain.promotion.query.service.PromotionService; + +@RestController("queryPromotionController") +@RequestMapping("/api/v1/promotion") +public class PromotionController { + private final PromotionService promotionService; + + @Autowired + public PromotionController(PromotionService promotionService) { + this.promotionService = promotionService; + } + + @Operation(summary = "프로모션 조건별 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = PromotionResponseMessage.class))}) + }) + @GetMapping + public ResponseEntity> getPromotions( + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "10") int size, + @RequestParam(required = false) String title, + @RequestParam(required = false) String memberId, + @RequestParam(required = false) String startDate, + @RequestParam(required = false) String endDate + ) + { + Pageable pageable = PageRequest.of(page, size); + PromotionSearchDTO promotionsearchDTO = new PromotionSearchDTO(title, memberId, startDate, endDate); + Page promotionDTOPage = promotionService.findPromotions(pageable,promotionsearchDTO); + + return ResponseEntity.ok(promotionDTOPage); + } +} + + diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionDTO.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionDTO.java new file mode 100644 index 00000000..5e186062 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionDTO.java @@ -0,0 +1,30 @@ +package stanl_2.final_backend.domain.promotion.query.dto; + + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class PromotionDTO { + private String promotionId; + + private String title; + + private String content; + + private String createdAt; + + private String updatedAt; + + private String deletedAt; + + private Boolean active; + + private String memberId; + +} diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionSearchDTO.java new file mode 100644 index 00000000..b3bd911c --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionSearchDTO.java @@ -0,0 +1,23 @@ +package stanl_2.final_backend.domain.promotion.query.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString +public class PromotionSearchDTO { + private String promotionId; + private String title; + private String memberId; + private String startDate; + private String endDate; + + public PromotionSearchDTO(String title, String memberId, String startDate, String endDate) { + this.title = title; + this.memberId = memberId; + this.startDate = startDate; + this.endDate = endDate; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.java new file mode 100644 index 00000000..e61fc75e --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.java @@ -0,0 +1,20 @@ +package stanl_2.final_backend.domain.promotion.query.repository; + +import org.apache.ibatis.annotations.Mapper; +import org.springframework.data.repository.query.Param; +import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; +import stanl_2.final_backend.domain.notices.query.dto.SearchDTO; +import stanl_2.final_backend.domain.promotion.query.dto.PromotionDTO; +import stanl_2.final_backend.domain.promotion.query.dto.PromotionSearchDTO; + +import java.util.List; + +@Mapper +public interface PromotionMapper { + List findPromotions( + @Param("offset") int offset, + @Param("size") int size, + @Param("promotionDTO") PromotionSearchDTO promotionSearchDTO + ); + Integer findPromotionsCount(@Param("promotionSearchDTO") PromotionSearchDTO promotionSearchDTO); +} diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionService.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionService.java new file mode 100644 index 00000000..99a04c8a --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionService.java @@ -0,0 +1,10 @@ +package stanl_2.final_backend.domain.promotion.query.service; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import stanl_2.final_backend.domain.promotion.query.dto.PromotionDTO; +import stanl_2.final_backend.domain.promotion.query.dto.PromotionSearchDTO; + +public interface PromotionService { + Page findPromotions(Pageable pageable, PromotionSearchDTO PromotionSearchDTO); +} diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java new file mode 100644 index 00000000..da50aa17 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java @@ -0,0 +1,34 @@ +package stanl_2.final_backend.domain.promotion.query.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.promotion.query.dto.PromotionDTO; +import stanl_2.final_backend.domain.promotion.query.dto.PromotionSearchDTO; +import stanl_2.final_backend.domain.promotion.query.repository.PromotionMapper; + +import java.util.List; + +@Service("queryPromotionServiceImpl") +public class PromotionServiceImpl implements PromotionService{ + private final PromotionMapper promotionMapper; + @Autowired + public PromotionServiceImpl(PromotionMapper promotionMapper) { + this.promotionMapper = promotionMapper; + } + + @Transactional + @Override + public Page findPromotions(Pageable pageable, PromotionSearchDTO promotionSearchDTO) { + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); + List promotions = promotionMapper.findPromotions(offset,size,promotionSearchDTO); + Integer count = promotionMapper.findPromotionsCount(promotionSearchDTO); + int totalCount = (count != null) ? promotionMapper.findPromotionsCount(promotionSearchDTO) : 0; + + return new PageImpl<>(promotions, pageable, totalCount); + } +} diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 23faa6a8..60bf5a48 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: create + ddl-auto: update logging: level: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 6618043e..eee7170c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -23,7 +23,7 @@ spring: jpa: show-sql: true hibernate: - ddl-auto: create + ddl-auto: update properties: hibernate.format_sql: true diff --git a/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml b/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml new file mode 100644 index 00000000..0bfb1b8f --- /dev/null +++ b/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 1fac24267e84894701e9e75d07f77bad19baefe4 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 20 Nov 2024 12:19:56 +0900 Subject: [PATCH 227/563] =?UTF-8?q?=ED=94=84=EB=A1=9C=EB=AA=A8=EC=85=98=20?= =?UTF-8?q?=EA=B0=9C=EB=B3=84=20=EC=A1=B0=ED=9A=8C(#61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/PromotionController.java | 18 +++++++-- .../query/repository/PromotionMapper.java | 2 + .../query/service/PromotionService.java | 2 + .../query/service/PromotionServiceImpl.java | 6 +++ .../notices/query/repository/NoticeMapper.xml | 40 +++++++++---------- .../query/repository/PromotionMapper.xml | 13 ++++++ 6 files changed, 57 insertions(+), 24 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/controller/PromotionController.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/controller/PromotionController.java index 66102095..3b8b5b73 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/query/controller/PromotionController.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/controller/PromotionController.java @@ -10,10 +10,9 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.notices.common.response.NoticeResponseMessage; +import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; import stanl_2.final_backend.domain.promotion.common.response.PromotionResponseMessage; import stanl_2.final_backend.domain.promotion.query.dto.PromotionDTO; import stanl_2.final_backend.domain.promotion.query.dto.PromotionSearchDTO; @@ -50,6 +49,17 @@ public ResponseEntity> getPromotions( return ResponseEntity.ok(promotionDTOPage); } + + @Operation(summary = "프로모션 Id로 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = PromotionResponseMessage.class))}) + }) + @GetMapping("{promotionId}") + public ResponseEntity getPromotion(@PathVariable String promotionId){ + PromotionDTO promotionDTO = promotionService.findPromotion(promotionId); + return ResponseEntity.ok(promotionDTO); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.java index e61fc75e..b82fa0b5 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.java @@ -17,4 +17,6 @@ List findPromotions( @Param("promotionDTO") PromotionSearchDTO promotionSearchDTO ); Integer findPromotionsCount(@Param("promotionSearchDTO") PromotionSearchDTO promotionSearchDTO); + + PromotionDTO findPromotion(@Param("promotionId") String promotionId); } diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionService.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionService.java index 99a04c8a..79c67512 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionService.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionService.java @@ -7,4 +7,6 @@ public interface PromotionService { Page findPromotions(Pageable pageable, PromotionSearchDTO PromotionSearchDTO); + + PromotionDTO findPromotion(String promotion); } diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java index da50aa17..272d6ef6 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java @@ -31,4 +31,10 @@ public Page findPromotions(Pageable pageable, PromotionSearchDTO p return new PageImpl<>(promotions, pageable, totalCount); } + + @Override + public PromotionDTO findPromotion(String promotionId) { + PromotionDTO promotionDTO = promotionMapper.findPromotion(promotionId); + return promotionDTO; + } } diff --git a/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml b/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml index 8f491171..557bb422 100644 --- a/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml @@ -52,16 +52,16 @@ SELECT - a.not_id, - a.not_ttl, - a.not_tag, - a.not_cla, - a.not_cont, - a.created_at, - a.updated_at, - a.deleted_at, - a.active, - a.mem_id + a.not_id, + a.not_ttl, + a.not_tag, + a.not_cla, + a.not_cont, + a.created_at, + a.updated_at, + a.deleted_at, + a.active, + a.mem_id FROM tb_notice a WHERE not_id = #{noticeId}; diff --git a/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml b/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml index 0bfb1b8f..7ee123b4 100644 --- a/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml @@ -66,4 +66,17 @@ + \ No newline at end of file From 98c10eabc0da863776cfdaeacc17d321fd1951d5 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 20 Nov 2024 14:25:10 +0900 Subject: [PATCH 228/563] =?UTF-8?q?xml=20=EC=88=98=EC=A0=95(#61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/promotion/query/dto/PromotionSearchDTO.java | 5 ++++- .../domain/promotion/query/repository/PromotionMapper.xml | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionSearchDTO.java index b3bd911c..16a0320f 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionSearchDTO.java @@ -1,16 +1,19 @@ package stanl_2.final_backend.domain.promotion.query.dto; +import jakarta.persistence.Column; import lombok.*; @AllArgsConstructor @NoArgsConstructor @Getter @Setter -@ToString public class PromotionSearchDTO { private String promotionId; private String title; private String memberId; + private String createdAt; + private String updatedAt; + private String deletedAt; private String startDate; private String endDate; diff --git a/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml b/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml index 7ee123b4..c921d04e 100644 --- a/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml @@ -19,6 +19,9 @@ + + + @@ -59,7 +62,7 @@ AND a.mem_id = #{memberId} - AND a.not_ttl LIKE CONCAT('%', #{title}, '%') + AND a.prm_ttl LIKE CONCAT('%', #{title}, '%') AND a.created_at BETWEEN #{startDate} AND #{endDate} From 87f37665296fdb649a6c06ce20c03af7e8d8288a Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Wed, 20 Nov 2024 14:28:29 +0900 Subject: [PATCH 229/563] =?UTF-8?q?refactor:=20=EB=B0=9C=EC=A3=BC=EC=84=9C?= =?UTF-8?q?=20=ED=9A=8C=EC=9B=90id=EA=B0=92=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B6=8C=ED=95=9C=EB=B3=84=20=EB=A9=94=EC=86=8C?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80(#107)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/dto/OrderModifyDTO.java | 1 - .../application/dto/OrderRegistDTO.java | 1 - .../query/service/OrderQueryServiceImpl.java | 5 +- .../controller/PurchaseOrderController.java | 32 ++-- .../dto/PurchaseOrderStatusModifyDTO.java | 4 - .../aggregate/entity/PurchaseOrder.java | 2 +- .../PurchaseOrderCommandServiceImpl.java | 22 +-- .../controller/PurchaseOrderController.java | 117 ++++++++++--- .../query/dto/PurchaseOrderSelectAllDTO.java | 4 - .../query/dto/PurchaseOrderSelectIdDTO.java | 4 - .../dto/PurchaseOrderSelectSearchDTO.java | 15 +- .../query/repository/PurchaseOrderMapper.java | 28 ++- .../service/PurchaseOrderQueryService.java | 10 +- .../PurchaseOrderQueryServiceImpl.java | 162 +++++++++++------- .../query/repository/PurchaseOrderMapper.xml | 143 ++++++++++++++-- 15 files changed, 384 insertions(+), 166 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderModifyDTO.java index 145eaddd..8d278610 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderModifyDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Setter @Getter -@ToString public class OrderModifyDTO { private String orderId; private String title; diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderRegistDTO.java index eb81b6e8..f9dc54e9 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderRegistDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Setter @Getter -@ToString public class OrderRegistDTO { private String title; private String content; diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java index e7373a08..f31aa437 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java @@ -58,9 +58,10 @@ public Page selectAllEmployee(String loginId, Pageable pageab } // 전체 개수 조회 - int totalElements = orderMapper.findOrderCountByMemberId(memberId); + Integer count = orderMapper.findOrderCountByMemberId(memberId); + int totalOrder = (count != null) ? count : 0; - return new PageImpl<>(orders, pageable, totalElements); + return new PageImpl<>(orders, pageable, totalOrder); } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java index 91b36162..4a415958 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java @@ -8,7 +8,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; -import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderModifyDTO; import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderRegistDTO; @@ -30,7 +29,7 @@ public PurchaseOrderController(PurchaseOrderCommandService purchaseOrderCommandS this.purchaseOrderCommandService = purchaseOrderCommandService; } - @Operation(summary = "발주서 등록") + @Operation(summary = "발주서 등록(영업관리자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "발주서 등록 성공", content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) @@ -49,16 +48,16 @@ public ResponseEntity postPurchaseOrder(@RequestBo .build()); } - @Operation(summary = "발주서 수정") + @Operation(summary = "발주서 수정(영업관리자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "발주서 수정 성공", content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) }) - @PutMapping("{id}") - public ResponseEntity putPurchaseOrder(@PathVariable String id, + @PutMapping("{purchaseOrderId}") + public ResponseEntity putPurchaseOrder(@PathVariable String purchaseOrderId, @RequestBody PurchaseOrderModifyDTO purchaseOrderModifyDTO, Principal principal) { - purchaseOrderModifyDTO.setPurchaseOrderId(id); + purchaseOrderModifyDTO.setPurchaseOrderId(purchaseOrderId); purchaseOrderModifyDTO.setMemberId(principal.getName()); PurchaseOrderModifyDTO purchaseOrderModifyResponse = purchaseOrderCommandService.modifyPurchaseOrder(purchaseOrderModifyDTO); @@ -69,16 +68,16 @@ public ResponseEntity putPurchaseOrder(@PathVariab .build()); } - @Operation(summary = "발주서 삭제") + @Operation(summary = "발주서 삭제(영업관리자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "발주서 삭제 성공", content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) }) - @DeleteMapping("{id}") - public ResponseEntity deletePurchaseOrder(@PathVariable String id, + @DeleteMapping("{purchaseOrderId}") + public ResponseEntity deletePurchaseOrder(@PathVariable String purchaseOrderId, Principal principal) { - purchaseOrderCommandService.deletePurchaseOrder(id, principal.getName()); + purchaseOrderCommandService.deletePurchaseOrder(purchaseOrderId, principal.getName()); return ResponseEntity.ok(PurchaseOrderResponseMessage.builder() .httpStatus(200) @@ -87,19 +86,18 @@ public ResponseEntity deletePurchaseOrder(@PathVar .build()); } - @Operation(summary = "발주서 승인 상태 수정(담당자)") + @Operation(summary = "발주서 승인 상태 수정(영업담당자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "발주서 승인 상태 수정 성공", content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) }) - @PutMapping("/stauts/{id}") - public ResponseEntity putPurchaseOrderStatus(@PathVariable String id, + @PutMapping("stauts/{purchaseOrderId}") + public ResponseEntity putPurchaseOrderStatus(@PathVariable String purchaseOrderId, PurchaseOrderStatusModifyDTO purchaseOrderStatusModifyDTO, - Authentication authentication) { + Principal principal) { - purchaseOrderStatusModifyDTO.setPurchaseOrderId(id); - purchaseOrderStatusModifyDTO.setRoles(authentication.getAuthorities()); - purchaseOrderStatusModifyDTO.setAdminId(authentication.getName()); + purchaseOrderStatusModifyDTO.setPurchaseOrderId(purchaseOrderId); + purchaseOrderStatusModifyDTO.setAdminId(principal.getName()); purchaseOrderCommandService.modifyPurchaseOrderStatus(purchaseOrderStatusModifyDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderStatusModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderStatusModifyDTO.java index d8896798..1ce6c560 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderStatusModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderStatusModifyDTO.java @@ -1,9 +1,6 @@ package stanl_2.final_backend.domain.purchase_order.command.application.dto; import lombok.*; -import org.springframework.security.core.GrantedAuthority; - -import java.util.Collection; @AllArgsConstructor @NoArgsConstructor @@ -13,5 +10,4 @@ public class PurchaseOrderStatusModifyDTO { private String purchaseOrderId; private String status; private String adminId; - private Collection roles; } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/aggregate/entity/PurchaseOrder.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/aggregate/entity/PurchaseOrder.java index 8009bb94..a464d56b 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/aggregate/entity/PurchaseOrder.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/aggregate/entity/PurchaseOrder.java @@ -49,7 +49,7 @@ public class PurchaseOrder { private String deletedAt; @Column(name = "PUR_ORD_STAT", nullable = false) - private String status; + private String status = "WAIT"; @Column(name = "ORD_ID", nullable = false) private String orderId; diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java index 90a96c59..78d3b340 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java @@ -17,8 +17,6 @@ import stanl_2.final_backend.domain.purchase_order.command.domain.repository.PurchaseOrderRepository; import stanl_2.final_backend.domain.purchase_order.common.exception.PurchaseOrderCommonException; import stanl_2.final_backend.domain.purchase_order.common.exception.PurchaseOrderErrorCode; -import stanl_2.final_backend.global.exception.GlobalCommonException; -import stanl_2.final_backend.global.exception.GlobalErrorCode; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -63,7 +61,6 @@ public void registerPurchaseOrder(PurchaseOrderRegistDTO purchaseOrderRegistDTO) } PurchaseOrder purchaseOrder = modelMapper.map(purchaseOrderRegistDTO, PurchaseOrder.class); - purchaseOrder.setStatus("WAIT"); purchaseOrderRepository.save(purchaseOrder); } @@ -92,7 +89,6 @@ public PurchaseOrderModifyDTO modifyPurchaseOrder(PurchaseOrderModifyDTO purchas } PurchaseOrder updatePurchaseOrder = modelMapper.map(purchaseOrderModifyDTO, PurchaseOrder.class); - updatePurchaseOrder.setCreatedAt(purchaseOrder.getCreatedAt()); updatePurchaseOrder.setUpdatedAt(purchaseOrder.getUpdatedAt()); updatePurchaseOrder.setStatus(purchaseOrder.getStatus()); @@ -107,12 +103,12 @@ public PurchaseOrderModifyDTO modifyPurchaseOrder(PurchaseOrderModifyDTO purchas @Override @Transactional - public void deletePurchaseOrder(String id, String loginId) { + public void deletePurchaseOrder(String purchaseOrderId, String loginId) { String memberId = authQueryService.selectMemberIdByLoginId(loginId); // 발주서가 해당 회원의 것인지 확인 - PurchaseOrder purchaseOrder = (PurchaseOrder) purchaseOrderRepository.findByPurchaseOrderIdAndMemberId(id, memberId) + PurchaseOrder purchaseOrder = (PurchaseOrder) purchaseOrderRepository.findByPurchaseOrderIdAndMemberId(purchaseOrderId, memberId) .orElseThrow(() -> new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND)); purchaseOrder.setActive(false); @@ -127,16 +123,12 @@ public void modifyPurchaseOrderStatus(PurchaseOrderStatusModifyDTO purchaseOrder String adminId = authQueryService.selectMemberIdByLoginId(purchaseOrderStatusModifyDTO.getAdminId()); - if ("[ROLE_REPRESENTATIVE]".equals(purchaseOrderStatusModifyDTO.getRoles())) { - PurchaseOrder purchaseOrder = purchaseOrderRepository.findByPurchaseOrderId(purchaseOrderStatusModifyDTO.getPurchaseOrderId()) - .orElseThrow(() -> new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND)); + PurchaseOrder purchaseOrder = purchaseOrderRepository.findByPurchaseOrderId(purchaseOrderStatusModifyDTO.getPurchaseOrderId()) + .orElseThrow(() -> new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND)); - purchaseOrder.setStatus(purchaseOrderStatusModifyDTO.getStatus()); - purchaseOrder.setAdminId(adminId); + purchaseOrder.setStatus(purchaseOrderStatusModifyDTO.getStatus()); + purchaseOrder.setAdminId(adminId); - purchaseOrderRepository.save(purchaseOrder); - } else { - throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); - } + purchaseOrderRepository.save(purchaseOrder); } } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java index e3bdcb2a..4c47365e 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java @@ -10,7 +10,6 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; -import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.purchase_order.common.response.PurchaseOrderResponseMessage; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectAllDTO; @@ -31,21 +30,21 @@ public PurchaseOrderController(PurchaseOrderQueryService purchaseOrderQueryServi this.purchaseOrderQueryService = purchaseOrderQueryService; } - @Operation(summary = "발주서 상세조회") + // 영업관리자 조회 + @Operation(summary = "발주서 상세조회(영업관리자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "발주서 상세조회 성공", content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) }) - @GetMapping("{id}") - public ResponseEntity getDetailPurchaseOrder(@PathVariable("id") String id, - Authentication authentication) { + @GetMapping("admin/{purchaseOrderId}") + public ResponseEntity getDetailPurchaseOrderAdmin(@PathVariable String purchaseOrderId, + Principal principal) { PurchaseOrderSelectIdDTO purchaseOrderSelectIdDTO = new PurchaseOrderSelectIdDTO(); - purchaseOrderSelectIdDTO.setPurchaseOrderId(id); - purchaseOrderSelectIdDTO.setMemberId(authentication.getName()); - purchaseOrderSelectIdDTO.setRoles(authentication.getAuthorities()); + purchaseOrderSelectIdDTO.setPurchaseOrderId(purchaseOrderId); + purchaseOrderSelectIdDTO.setMemberId(principal.getName()); - PurchaseOrderSelectIdDTO responsePurchaseOrder = purchaseOrderQueryService.selectDetailPurchaseOrder(purchaseOrderSelectIdDTO); + PurchaseOrderSelectIdDTO responsePurchaseOrder = purchaseOrderQueryService.selectDetailPurchaseOrderAdmin(purchaseOrderSelectIdDTO); return ResponseEntity.ok(PurchaseOrderResponseMessage.builder() .httpStatus(200) @@ -54,18 +53,18 @@ public ResponseEntity getDetailPurchaseOrder(@Path .build()); } - @Operation(summary = "발주서 전체조회") + @Operation(summary = "발주서 전체조회(영업관리자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "발주서 전체조회 성공", content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) }) - @GetMapping("") - public ResponseEntity getAllPurchaseOrders(Authentication authentication, + @GetMapping("admin") + public ResponseEntity getAllPurchaseOrdersAdmin(Principal principal, @PageableDefault(size = 10) Pageable pageable) { PurchaseOrderSelectAllDTO purchaseOrderSelectAllDTO = new PurchaseOrderSelectAllDTO(); - purchaseOrderSelectAllDTO.setRoles(authentication.getAuthorities()); + purchaseOrderSelectAllDTO.setMemberId(principal.getName()); - Page responsePurchaseOrders = purchaseOrderQueryService.selectAllPurchaseOrder(pageable, purchaseOrderSelectAllDTO); + Page responsePurchaseOrders = purchaseOrderQueryService.selectAllPurchaseOrderAdmin(pageable, purchaseOrderSelectAllDTO); return ResponseEntity.ok(PurchaseOrderResponseMessage.builder() .httpStatus(200) @@ -74,25 +73,31 @@ public ResponseEntity getAllPurchaseOrders(Authent .build()); } - @Operation(summary = "발주서 검색조회") + @Operation(summary = "발주서 검색조회(영업관리자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "발주서 검색조회 성공", content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) }) - @GetMapping("/search") - public ResponseEntity getSearchPurchaseOrders(@RequestParam(required = false) String title, + @GetMapping("admin/search") + public ResponseEntity getSearchPurchaseOrdersAdmin(@RequestParam(required = false) String title, @RequestParam(required = false) String status, @RequestParam(required = false) String adminId, @RequestParam(required = false) String searchMemberId, @RequestParam(required = false) String startDate, @RequestParam(required = false) String endDate, - Authentication authentication, + Principal principal, @PageableDefault(size = 10) Pageable pageable) { - PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO = new PurchaseOrderSelectSearchDTO(title, status, - adminId, searchMemberId, startDate, endDate, authentication.getAuthorities()); + PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO = new PurchaseOrderSelectSearchDTO(); + purchaseOrderSelectSearchDTO.setTitle(title); + purchaseOrderSelectSearchDTO.setStatus(status); + purchaseOrderSelectSearchDTO.setAdminId(adminId); + purchaseOrderSelectSearchDTO.setStartDate(startDate); + purchaseOrderSelectSearchDTO.setEndDate(endDate); + purchaseOrderSelectSearchDTO.setSearchMemberId(searchMemberId); + purchaseOrderSelectSearchDTO.setMemberId(principal.getName()); - Page responsePurchaseOrder = purchaseOrderQueryService.selectSearchPurchaseOrder(purchaseOrderSelectSearchDTO, pageable); + Page responsePurchaseOrder = purchaseOrderQueryService.selectSearchPurchaseOrderAdmin(purchaseOrderSelectSearchDTO, pageable); return ResponseEntity.ok(PurchaseOrderResponseMessage.builder() .httpStatus(200) @@ -100,5 +105,75 @@ public ResponseEntity getSearchPurchaseOrders(@Req .result(responsePurchaseOrder) .build()); } + + // 영업담당자 조회 + @Operation(summary = "발주서 상세조회(영업담당자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "발주서 상세조회 성공", + content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) + }) + @GetMapping("{purchaseOrderId}") + public ResponseEntity getDetailPurchaseOrder(@PathVariable String purchaseOrderId) { + + PurchaseOrderSelectIdDTO purchaseOrderSelectIdDTO = new PurchaseOrderSelectIdDTO(); + purchaseOrderSelectIdDTO.setPurchaseOrderId(purchaseOrderId); + + PurchaseOrderSelectIdDTO responsePurchaseOrder = purchaseOrderQueryService.selectDetailPurchaseOrder(purchaseOrderSelectIdDTO); + + return ResponseEntity.ok(PurchaseOrderResponseMessage.builder() + .httpStatus(200) + .msg("발주서 상세 조회 성공") + .result(responsePurchaseOrder) + .build()); + } + + @Operation(summary = "발주서 전체조회(영업담당자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "발주서 전체조회 성공", + content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) + }) + @GetMapping("") + public ResponseEntity getAllPurchaseOrders(@PageableDefault(size = 10) Pageable pageable) { + PurchaseOrderSelectAllDTO purchaseOrderSelectAllDTO = new PurchaseOrderSelectAllDTO(); + + Page responsePurchaseOrders = purchaseOrderQueryService.selectAllPurchaseOrder(pageable, purchaseOrderSelectAllDTO); + + return ResponseEntity.ok(PurchaseOrderResponseMessage.builder() + .httpStatus(200) + .msg("발주서 전체 조회 성공") + .result(responsePurchaseOrders) + .build()); + } + + @Operation(summary = "발주서 검색조회(영업담당자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "발주서 검색조회 성공", + content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) + }) + @GetMapping("search") + public ResponseEntity getSearchPurchaseOrders(@RequestParam(required = false) String title, + @RequestParam(required = false) String status, + @RequestParam(required = false) String adminId, + @RequestParam(required = false) String searchMemberId, + @RequestParam(required = false) String startDate, + @RequestParam(required = false) String endDate, + @PageableDefault(size = 10) Pageable pageable) { + + PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO = new PurchaseOrderSelectSearchDTO(); + purchaseOrderSelectSearchDTO.setTitle(title); + purchaseOrderSelectSearchDTO.setStatus(status); + purchaseOrderSelectSearchDTO.setAdminId(adminId); + purchaseOrderSelectSearchDTO.setStartDate(startDate); + purchaseOrderSelectSearchDTO.setEndDate(endDate); + purchaseOrderSelectSearchDTO.setSearchMemberId(searchMemberId); + + Page responsePurchaseOrder = purchaseOrderQueryService.selectSearchPurchaseOrder(purchaseOrderSelectSearchDTO, pageable); + + return ResponseEntity.ok(PurchaseOrderResponseMessage.builder() + .httpStatus(200) + .msg("발주서 검색 조회 성공") + .result(responsePurchaseOrder) + .build()); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectAllDTO.java index 5b45eb4b..d821772b 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectAllDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectAllDTO.java @@ -4,9 +4,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import org.springframework.security.core.GrantedAuthority; - -import java.util.Collection; @AllArgsConstructor @NoArgsConstructor @@ -21,5 +18,4 @@ public class PurchaseOrderSelectAllDTO { private String memberName; private String productName; private String memberId; - private Collection roles; } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectIdDTO.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectIdDTO.java index e71e2065..86abfde9 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectIdDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectIdDTO.java @@ -4,9 +4,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import org.springframework.security.core.GrantedAuthority; - -import java.util.Collection; @AllArgsConstructor @NoArgsConstructor @@ -24,5 +21,4 @@ public class PurchaseOrderSelectIdDTO { private String orderId; private String adminId; private String memberId; - private Collection roles; } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectSearchDTO.java index e8addb1e..5c8550a7 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderSelectSearchDTO.java @@ -4,9 +4,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import org.springframework.security.core.GrantedAuthority; - -import java.util.Collection; @AllArgsConstructor @NoArgsConstructor @@ -21,18 +18,8 @@ public class PurchaseOrderSelectSearchDTO { private String memberName; private String productName; private String adminId; + private String memberId; private String searchMemberId; private String startDate; private String endDate; - private Collection roles; - - public PurchaseOrderSelectSearchDTO(String title, String status, String adminId, String searchMemberId, String startDate, String endDate, Collection roles) { - this.title = title; - this.status = status; - this.adminId = adminId; - this.searchMemberId = searchMemberId; - this.startDate = startDate; - this.endDate = endDate; - this.roles = roles; - } } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java index 3c3409be..af63e466 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java @@ -1,22 +1,38 @@ package stanl_2.final_backend.domain.purchase_order.query.repository; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectAllDTO; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectIdDTO; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectSearchDTO; import java.util.List; -import java.util.Map; @Mapper public interface PurchaseOrderMapper { - PurchaseOrderSelectIdDTO findPurchaseOrderById(String purchaseOrderId); + PurchaseOrderSelectIdDTO findPurchaseOrderByPurchaseOrderIdAndMemberId(@Param("purchaseOrderId") String purchaseOrderId, + @Param("memberId") String memberId); - List findAllPurchaseOrder(int offset, int pageSize); + List findAllPurchaseOrderByMemberId(@Param("offset") int offset, + @Param("pageSize") int pageSize, + @Param("memberId") String memberId); + + Integer findAllPurchaseOrderCountByMemberId(String memberId); + + List findSearchPurchaseOrder(@Param("offset") int offset, + @Param("pageSize") int pageSize, + @Param("purchaseOrderSelectSearchDTO") + PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO); + + List findSearchPurchaseOrderMemberId(int offset, int pageSize, PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO); - int findAllPurchaseOrderCount(); + Integer findSearchPurchaseOrderCountMemberId(PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO); + + PurchaseOrderSelectIdDTO findPurchaseOrderByPurchaseOrderId(String purchaseOrderId); + + List findAllPurchaseOrder(int offset, int pageSize); - List findSearchPurchaseOrder(Map map); + Integer findAllPurchaseOrderCount(); - int findSearchPurchaseOrderCount(); + Integer findSearchPurchaseOrderCount(PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java index 881ae4c9..d5720268 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java @@ -7,9 +7,15 @@ import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectSearchDTO; public interface PurchaseOrderQueryService { - PurchaseOrderSelectIdDTO selectDetailPurchaseOrder(PurchaseOrderSelectIdDTO purchaseOrderSelectIdDTO); + PurchaseOrderSelectIdDTO selectDetailPurchaseOrderAdmin(PurchaseOrderSelectIdDTO purchaseOrderSelectIdDTO); - Page selectAllPurchaseOrder(Pageable pageable, PurchaseOrderSelectAllDTO purchaseOrderSelectAllDTO); + Page selectAllPurchaseOrderAdmin(Pageable pageable, PurchaseOrderSelectAllDTO purchaseOrderSelectAllDTO); + + Page selectSearchPurchaseOrderAdmin(PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO, Pageable pageable); Page selectSearchPurchaseOrder(PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO, Pageable pageable); + + Page selectAllPurchaseOrder(Pageable pageable, PurchaseOrderSelectAllDTO purchaseOrderSelectAllDTO); + + PurchaseOrderSelectIdDTO selectDetailPurchaseOrder(PurchaseOrderSelectIdDTO purchaseOrderSelectIdDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java index 7688538b..c020f5a9 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java @@ -4,119 +4,153 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.purchase_order.common.exception.PurchaseOrderCommonException; import stanl_2.final_backend.domain.purchase_order.common.exception.PurchaseOrderErrorCode; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectAllDTO; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectIdDTO; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectSearchDTO; import stanl_2.final_backend.domain.purchase_order.query.repository.PurchaseOrderMapper; -import stanl_2.final_backend.global.exception.GlobalCommonException; -import stanl_2.final_backend.global.exception.GlobalErrorCode; -import stanl_2.final_backend.global.utils.AESUtils; - -import java.security.GeneralSecurityException; -import java.util.HashMap; import java.util.List; -import java.util.Map; - @Service -@Transactional(readOnly = true) public class PurchaseOrderQueryServiceImpl implements PurchaseOrderQueryService { private final PurchaseOrderMapper purchaseOrderMapper; - private final AESUtils aesUtils; + private final AuthQueryService authQueryService; + private final RedisTemplate redisTemplate; @Autowired - public PurchaseOrderQueryServiceImpl(PurchaseOrderMapper purchaseOrderMapper, AESUtils aesUtils) { + public PurchaseOrderQueryServiceImpl(PurchaseOrderMapper purchaseOrderMapper, AuthQueryService authQueryService, RedisTemplate redisTemplate) { this.purchaseOrderMapper = purchaseOrderMapper; - this.aesUtils = aesUtils; + this.authQueryService = authQueryService; + this.redisTemplate = redisTemplate; } + // 영업 관리자 조회 @Override - public PurchaseOrderSelectIdDTO selectDetailPurchaseOrder(PurchaseOrderSelectIdDTO purchaseOrderSelectIdDTO) { - if (purchaseOrderSelectIdDTO.getRoles().stream() - .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()) || "ROLE_REPRESENTATIVE".equals(role.getAuthority()))) { + @Transactional(readOnly = true) + public PurchaseOrderSelectIdDTO selectDetailPurchaseOrderAdmin(PurchaseOrderSelectIdDTO purchaseOrderSelectIdDTO) { - PurchaseOrderSelectIdDTO purchaseOrder = purchaseOrderMapper.findPurchaseOrderById(purchaseOrderSelectIdDTO.getPurchaseOrderId()); + String memberId = authQueryService.selectMemberIdByLoginId(purchaseOrderSelectIdDTO.getMemberId()); - if (purchaseOrder == null) { - throw new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND); - } - return purchaseOrder; - } else { - throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + PurchaseOrderSelectIdDTO purchaseOrder = purchaseOrderMapper.findPurchaseOrderByPurchaseOrderIdAndMemberId(purchaseOrderSelectIdDTO.getPurchaseOrderId(), memberId); + + if (purchaseOrder == null) { + throw new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND); } + return purchaseOrder; } @Override - public Page selectAllPurchaseOrder(Pageable pageable, PurchaseOrderSelectAllDTO purchaseOrderSelectAllDTO) { + @Transactional(readOnly = true) + public Page selectAllPurchaseOrderAdmin(Pageable pageable, PurchaseOrderSelectAllDTO purchaseOrderSelectAllDTO) { + + String memberId = authQueryService.selectMemberIdByLoginId(purchaseOrderSelectAllDTO.getMemberId()); int offset = Math.toIntExact(pageable.getOffset()); int pageSize = pageable.getPageSize(); - if (purchaseOrderSelectAllDTO.getRoles().stream() - .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()) || "ROLE_REPRESENTATIVE".equals(role.getAuthority()))) { + String cacheKey = "myCache::purchaseOrders::offset=" + offset + "::size=" + pageSize; + + // 캐시 조회 + List purchaseOrders = (List) redisTemplate.opsForValue().get(cacheKey); - List purchaseOrders = purchaseOrderMapper.findAllPurchaseOrder(offset, pageSize); + if (purchaseOrders == null) { + purchaseOrders = purchaseOrderMapper.findAllPurchaseOrderByMemberId(offset, pageSize, memberId); - if (purchaseOrders == null || purchaseOrders.isEmpty()) { + if(purchaseOrders == null) { throw new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND); } - purchaseOrders.forEach(purchaseOrder -> { - try { - purchaseOrder.setMemberName(aesUtils.decrypt(purchaseOrder.getMemberName())); - } catch (GeneralSecurityException e) { - throw new RuntimeException(e); - } - }); + redisTemplate.opsForValue().set(cacheKey, purchaseOrders); + } + + Integer count = purchaseOrderMapper.findAllPurchaseOrderCountByMemberId(memberId); + int totalPurchaseOrder = (count != null) ? count : 0; - int total = purchaseOrderMapper.findAllPurchaseOrderCount(); + return new PageImpl<>(purchaseOrders, pageable, totalPurchaseOrder); + } + + @Override + @Transactional(readOnly = true) + public Page selectSearchPurchaseOrderAdmin(PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO, Pageable pageable) { - return new PageImpl<>(purchaseOrders, pageable, total); - } else { - throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + String memberId = authQueryService.selectMemberIdByLoginId(purchaseOrderSelectSearchDTO.getMemberId()); + purchaseOrderSelectSearchDTO.setMemberId(memberId); + + int offset = Math.toIntExact(pageable.getOffset()); + int pageSize = pageable.getPageSize(); + List purchaseOrders = purchaseOrderMapper.findSearchPurchaseOrderMemberId(offset, pageSize, purchaseOrderSelectSearchDTO); + + if (purchaseOrders == null) { + throw new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND); } + + Integer count = purchaseOrderMapper.findSearchPurchaseOrderCountMemberId(purchaseOrderSelectSearchDTO); + int totalPurchaseOrder = (count != null) ? count : 0; + + return new PageImpl<>(purchaseOrders, pageable, totalPurchaseOrder); } + // 영업 담장자 조회 @Override - public Page selectSearchPurchaseOrder(PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO, Pageable pageable) { + @Transactional(readOnly = true) + public PurchaseOrderSelectIdDTO selectDetailPurchaseOrder(PurchaseOrderSelectIdDTO purchaseOrderSelectIdDTO) { - if (purchaseOrderSelectSearchDTO.getRoles().stream() - .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()) || "ROLE_REPRESENTATIVE".equals(role.getAuthority()))) { + PurchaseOrderSelectIdDTO purchaseOrder = purchaseOrderMapper.findPurchaseOrderByPurchaseOrderId(purchaseOrderSelectIdDTO.getPurchaseOrderId()); - Map map = new HashMap<>(); - map.put("title", purchaseOrderSelectSearchDTO.getTitle()); - map.put("status", purchaseOrderSelectSearchDTO.getStatus()); - map.put("adminId", purchaseOrderSelectSearchDTO.getAdminId()); - map.put("searchMemberId", purchaseOrderSelectSearchDTO.getSearchMemberId()); - map.put("startDate", purchaseOrderSelectSearchDTO.getStartDate()); - map.put("endDate", purchaseOrderSelectSearchDTO.getEndDate()); - map.put("roles", purchaseOrderSelectSearchDTO.getRoles()); - map.put("pageSize", pageable.getPageSize()); - map.put("offset", pageable.getOffset()); + if (purchaseOrder == null) { + throw new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND); + } + return purchaseOrder; + } - List purchaseOrders = purchaseOrderMapper.findSearchPurchaseOrder(map); + @Override + @Transactional(readOnly = true) + public Page selectAllPurchaseOrder(Pageable pageable, PurchaseOrderSelectAllDTO purchaseOrderSelectAllDTO) { - if (purchaseOrders == null || purchaseOrders.isEmpty()) { + int offset = Math.toIntExact(pageable.getOffset()); + int pageSize = pageable.getPageSize(); + + String cacheKey = "myCache::purchaseOrders::offset=" + offset + "::size=" + pageSize; + + // 캐시 조회 + List purchaseOrders = (List) redisTemplate.opsForValue().get(cacheKey); + + if (purchaseOrders == null) { + purchaseOrders = purchaseOrderMapper.findAllPurchaseOrder(offset, pageSize); + + if(purchaseOrders == null) { throw new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND); } - purchaseOrders.forEach(purchaseOrder -> { - try { - purchaseOrder.setMemberName(aesUtils.decrypt(purchaseOrder.getMemberName())); - } catch (GeneralSecurityException e) { - throw new RuntimeException(e); - } - }); + redisTemplate.opsForValue().set(cacheKey, purchaseOrders); + } - int total = purchaseOrderMapper.findSearchPurchaseOrderCount(); + Integer count = purchaseOrderMapper.findAllPurchaseOrderCount(); + int totalPurchaseOrder = (count != null) ? count : 0; - return new PageImpl<>(purchaseOrders, pageable, total); - } else { - throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + return new PageImpl<>(purchaseOrders, pageable, totalPurchaseOrder); + } + + @Override + @Transactional(readOnly = true) + public Page selectSearchPurchaseOrder(PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO, Pageable pageable) { + + int offset = Math.toIntExact(pageable.getOffset()); + int pageSize = pageable.getPageSize(); + List purchaseOrders = purchaseOrderMapper.findSearchPurchaseOrder(offset, pageSize, purchaseOrderSelectSearchDTO); + + if (purchaseOrders == null) { + throw new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND); } + + Integer count = purchaseOrderMapper.findSearchPurchaseOrderCount(purchaseOrderSelectSearchDTO); + int totalPurchaseOrder = (count != null) ? count : 0; + + return new PageImpl<>(purchaseOrders, pageable, totalPurchaseOrder); } } \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml b/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml index 567ce363..d51523ca 100644 --- a/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml @@ -28,7 +28,8 @@ - SELECT a.pur_ord_id, a.pur_ord_ttl, @@ -42,11 +43,12 @@ a.admin_id, a.mem_id FROM tb_purchase_order a - WHERE a.active = TRUE + WHERE a.mem_id = #{memberId} + AND a.active = TRUE AND a.pur_ord_id = #{purchaseOrderId}; - SELECT a.pur_ord_id, a.pur_ord_ttl, @@ -66,19 +68,21 @@ ON b.conr_id = e.conr_id LEFT JOIN tb_product f ON e.prod_id = f.prod_id - WHERE a.active = TRUE + WHERE a.mem_id = #{memberId} + AND a.active = TRUE ORDER BY a.created_at DESC LIMIT #{pageSize} OFFSET #{offset}; - SELECT COUNT(*) AS cnt FROM tb_purchase_order a - WHERE a.active = TRUE; + WHERE a.mem_id = #{memberId} + AND a.active = TRUE; - SELECT a.pur_ord_id, a.pur_ord_ttl, @@ -99,12 +103,40 @@ LEFT JOIN tb_product f ON e.prod_id = f.prod_id + a.mem_id = #{purchaseOrderSelectSearchDTOtitle.memberId} a.active = TRUE + + AND a.pur_ord_ttl LIKE CONCAT('%', #{purchaseOrderSelectSearchDTOtitle}, '%') + + + AND a.pur_ord_stat = #{purchaseOrderSelectSearchDTOstatus} + + + AND a.admin_id = #{purchaseOrderSelectSearchDTOadminId} + + + AND a.mem_id = #{purchaseOrderSelectSearchDTOsearchMemberId} + + + AND a.created_at BETWEEN #{purchaseOrderSelectSearchDTOstartDate} AND #{purchaseOrderSelectSearchDTOendDate} + + + ORDER BY a.created_at DESC + LIMIT #{pageSize} OFFSET #{offset}; + + + + + + + + + + + + - SELECT COUNT(*) AS cnt FROM tb_purchase_order a From a5b4b5cf1255c3e92d42c2e711e0682bddb18f8a Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Wed, 20 Nov 2024 15:05:08 +0900 Subject: [PATCH 230/563] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1(#99)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 13 +- logs/app-logs.json | 303 ------------------ src/logstash/conf/logstash.conf | 27 -- .../global/log/LoggingAspect.java | 121 +++++++ .../global/log/aggregate/Log.java | 29 ++ .../global/log/repository/LogRepository.java | 9 + .../global/logging/LoggingAspect.java | 36 --- .../global/utils/RequestUtils.java | 29 -- src/main/resources/logback-spring.xml | 25 -- 9 files changed, 169 insertions(+), 423 deletions(-) delete mode 100644 logs/app-logs.json delete mode 100644 src/logstash/conf/logstash.conf create mode 100644 src/main/java/stanl_2/final_backend/global/log/LoggingAspect.java create mode 100644 src/main/java/stanl_2/final_backend/global/log/aggregate/Log.java create mode 100644 src/main/java/stanl_2/final_backend/global/log/repository/LogRepository.java delete mode 100644 src/main/java/stanl_2/final_backend/global/logging/LoggingAspect.java delete mode 100644 src/main/java/stanl_2/final_backend/global/utils/RequestUtils.java delete mode 100644 src/main/resources/logback-spring.xml diff --git a/build.gradle b/build.gradle index 9e848586..dfe7444e 100644 --- a/build.gradle +++ b/build.gradle @@ -35,7 +35,8 @@ dependencies { developmentOnly 'org.springframework.boot:spring-boot-devtools' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' - runtimeOnly 'org.mariadb.jdbc:mariadb-java-client' +// runtimeOnly 'org.mariadb.jdbc:mariadb-java-client' + implementation 'org.mariadb.jdbc:mariadb-java-client:3.3.3' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' // .env file @@ -58,8 +59,14 @@ dependencies { //hibernate core implementation 'org.hibernate.orm:hibernate-core:6.5.2.Final' - // Logback Logstash Encoder - implementation 'net.logstash.logback:logstash-logback-encoder:7.4' + // Logback and SLF4J Dependencies (Updated versions) +// implementation 'ch.qos.logback:logback-classic:1.4.11' // Updated Logback version +// implementation 'org.slf4j:slf4j-api:2.0.7' // Compatible SLF4J version for Logback 1.4.11 +// implementation 'ch.qos.logback:logback-classic:1.2.11' +// implementation 'ch.qos.logback:logback-core:1.2.11' + + + } tasks.named('test') { diff --git a/logs/app-logs.json b/logs/app-logs.json deleted file mode 100644 index 14dd3df2..00000000 --- a/logs/app-logs.json +++ /dev/null @@ -1,303 +0,0 @@ -HV000001: Hibernate Validator 8.0.1.Final -Starting FinalBackendApplication using Java 17.0.13 with PID 56754 (/Users/bdh/Final_Backend/build/classes/java/main started by bdh in /Users/bdh/Final_Backend) -The following 1 profile is active: "prod" -Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable -For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG' -Multiple Spring Data modules found, entering strict repository configuration mode -Bootstrapping Spring Data JPA repositories in DEFAULT mode. -Finished Spring Data repository scanning in 360 ms. Found 19 JPA repository interfaces. -Multiple Spring Data modules found, entering strict repository configuration mode -Bootstrapping Spring Data Redis repositories in DEFAULT mode. -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.A_sample.command.domain.repository.SampleRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.career.command.domain.repository.CareerRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.center.command.domain.repository.CenterRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.certification.command.domain.repository.CertificationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.contract.command.domain.repository.ContractRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.customer.command.domain.repository.CustomerRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.education.command.domain.repository.EducationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.evaluation.command.domain.repository.EvaluationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.family.command.domain.repository.FamilyRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.member.command.domain.repository.MemberRoleRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.notices.command.domain.repository.NoticeRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.order.command.domain.repository.OrderRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.organization.command.domain.repository.OrganizationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.problem.command.domain.aggregate.repository.ProblemRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.promotion.command.domain.aggregate.repository.PromotionRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.purchase_order.command.domain.repository.PurchaseOrderRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.sales_history.command.domain.repository.SalesHistoryRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.schedule.command.domain.repository.ScheduleRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Finished Spring Data repository scanning in 40 ms. Found 0 Redis repository interfaces. -Skipping MapperFactoryBean with name 'careerMapper' and 'stanl_2.final_backend.domain.career.query.repository.CareerMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'certificationMapper' and 'stanl_2.final_backend.domain.certification.query.repository.CertificationMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'customerMapper' and 'stanl_2.final_backend.domain.customer.query.repository.CustomerMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'educationMapper' and 'stanl_2.final_backend.domain.education.query.repository.EducationMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'familyMapper' and 'stanl_2.final_backend.domain.family.query.repository.FamilyMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'authMapper' and 'stanl_2.final_backend.domain.member.query.repository.AuthMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'memberMapper' and 'stanl_2.final_backend.domain.member.query.repository.MemberMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'memberRoleMapper' and 'stanl_2.final_backend.domain.member.query.repository.MemberRoleMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'noticeMapper' and 'stanl_2.final_backend.domain.notices.query.repository.NoticeMapper' mapperInterface. Bean already defined with the same name! -Tomcat initialized with port 8080 (http) -Initializing ProtocolHandler ["http-nio-8080"] -Starting service [Tomcat] -Starting Servlet engine: [Apache Tomcat/10.1.31] -Initializing Spring embedded WebApplicationContext -Root WebApplicationContext: initialization completed in 3128 ms -HHH000204: Processing PersistenceUnitInfo [name: default] -HHH000412: Hibernate ORM core version 6.5.2.Final -HHH000026: Second-level cache disabled -No LoadTimeWeaver setup: ignoring JPA class transformer -HikariPool-1 - Starting... -HikariPool-1 - Added connection org.mariadb.jdbc.Connection@573d554f -HikariPool-1 - Start completed. -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration) -Initialized JPA EntityManagerFactory for persistence unit 'default' -spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning -Global AuthenticationManager configured with AuthenticationProvider bean with name prodUsernamePwdAuthenticationProvider -Global AuthenticationManager configured with an AuthenticationProvider bean. UserDetailsService beans will not be used for username/password login. Consider removing the AuthenticationProvider bean. Alternatively, consider using the UserDetailsService in a manually instantiated DaoAuthenticationProvider. -Validated configuration attributes -Will secure any request with filters: DisableEncodeUrlFilter, ChannelProcessingFilter, WebAsyncManagerIntegrationFilter, SecurityContextHolderFilter, HeaderWriterFilter, CorsFilter, LogoutFilter, JWTTokenValidatorFilter, UsernamePasswordAuthenticationFilter, DefaultLoginPageGeneratingFilter, DefaultLogoutPageGeneratingFilter, BasicAuthenticationFilter, RequestCacheAwareFilter, SecurityContextHolderAwareRequestFilter, AnonymousAuthenticationFilter, ExceptionTranslationFilter, AuthorizationFilter -Starting ProtocolHandler ["http-nio-8080"] -Tomcat started on port 8080 (http) with context path '/' -Started FinalBackendApplication in 11.412 seconds (process running for 12.476) -Initializing Spring DispatcherServlet 'dispatcherServlet' -Initializing Servlet 'dispatcherServlet' -Completed initialization in 2 ms -Securing POST /api/v1/auth/signin -Request: filter invocation [POST /api/v1/auth/signin]; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL] -Secured POST /api/v1/auth/signin -Request Info: IP=0:0:0:0:0:0:0:1, API=/api/v1/auth/signin, Method=POST, UserPK=null, RequestID=7ee91c36-d5b9-413d-82ef-d0c656e4954c -Set SecurityContextHolder to anonymous SecurityContext -Securing GET /api/v1/certification/other/test0 -Request: filter invocation [GET /api/v1/certification/other/test0]; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL] -Secured GET /api/v1/certification/other/test0 -Request Info: IP=0:0:0:0:0:0:0:1, API=/api/v1/certification/other/test0, Method=GET, UserPK=null, RequestID=57d48e1c-c54e-49d9-b2a8-d88555951264 -Securing GET /api/v1/education/other/test0 -Request: filter invocation [GET /api/v1/education/other/test0]; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL] -Secured GET /api/v1/education/other/test0 -Request Info: IP=0:0:0:0:0:0:0:1, API=/api/v1/education/other/test0, Method=GET, UserPK=null, RequestID=0bb6121a-c778-4430-aa8f-e3ae8c3216d4 -Closing JPA EntityManagerFactory for persistence unit 'default' -HikariPool-1 - Shutdown initiated... -HikariPool-1 - Shutdown completed. -HV000001: Hibernate Validator 8.0.1.Final -Starting FinalBackendApplication using Java 17.0.13 with PID 59037 (/Users/bdh/Final_Backend/build/classes/java/main started by bdh in /Users/bdh/Final_Backend) -The following 1 profile is active: "prod" -Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable -For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG' -Multiple Spring Data modules found, entering strict repository configuration mode -Bootstrapping Spring Data JPA repositories in DEFAULT mode. -Finished Spring Data repository scanning in 360 ms. Found 19 JPA repository interfaces. -Multiple Spring Data modules found, entering strict repository configuration mode -Bootstrapping Spring Data Redis repositories in DEFAULT mode. -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.A_sample.command.domain.repository.SampleRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.career.command.domain.repository.CareerRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.center.command.domain.repository.CenterRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.certification.command.domain.repository.CertificationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.contract.command.domain.repository.ContractRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.customer.command.domain.repository.CustomerRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.education.command.domain.repository.EducationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.evaluation.command.domain.repository.EvaluationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.family.command.domain.repository.FamilyRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.member.command.domain.repository.MemberRoleRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.notices.command.domain.repository.NoticeRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.order.command.domain.repository.OrderRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.organization.command.domain.repository.OrganizationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.problem.command.domain.aggregate.repository.ProblemRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.promotion.command.domain.aggregate.repository.PromotionRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.purchase_order.command.domain.repository.PurchaseOrderRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.sales_history.command.domain.repository.SalesHistoryRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.schedule.command.domain.repository.ScheduleRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Finished Spring Data repository scanning in 42 ms. Found 0 Redis repository interfaces. -Skipping MapperFactoryBean with name 'careerMapper' and 'stanl_2.final_backend.domain.career.query.repository.CareerMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'certificationMapper' and 'stanl_2.final_backend.domain.certification.query.repository.CertificationMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'customerMapper' and 'stanl_2.final_backend.domain.customer.query.repository.CustomerMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'educationMapper' and 'stanl_2.final_backend.domain.education.query.repository.EducationMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'familyMapper' and 'stanl_2.final_backend.domain.family.query.repository.FamilyMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'authMapper' and 'stanl_2.final_backend.domain.member.query.repository.AuthMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'memberMapper' and 'stanl_2.final_backend.domain.member.query.repository.MemberMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'memberRoleMapper' and 'stanl_2.final_backend.domain.member.query.repository.MemberRoleMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'noticeMapper' and 'stanl_2.final_backend.domain.notices.query.repository.NoticeMapper' mapperInterface. Bean already defined with the same name! -Tomcat initialized with port 8080 (http) -Initializing ProtocolHandler ["http-nio-8080"] -Starting service [Tomcat] -Starting Servlet engine: [Apache Tomcat/10.1.31] -Initializing Spring embedded WebApplicationContext -Root WebApplicationContext: initialization completed in 2908 ms -HHH000204: Processing PersistenceUnitInfo [name: default] -HHH000412: Hibernate ORM core version 6.5.2.Final -HHH000026: Second-level cache disabled -No LoadTimeWeaver setup: ignoring JPA class transformer -HikariPool-1 - Starting... -HikariPool-1 - Added connection org.mariadb.jdbc.Connection@7f5f6a2e -HikariPool-1 - Start completed. -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration) -Initialized JPA EntityManagerFactory for persistence unit 'default' -spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning -Global AuthenticationManager configured with AuthenticationProvider bean with name prodUsernamePwdAuthenticationProvider -Global AuthenticationManager configured with an AuthenticationProvider bean. UserDetailsService beans will not be used for username/password login. Consider removing the AuthenticationProvider bean. Alternatively, consider using the UserDetailsService in a manually instantiated DaoAuthenticationProvider. -Validated configuration attributes -Will secure any request with filters: DisableEncodeUrlFilter, ChannelProcessingFilter, WebAsyncManagerIntegrationFilter, SecurityContextHolderFilter, HeaderWriterFilter, CorsFilter, LogoutFilter, JWTTokenValidatorFilter, UsernamePasswordAuthenticationFilter, DefaultLoginPageGeneratingFilter, DefaultLogoutPageGeneratingFilter, BasicAuthenticationFilter, RequestCacheAwareFilter, SecurityContextHolderAwareRequestFilter, AnonymousAuthenticationFilter, ExceptionTranslationFilter, AuthorizationFilter -Starting ProtocolHandler ["http-nio-8080"] -Tomcat started on port 8080 (http) with context path '/' -Started FinalBackendApplication in 9.504 seconds (process running for 10.419) -Initializing Spring DispatcherServlet 'dispatcherServlet' -Initializing Servlet 'dispatcherServlet' -Completed initialization in 1 ms -Securing POST /api/v1/auth/signin -Request: filter invocation [POST /api/v1/auth/signin]; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL] -Secured POST /api/v1/auth/signin -Request Info: IP=0:0:0:0:0:0:0:1, API=/api/v1/auth/signin, Method=POST, UserPK=null, RequestID=2bdc6b15-2b71-49e8-bec1-1e8b218c3ea3 -Set SecurityContextHolder to anonymous SecurityContext -Securing GET /api/v1/member -Request: filter invocation [GET /api/v1/member]; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL] -Secured GET /api/v1/member -Request Info: IP=0:0:0:0:0:0:0:1, API=/api/v1/member, Method=GET, UserPK=null, RequestID=496b10c9-ab8d-4775-850c-938a57709b8d -Securing POST /api/v1/career -Request: filter invocation [POST /api/v1/career]; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL] -Secured POST /api/v1/career -Request Info: IP=0:0:0:0:0:0:0:1, API=/api/v1/career, Method=POST, UserPK=null, RequestID=fa0305fe-e76c-4578-bf7b-7fa2c1618d46 -Securing GET /api/v1/career/other/test0 -Request: filter invocation [GET /api/v1/career/other/test0]; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL] -Secured GET /api/v1/career/other/test0 -Request Info: IP=0:0:0:0:0:0:0:1, API=/api/v1/career/other/test0, Method=GET, UserPK=null, RequestID=712703f2-4790-49b8-89f7-74da83c7ef40 -Securing GET /api/v1/career -Request: filter invocation [GET /api/v1/career]; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL] -Secured GET /api/v1/career -Request Info: IP=0:0:0:0:0:0:0:1, API=/api/v1/career, Method=GET, UserPK=null, RequestID=4924a332-633e-415c-a85b-109ee2a59bf6 -Securing POST /api/v1/certification -Request: filter invocation [POST /api/v1/certification]; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL] -Secured POST /api/v1/certification -Request Info: IP=0:0:0:0:0:0:0:1, API=/api/v1/certification, Method=POST, UserPK=null, RequestID=4e0773a7-69ea-4167-8a5a-62201040f2dd -Closing JPA EntityManagerFactory for persistence unit 'default' -HikariPool-1 - Shutdown initiated... -HikariPool-1 - Shutdown completed. -HV000001: Hibernate Validator 8.0.1.Final -Starting FinalBackendApplication using Java 17.0.13 with PID 61658 (/Users/bdh/Final_Backend/build/classes/java/main started by bdh in /Users/bdh/Final_Backend) -The following 1 profile is active: "prod" -Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable -For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG' -Multiple Spring Data modules found, entering strict repository configuration mode -Bootstrapping Spring Data JPA repositories in DEFAULT mode. -Finished Spring Data repository scanning in 311 ms. Found 19 JPA repository interfaces. -Multiple Spring Data modules found, entering strict repository configuration mode -Bootstrapping Spring Data Redis repositories in DEFAULT mode. -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.A_sample.command.domain.repository.SampleRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.career.command.domain.repository.CareerRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.center.command.domain.repository.CenterRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.certification.command.domain.repository.CertificationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.contract.command.domain.repository.ContractRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.customer.command.domain.repository.CustomerRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.education.command.domain.repository.EducationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.evaluation.command.domain.repository.EvaluationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.family.command.domain.repository.FamilyRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.member.command.domain.repository.MemberRoleRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.notices.command.domain.repository.NoticeRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.order.command.domain.repository.OrderRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.organization.command.domain.repository.OrganizationRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.problem.command.domain.aggregate.repository.ProblemRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.promotion.command.domain.aggregate.repository.PromotionRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.purchase_order.command.domain.repository.PurchaseOrderRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.sales_history.command.domain.repository.SalesHistoryRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Spring Data Redis - Could not safely identify store assignment for repository candidate interface stanl_2.final_backend.domain.schedule.command.domain.repository.ScheduleRepository; If you want this repository to be a Redis repository, consider annotating your entities with one of these annotations: org.springframework.data.redis.core.RedisHash (preferred), or consider extending one of the following types with your repository: org.springframework.data.keyvalue.repository.KeyValueRepository -Finished Spring Data repository scanning in 41 ms. Found 0 Redis repository interfaces. -Skipping MapperFactoryBean with name 'careerMapper' and 'stanl_2.final_backend.domain.career.query.repository.CareerMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'certificationMapper' and 'stanl_2.final_backend.domain.certification.query.repository.CertificationMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'customerMapper' and 'stanl_2.final_backend.domain.customer.query.repository.CustomerMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'educationMapper' and 'stanl_2.final_backend.domain.education.query.repository.EducationMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'familyMapper' and 'stanl_2.final_backend.domain.family.query.repository.FamilyMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'authMapper' and 'stanl_2.final_backend.domain.member.query.repository.AuthMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'memberMapper' and 'stanl_2.final_backend.domain.member.query.repository.MemberMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'memberRoleMapper' and 'stanl_2.final_backend.domain.member.query.repository.MemberRoleMapper' mapperInterface. Bean already defined with the same name! -Skipping MapperFactoryBean with name 'noticeMapper' and 'stanl_2.final_backend.domain.notices.query.repository.NoticeMapper' mapperInterface. Bean already defined with the same name! -Tomcat initialized with port 8080 (http) -Initializing ProtocolHandler ["http-nio-8080"] -Starting service [Tomcat] -Starting Servlet engine: [Apache Tomcat/10.1.31] -Initializing Spring embedded WebApplicationContext -Root WebApplicationContext: initialization completed in 2775 ms -HHH000204: Processing PersistenceUnitInfo [name: default] -HHH000412: Hibernate ORM core version 6.5.2.Final -HHH000026: Second-level cache disabled -No LoadTimeWeaver setup: ignoring JPA class transformer -HikariPool-1 - Starting... -HikariPool-1 - Added connection org.mariadb.jdbc.Connection@68626282 -HikariPool-1 - Start completed. -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000069: Duplicate generator name PrefixGeneratorConfig -HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration) -Initialized JPA EntityManagerFactory for persistence unit 'default' -spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning -Global AuthenticationManager configured with AuthenticationProvider bean with name prodUsernamePwdAuthenticationProvider -Global AuthenticationManager configured with an AuthenticationProvider bean. UserDetailsService beans will not be used for username/password login. Consider removing the AuthenticationProvider bean. Alternatively, consider using the UserDetailsService in a manually instantiated DaoAuthenticationProvider. -Validated configuration attributes -Will secure any request with filters: DisableEncodeUrlFilter, ChannelProcessingFilter, WebAsyncManagerIntegrationFilter, SecurityContextHolderFilter, HeaderWriterFilter, CorsFilter, LogoutFilter, JWTTokenValidatorFilter, UsernamePasswordAuthenticationFilter, DefaultLoginPageGeneratingFilter, DefaultLogoutPageGeneratingFilter, BasicAuthenticationFilter, RequestCacheAwareFilter, SecurityContextHolderAwareRequestFilter, AnonymousAuthenticationFilter, ExceptionTranslationFilter, AuthorizationFilter -Starting ProtocolHandler ["http-nio-8080"] -Tomcat started on port 8080 (http) with context path '/' -Started FinalBackendApplication in 9.197 seconds (process running for 10.025) -Initializing Spring DispatcherServlet 'dispatcherServlet' -Initializing Servlet 'dispatcherServlet' -Completed initialization in 1 ms -Securing POST /api/v1/auth/signin -Request: filter invocation [POST /api/v1/auth/signin]; ConfigAttributes: [REQUIRES_INSECURE_CHANNEL] -Secured POST /api/v1/auth/signin -Request Info: IP=0:0:0:0:0:0:0:1, API=/api/v1/auth/signin, Method=POST, UserPK=null, RequestID=82151ecb-f6cd-403a-9407-7f3e2f1e4bd4 -사용자 예외처리: 유저 정보가 없습니다. -Resolved [stanl_2.final_backend.global.exception.GlobalCommonException: 유저 정보가 없습니다.] -Set SecurityContextHolder to anonymous SecurityContext -Closing JPA EntityManagerFactory for persistence unit 'default' -HikariPool-1 - Shutdown initiated... -HikariPool-1 - Shutdown completed. diff --git a/src/logstash/conf/logstash.conf b/src/logstash/conf/logstash.conf deleted file mode 100644 index b6b7ef3d..00000000 --- a/src/logstash/conf/logstash.conf +++ /dev/null @@ -1,27 +0,0 @@ -input { - file { - path => "/src/logs/*.json" # JSON 로그 파일이 위치한 경로 - start_position => "beginning" # 로그를 처음부터 읽도록 설정 - sincedb_path => "/dev/null" # 모든 실행에서 새로 시작하도록 설정 - } -} - -filter { - json { - source => "message" # JSON 포맷의 메시지 필드를 파싱 - } -} - -output { - elasticsearch { - hosts => ["http://localhost:9200"] # Elasticsearch 서버 주소 - index => "app-logs" # Elasticsearch 인덱스 이름 - } - stdout { codec => rubydebug } # 디버깅을 위한 콘솔 출력 - - # 파일 출력 추가 - file { - path => "/output/logstash_logs.log" # 저장될 파일 경로 - codec => "json_lines" # JSON 형태로 저장 - } -} diff --git a/src/main/java/stanl_2/final_backend/global/log/LoggingAspect.java b/src/main/java/stanl_2/final_backend/global/log/LoggingAspect.java new file mode 100644 index 00000000..3675871d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/log/LoggingAspect.java @@ -0,0 +1,121 @@ +package stanl_2.final_backend.global.log; + +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import stanl_2.final_backend.global.log.repository.LogRepository; +import stanl_2.final_backend.global.log.aggregate.Log; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@Slf4j +@Aspect +@Component +public class LoggingAspect { + + private final ObjectMapper objectMapper; + private final LogRepository logRepository; + + @Autowired + public LoggingAspect(LogRepository logRepository) { + this.objectMapper = new ObjectMapper(); + this.logRepository = logRepository; + } + + @Before("within(@org.springframework.web.bind.annotation.RestController *)") + public void logRequestInfo(JoinPoint joinPoint) { + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if (attributes == null) return; + + HttpServletRequest request = attributes.getRequest(); + + Map logData = new HashMap<>(); + + // 사용자 정보 + Map userData = new HashMap<>(); + userData.put("session_id", safeValue(request.getRequestedSessionId())); + userData.put("user_agent", safeValue(request.getHeader("User-Agent"))); + logData.put("user", userData); + + // 네트워크 정보 + Map networkData = new HashMap<>(); + networkData.put("ip_address", safeValue(getClientIp(request))); + networkData.put("host_name", safeValue(request.getRemoteHost())); + networkData.put("remote_port", request.getRemotePort()); + logData.put("network", networkData); + + // 요청 정보 + Map requestData = new HashMap<>(); + requestData.put("uri", safeValue(request.getRequestURI())); + requestData.put("method", safeValue(request.getMethod())); + requestData.put("query_string", safeValue(request.getQueryString())); + requestData.put("headers", getHeaders(request)); + logData.put("request", requestData); + + // 시간 정보 + Map timeData = new HashMap<>(); + timeData.put("request_time", LocalDateTime.now().toString()); + logData.put("time", timeData); + + // 추가 정보 + logData.put("transaction_id", UUID.randomUUID().toString()); + + try { + log.info("Request Info: {}", objectMapper.writeValueAsString(logData)); + + // 로그 엔티티 저장 + Log logEntry = new Log(); + logEntry.setSessionId(safeValue(request.getRequestedSessionId())); + logEntry.setUserAgent(safeValue(request.getHeader("User-Agent"))); + logEntry.setIpAddress(safeValue(getClientIp(request))); + logEntry.setUri(safeValue(request.getRequestURI())); + logEntry.setMethod(safeValue(request.getMethod())); + logEntry.setQueryString(safeValue(request.getQueryString())); + logEntry.setRequestTime(LocalDateTime.now()); + + logRepository.save(logEntry); + } catch (Exception e) { + log.error("Failed to log request info", e); + } + } + + private String getClientIp(HttpServletRequest request) { + String[] headers = { + "X-Forwarded-For", + "Proxy-Client-IP", + "WL-Proxy-Client-IP", + "HTTP_CLIENT_IP", + "HTTP_X_FORWARDED_FOR" + }; + + for (String header : headers) { + String ip = request.getHeader(header); + if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) { + return ip.split(",")[0]; + } + } + return request.getRemoteAddr(); + } + + private Map getHeaders(HttpServletRequest request) { + Map headers = new HashMap<>(); + request.getHeaderNames().asIterator().forEachRemaining(headerName -> + headers.put(headerName, safeValue(request.getHeader(headerName))) + ); + return headers; + } + + private String safeValue(String value) { + return value != null ? value : "N/A"; + } +} diff --git a/src/main/java/stanl_2/final_backend/global/log/aggregate/Log.java b/src/main/java/stanl_2/final_backend/global/log/aggregate/Log.java new file mode 100644 index 00000000..f0e6fd52 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/log/aggregate/Log.java @@ -0,0 +1,29 @@ +package stanl_2.final_backend.global.log.aggregate; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +@Entity +public class Log { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String sessionId; + private String userAgent; + private String ipAddress; + private String uri; + private String method; + private String queryString; + private LocalDateTime requestTime; +} + + diff --git a/src/main/java/stanl_2/final_backend/global/log/repository/LogRepository.java b/src/main/java/stanl_2/final_backend/global/log/repository/LogRepository.java new file mode 100644 index 00000000..93f9119a --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/log/repository/LogRepository.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.global.log.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import stanl_2.final_backend.global.log.aggregate.Log; + +@Repository +public interface LogRepository extends JpaRepository { +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/global/logging/LoggingAspect.java b/src/main/java/stanl_2/final_backend/global/logging/LoggingAspect.java deleted file mode 100644 index 596c4338..00000000 --- a/src/main/java/stanl_2/final_backend/global/logging/LoggingAspect.java +++ /dev/null @@ -1,36 +0,0 @@ -package stanl_2.final_backend.global.logging; - -import jakarta.servlet.http.HttpServletRequest; -import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Before; -import org.springframework.stereotype.Component; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - -import java.util.UUID; - -@Slf4j -@Aspect -@Component -public class LoggingAspect { - - @Before("within(@org.springframework.web.bind.annotation.RestController *)") - public void logRequestInfo(JoinPoint joinPoint) { - // HttpServletRequest를 가져옵니다. - ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); - if (attributes == null) return; - - HttpServletRequest request = attributes.getRequest(); - String ipAddress = request.getRemoteAddr(); - String apiEndpoint = request.getRequestURI(); - String method = request.getMethod(); - String userPk = request.getHeader("User-Pk"); // 예시 헤더로 사용자 PK 전달 - String requestId = UUID.randomUUID().toString(); - - // 로그 출력 - log.info("Request Info: IP={}, API={}, Method={}, UserPK={}, RequestID={}", - ipAddress, apiEndpoint, method, userPk, requestId); - } -} diff --git a/src/main/java/stanl_2/final_backend/global/utils/RequestUtils.java b/src/main/java/stanl_2/final_backend/global/utils/RequestUtils.java deleted file mode 100644 index 3b28bde8..00000000 --- a/src/main/java/stanl_2/final_backend/global/utils/RequestUtils.java +++ /dev/null @@ -1,29 +0,0 @@ -package stanl_2.final_backend.global.utils; - -import jakarta.servlet.http.HttpServletRequest; - - -public class RequestUtils { - - public static String getClientIp(HttpServletRequest request) { - String ip = request.getHeader("X-Forwarded-For"); - - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { - ip = request.getHeader("Proxy-Client-IP"); - } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { - ip = request.getHeader("WL-Proxy-Client-IP"); - } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { - ip = request.getHeader("HTTP_CLIENT_IP"); - } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { - ip = request.getHeader("HTTP_X_FORWARDED_FOR"); - } - if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { - ip = request.getRemoteAddr(); - } - - return ip; - } -} \ No newline at end of file diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml deleted file mode 100644 index 1e95594e..00000000 --- a/src/main/resources/logback-spring.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - %d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} - %msg%n - - - - - - logs/app-logs.json - - logs/app-logs-%d{yyyy-MM-dd}.json - 30 - - - %msg%n - - - - - - - - From eebfbe8d08ae09795ce729d1ea09b92215188283 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 20 Nov 2024 15:05:20 +0900 Subject: [PATCH 231/563] =?UTF-8?q?feat=20:=20=EC=BF=BC=EB=A6=AC=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EC=A4=91(#62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/repository/ProblemMapper.xml | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml diff --git a/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml b/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml new file mode 100644 index 00000000..14cbdce3 --- /dev/null +++ b/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 1941443659614ba24846d23a5c78a95723953403 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 20 Nov 2024 15:21:48 +0900 Subject: [PATCH 232/563] =?UTF-8?q?xml=20=EC=88=98=EC=A0=95(#61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../problem/query/repository/ProblemMapper.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml b/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml index 14cbdce3..fc797f10 100644 --- a/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml @@ -28,17 +28,17 @@ + SELECT + a.alr_id, + a.alr_msg, + a.alr_url, + a.alr_type, + a.alr_read_stat, + a.mem_id + FROM tb_alarm a + WHERE mem_id = #{memberId} + ORDER BY CAST(SUBSTRING(ALR_ID, 5) AS UNSIGNED) DESC + LIMIT #{size} + + + + + + + + + + + + + \ No newline at end of file From 8d76307972ce1c65b80addb486ad272dbab6bd2e Mon Sep 17 00:00:00 2001 From: giuseog Date: Wed, 20 Nov 2024 16:18:03 +0900 Subject: [PATCH 236/563] =?UTF-8?q?feat:=20=EC=82=AC=EC=9B=90=20=ED=86=B5?= =?UTF-8?q?=EA=B3=84=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84=EC=A4=91(#7?= =?UTF-8?q?3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/dto/EvaluationSearchDTO.java | 1 - .../controller/SalesHistoryController.java | 182 ++++++++++++++- ...tIdDTO.java => SalesHistorySearchDTO.java} | 6 +- .../query/dto/SalesHistorySelectDTO.java | 8 +- .../query/dto/SalesHistoryStatisticsDTO.java | 14 ++ .../query/repository/SalesHistoryMapper.java | 21 +- .../service/SalesHistoryQueryService.java | 16 +- .../service/SalesHistoryQueryServiceImpl.java | 110 +++++++--- src/main/resources/application-dev.yml | 2 +- src/main/resources/application-prod.yml | 2 +- src/main/resources/application.yml | 2 +- .../query/repository/SalesHistoryMapper.xml | 207 +++++++----------- 12 files changed, 396 insertions(+), 175 deletions(-) rename src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/{SalesHistorySelectIdDTO.java => SalesHistorySearchDTO.java} (64%) create mode 100644 src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationSearchDTO.java index b94e638e..42c18f7b 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationSearchDTO.java @@ -20,6 +20,5 @@ public class EvaluationSearchDTO { private String centerId; private String startDate; private String endDate; - private Collection roles; private String searcherName; } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java index 464caf69..0bc2a7b9 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java @@ -8,15 +8,19 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.sales_history.common.response.SalesHistoryResponseMessage; +import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySearchDTO; import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectDTO; +import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistoryStatisticsDTO; import stanl_2.final_backend.domain.sales_history.query.service.SalesHistoryQueryService; +import java.security.Principal; +import java.util.Map; + @RestController(value = "querySalesHistoryController") @RequestMapping("/api/v1/salesHistory") public class SalesHistoryController { @@ -27,6 +31,41 @@ public SalesHistoryController(SalesHistoryQueryService salesHistoryQueryService) this.salesHistoryQueryService = salesHistoryQueryService; } + /* 설명. + * - 사원 -> 일주일별, 월별, 연별(조회기간별) + + 본인 판매내역 조회 0 + + 본인 판매내역 상세 0 + + 본인 실적,수당, 매출액 합 0 + + 본인 검색 + * */ + + @Operation(summary = "사원 판매내역 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/employee") + public ResponseEntity getAllSalesHistoryByEmployee(Principal principal, + @PageableDefault(size = 20) Pageable pageable){ + SalesHistorySelectDTO salesHistorySelectDTO = new SalesHistorySelectDTO(); + + salesHistorySelectDTO.setSearcherName(principal.getName()); + + Page responseSalesHistory = salesHistoryQueryService.selectAllSalesHistoryByEmployee(salesHistorySelectDTO, pageable); + + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("사원 판매내역 조회 성공") + .result(responseSalesHistory) + .build()); + } + @Operation(summary = "판매내역 조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", @@ -35,21 +74,146 @@ public SalesHistoryController(SalesHistoryQueryService salesHistoryQueryService) content = @Content(mediaType = "application/json")) }) @GetMapping("") - public ResponseEntity getAllSalesHistory(Authentication authentication, - Pageable pageable){ + public ResponseEntity getAllSalesHistory(@PageableDefault(size = 20) Pageable pageable){ + + Page responseSalesHistory = salesHistoryQueryService.selectAllSalesHistory(pageable); + + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("판매내역 조회 성공") + .result(responseSalesHistory) + .build()); + } + + @Operation(summary = "판매내역 상세 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("employee/{salesHistoryId}") + public ResponseEntity getSalesHistoryDetail(@PathVariable("salesHistoryId") String salesHistoryId){ + SalesHistorySelectDTO salesHistorySelectDTO = new SalesHistorySelectDTO(); - salesHistorySelectDTO.setRoles(authentication.getAuthorities()); - salesHistorySelectDTO.setSearcherName(authentication.getName()); + salesHistorySelectDTO.setSalesHistoryId(salesHistoryId); - Page responseSalesHistory = salesHistoryQueryService.selectAllSalesHistory(salesHistorySelectDTO, pageable); + SalesHistorySelectDTO responseSalesHistory = salesHistoryQueryService.selectSalesHistoryDetail(salesHistorySelectDTO); return ResponseEntity.ok(SalesHistoryResponseMessage.builder() .httpStatus(200) - .msg("성공") + .msg("판매내역 상세 조회 성공") .result(responseSalesHistory) .build()); } + + @Operation(summary = "사원 통계(실적,수당,매출액) 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("employee/statistics") + public ResponseEntity getStatisticsByEmployee(Principal principal){ + + SalesHistorySelectDTO salesHistorySelectDTO = new SalesHistorySelectDTO(); + + salesHistorySelectDTO.setSearcherName(principal.getName()); + + SalesHistoryStatisticsDTO responseSalesHistory = salesHistoryQueryService.selectStatisticsByEmployee(salesHistorySelectDTO); + + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("사원 통계(실적,수당,매출액) 조회 성공") + .result(responseSalesHistory) + .build()); + } + + @Operation(summary = "사원 통계(실적,수당,매출액) 조회기간별 검색") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("employee/statistics/search") + public ResponseEntity getStatisticsSearchByEmployee(@RequestParam Map params + ,Principal principal, + @PageableDefault(size = 20) Pageable pageable){ + + SalesHistorySearchDTO salesHistorySearchDTO = new SalesHistorySearchDTO(); + + salesHistorySearchDTO.setSearcherName(principal.getName()); + salesHistorySearchDTO.setStartDate(params.get("startDate")); + salesHistorySearchDTO.setEndDate(params.get("endDate")); + + SalesHistoryStatisticsDTO responseSalesHistory = salesHistoryQueryService.selectStatisticsSearchByEmployee(salesHistorySearchDTO); + + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("사원 통계(실적,수당,매출액) 검색 성공") + .result(responseSalesHistory) + .build()); + } + + @Operation(summary = "사원 통계(실적,수당,매출액) 월 별 검색") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("employee/statistics/search/month") + public ResponseEntity getStatisticsSearchMonthByEmployee(@RequestParam Map params + ,Principal principal, + @PageableDefault(size = 20) Pageable pageable){ + + SalesHistorySearchDTO salesHistorySearchDTO = new SalesHistorySearchDTO(); + + /* 설명. 2024-07 식으로 와야함 !!!!! */ + salesHistorySearchDTO.setSearcherName(principal.getName()); + salesHistorySearchDTO.setStartDate(params.get("startDate")); + salesHistorySearchDTO.setEndDate(params.get("endDate")); + + SalesHistoryStatisticsDTO responseSalesHistory = salesHistoryQueryService.selectStatisticsSearchMonthByEmployee(salesHistorySearchDTO); + + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("사원 통계(실적,수당,매출액) 월 별 검색 성공") + .result(responseSalesHistory) + .build()); + } + + @Operation(summary = "사원 통계(실적,수당,매출액) 연도 별 검색") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("employee/statistics/search/year") + public ResponseEntity getStatisticsSearchYearByEmployee(@RequestParam Map params + ,Principal principal, + @PageableDefault(size = 20) Pageable pageable){ + + SalesHistorySearchDTO salesHistorySearchDTO = new SalesHistorySearchDTO(); + + /* 설명. 2024 식으로 와야함 !!!!! */ + salesHistorySearchDTO.setSearcherName(principal.getName()); + salesHistorySearchDTO.setStartDate(params.get("startDate")); + salesHistorySearchDTO.setEndDate(params.get("endDate")); + + SalesHistoryStatisticsDTO responseSalesHistory = salesHistoryQueryService.selectStatisticsSearchYearBb yEmployee(salesHistorySearchDTO); + + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("사원 통계(실적,수당,매출액) 월 별 검색 성공") + .result(responseSalesHistory) + .build()); + } + } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectIdDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java similarity index 64% rename from src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectIdDTO.java rename to src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java index c025ceda..38511d84 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectIdDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java @@ -9,6 +9,8 @@ @NoArgsConstructor @Getter @Setter -public class SalesHistorySelectIdDTO { - private String temp2; +public class SalesHistorySearchDTO { + private String searcherName; + private String startDate; + private String endDate; } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectDTO.java index 4e628315..e0ee7cd7 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectDTO.java @@ -14,9 +14,9 @@ @Setter public class SalesHistorySelectDTO { private String salesHistoryId; - private String salesHistoryNumberOfVehicles; - private String salesHistoryTotalSales; - private String salesHistoryIncentive; + private Integer salesHistoryNumberOfVehicles; + private Integer salesHistoryTotalSales; + private Integer salesHistoryIncentive; private String createdAt; private String deletedAt; private String active; @@ -25,6 +25,6 @@ public class SalesHistorySelectDTO { private String productId; private String memberId; private String centerId; - private Collection roles; private String searcherName; + } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsDTO.java new file mode 100644 index 00000000..72d6c106 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsDTO.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.sales_history.query.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString +public class SalesHistoryStatisticsDTO { + private int incentive; + private int performance; + private int totalSales; +} diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java index b96394e2..777453b2 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java @@ -2,20 +2,33 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; +import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySearchDTO; import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectDTO; +import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistoryStatisticsDTO; import java.util.List; @Mapper public interface SalesHistoryMapper { + + List findSalesHistoryByEmployee(@Param("size") int size + ,@Param("offset") int offset + ,@Param("searcherId") String searcherId); + + int findSalesHistoryCountByEmployee(@Param("searcherId") String searcherId); + List findAllSalesHistory(@Param("size") int size ,@Param("offset") int offset); + SalesHistorySelectDTO findSalesHistoryDetail(@Param("salesHistorySelectDTO") SalesHistorySelectDTO salesHistorySelectDTO); + int findSalesHistoryCount(); - List findSalesHistoryByMember(@Param("size") int size - ,@Param("offset") int offset - ,@Param("searcherId") String searcherId); + SalesHistoryStatisticsDTO findStatisticsByEmployee(@Param("salesHistorySelectDTO") SalesHistorySelectDTO salesHistorySelectDTO); + + SalesHistoryStatisticsDTO findStatisticsSearchByEmployee(@Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); + + SalesHistoryStatisticsDTO findStatisticsSearchMonthByEmployee(@Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); - int findSalesHistoryCountByMember(@Param("searcherId") String searcherId); + SalesHistoryStatisticsDTO findStatisticsSearchYearByEmployee(@Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java index d821d33f..1a7774df 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java @@ -2,10 +2,24 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySearchDTO; import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectDTO; +import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistoryStatisticsDTO; import java.security.GeneralSecurityException; public interface SalesHistoryQueryService { - Page selectAllSalesHistory(SalesHistorySelectDTO salesHistorySelectDTO, Pageable pageable) throws GeneralSecurityException; + Page selectAllSalesHistoryByEmployee(SalesHistorySelectDTO salesHistorySelectDTO, Pageable pageable); + + SalesHistorySelectDTO selectSalesHistoryDetail(SalesHistorySelectDTO salesHistorySelectDTO); + + Page selectAllSalesHistory(Pageable pageable); + + SalesHistoryStatisticsDTO selectStatisticsByEmployee(SalesHistorySelectDTO salesHistorySelectDTO); + + SalesHistoryStatisticsDTO selectStatisticsSearchByEmployee(SalesHistorySearchDTO salesHistorySearchDTO); + + SalesHistoryStatisticsDTO selectStatisticsSearchMonthByEmployee(SalesHistorySearchDTO salesHistorySearchDTO); + + SalesHistoryStatisticsDTO selectStatisticsSearchYearByEmployee(SalesHistorySearchDTO salesHistorySearchDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java index 01c9de96..684681b2 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java @@ -11,13 +11,14 @@ import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import stanl_2.final_backend.domain.sales_history.common.exception.SalesHistoryCommonException; import stanl_2.final_backend.domain.sales_history.common.exception.SalesHistoryErrorCode; +import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySearchDTO; import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectDTO; +import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistoryStatisticsDTO; import stanl_2.final_backend.domain.sales_history.query.repository.SalesHistoryMapper; -import stanl_2.final_backend.global.exception.GlobalCommonException; -import stanl_2.final_backend.global.exception.GlobalErrorCode; import stanl_2.final_backend.global.utils.AESUtils; +import org.springframework.data.redis.core.RedisTemplate; -import java.security.GeneralSecurityException; +import java.time.Duration; import java.util.List; @Service @@ -27,53 +28,110 @@ public class SalesHistoryQueryServiceImpl implements SalesHistoryQueryService { private final AESUtils aesUtils; private final MemberQueryService memberQueryService; private final AuthQueryService authQueryService; + private final RedisTemplate redisTemplate; @Autowired - public SalesHistoryQueryServiceImpl(SalesHistoryMapper salesHistoryMapper, AESUtils aesUtils, MemberQueryService memberQueryService, @Qualifier("authQueryService") AuthQueryService authQueryService) { + public SalesHistoryQueryServiceImpl(SalesHistoryMapper salesHistoryMapper, AESUtils aesUtils, MemberQueryService memberQueryService, AuthQueryService authQueryService, RedisTemplate redisTemplate) { this.salesHistoryMapper = salesHistoryMapper; this.aesUtils = aesUtils; this.memberQueryService = memberQueryService; this.authQueryService = authQueryService; + this.redisTemplate = redisTemplate; } @Override @Transactional(readOnly = true) - public Page selectAllSalesHistory(SalesHistorySelectDTO salesHistorySelectDTO, Pageable pageable) throws GeneralSecurityException { + public Page selectAllSalesHistoryByEmployee(SalesHistorySelectDTO salesHistorySelectDTO, Pageable pageable) { int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); - if(salesHistorySelectDTO.getRoles().stream() - .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()) - || "ROLE_REPRESENTATIVE".equals(role.getAuthority())) - ){ + String searcherId = authQueryService.selectMemberIdByLoginId(salesHistorySelectDTO.getSearcherName()); - List salesHistoryList = salesHistoryMapper.findAllSalesHistory(size,offset); + List salesHistoryList = salesHistoryMapper.findSalesHistoryByEmployee(size,offset, searcherId); - int total = salesHistoryMapper.findSalesHistoryCount(); + int total = salesHistoryMapper.findSalesHistoryCountByEmployee(searcherId); - if(salesHistoryList.isEmpty() || total == 0){ - throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); - } - return new PageImpl<>(salesHistoryList, pageable, total); - }else if(salesHistorySelectDTO.getRoles().stream() - .anyMatch(role -> "ROLE_MEMBER".equals(role.getAuthority()))) { + if(salesHistoryList.isEmpty() || total == 0){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } + return new PageImpl<>(salesHistoryList, pageable, total); + } - String searcherId = authQueryService.selectMemberIdByLoginId(salesHistorySelectDTO.getSearcherName()); + @Override + @Transactional(readOnly = true) + public SalesHistorySelectDTO selectSalesHistoryDetail(SalesHistorySelectDTO salesHistorySelectDTO) { - List salesHistoryList = salesHistoryMapper.findSalesHistoryByMember(size,offset, searcherId); + SalesHistorySelectDTO salesHistoryDetailDTO = salesHistoryMapper.findSalesHistoryDetail(salesHistorySelectDTO); - int total = salesHistoryMapper.findSalesHistoryCountByMember(searcherId); + return salesHistoryDetailDTO; + } - if(salesHistoryList.isEmpty() || total == 0){ - throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); - } - return new PageImpl<>(salesHistoryList, pageable, total); + @Override + @Transactional(readOnly = true) + public Page selectAllSalesHistory(Pageable pageable) { + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); + + List salesHistoryList = salesHistoryMapper.findAllSalesHistory(size,offset); + + int total = salesHistoryMapper.findSalesHistoryCount(); + + if(salesHistoryList.isEmpty() || total == 0){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } + + return new PageImpl<>(salesHistoryList, pageable, total); + } + + @Override + @Transactional(readOnly = true) + public SalesHistoryStatisticsDTO selectStatisticsByEmployee(SalesHistorySelectDTO salesHistorySelectDTO) { - }else{ - throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + salesHistorySelectDTO.setSearcherName(authQueryService.selectMemberIdByLoginId(salesHistorySelectDTO.getSearcherName())); + + String cacheKey = "salesHistory::statistics::searcherId=" + salesHistorySelectDTO.getSearcherName(); + + SalesHistoryStatisticsDTO salesHistoryStatisticsDTO = (SalesHistoryStatisticsDTO) redisTemplate.opsForValue().get(cacheKey); + + if (salesHistoryStatisticsDTO == null) { + // 캐시에 데이터가 없을 경우 DB에서 조회 + salesHistoryStatisticsDTO = salesHistoryMapper.findStatisticsByEmployee(salesHistorySelectDTO); + + // Redis에 데이터 저장 /만료 시간 설정 + if (salesHistoryStatisticsDTO != null) { + redisTemplate.opsForValue().set(cacheKey, salesHistoryStatisticsDTO, Duration.ofMinutes(10)); // 캐시 10분 유지 + } } + return salesHistoryStatisticsDTO; + } + + @Override + @Transactional(readOnly = true) + public SalesHistoryStatisticsDTO selectStatisticsSearchByEmployee(SalesHistorySearchDTO salesHistorySearchDTO) { + + salesHistorySearchDTO.setSearcherName(authQueryService.selectMemberIdByLoginId(salesHistorySearchDTO.getSearcherName())); + SalesHistoryStatisticsDTO salesHistoryStatisticsDTO = salesHistoryMapper.findStatisticsSearchByEmployee(salesHistorySearchDTO); + + return salesHistoryStatisticsDTO; + } + + @Override + @Transactional(readOnly = true) + public SalesHistoryStatisticsDTO selectStatisticsSearchMonthByEmployee(SalesHistorySearchDTO salesHistorySearchDTO) { + salesHistorySearchDTO.setSearcherName(authQueryService.selectMemberIdByLoginId(salesHistorySearchDTO.getSearcherName())); + + SalesHistoryStatisticsDTO salesHistoryStatisticsDTO = salesHistoryMapper.findStatisticsSearchMonthByEmployee(salesHistorySearchDTO); + + return salesHistoryStatisticsDTO; } + @Override + public SalesHistoryStatisticsDTO selectStatisticsSearchYearByEmployee(SalesHistorySearchDTO salesHistorySearchDTO) { + salesHistorySearchDTO.setSearcherName(authQueryService.selectMemberIdByLoginId(salesHistorySearchDTO.getSearcherName())); + SalesHistoryStatisticsDTO salesHistoryStatisticsDTO = salesHistoryMapper.findStatisticsSearchYearByEmployee(salesHistorySearchDTO); + + return salesHistoryStatisticsDTO; + } } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index e9390662..aabe59aa 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: create + ddl-auto: update logging: level: diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 23faa6a8..60bf5a48 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: create + ddl-auto: update logging: level: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 6618043e..eee7170c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -23,7 +23,7 @@ spring: jpa: show-sql: true hibernate: - ddl-auto: create + ddl-auto: update properties: hibernate.format_sql: true diff --git a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml index f91b1bcf..4b9458ca 100644 --- a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml @@ -2,9 +2,9 @@ - + - + @@ -17,176 +17,133 @@ - SELECT - a.eval_id, - a.eval_ttl, + a.sal_hist_id, + a.sal_hist_ince, + a.sal_hist_no_of_veh, + a.sal_hist_tota_sale, a.cent_id, + a.cust_id, a.mem_id, - a.wri_id, + a.prod_id, + a.conr_id, a.created_at - FROM tb_evaluation a - WHERE a.active = TRUE + FROM tb_sales_history a + WHERE a.active = TRUE AND a.mem_id = #{searcherId} ORDER BY a.created_at DESC LIMIT #{size} OFFSET #{offset} - SELECT - a.eval_id, - a.eval_ttl, + a.sal_hist_id, + a.sal_hist_ince, + a.sal_hist_no_of_veh, + a.sal_hist_tota_sale, a.cent_id, + a.cust_id, a.mem_id, - a.wri_id, + a.prod_id, + a.conr_id, a.created_at - FROM tb_evaluation a - WHERE a.cent_id = #{centerId} AND a.active = TRUE + FROM tb_sales_history a + WHERE a.active = TRUE ORDER BY a.created_at DESC LIMIT #{size} OFFSET #{offset} - SELECT - a.eval_id, - a.eval_ttl, - a.eval_cont, + a.sal_hist_id, + a.sal_hist_ince, + a.sal_hist_no_of_veh, + a.sal_hist_tota_sale, a.cent_id, + a.cust_id, a.mem_id, - a.wri_id, - a.created_at, - a.updated_at - FROM tb_evaluation a - WHERE a.eval_id = #{id} AND a.active = TRUE + a.prod_id, + a.conr_id, + a.created_at + FROM tb_sales_history a + WHERE a.sal_hist_id = #{salesHistorySelectDTO.salesHistoryId} AND a.active = TRUE - SELECT - a.eval_id, - a.eval_ttl, - a.cent_id, - a.mem_id, - a.wri_id, - a.created_at - FROM tb_evaluation a - - a.active = TRUE AND a.cent_id = #{centerId} - - AND a.eval_id = #{evaluationSearchDTO.evalId} - - - AND a.wri_id = #{evaluationSearchDTO.writerName} - - - AND a.mem_id = #{evaluationSearchDTO.memberName} - - - AND a.eval_ttl LIKE CONCAT('%', #{evaluationSearchDTO.title}, '%') - - - AND a.created_at BETWEEN #{evaluationSearchDTO.startDate} AND #{evaluationSearchDTO.endDate} - - - ORDER BY a.created_at DESC - LIMIT #{size} OFFSET #{offset} + SUM(a.sal_hist_ince) as INCENTIVE, + SUM(a.sal_hist_no_of_veh) as PERFORMANCE, + SUM(a.sal_hist_tota_sale) as TOTAL_SALES + FROM tb_sales_history a + WHERE a.mem_id = #{salesHistorySelectDTO.searcherName} AND a.active = TRUE; - SELECT - a.eval_id, - a.eval_ttl, - a.cent_id, - a.mem_id, - a.wri_id, - a.created_at - FROM tb_evaluation a + SUM(a.sal_hist_ince) as INCENTIVE, + SUM(a.sal_hist_no_of_veh) as PERFORMANCE, + SUM(a.sal_hist_tota_sale) as TOTAL_SALES + FROM tb_sales_history a - a.active = TRUE - - AND a.eval_id = #{evaluationSearchDTO.evalId} - - - AND a.wri_id = #{evaluationSearchDTO.writerName} - - - AND a.mem_id = #{evaluationSearchDTO.memberName} - - - AND a.cent_id = #{evaluationSearchDTO.centerId} - - - AND a.eval_ttl LIKE CONCAT('%', #{evaluationSearchDTO.title}, '%') - - - AND a.created_at BETWEEN #{evaluationSearchDTO.startDate} AND #{evaluationSearchDTO.endDate} + a.mem_id = #{salesHistorySearchDTO.searcherName} AND a.active = TRUE + + AND a.created_at BETWEEN #{salesHistorySearchDTO.startDate} AND #{salesHistorySearchDTO.endDate} - ORDER BY a.created_at DESC - LIMIT #{size} OFFSET #{offset} - SELECT COUNT(*) AS cnt - FROM tb_evaluation a - WHERE a.active = TRUE + FROM tb_sales_history a + WHERE a.active = TRUE AND a.mem_id = #{searcherId} + ORDER BY a.created_at DESC - SELECT COUNT(*) AS cnt - FROM tb_evaluation a - WHERE a.cent_id = #{centerId} AND a.active = TRUE; + FROM tb_sales_history a + WHERE a.active = TRUE; - SELECT - COUNT(*) AS cnt - FROM tb_evaluation a + LEFT(a.created_at, 7) AS MONTH, + SUM(a.sal_hist_ince) AS INCENTIVE, + SUM(a.sal_hist_no_of_veh) AS PERFORMANCE, + SUM(a.sal_hist_tota_sale) AS TOTAL_SALES + FROM tb_sales_history a - a.active = TRUE - - AND a.eval_id = #{evaluationSearchDTO.evalId} - - - AND a.wri_id = #{evaluationSearchDTO.writerName} - - - AND a.mem_id = #{evaluationSearchDTO.memberName} - - - AND a.cent_id = #{evaluationSearchDTO.centerId} - - - AND a.eval_ttl LIKE CONCAT('%', #{evaluationSearchDTO.title}, '%') - - - AND a.created_at BETWEEN #{evaluationSearchDTO.startDate} AND #{evaluationSearchDTO.endDate} + a.mem_id = #{salesHistorySearchDTO.searcherName} AND a.active = TRUE + + AND a.created_at BETWEEN #{salesHistorySearchDTO.startDate} AND #{salesHistorySearchDTO.endDate} + GROUP BY LEFT(a.created_at, 7) + ORDER BY MONTH ASC - SELECT - COUNT(*) AS cnt - FROM tb_evaluation a + LEFT(a.created_at, 4) AS YEAR, + SUM(a.sal_hist_ince) AS INCENTIVE, + SUM(a.sal_hist_no_of_veh) AS PERFORMANCE, + SUM(a.sal_hist_tota_sale) AS TOTAL_SALES + FROM tb_sales_history a - a.active = TRUE AND a.cent_id = #{centerId} - - AND a.eval_id = #{evaluationSearchDTO.evalId} - - - AND a.wri_id = #{evaluationSearchDTO.writerName} - - - AND a.mem_id = #{evaluationSearchDTO.memberName} - - - AND a.eval_ttl LIKE CONCAT('%', #{evaluationSearchDTO.title}, '%') - - - AND a.created_at BETWEEN #{evaluationSearchDTO.startDate} AND #{evaluationSearchDTO.endDate} + a.mem_id = #{salesHistorySearchDTO.searcherName} AND a.active = TRUE + + AND a.created_at BETWEEN #{salesHistorySearchDTO.startDate} AND #{salesHistorySearchDTO.endDate} + GROUP BY LEFT(a.created_at, 4) + ORDER BY YEAR ASC - From 4db02f2f909a99086e3f63eef25be5b938acf6f9 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Wed, 20 Nov 2024 16:25:57 +0900 Subject: [PATCH 237/563] =?UTF-8?q?feat:=20=EA=B3=84=EC=95=BD=EC=84=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=A4=91(#112)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ContractController.java | 125 +++---------- .../application/dto/ContractModifyDTO.java | 3 +- .../application/dto/ContractRegistDTO.java | 3 +- .../dto/ContractStatusModifyDTO.java | 1 - .../service/ContractCommandService.java | 2 +- .../domain/aggregate/entity/Contract.java | 6 + .../service/ContractCommandServiceImpl.java | 165 +++++++++--------- .../query/dto/ContractSeletIdDTO.java | 2 + .../domain/aggregate/entity/Customer.java | 2 +- .../query/repository/CustomerMapper.java | 1 + .../query/service/CustomerQueryService.java | 2 + .../service/CustomerQueryServiceImpl.java | 15 ++ .../query/repository/ContractMapper.xml | 7 + .../query/repository/CustomerMapper.xml | 18 ++ 14 files changed, 165 insertions(+), 187 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java index 590fa3ad..9aaaddb7 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java @@ -16,11 +16,7 @@ import stanl_2.final_backend.domain.contract.command.application.service.ContractCommandService; import stanl_2.final_backend.domain.contract.common.response.ContractResponseMessage; -import org.springframework.security.core.GrantedAuthority; - -import java.security.GeneralSecurityException; -import java.util.List; -import java.util.stream.Collectors; +import java.security.Principal; @RestController("contractController") @RequestMapping("/api/v1/contract") @@ -33,92 +29,37 @@ public ContractController(ContractCommandService contractCommandService) { this.contractCommandService = contractCommandService; } - /** - * [POST] http://localhost:8080/api/v1/contract - * Request - * { - * "name": "Sample Contract", - * "custName": "John Doe", - * "custIdenNo": "123456-7890123", - * "custAddrress": "123 Main Street, City, Country", - * "custEmail": "johndoe@example.com", - * "custPhone": "+1-234-567-8901", - * "compName": "Doe Industries", - * "custCla": "Premium", - * "custPurCond": "Full Payment", - * "seriNum": "A1B2C3D4", - * "seleOpti": "Extended Warranty", - * "downPay": 10000, - * "intePay": 500, - * "remPay": 15000, - * "consPay": 5000, - * "delvDate": "2024-12-15", - * "delvLoc": "Warehouse No. 3, Industrial Park", - * "state": "WAIT", - * "noOfVeh": "2", - * "createdUrl": "\n\n\n \n \n 자동차 매매 계약서\n \n\n\n
\n
\n

기아 자동차 매매 계약서

\n
\n
\n

계약 정보

\n \n \n \n
계약 번호KL-JS계약일2022-11-17계약장소서울 강남구
담당자유혜진전화번호010-7158-8796
\n
\n
\n

고객사항

\n
성명홍길동상호
주민등록번호****-*******사업자등록번호
주소**********사업자등록주소OOOOOOOO
전화(휴대폰)010-****-****구분개인
E-mail****@****.com구매유형현금
\n
\n
\n

차량사항

\n
차종Q4 e-tron 40일련번호2Y2Y
선택옵션AO대수1대
인도예정일2023년 인도인도장소서울지점
특약사항- 특약사항 내용이 여기에 표시됩니다.
\n
\n
\n

금액사항

\n
차량가격66,700,000원
계약금900,000원
중도금100,000원
인도금65,700,000원
탁송료65,700,000원
\n
\n
\n

본 계약서 주요 내용을 확인하고 계약을 체결하였음을 확인합니다.

\n
\n
매수인 (서명): ****
\n
매도인 (서명): ****
\n
\n
\n
\n\n", - * "memId": "MEM_000000001" - * } - * */ - @Operation(summary = "계약서 등록") + @Operation(summary = "계약서 등록(영업사원)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "계약서 등록 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) @PostMapping("") public ResponseEntity postTest(@RequestBody ContractRegistDTO contractRegistRequestDTO, - Authentication authentication) throws GeneralSecurityException { + Principal principal) { - contractRegistRequestDTO.setMemberId(authentication.getName()); - contractRegistRequestDTO.setRoles(authentication.getAuthorities()); + contractRegistRequestDTO.setMemberId(principal.getName()); contractCommandService.registerContract(contractRegistRequestDTO); return ResponseEntity.ok(ContractResponseMessage.builder() - .httpStatus(200) - .msg("계약서가 성공적으로 등록되었습니다.") - .result(null) - .build()); - } + .httpStatus(200) + .msg("계약서가 성공적으로 등록되었습니다.") + .result(null) + .build()); + } - /** - * [PUT] http://localhost:8080/api/v1/contract/CON_000000011 - * Request - * { - * "name": "계약서 수정", - * "custName": "John Doe", - * "custIdenNo": "123456-7890123", - * "custAddrress": "123 Main Street, City, Country", - * "custEmail": "johndoe@example.com", - * "custPhone": "+1-234-567-8901", - * "compName": "Doe Industries", - * "custCla": "Premium", - * "custPurCond": "Full Payment", - * "seriNum": "A1B2C3D4", - * "seleOpti": "Extended Warranty", - * "downPay": 10000, - * "intePay": 500, - * "remPay": 15000, - * "consPay": 5000, - * "delvDate": "2024-12-15", - * "delvLoc": "Warehouse No. 3, Industrial Park", - * "state": "WAIT", - * "noOfVeh": "2", - * "memId": "MEM_000000001" - * } - * */ - @Operation(summary = "계약서 수정") + @Operation(summary = "계약서 수정(영업사원)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "계약서 수정 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) - @PutMapping("{id}") - public ResponseEntity putContract(@PathVariable String id, + @PutMapping("{contractId}") + public ResponseEntity putContract(@PathVariable String contractId, @RequestBody ContractModifyDTO contractModifyRequestDTO, - Authentication authentication) throws GeneralSecurityException { + Principal principal) { - contractModifyRequestDTO.setContractId(id); - contractModifyRequestDTO.setMemberId(authentication.getName()); - contractModifyRequestDTO.setRoles(authentication.getAuthorities()); + contractModifyRequestDTO.setContractId(contractId); + contractModifyRequestDTO.setMemberId(principal.getName()); ContractModifyDTO contractModifyDTO = contractCommandService.modifyContract(contractModifyRequestDTO); return ResponseEntity.ok(ContractResponseMessage.builder() @@ -128,22 +69,18 @@ public ResponseEntity putContract(@PathVariable String .build()); } - /** - * [DELETE] http://localhost:8080/api/v1/contract/CON_000000011 - * */ - @Operation(summary = "계약서 삭제") + @Operation(summary = "계약서 삭제(영업사원)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "계약서 삭제 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) - @DeleteMapping("{id}") - public ResponseEntity deleteContract(@PathVariable String id, - Authentication authentication) { + @DeleteMapping("{contractId}") + public ResponseEntity deleteContract(@PathVariable String contractId, + Principal principal) { ContractDeleteDTO contractDeleteDTO = new ContractDeleteDTO(); - contractDeleteDTO.setContractId(id); - contractDeleteDTO.setMemberId(authentication.getName()); - contractDeleteDTO.setRoles(authentication.getAuthorities()); + contractDeleteDTO.setContractId(contractId); + contractDeleteDTO.setMemberId(principal.getName()); contractCommandService.deleteContract(contractDeleteDTO); return ResponseEntity.ok(ContractResponseMessage.builder() @@ -158,21 +95,14 @@ public ResponseEntity deleteContract(@PathVariable Stri @ApiResponse(responseCode = "200", description = "계약서 승인상태 수정 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) - @PutMapping("/status/{id}") - public ResponseEntity putContractStatus( - @PathVariable String id, - @RequestBody ContractStatusModifyDTO contractStatusModifyDTO, - Authentication authentication) { - - // 권한 정보를 String 리스트로 변환 - List roles = authentication.getAuthorities().stream() - .map(role -> role.getAuthority()) - .toList(); + @PutMapping("/status/{contractId}") + public ResponseEntity putContractStatus(@PathVariable String contractId, + @RequestBody ContractStatusModifyDTO contractStatusModifyDTO, + Principal principal) { // DTO에 설정 - contractStatusModifyDTO.setRoles(roles); - contractStatusModifyDTO.setContractId(id); - contractStatusModifyDTO.setAdminId(authentication.getName()); + contractStatusModifyDTO.setContractId(contractId); + contractStatusModifyDTO.setAdminId(principal.getName()); // 서비스 호출 contractCommandService.modifyContractStatus(contractStatusModifyDTO); @@ -184,5 +114,4 @@ public ResponseEntity putContractStatus( .build()); } - } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java index bcb1cb4a..26048c2e 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java @@ -14,7 +14,9 @@ public class ContractModifyDTO { private String contractId; private String title; private String customerName; + private String customerSex; private String customerIdentifiNo; + private Integer customerAge; private String customerAddrress; private String customerEmail; private String customerPhone; @@ -41,5 +43,4 @@ public class ContractModifyDTO { private String centerId; private String customerId; private String productId; - private Collection roles; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java index 3f5b3990..419f98ac 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java @@ -13,7 +13,9 @@ public class ContractRegistDTO { private String title; private String customerName; + private String customerSex; private String customerIdentifiNo; + private Integer customerAge; private String customerAddrress; private String customerEmail; private String customerPhone; @@ -34,5 +36,4 @@ public class ContractRegistDTO { private String numberOfVehicles; private String createdUrl; private String memberId; - private Collection roles; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractStatusModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractStatusModifyDTO.java index 67a67af5..d7ffce7b 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractStatusModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractStatusModifyDTO.java @@ -14,5 +14,4 @@ public class ContractStatusModifyDTO { private String contractId; private String status; private String adminId; - private List roles; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java index 2b32ac17..e976b005 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java @@ -10,7 +10,7 @@ public interface ContractCommandService { void registerContract(ContractRegistDTO contractRegistRequestDTO) throws GeneralSecurityException; - ContractModifyDTO modifyContract(ContractModifyDTO contractModifyRequestDTO) throws GeneralSecurityException; + ContractModifyDTO modifyContract(ContractModifyDTO contractModifyRequestDTO); void deleteContract(ContractDeleteDTO contractDeleteDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java index 4f381169..b8546750 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java @@ -37,9 +37,15 @@ public class Contract { @Column(name = "CONR_CUST_NAME", nullable = false) private String customerName; + @Column(name = "CONR_CUST_SEX", nullable = false) + private String customerSex; + @Column(name = "CONR_CUST_IDEN_NO", nullable = false) private String customerIdentifiNo; + @Column(name = "CONR_CUST_AGE", nullable = false) + private String customerAge; + @Column(name = "CONR_CUST_ADR", nullable = false) private String customerAddrress; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index 35e1c04f..f9cdc8c4 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -12,6 +12,10 @@ import stanl_2.final_backend.domain.contract.command.domain.repository.ContractRepository; import stanl_2.final_backend.domain.contract.common.exception.ContractCommonException; import stanl_2.final_backend.domain.contract.common.exception.ContractErrorCode; +import stanl_2.final_backend.domain.customer.command.application.dto.CustomerRegistDTO; +import stanl_2.final_backend.domain.customer.command.application.service.CustomerCommandService; +import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; +import stanl_2.final_backend.domain.customer.query.service.CustomerQueryService; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.member.query.service.MemberQueryService; @@ -30,14 +34,18 @@ public class ContractCommandServiceImpl implements ContractCommandService { private final ContractRepository contractRepository; private final AuthQueryService authQueryService; + private final CustomerQueryService customerQueryService; private final MemberQueryService memberQueryService; + private final CustomerCommandService customerCommandService; private final ProductService productService; private final ModelMapper modelMapper; - public ContractCommandServiceImpl(ContractRepository contractRepository, AuthQueryService authQueryService, MemberQueryService memberQueryService, ProductService productService, ModelMapper modelMapper) { + public ContractCommandServiceImpl(ContractRepository contractRepository, AuthQueryService authQueryService, CustomerQueryService customerQueryService, MemberQueryService memberQueryService, CustomerCommandService customerCommandService, ProductService productService, ModelMapper modelMapper) { this.contractRepository = contractRepository; this.authQueryService = authQueryService; + this.customerQueryService = customerQueryService; this.memberQueryService = memberQueryService; + this.customerCommandService = customerCommandService; this.productService = productService; this.modelMapper = modelMapper; } @@ -47,136 +55,125 @@ private String getCurrentTime() { return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } + private String handleCustomerInfo(ContractRegistDTO contractRegistRequestDTO, String memberId) throws GeneralSecurityException { + // 고객 정보 조회 + CustomerDTO customerDTO = customerQueryService.selectCustomerInfoByPhone(contractRegistRequestDTO.getCustomerPhone()); + + // 고객이 없으면 새로 등록 + if (customerDTO == null) { + CustomerRegistDTO customerRegistDTO = new CustomerRegistDTO(); + customerRegistDTO.setName(contractRegistRequestDTO.getCustomerName()); + customerRegistDTO.setAge(contractRegistRequestDTO.getCustomerAge()); + customerRegistDTO.setPhone(contractRegistRequestDTO.getCustomerPhone()); + customerRegistDTO.setEmail(contractRegistRequestDTO.getCustomerEmail()); + customerRegistDTO.setMemberId(memberId); + + customerCommandService.registerCustomerInfo(customerRegistDTO); + + // 등록 후 고객 정보 재조회 + customerDTO = customerQueryService.selectCustomerInfoByPhone(contractRegistRequestDTO.getCustomerPhone()); + } + return customerDTO.getCustomerId(); + } + @Override @Transactional public void registerContract(ContractRegistDTO contractRegistRequestDTO) throws GeneralSecurityException { - if(contractRegistRequestDTO.getRoles().stream() - .anyMatch(role -> "ROLE_EMPLOYEE".equals(role.getAuthority()))){ + // 영업사원 번호 + String memberId = authQueryService.selectMemberIdByLoginId(contractRegistRequestDTO.getMemberId()); - // 영업사원 번호 - String memberId = authQueryService.selectMemberIdByLoginId(contractRegistRequestDTO.getMemberId()); + // 일련번호로 제품테이블의 총식별번호 찾아서 제품 가져오기 + ProductSelectIdDTO productSelectIdDTO = productService.selectByProductSerialNumber(contractRegistRequestDTO.getSerialNum()); + String productId = productSelectIdDTO.getId(); - // 일련번호로 제품테이블의 총식별번호 찾아서 제품 가져오기 -// ProductSelectIdDTO productSelectIdDTO = productService.selectByProductSerialNumber(contractRegistRequestDTO.getSerialNum()); -// -// System.out.println("제품!!!!!!!!!!: " + productSelectIdDTO); -// String productId = productSelectIdDTO.getId(); - - // 판매내역 업로드 + // 판매내역 업로드 - // 제품 재고 수 줄이기 + // 제품 재고 수 줄이기 - // 고객전화번호로 고객테이블 찾아서 고객이 있으면 넘어가고, 고객이 없으면 고객테이블에 고객 정보 넣기 + // 고객전화번호로 고객테이블 찾아서 고객이 있으면 넘어가고, 고객이 없으면 고객테이블에 고객 정보 넣기 + String customerId = handleCustomerInfo(contractRegistRequestDTO, memberId); - // 회원의 영업 매장번호 -// MemberDTO memberDTO = memberQueryService.selectMemberInfo(contractRegistRequestDTO.getMemberId()); -// String centerId = memberDTO.getCenterId(); + // 회원의 영업 매장번호 + MemberDTO memberDTO = memberQueryService.selectMemberInfo(contractRegistRequestDTO.getMemberId()); + String centerId = memberDTO.getCenterId(); - Contract contract = modelMapper.map(contractRegistRequestDTO, Contract.class); + Contract contract = modelMapper.map(contractRegistRequestDTO, Contract.class); - contract.setMemberId(memberId); - contract.setCenterId("CEN_000000001"); // 회원의 매장번호 넣기 - contract.setProductId("PRO_000000001"); // 제품 번호 넣기 - contract.setCustomerId("CUS_000000001"); + contract.setMemberId(memberId); + contract.setCenterId(centerId); // 회원의 매장번호 넣기 + contract.setProductId(productId); // 제품 번호 넣기 + contract.setCustomerId(customerId); - contractRepository.save(contract); - } else { - throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); - } + contractRepository.save(contract); } @Override @Transactional public ContractModifyDTO modifyContract(ContractModifyDTO contractModifyRequestDTO) { - if(contractModifyRequestDTO.getRoles().stream() - .anyMatch(role -> "ROLE_EMPLOYEE".equals(role.getAuthority()))) { + String memberId = authQueryService.selectMemberIdByLoginId(contractModifyRequestDTO.getMemberId()); - String memberId = authQueryService.selectMemberIdByLoginId(contractModifyRequestDTO.getMemberId()); - - // 일련번호로 제품테이블의 총식별번호 찾아서 제품 가져오기 + // 일련번호로 제품테이블의 총식별번호 찾아서 제품 가져오기 // ProductSelectIdDTO productSelectIdDTO = productService.selectByProductSerialNumber(contractModifyRequestDTO.getSerialNum()); // String productId = productSelectIdDTO.getId(); - contractModifyRequestDTO.setProductId("PRO_000000001"); - // 판매내역 수정 + // 판매내역 수정 - // 고객전화번호로 고객테이블 찾아서 가져오기 + // 고객전화번호로 고객테이블 찾아서 가져오기 - // 가져온 고객 정보에 수정된 값 넣기 + // 가져온 고객 정보에 수정된 값 넣기 - Contract contract = (Contract) contractRepository.findByContractIdAndMemberId(contractModifyRequestDTO.getContractId(), memberId) - .orElseThrow(() -> new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND)); + Contract contract = (Contract) contractRepository.findByContractIdAndMemberId(contractModifyRequestDTO.getContractId(), memberId) + .orElseThrow(() -> new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND)); - Contract updateContract = modelMapper.map(contractModifyRequestDTO, Contract.class); - updateContract.setCreatedAt(contract.getCreatedAt()); - updateContract.setUpdatedAt(contract.getUpdatedAt()); - updateContract.setActive(contract.isActive()); - updateContract.setCenterId(contract.getCenterId()); - updateContract.setCreatedUrl(contract.getCreatedUrl()); + Contract updateContract = modelMapper.map(contractModifyRequestDTO, Contract.class); + updateContract.setCreatedAt(contract.getCreatedAt()); + updateContract.setUpdatedAt(contract.getUpdatedAt()); + updateContract.setActive(contract.isActive()); + updateContract.setCenterId(contract.getCenterId()); + updateContract.setCreatedUrl(contract.getCreatedUrl()); - contractRepository.save(updateContract); - - ContractModifyDTO contractModifyDTO = modelMapper.map(updateContract, ContractModifyDTO.class); - - return contractModifyDTO; - } else { - throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); - } + contractRepository.save(updateContract); + ContractModifyDTO contractModifyDTO = modelMapper.map(updateContract, ContractModifyDTO.class); + return contractModifyDTO; } @Override @Transactional public void deleteContract(ContractDeleteDTO contractDeleteDTO) { - if(contractDeleteDTO.getRoles().stream() - .anyMatch(role -> "ROLE_EMPLOYEE".equals(role.getAuthority()))) { - - String memberId = authQueryService.selectMemberIdByLoginId(contractDeleteDTO.getMemberId()); - - Contract contract = (Contract) contractRepository.findByContractIdAndMemberId(contractDeleteDTO.getContractId(), memberId) - .orElseThrow(() -> new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND)); - - contract.setActive(false); - contract.setDeletedAt(getCurrentTime()); + String memberId = authQueryService.selectMemberIdByLoginId(contractDeleteDTO.getMemberId()); - // 고객정보도 false + Contract contract = (Contract) contractRepository.findByContractIdAndMemberId(contractDeleteDTO.getContractId(), memberId) + .orElseThrow(() -> new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND)); - // 판매내역도 false 해서 -1 + contract.setActive(false); + contract.setDeletedAt(getCurrentTime()); - contractRepository.save(contract); + // 판매내역도 false 해서 -1 - } else { - throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); - } + contractRepository.save(contract); } @Override @Transactional public void modifyContractStatus(ContractStatusModifyDTO contractStatusModifyDTO) { - // 역할 확인 - if (contractStatusModifyDTO.getRoles().stream() - .anyMatch(role -> "ROLE_MANAGER".equals(role) || "ROLE_REPRESENTATIVE".equals(role))) { - - // 관리자 ID 조회 - String adminId = authQueryService.selectMemberIdByLoginId(contractStatusModifyDTO.getAdminId()); - // 계약 조회 및 수정 - Contract contract = contractRepository.findByContractId(contractStatusModifyDTO.getContractId()); + // 관리자 ID 조회 + String adminId = authQueryService.selectMemberIdByLoginId(contractStatusModifyDTO.getAdminId()); - if (contract != null) { - contract.setStatus(contractStatusModifyDTO.getStatus()); - contract.setAdminId(adminId); + // 계약 조회 및 수정 + Contract contract = contractRepository.findByContractId(contractStatusModifyDTO.getContractId()); - contractRepository.save(contract); - } - } else { - throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + if (contract == null) { + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); } - } - + contract.setStatus(contractStatusModifyDTO.getStatus()); + contract.setAdminId(adminId); + contractRepository.save(contract); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java index 89056236..38d19932 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java @@ -15,7 +15,9 @@ public class ContractSeletIdDTO { private String contractId; private String title; private String customerName; + private String customerSex; private String customerIdentifiNo; + private Integer customerAge; private String customerAddrress; private String customerEmail; private String customerPhone; diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/aggregate/entity/Customer.java b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/aggregate/entity/Customer.java index cc32dbba..12d0052f 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/aggregate/entity/Customer.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/aggregate/entity/Customer.java @@ -40,7 +40,7 @@ public class Customer { @Column(name = "CUST_PHO", nullable = false) private String phone; - @Column(name = "CUST_EMER_PHO", nullable = false) + @Column(name = "CUST_EMER_PHO") private String emergePhone; @Column(name = "CUST_EMA", nullable = false) diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java b/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java index 7816ee9d..6b20b5f7 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java @@ -19,4 +19,5 @@ public interface CustomerMapper { int findCustomerCnt(Map map); + CustomerDTO selectCustomerInfoByPhone(String customerPhone); } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java index 14e14efe..ddc4e1e2 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java @@ -13,4 +13,6 @@ public interface CustomerQueryService { Page selectCustomerList(Pageable pageable) throws GeneralSecurityException; Page findCustomerByCondition(Pageable pageable, CustomerSearchDTO customerSearchDTO) throws GeneralSecurityException; + + CustomerDTO selectCustomerInfoByPhone(String customerPhone) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java index 74308c9a..9ede45f7 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java @@ -7,6 +7,8 @@ import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.customer.common.exception.CustomerCommonException; +import stanl_2.final_backend.domain.customer.common.exception.CustomerErrorCode; import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; import stanl_2.final_backend.domain.customer.query.dto.CustomerSearchDTO; import stanl_2.final_backend.domain.customer.query.repository.CustomerMapper; @@ -90,4 +92,17 @@ public Page findCustomerByCondition(Pageable pageable, CustomerSear return new PageImpl<>(customerList, pageable, count); } + + @Override + @Transactional(readOnly = true) + public CustomerDTO selectCustomerInfoByPhone(String customerPhone) throws GeneralSecurityException { + + CustomerDTO customerInfoDTO = customerMapper.selectCustomerInfoByPhone(customerPhone); + + customerInfoDTO.setPhone(aesUtils.decrypt(customerInfoDTO.getPhone())); + customerInfoDTO.setEmergePhone(aesUtils.decrypt(customerInfoDTO.getEmergePhone())); + customerInfoDTO.setEmail(aesUtils.decrypt(customerInfoDTO.getEmail())); + + return customerInfoDTO; + } } diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index 2a44829e..e0d1e918 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -8,7 +8,9 @@ + + @@ -43,6 +45,7 @@ + @@ -82,7 +85,9 @@ a.conr_id, a.conr_ttl, a.conr_cust_name, + a.conr_cust_sex, a.conr_cust_iden_no, + a.conr_cust_age, a.conr_cust_adr, a.conr_cust_ema, a.conr_cust_pho, @@ -120,7 +125,9 @@ a.conr_id, a.conr_ttl, a.conr_cust_name, + a.conr_cust_sex, a.conr_cust_iden_no, + a.conr_cust_age, a.conr_cust_adr, a.conr_cust_ema, a.conr_cust_pho, diff --git a/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml b/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml index 3592a2eb..33bf82ca 100644 --- a/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml @@ -110,4 +110,22 @@
+ + \ No newline at end of file From 24f68a6eb7f236303f854c917e1e80c93e40e302 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Wed, 20 Nov 2024 17:55:25 +0900 Subject: [PATCH 238/563] =?UTF-8?q?refactor:=20=EA=B3=84=EC=95=BD=EC=84=9C?= =?UTF-8?q?=20=EB=93=B1=EB=A1=9D=20=EC=88=98=EC=A0=95=EC=A4=91(#112)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ContractController.java | 1 - .../service/ContractCommandService.java | 4 +- .../service/ContractCommandServiceImpl.java | 35 +++++++++--- .../service/CustomerQueryServiceImpl.java | 4 ++ .../service/ProductCommandService.java | 7 +++ .../domain/repository/ProductRepository.java | 9 ++++ .../service/ProductCommandServiceImpl.java | 28 ++++++++++ .../exception/ProductCommonException.java | 16 ++++++ .../common/exception/ProductErrorCode.java | 54 +++++++++++++++++++ .../exception/ProductExceptionResponse.java | 22 ++++++++ src/main/resources/application.yml | 2 +- 11 files changed, 169 insertions(+), 13 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/product/command/application/command/service/ProductCommandService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/product/command/application/domain/repository/ProductRepository.java create mode 100644 src/main/java/stanl_2/final_backend/domain/product/command/application/domain/service/ProductCommandServiceImpl.java create mode 100644 src/main/java/stanl_2/final_backend/domain/product/common/exception/ProductCommonException.java create mode 100644 src/main/java/stanl_2/final_backend/domain/product/common/exception/ProductErrorCode.java create mode 100644 src/main/java/stanl_2/final_backend/domain/product/common/exception/ProductExceptionResponse.java diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java index 9aaaddb7..9bf81a2a 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java @@ -7,7 +7,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; -import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.contract.command.application.dto.ContractDeleteDTO; import stanl_2.final_backend.domain.contract.command.application.dto.ContractModifyDTO; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java index e976b005..5e050d58 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java @@ -5,10 +5,8 @@ import stanl_2.final_backend.domain.contract.command.application.dto.ContractRegistDTO; import stanl_2.final_backend.domain.contract.command.application.dto.ContractStatusModifyDTO; -import java.security.GeneralSecurityException; - public interface ContractCommandService { - void registerContract(ContractRegistDTO contractRegistRequestDTO) throws GeneralSecurityException; + void registerContract(ContractRegistDTO contractRegistRequestDTO); ContractModifyDTO modifyContract(ContractModifyDTO contractModifyRequestDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index f9cdc8c4..abb6f172 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -19,8 +19,10 @@ import stanl_2.final_backend.domain.member.query.dto.MemberDTO; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.member.query.service.MemberQueryService; +import stanl_2.final_backend.domain.product.command.application.command.service.ProductCommandService; import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; import stanl_2.final_backend.domain.product.query.service.ProductService; +import stanl_2.final_backend.domain.sales_history.command.application.service.SalesHistoryCommandService; import stanl_2.final_backend.global.exception.GlobalCommonException; import stanl_2.final_backend.global.exception.GlobalErrorCode; @@ -38,15 +40,19 @@ public class ContractCommandServiceImpl implements ContractCommandService { private final MemberQueryService memberQueryService; private final CustomerCommandService customerCommandService; private final ProductService productService; + private final ProductCommandService productCommandService; + private final SalesHistoryCommandService salesHistoryCommandService; private final ModelMapper modelMapper; - public ContractCommandServiceImpl(ContractRepository contractRepository, AuthQueryService authQueryService, CustomerQueryService customerQueryService, MemberQueryService memberQueryService, CustomerCommandService customerCommandService, ProductService productService, ModelMapper modelMapper) { + public ContractCommandServiceImpl(ContractRepository contractRepository, AuthQueryService authQueryService, CustomerQueryService customerQueryService, MemberQueryService memberQueryService, CustomerCommandService customerCommandService, ProductService productService, ProductCommandService productCommandService, SalesHistoryCommandService salesHistoryCommandService, ModelMapper modelMapper) { this.contractRepository = contractRepository; this.authQueryService = authQueryService; this.customerQueryService = customerQueryService; this.memberQueryService = memberQueryService; this.customerCommandService = customerCommandService; this.productService = productService; + this.productCommandService = productCommandService; + this.salesHistoryCommandService = salesHistoryCommandService; this.modelMapper = modelMapper; } @@ -78,7 +84,7 @@ private String handleCustomerInfo(ContractRegistDTO contractRegistRequestDTO, St @Override @Transactional - public void registerContract(ContractRegistDTO contractRegistRequestDTO) throws GeneralSecurityException { + public void registerContract(ContractRegistDTO contractRegistRequestDTO) { // 영업사원 번호 String memberId = authQueryService.selectMemberIdByLoginId(contractRegistRequestDTO.getMemberId()); @@ -87,16 +93,23 @@ public void registerContract(ContractRegistDTO contractRegistRequestDTO) throws ProductSelectIdDTO productSelectIdDTO = productService.selectByProductSerialNumber(contractRegistRequestDTO.getSerialNum()); String productId = productSelectIdDTO.getId(); - // 판매내역 업로드 - - // 제품 재고 수 줄이기 + String customerId = null; // 고객 ID 변수 선언 + String centerId = null; // 매장 ID 변수 선언 // 고객전화번호로 고객테이블 찾아서 고객이 있으면 넘어가고, 고객이 없으면 고객테이블에 고객 정보 넣기 - String customerId = handleCustomerInfo(contractRegistRequestDTO, memberId); + try { + customerId = handleCustomerInfo(contractRegistRequestDTO, memberId); + } catch (GeneralSecurityException e) { + throw new RuntimeException("고객 정보 처리 중 문제가 발생했습니다.", e); + } // 회원의 영업 매장번호 - MemberDTO memberDTO = memberQueryService.selectMemberInfo(contractRegistRequestDTO.getMemberId()); - String centerId = memberDTO.getCenterId(); + try { + MemberDTO memberDTO = memberQueryService.selectMemberInfo(contractRegistRequestDTO.getMemberId()); + centerId = memberDTO.getCenterId(); + } catch (Exception e) { + throw new RuntimeException("회원 정보 조회 중 문제가 발생했습니다.", e); + } Contract contract = modelMapper.map(contractRegistRequestDTO, Contract.class); @@ -175,5 +188,11 @@ public void modifyContractStatus(ContractStatusModifyDTO contractStatusModifyDTO contract.setAdminId(adminId); contractRepository.save(contract); + +// ProductSelectIdDTO productSelectIdDTO = productService.selectByProductSerialNumber(contractRegistRequestDTO.getSerialNum()); +// String productId = productSelectIdDTO.getId(); +// +// // 제품 재고 수 줄이기 +// productCommandService.modifyProductStock(productId); } } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java index 9ede45f7..86521df3 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java @@ -99,6 +99,10 @@ public CustomerDTO selectCustomerInfoByPhone(String customerPhone) throws Genera CustomerDTO customerInfoDTO = customerMapper.selectCustomerInfoByPhone(customerPhone); + if (customerInfoDTO == null) { + return customerInfoDTO; + } + customerInfoDTO.setPhone(aesUtils.decrypt(customerInfoDTO.getPhone())); customerInfoDTO.setEmergePhone(aesUtils.decrypt(customerInfoDTO.getEmergePhone())); customerInfoDTO.setEmail(aesUtils.decrypt(customerInfoDTO.getEmail())); diff --git a/src/main/java/stanl_2/final_backend/domain/product/command/application/command/service/ProductCommandService.java b/src/main/java/stanl_2/final_backend/domain/product/command/application/command/service/ProductCommandService.java new file mode 100644 index 00000000..b08a5329 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/product/command/application/command/service/ProductCommandService.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.product.command.application.command.service; + +import stanl_2.final_backend.domain.contract.command.application.dto.ContractDeleteDTO; + +public interface ProductCommandService { + void modifyProductStock(String productId); +} diff --git a/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/repository/ProductRepository.java b/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/repository/ProductRepository.java new file mode 100644 index 00000000..972507f7 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/repository/ProductRepository.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.product.command.application.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import stanl_2.final_backend.domain.product.command.application.domain.aggregate.entity.Product; + +@Repository +public interface ProductRepository extends JpaRepository { +} diff --git a/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/service/ProductCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/service/ProductCommandServiceImpl.java new file mode 100644 index 00000000..a02fd6bb --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/service/ProductCommandServiceImpl.java @@ -0,0 +1,28 @@ +package stanl_2.final_backend.domain.product.command.application.domain.service; + +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.product.command.application.command.service.ProductCommandService; +import stanl_2.final_backend.domain.product.command.application.domain.aggregate.entity.Product; +import stanl_2.final_backend.domain.product.command.application.domain.repository.ProductRepository; +import stanl_2.final_backend.domain.product.common.exception.ProductCommonException; +import stanl_2.final_backend.domain.product.common.exception.ProductErrorCode; + +@Service +public class ProductCommandServiceImpl implements ProductCommandService { + + private final ProductRepository productRepository; + + public ProductCommandServiceImpl(ProductRepository productRepository) { + this.productRepository = productRepository; + } + + @Override + public void modifyProductStock(String productId) { + Product product = productRepository.findById(productId) + .orElseThrow(() -> new ProductCommonException(ProductErrorCode.PRODUCT_NOT_FOUND)); + + product.setStock(product.getStock() - 1); + + productRepository.save(product); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/product/common/exception/ProductCommonException.java b/src/main/java/stanl_2/final_backend/domain/product/common/exception/ProductCommonException.java new file mode 100644 index 00000000..d1d51f8d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/product/common/exception/ProductCommonException.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.product.common.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class ProductCommonException extends RuntimeException { + private final ProductErrorCode errorCode; + + // 에러 발생시 ErroCode 별 메시지 + @Override + public String getMessage() { + return this.errorCode.getMsg(); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/product/common/exception/ProductErrorCode.java b/src/main/java/stanl_2/final_backend/domain/product/common/exception/ProductErrorCode.java new file mode 100644 index 00000000..35e41847 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/product/common/exception/ProductErrorCode.java @@ -0,0 +1,54 @@ +package stanl_2.final_backend.domain.product.common.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum ProductErrorCode { + + /** + * 400(Bad Request) + * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. + */ + + + + /** + * 401(Unauthorized) + * 비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만, + * 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다. + * 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다. + */ + + + /** + * 403(Forbidden) + * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. + * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다. + */ + + + + /** + * 404(Not Found) + * 서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다. + * 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있습니다. + * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. + * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. + */ + PRODUCT_NOT_FOUND(40401, HttpStatus.NOT_FOUND, "제품을 찾을 수 없습니다."), + + + /** + * 500(Internal Server Error) + * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. + */ + INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); + + + private final Integer code; + private final HttpStatus httpStatus; + private final String msg; +} diff --git a/src/main/java/stanl_2/final_backend/domain/product/common/exception/ProductExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/product/common/exception/ProductExceptionResponse.java new file mode 100644 index 00000000..a73fac08 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/product/common/exception/ProductExceptionResponse.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.product.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class ProductExceptionResponse { + private final Integer code; + private final String msg; + private final HttpStatus httpStatus; + + public ProductExceptionResponse(ProductErrorCode errorCode) { + this.code = errorCode.getCode(); + this.msg = errorCode.getMsg(); + this.httpStatus = errorCode.getHttpStatus(); + } + + public static ProductExceptionResponse of(ProductErrorCode errorCode) { + return new ProductExceptionResponse(errorCode); + } + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 6618043e..eee7170c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -23,7 +23,7 @@ spring: jpa: show-sql: true hibernate: - ddl-auto: create + ddl-auto: update properties: hibernate.format_sql: true From 268751133c03032540651838e03b867403c975a1 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Wed, 20 Nov 2024 18:02:40 +0900 Subject: [PATCH 239/563] =?UTF-8?q?feat:=20=EC=9D=BC=EC=A0=95=20=EC=B9=B4?= =?UTF-8?q?=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EC=A1=B0=ED=9A=8C=20=EC=99=84?= =?UTF-8?q?=EC=84=B1=20=EB=B0=8F=20=EC=9D=BC=EC=A0=95=20=EC=83=81=EC=84=B8?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84=20=EC=A4=91=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AlarmController.java | 30 +++++++-- ...armCommandDTO.java => AlarmRegistDTO.java} | 2 +- .../service/AlarmCommandService.java | 6 +- .../domain/aggregate/entity/Alarm.java | 3 - .../domain/repository/AlarmRepository.java | 1 + .../service/AlarmCommandServiceImpl.java | 36 ++++++++-- .../common/exception/AlarmErrorCode.java | 1 + .../query/controller/AlarmController.java | 49 ++++++++++---- .../query/dto/AlarmSelectAllDetailDTO.java | 17 +++++ ...ueryDTO.java => AlarmSelectDetailDTO.java} | 8 +-- .../alarm/query/dto/AlarmSelectTypeDTO.java | 15 +++++ .../domain/alarm/query/dto/CursorDTO.java | 20 ------ .../alarm/query/repository/AlarmMapper.java | 21 +++--- .../query/service/AlarmQueryService.java | 11 ++- .../query/service/AlarmQueryServiceImpl.java | 67 ++++++++++--------- .../alarm/scheduler/AlarmScheduler.java | 2 +- .../alarm/query/repository/AlarmMapper.xml | 62 ++++++++--------- 17 files changed, 224 insertions(+), 127 deletions(-) rename src/main/java/stanl_2/final_backend/domain/alarm/command/application/dto/{AlarmCommandDTO.java => AlarmRegistDTO.java} (89%) create mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectAllDetailDTO.java rename src/main/java/stanl_2/final_backend/domain/alarm/query/dto/{AlarmQueryDTO.java => AlarmSelectDetailDTO.java} (69%) create mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectTypeDTO.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/query/dto/CursorDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/application/controller/AlarmController.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/controller/AlarmController.java index 41002959..8d3b75af 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/application/controller/AlarmController.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/controller/AlarmController.java @@ -11,8 +11,10 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import stanl_2.final_backend.domain.alarm.command.application.dto.AlarmCommandDTO; +import stanl_2.final_backend.domain.alarm.command.application.dto.AlarmRegistDTO; import stanl_2.final_backend.domain.alarm.command.application.service.AlarmCommandService; +import stanl_2.final_backend.domain.alarm.common.response.AlarmResponseMessage; +import stanl_2.final_backend.domain.center.common.response.ResponseMessage; import stanl_2.final_backend.domain.schedule.common.response.ScheduleResponseMessage; import java.security.Principal; @@ -41,10 +43,28 @@ public ResponseEntity subscribe(Principal principal, String memberLoginId = principal.getName(); - AlarmCommandDTO alarmCommandDTO = new AlarmCommandDTO(); - alarmCommandDTO.setMemberLoginId(memberLoginId); - alarmCommandDTO.setLastEventId(lastEventId); + AlarmRegistDTO alarmRegistDTO = new AlarmRegistDTO(); + alarmRegistDTO.setMemberLoginId(memberLoginId); + alarmRegistDTO.setLastEventId(lastEventId); - return ResponseEntity.ok(alarmCommandService.subscribe(alarmCommandDTO, response)); + return ResponseEntity.ok(alarmCommandService.subscribe(alarmRegistDTO, response)); + } + + + @Operation(summary = "회원 알림 읽음 처리") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "회원 알림 읽음 처리 완료", + content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) + }) + @PutMapping("{alarmId}") + public ResponseEntity updateReadStatus(@PathVariable String alarmId){ + + Boolean answer = alarmCommandService.updateReadStatus(alarmId); + + return ResponseEntity.ok(AlarmResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(answer) + .build()); } } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/application/dto/AlarmCommandDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/dto/AlarmRegistDTO.java similarity index 89% rename from src/main/java/stanl_2/final_backend/domain/alarm/command/application/dto/AlarmCommandDTO.java rename to src/main/java/stanl_2/final_backend/domain/alarm/command/application/dto/AlarmRegistDTO.java index c60a4461..947ea30b 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/application/dto/AlarmCommandDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/dto/AlarmRegistDTO.java @@ -7,7 +7,7 @@ @Getter @Setter @ToString -public class AlarmCommandDTO { +public class AlarmRegistDTO { private String memberId; private String memberLoginId; diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/application/service/AlarmCommandService.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/service/AlarmCommandService.java index 1fa36586..d74db3e9 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/application/service/AlarmCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/service/AlarmCommandService.java @@ -2,12 +2,12 @@ import jakarta.servlet.http.HttpServletResponse; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import stanl_2.final_backend.domain.alarm.command.application.dto.AlarmCommandDTO; +import stanl_2.final_backend.domain.alarm.command.application.dto.AlarmRegistDTO; import stanl_2.final_backend.domain.alarm.command.domain.aggregate.entity.Alarm; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeAlarmDTO; public interface AlarmCommandService { - SseEmitter subscribe(AlarmCommandDTO alarmCommandDTO, HttpServletResponse response); + SseEmitter subscribe(AlarmRegistDTO alarmRegistDTO, HttpServletResponse response); void sendToClient(SseEmitter emitter, String emitterId, Object data); @@ -16,4 +16,6 @@ public interface AlarmCommandService { Alarm createAlarm(String memberId, String message, String redirectUrl, String type, String createdAt); void sendNoticeAlarm(NoticeAlarmDTO noticeAlarmDTO); + + Boolean updateReadStatus(String alarmId); } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/aggregate/entity/Alarm.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/aggregate/entity/Alarm.java index d1d0727e..805a7484 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/aggregate/entity/Alarm.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/aggregate/entity/Alarm.java @@ -41,9 +41,6 @@ public class Alarm { @Column(name = "CREATED_AT", nullable = false) private String createdAt; -// @Column(name = "ALR_STAT", nullable = false) -// private String status = "PENDING"; - @Column(name = "MEM_ID", nullable = false) private String memberId; diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/repository/AlarmRepository.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/repository/AlarmRepository.java index 2b7d66f2..5421ce10 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/repository/AlarmRepository.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/repository/AlarmRepository.java @@ -6,4 +6,5 @@ @Repository public interface AlarmRepository extends JpaRepository { + Alarm findByAlarmId(String alarmId); } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java index b35053c7..5e91179e 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java @@ -3,17 +3,22 @@ import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import stanl_2.final_backend.domain.alarm.command.application.dto.AlarmCommandDTO; +import stanl_2.final_backend.domain.alarm.command.application.dto.AlarmRegistDTO; import stanl_2.final_backend.domain.alarm.command.domain.aggregate.entity.Alarm; import stanl_2.final_backend.domain.alarm.command.application.service.AlarmCommandService; import stanl_2.final_backend.domain.alarm.command.domain.repository.AlarmRepository; import stanl_2.final_backend.domain.alarm.command.domain.repository.EmitterRepository; +import stanl_2.final_backend.domain.alarm.common.exception.AlarmCommonException; +import stanl_2.final_backend.domain.alarm.common.exception.AlarmErrorCode; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeAlarmDTO; +import stanl_2.final_backend.domain.schedule.common.exception.ScheduleCommonException; +import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; import java.io.IOException; import java.time.ZoneId; @@ -48,10 +53,10 @@ public AlarmCommandServiceImpl(AlarmRepository alarmRepository, EmitterRepositor } @Override - public SseEmitter subscribe(AlarmCommandDTO alarmCommandDTO, HttpServletResponse response) { + public SseEmitter subscribe(AlarmRegistDTO alarmRegistDTO, HttpServletResponse response) { - String lastEventId = alarmCommandDTO.getLastEventId(); - String memberId = authQueryService.selectMemberIdByLoginId(alarmCommandDTO.getMemberLoginId()); + String lastEventId = alarmRegistDTO.getLastEventId(); + String memberId = authQueryService.selectMemberIdByLoginId(alarmRegistDTO.getMemberLoginId()); String emitterId = memberId + "_" + System.currentTimeMillis(); // 클라이언트의 sse 연결 요청에 응답하기 위한 SseEmitter 객체 생성 @@ -138,4 +143,27 @@ public void sendNoticeAlarm(NoticeAlarmDTO noticeAlarmDTO){ }); } + @Override + @Transactional + public Boolean updateReadStatus(String alarmId) { + + Alarm alarm = alarmRepository.findByAlarmId(alarmId); + + if(alarm == null){ + throw new AlarmCommonException(AlarmErrorCode.ALARM_NOT_FOUND); + } + + try { + alarm.setReadStatus(true); + + alarmRepository.save(alarm); + return true; + } catch (DataIntegrityViolationException e) { + // 데이터 무결성 위반 예외 처리 + throw new AlarmCommonException(AlarmErrorCode.DATA_INTEGRITY_VIOLATION); + } catch (Exception e) { + // 서버 오류 + throw new AlarmCommonException(AlarmErrorCode.INTERNAL_SERVER_ERROR); + } + } } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmErrorCode.java b/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmErrorCode.java index 6453c9f1..56f8b51a 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmErrorCode.java @@ -43,6 +43,7 @@ public enum AlarmErrorCode { * 500(Internal Server Error) * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. */ + DATA_INTEGRITY_VIOLATION(40001, HttpStatus.BAD_REQUEST, "데이터 무결 위반하였습니다."), INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/controller/AlarmController.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/controller/AlarmController.java index 37a9b9fa..528cb680 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/controller/AlarmController.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/controller/AlarmController.java @@ -6,17 +6,23 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; 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.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import stanl_2.final_backend.domain.alarm.common.response.AlarmResponseMessage; -import stanl_2.final_backend.domain.alarm.query.dto.CursorDTO; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectAllDetailDTO; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectDetailDTO; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectTypeDTO; import stanl_2.final_backend.domain.alarm.query.service.AlarmQueryService; import stanl_2.final_backend.domain.schedule.common.response.ScheduleResponseMessage; import java.security.Principal; +import java.util.List; @RestController("queryAlarmController") @RequestMapping("/api/v1/alarm") @@ -29,28 +35,47 @@ public AlarmController(AlarmQueryService alarmQueryService) { this.alarmQueryService = alarmQueryService; } - @Operation(summary = "고객 알림창 조회") + @Operation(summary = "회원 알림창 전체 조회") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "고객 알림창 조회 성공", + @ApiResponse(responseCode = "200", description = "회원 알림창 전체 조회 완료", content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) }) @GetMapping("") - public ResponseEntity readMemberAlarms(Principal principal, - @RequestParam(value = "cursor", required = false) Long cursorId){ + public ResponseEntity selectMemberAlarmType(Principal principal){ String memberLoginId = principal.getName(); - Integer size = 3; - CursorDTO cursorDTO = new CursorDTO(); - cursorDTO.setCursorId(cursorId); - cursorDTO.setSize(size); + AlarmSelectTypeDTO AlarmSelectTypeDTO = alarmQueryService.selectMemberByAlarmType(memberLoginId); - CursorDTO alarmResponseDTO = alarmQueryService.readMemberAlarms(cursorDTO, memberLoginId); + return ResponseEntity.ok(AlarmResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(AlarmSelectTypeDTO) + .build()); + } + + @Operation(summary = "회원 알림 상세 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "회원 알림 상세 조회 완료", + content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) + }) + @GetMapping("/detail/{type}") + public ResponseEntity selectDetailAlarm(Principal principal, + @PathVariable String type, + @PageableDefault(size = 8) Pageable pageable){ + + String memberLoginId = principal.getName(); + AlarmSelectDetailDTO alarmSelectDetailDTO = new AlarmSelectDetailDTO(); + alarmSelectDetailDTO.setMemberLoginId(memberLoginId); + alarmSelectDetailDTO.setType(type); + + Page alarmSelectAllDetailDTO + = alarmQueryService.selectDetailAlarmByType(alarmSelectDetailDTO , pageable); return ResponseEntity.ok(AlarmResponseMessage.builder() .httpStatus(200) .msg("성공") - .result(alarmResponseDTO) + .result(alarmSelectAllDetailDTO) .build()); } } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectAllDetailDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectAllDetailDTO.java new file mode 100644 index 00000000..75439cb8 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectAllDetailDTO.java @@ -0,0 +1,17 @@ +package stanl_2.final_backend.domain.alarm.query.dto; + +import lombok.*; + +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString +public class AlarmSelectAllDetailDTO { + + List readAlarmList; + List notReadAlarmList; + +} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmQueryDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDetailDTO.java similarity index 69% rename from src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmQueryDTO.java rename to src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDetailDTO.java index d1be294b..6ca2d602 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmQueryDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDetailDTO.java @@ -7,14 +7,12 @@ @Getter @Setter @ToString -public class AlarmQueryDTO { +public class AlarmSelectDetailDTO { - private String alarmId; private String message; - private String redirectUrl; private String type; + private String redirectUrl; private Boolean readStatus; - private String createAt; - private String memberId; + private String memberLoginId; } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectTypeDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectTypeDTO.java new file mode 100644 index 00000000..0baeed6d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectTypeDTO.java @@ -0,0 +1,15 @@ +package stanl_2.final_backend.domain.alarm.query.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString +public class AlarmSelectTypeDTO { + + private Integer scheduleAlarmCount; + private Integer noticeAlarmCount; + private Integer contractAlarmCount; +} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/CursorDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/CursorDTO.java deleted file mode 100644 index bef0f042..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/CursorDTO.java +++ /dev/null @@ -1,20 +0,0 @@ -package stanl_2.final_backend.domain.alarm.query.dto; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -import java.util.List; - -@Getter -@Setter -@AllArgsConstructor -@NoArgsConstructor -public class CursorDTO { - - private Long cursorId; - private List comment; - private Boolean hasNext; - private Integer size; -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.java index e358df16..683707a4 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.java @@ -2,20 +2,23 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; -import stanl_2.final_backend.domain.alarm.command.domain.aggregate.entity.Alarm; -import stanl_2.final_backend.domain.alarm.query.dto.AlarmQueryDTO; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectDetailDTO; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectTypeDTO; import java.util.List; +import java.util.Map; @Mapper public interface AlarmMapper { - List findAlarmsByMemberId(@Param("memberId") String memberId, - @Param("size") Integer size); + AlarmSelectTypeDTO findNumberOfAlarmsByType(String memberId); - List findAlarmsByMemberIdAndCursorId(@Param("memberId") String memberId, - @Param("cursorId") Long cursorId, - @Param("size") Integer size); + List findReadAlarmsByType(@Param("offset") Integer offset, + @Param("pageSize") Integer pageSize, + @Param("memberId") String memberId, + @Param("type") String type); + List findNotReadAlarmsByType(@Param("offset") Integer offset, + @Param("pageSize") Integer pageSize, + @Param("memberId") String memberId, + @Param("type") String type); } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryService.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryService.java index c5af7f57..e0350e9e 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryService.java @@ -1,7 +1,14 @@ package stanl_2.final_backend.domain.alarm.query.service; -import stanl_2.final_backend.domain.alarm.query.dto.CursorDTO; +import org.springframework.data.domain.Pageable; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectAllDetailDTO; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectDetailDTO; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectTypeDTO; + +import java.util.List; public interface AlarmQueryService { - CursorDTO readMemberAlarms(CursorDTO cursorDTO, String memberLoginId); + AlarmSelectTypeDTO selectMemberByAlarmType(String memberLoginId); + + AlarmSelectAllDetailDTO selectDetailAlarmByType(AlarmSelectDetailDTO alarmSelectDetailDTO, Pageable pageable); } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryServiceImpl.java index 7ec7e795..31112e66 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryServiceImpl.java @@ -1,16 +1,24 @@ package stanl_2.final_backend.domain.alarm.query.service; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; -import stanl_2.final_backend.domain.alarm.common.exception.AlarmCommonException; -import stanl_2.final_backend.domain.alarm.common.exception.AlarmErrorCode; -import stanl_2.final_backend.domain.alarm.query.dto.AlarmQueryDTO; -import stanl_2.final_backend.domain.alarm.query.dto.CursorDTO; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectAllDetailDTO; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectDetailDTO; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectTypeDTO; import stanl_2.final_backend.domain.alarm.query.repository.AlarmMapper; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.order.common.exception.OrderCommonException; +import stanl_2.final_backend.domain.order.common.exception.OrderErrorCode; +import java.util.HashMap; import java.util.List; +import java.util.Map; +@Slf4j @Service public class AlarmQueryServiceImpl implements AlarmQueryService{ @@ -24,42 +32,41 @@ public AlarmQueryServiceImpl(AlarmMapper alarmMapper, AuthQueryService authQuery } @Override - public CursorDTO readMemberAlarms(CursorDTO cursorDTO, String memberLoginId) { + public AlarmSelectTypeDTO selectMemberByAlarmType(String memberLoginId) { String memberId = authQueryService.selectMemberIdByLoginId(memberLoginId); - Integer fetchSize = cursorDTO.getSize(); - List alarmList; + AlarmSelectTypeDTO alarmSelectTypeDTO = alarmMapper.findNumberOfAlarmsByType(memberId); - if (cursorDTO.getCursorId() == null) { - alarmList = alarmMapper.findAlarmsByMemberId(memberId, fetchSize); - } else { - alarmList = alarmMapper.findAlarmsByMemberIdAndCursorId( - memberId, cursorDTO.getCursorId(), fetchSize - ); - } + return alarmSelectTypeDTO; + } - if (alarmList.isEmpty()) { - throw new AlarmCommonException(AlarmErrorCode.ALARM_NOT_FOUND); - } + @Override + public Page selectDetailAlarmByType(AlarmSelectDetailDTO alarmSelectDetailDTO, Pageable pageable) { - // 실제 데이터 수가 요청한 size보다 큰 경우 다음 페이지가 있다고 판단 - Boolean hasNext = alarmList.size() > cursorDTO.getSize(); + Integer offset = Math.toIntExact(pageable.getOffset()); + Integer pageSize = pageable.getPageSize(); - // 데이터가 size + 1개로 조회되었으므로 마지막 데이터 제거 - if (hasNext) { - alarmList.remove(alarmList.size() - 1); + String memberId = authQueryService.selectMemberIdByLoginId(alarmSelectDetailDTO.getMemberLoginId()); + + List readAlarmList + = alarmMapper.findReadAlarmsByType(offset, pageSize, memberId, alarmSelectDetailDTO.getType()); + + List notReadAlarmList + = alarmMapper.findNotReadAlarmsByType(offset, pageSize, memberId, alarmSelectDetailDTO.getType()); + + if (readAlarmList == null || readAlarmList.isEmpty()) { + throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); } - // 마지막 알림 번호 - Long lastCursorId = alarmList.isEmpty() ? null : - Long.parseLong(alarmList.get(alarmList.size() - 1).getAlarmId().replace("ALR_", "")); + if (notReadAlarmList == null || notReadAlarmList.isEmpty()) { + throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); + } - CursorDTO cursorResponseDTO = new CursorDTO(); - cursorResponseDTO.setCursorId(lastCursorId); - cursorResponseDTO.setHasNext(hasNext); - cursorResponseDTO.setComment(alarmList); + AlarmSelectAllDetailDTO alarmSelectAllDetailDTO = new AlarmSelectAllDetailDTO(); + alarmSelectAllDetailDTO.setReadAlarmList(readAlarmList); + alarmSelectAllDetailDTO.setNotReadAlarmList(notReadAlarmList); - return cursorResponseDTO; + return new PageImpl<>(alarmSelectAllDetailDTO, pageable, 1); } } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java index 1aa26d3e..1b7de03f 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java @@ -44,7 +44,7 @@ public void alarmTodaySchedule(){ String Minute = schedule.getStartAt().substring(14,16); String memberId = schedule.getMemberId(); - String type = schedule.getTag(); + String type = "SCHEDULE"; String message = "[" + type + "] 금일 " + Hour + "시 " + Minute + "분에 '" + schedule.getName() + "' 일정이 있습니다"; String redirectUrl = "/api/v1/schedule/" + schedule.getMemberId(); diff --git a/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml b/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml index e22b8e21..838839b2 100644 --- a/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml @@ -3,50 +3,46 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> - - - - - - - - + + + + - SELECT - a.alr_id, - a.alr_msg, - a.alr_url, - a.alr_type, - a.alr_read_stat, - a.mem_id - FROM tb_alarm a - WHERE mem_id = #{memberId} - ORDER BY CAST(SUBSTRING(ALR_ID, 5) AS UNSIGNED) DESC - LIMIT #{size} + SUM(CASE WHEN a.alr_type = 'SCHEDULE' THEN 1 ELSE 0 END) AS SCH_ALR_CNT, + SUM(CASE WHEN a.alr_type = 'NOTICE' THEN 1 ELSE 0 END) AS NOT_ALR_CNT, + SUM(CASE WHEN a.alr_type = 'CONTRACT' THEN 1 ELSE 0 END) AS CONR_ALR_CNT + FROM tb_alarm a + WHERE a.mem_id = #{memberId} - - + - + - - - SELECT - a.alr_id, a.alr_msg, - a.alr_url, a.alr_type, - a.alr_read_stat, - a.mem_id + a.alr_url, + a.alr_read_stat FROM tb_alarm a - WHERE mem_id = #{memberId} - AND CAST(SUBSTRING(ALR_ID, 5) AS UNSIGNED) > #{cursorId} - ORDER BY CAST(SUBSTRING(ALR_ID, 5) AS UNSIGNED) DESC - LIMIT #{size} + WHERE a.mem_id = #{memberId} + AND a.alr_type = #{type} + AND a.alr_read_stat = true + + \ No newline at end of file From c1185c489377de5f72e3aab2517d154c5f7aeee3 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 20 Nov 2024 18:10:26 +0900 Subject: [PATCH 240/563] =?UTF-8?q?=EB=AC=B8=EC=A0=9C=EC=82=AC=ED=95=AD=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EA=B0=9C=EB=B0=9C=EC=A4=91=20(#62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 6 +- .../query/config/MyBatisConfiguration.java | 9 ++ .../query/controller/ProblemController.java | 62 ++++++++++++ .../domain/problem/query/dto/ProblemDTO.java | 26 +++++ .../problem/query/dto/ProblemSearchDTO.java | 14 +++ .../query/repository/ProblemMapper.java | 21 +++++ .../problem/query/service/ProblemService.java | 13 +++ .../query/service/ProblemServiceImpl.java | 39 ++++++++ .../query/repository/ProblemMapper.xml | 94 ++++++++++++------- 9 files changed, 244 insertions(+), 40 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/problem/query/config/MyBatisConfiguration.java create mode 100644 src/main/java/stanl_2/final_backend/domain/problem/query/controller/ProblemController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemSearchDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.java create mode 100644 src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java diff --git a/build.gradle b/build.gradle index 818414e9..d3510cba 100644 --- a/build.gradle +++ b/build.gradle @@ -62,11 +62,7 @@ dependencies { //hibernate core implementation 'org.hibernate.orm:hibernate-core:6.5.2.Final' - // Logback and SLF4J Dependencies (Updated versions) -// implementation 'ch.qos.logback:logback-classic:1.4.11' // Updated Logback version -// implementation 'org.slf4j:slf4j-api:2.0.7' // Compatible SLF4J version for Logback 1.4.11 -// implementation 'ch.qos.logback:logback-classic:1.2.11' -// implementation 'ch.qos.logback:logback-core:1.2.11' + diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/config/MyBatisConfiguration.java b/src/main/java/stanl_2/final_backend/domain/problem/query/config/MyBatisConfiguration.java new file mode 100644 index 00000000..63c22b3d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/config/MyBatisConfiguration.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.problem.query.config; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Configuration; + +@Configuration("problemMybatisConfiguration") +@MapperScan(basePackages = "stanl_2.final_backend.domain.problem.query.repository") +public class MyBatisConfiguration { +} diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/controller/ProblemController.java b/src/main/java/stanl_2/final_backend/domain/problem/query/controller/ProblemController.java new file mode 100644 index 00000000..c227d1c7 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/controller/ProblemController.java @@ -0,0 +1,62 @@ +package stanl_2.final_backend.domain.problem.query.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.problem.query.service.ProblemService; +import stanl_2.final_backend.domain.promotion.common.response.PromotionResponseMessage; +import stanl_2.final_backend.domain.promotion.query.dto.PromotionDTO; +import stanl_2.final_backend.domain.promotion.query.dto.PromotionSearchDTO; + +@RestController("queryPromotionController") +@RequestMapping("/api/v1/problem") +public class ProblemController { + + private final ProblemService problemService; + + @Autowired + public ProblemController(ProblemService problemService) { + this.problemService = problemService; + } + + @Operation(summary = "문제사항 조건별 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = PromotionResponseMessage.class))}) + }) + @GetMapping + public ResponseEntity> getPromotions( + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "10") int size, + @RequestParam(required = false) String title, + @RequestParam(required = false) String memberId, + @RequestParam(required = false) String startDate, + @RequestParam(required = false) String endDate + ) + { + Pageable pageable = PageRequest.of(page, size); + PromotionSearchDTO promotionsearchDTO = new PromotionSearchDTO(title, memberId, startDate, endDate); + Page promotionDTOPage = promotionService.findPromotions(pageable,promotionsearchDTO); + + return ResponseEntity.ok(promotionDTOPage); + } + + @Operation(summary = "프로모션 Id로 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = PromotionResponseMessage.class))}) + }) + @GetMapping("{promotionId}") + public ResponseEntity getPromotion(@PathVariable String promotionId){ + PromotionDTO promotionDTO = promotionService.findPromotion(promotionId); + return ResponseEntity.ok(promotionDTO); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemDTO.java b/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemDTO.java new file mode 100644 index 00000000..a818b24a --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemDTO.java @@ -0,0 +1,26 @@ +package stanl_2.final_backend.domain.problem.query.dto; + +import jakarta.persistence.Column; + +public class ProblemDTO { + private String problemId; + + private String title; + + private String content; + + private String createdAt; + + private String updatedAt; + + private String deletedAt; + + private Boolean active; + + private String customerId; + + private String memberId; + + private String productId; + +} diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemSearchDTO.java new file mode 100644 index 00000000..a27391e7 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemSearchDTO.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.problem.query.dto; + +public class ProblemSearchDTO { + private String promotionId; + private String title; + private String memberId; + private String productId; + private String customerId; + private String createdAt; + private String updatedAt; + private String deletedAt; + private String startDate; + private String endDate; +} diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.java b/src/main/java/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.java new file mode 100644 index 00000000..aba48127 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.java @@ -0,0 +1,21 @@ +package stanl_2.final_backend.domain.problem.query.repository; + +import org.apache.ibatis.annotations.Mapper; +import org.springframework.data.repository.query.Param; +import stanl_2.final_backend.domain.problem.query.dto.ProblemDTO; +import stanl_2.final_backend.domain.problem.query.dto.ProblemSearchDTO; +import stanl_2.final_backend.domain.promotion.query.dto.PromotionDTO; + +import java.util.List; + +@Mapper +public interface ProblemMapper { + List findProblems( + @Param("offset") int offset, + @Param("size") int size, + @Param("problemDTO") ProblemSearchDTO problemSearchDTO + ); + Integer findProblemsCount(@Param("problemSearchDTO") ProblemSearchDTO problemSearchDTO); + + ProblemDTO findProblem(@Param("problemId") String problemId); +} diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemService.java b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemService.java new file mode 100644 index 00000000..5ea78365 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemService.java @@ -0,0 +1,13 @@ +package stanl_2.final_backend.domain.problem.query.service; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import stanl_2.final_backend.domain.notices.query.dto.SearchDTO; +import stanl_2.final_backend.domain.problem.query.dto.ProblemDTO; +import stanl_2.final_backend.domain.problem.query.dto.ProblemSearchDTO; + +public interface ProblemService { + + Page findProblems(Pageable pageable, ProblemSearchDTO problemSearchDTO); + ProblemDTO findProblem(String noticeId); +} diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java new file mode 100644 index 00000000..4136e7f4 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java @@ -0,0 +1,39 @@ +package stanl_2.final_backend.domain.problem.query.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import stanl_2.final_backend.domain.problem.query.dto.ProblemDTO; +import stanl_2.final_backend.domain.problem.query.dto.ProblemSearchDTO; +import stanl_2.final_backend.domain.problem.query.repository.ProblemMapper; +import stanl_2.final_backend.domain.promotion.query.dto.PromotionDTO; + +import java.util.List; + +@Service("queryProblemServiceImpl") +public class ProblemServiceImpl implements ProblemService { + private final ProblemMapper problemMapper; + @Autowired + public ProblemServiceImpl(ProblemMapper problemMapper) { + this.problemMapper = problemMapper; + } + + @Override + public Page findProblems(Pageable pageable, ProblemSearchDTO problemSearchDTO) { + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); + List problems = problemMapper.findProblems(offset,size,problemSearchDTO); + Integer count = problemMapper.findProblemsCount(problemSearchDTO); + int totalCount = (count != null) ? problemMapper.findProblemsCount(problemSearchDTO) : 0; + + return new PageImpl<>(problems, pageable, totalCount); + } + + @Override + public ProblemDTO findProblem(String problemId) { + ProblemDTO problemDTO = problemMapper.findProblem(problemId); + return problemDTO; + } +} diff --git a/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml b/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml index f23f1886..1167f852 100644 --- a/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml @@ -26,7 +26,7 @@ - SELECT A.prob_id, A.prob_ttl, @@ -41,19 +41,19 @@ FROM tb_problem A a.active != 'false' - - AND A.NOT_TAG = #{searchDTO.tag} - - - AND A.NOT_CLA = #{searchDTO.classification} + + AND A.NOT_TTL LIKE CONCAT('%', #{searchDTO.title}, '%') - + AND A.MEM_ID = #{searchDTO.memberId} - - AND A.NOT_TTL LIKE CONCAT('%', #{searchDTO.title}, '%') + + AND A.PROD_ID = #{searchDTO.productId} - + + AND A.CUST_ID = #{searchDTO.customerId} + + AND A.CREATED_AT BETWEEN #{searchDTO.startDate} AND #{searchDTO.endDate} @@ -65,41 +65,65 @@ - + SELECT + A.prob_id, + A.prob_ttl, + A.prob_cont, + A.created_at, + A.updated_at, + A.deleted_at, + A.active, + A.cust_id, + A.mem_id, + A.prod_id + FROM tb_problem A + WHERE prob_id = #{problemId}; + + + \ No newline at end of file From cabebb539a8256fdd231b5f0cbc8713c0c086128 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Wed, 20 Nov 2024 20:55:58 +0900 Subject: [PATCH 241/563] =?UTF-8?q?feat:=20=EC=97=90=EB=9F=AC=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=20=EC=B6=9C=EB=A0=A5(#110)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 12 +- .../domain/log/command/aggregate/Log.java | 66 ++++++ .../domain/log/command/aop/LoggingAspect.java | 170 ++++++++++++++ .../command}/repository/LogRepository.java | 5 +- .../common/exception/LogCommonException.java | 16 ++ .../log/common/exception/LogErrorCode.java | 53 +++++ .../exception/LogExceptionResponse.java | 22 ++ .../common/response/LogResponseMessage.java | 14 ++ .../query/config/MybatisConfiguration.java | 9 + .../log/query/controller/LogController.java | 48 ++++ .../domain/log/query/dto/LogDTO.java | 37 +++ .../log/query/repository/LogMapper.java | 11 + .../log/query/service/LogQueryService.java | 9 + .../query/service/LogQueryServiceImpl.java | 31 +++ .../exception/GlobalExceptionHandler.java | 222 ++++++++++++++---- .../global/log/LoggingAspect.java | 121 ---------- .../global/log/LoggingFilter.java | 193 +++++++++++++++ .../global/log/aggregate/Log.java | 29 --- .../security/config/DevSecurityConfig.java | 11 +- .../security/config/ProdSecurityConfig.java | 38 ++- .../filter/JWTTokenValidatorFilter.java | 90 +++++-- src/main/resources/application-prod.yml | 2 +- .../domain/log/query/repository/LogMapper.xml | 26 ++ 23 files changed, 1007 insertions(+), 228 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java create mode 100644 src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java rename src/main/java/stanl_2/final_backend/{global/log => domain/log/command}/repository/LogRepository.java (52%) create mode 100644 src/main/java/stanl_2/final_backend/domain/log/common/exception/LogCommonException.java create mode 100644 src/main/java/stanl_2/final_backend/domain/log/common/exception/LogErrorCode.java create mode 100644 src/main/java/stanl_2/final_backend/domain/log/common/exception/LogExceptionResponse.java create mode 100644 src/main/java/stanl_2/final_backend/domain/log/common/response/LogResponseMessage.java create mode 100644 src/main/java/stanl_2/final_backend/domain/log/query/config/MybatisConfiguration.java create mode 100644 src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/log/query/dto/LogDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/log/query/repository/LogMapper.java create mode 100644 src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java delete mode 100644 src/main/java/stanl_2/final_backend/global/log/LoggingAspect.java create mode 100644 src/main/java/stanl_2/final_backend/global/log/LoggingFilter.java delete mode 100644 src/main/java/stanl_2/final_backend/global/log/aggregate/Log.java create mode 100644 src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml diff --git a/build.gradle b/build.gradle index 818414e9..5f8053f7 100644 --- a/build.gradle +++ b/build.gradle @@ -35,8 +35,7 @@ dependencies { developmentOnly 'org.springframework.boot:spring-boot-devtools' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' -// runtimeOnly 'org.mariadb.jdbc:mariadb-java-client' - implementation 'org.mariadb.jdbc:mariadb-java-client:3.3.3' + runtimeOnly 'org.mariadb.jdbc:mariadb-java-client' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' // .env file @@ -61,15 +60,6 @@ dependencies { //hibernate core implementation 'org.hibernate.orm:hibernate-core:6.5.2.Final' - - // Logback and SLF4J Dependencies (Updated versions) -// implementation 'ch.qos.logback:logback-classic:1.4.11' // Updated Logback version -// implementation 'org.slf4j:slf4j-api:2.0.7' // Compatible SLF4J version for Logback 1.4.11 -// implementation 'ch.qos.logback:logback-classic:1.2.11' -// implementation 'ch.qos.logback:logback-core:1.2.11' - - - } tasks.named('test') { diff --git a/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java b/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java new file mode 100644 index 00000000..a69c0448 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java @@ -0,0 +1,66 @@ +package stanl_2.final_backend.domain.log.command.aggregate; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +import java.time.LocalDateTime; + +@Getter +@Setter +@Entity +public class Log { + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "LOG") + ) + @Column(name = "LOG_ID", nullable = false) + private String logId; + + // User Information + @Column(name = "SESSION_ID") + private String sessionId; + + @Column(name = "USER_AGENT") + private String userAgent; + + // Network Information + @Column(name = "IP_ADDRESS") + private String ipAddress; + + @Column(name = "HOST_NAME") + private String hostName; + + @Column(name = "REMOTE_PORT") + private Integer remotePort; + + // Request Information + @Column(name = "URI") + private String uri; + + @Column(name = "METHOD") + private String method; + + @Column(name = "QUERY_STRING") + private String queryString; + + // Time Information + @Column(name = "REQUEST_TIME") + private LocalDateTime requestTime; + + // Additional Information + @Column(name = "TRANSACTION_ID") + private String transactionId; + + @Column(name = "STATUS") + private String status; + + @Column(name = "ERROR_MESSAGE") + private String errorMessage; +} + + diff --git a/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java b/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java new file mode 100644 index 00000000..8b0b2b78 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java @@ -0,0 +1,170 @@ +//package stanl_2.final_backend.domain.log.command.aop; +// +//import com.fasterxml.jackson.databind.ObjectMapper; +//import jakarta.servlet.http.HttpServletRequest; +//import lombok.extern.slf4j.Slf4j; +//import org.aspectj.lang.JoinPoint; +//import org.aspectj.lang.annotation.AfterReturning; +//import org.aspectj.lang.annotation.Aspect; +//import org.aspectj.lang.annotation.Before; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.stereotype.Component; +//import org.springframework.web.context.request.RequestContextHolder; +//import org.springframework.web.context.request.ServletRequestAttributes; +//import stanl_2.final_backend.domain.log.command.aggregate.Log; +//import stanl_2.final_backend.domain.log.command.repository.LogRepository; +// +//import java.time.LocalDateTime; +//import java.util.HashMap; +//import java.util.Map; +//import java.util.UUID; +// +//@Slf4j +//@Aspect +//@Component +//public class LoggingAspect { +// +// private final ObjectMapper objectMapper; +// private final LogRepository logRepository; +// private static final ThreadLocal currentTransactionId = new ThreadLocal<>(); +// +// @Autowired +// public LoggingAspect(LogRepository logRepository) { +// this.objectMapper = new ObjectMapper(); +// this.logRepository = logRepository; +// } +// +// @Before("within(@org.springframework.web.bind.annotation.RestController *)") +// public void logRequestInfo(JoinPoint joinPoint) { +// ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); +// if (attributes == null) return; +// +// HttpServletRequest request = attributes.getRequest(); +// +// Map logData = new HashMap<>(); +// +// // 사용자 정보 +// Map userData = new HashMap<>(); +// userData.put("session_id", safeValue(request.getRequestedSessionId())); +// userData.put("user_agent", safeValue(request.getHeader("User-Agent"))); +// logData.put("user", userData); +// +// // 네트워크 정보 +// Map networkData = new HashMap<>(); +// networkData.put("ip_address", safeValue(getClientIp(request))); +// networkData.put("host_name", safeValue(request.getRemoteHost())); +// networkData.put("remote_port", request.getRemotePort()); +// logData.put("network", networkData); +// +// // 요청 정보 +// Map requestData = new HashMap<>(); +// requestData.put("uri", safeValue(request.getRequestURI())); +// requestData.put("method", safeValue(request.getMethod())); +// requestData.put("query_string", safeValue(request.getQueryString())); +// logData.put("request", requestData); +// +// // 시간 정보 +// Map timeData = new HashMap<>(); +// timeData.put("request_time", LocalDateTime.now().toString()); +// logData.put("time", timeData); +// +// // 추가 정보 +// String transactionId = UUID.randomUUID().toString(); +// currentTransactionId.set(transactionId); +// logData.put("transaction_id", transactionId); +// +// try { +// log.info("Request Info: {}", objectMapper.writeValueAsString(logData)); +// +// // 로그 엔티티 저장 +// Log logEntry = new Log(); +// +// // User Information +// logEntry.setSessionId(safeValue(request.getRequestedSessionId())); +// logEntry.setUserAgent(safeValue(request.getHeader("User-Agent"))); +// +// // Network Information +// logEntry.setIpAddress(safeValue(getClientIp(request))); +// logEntry.setHostName(safeValue(request.getRemoteHost())); +// logEntry.setRemotePort(request.getRemotePort()); +// +// // Request Information +// logEntry.setUri(safeValue(request.getRequestURI())); +// logEntry.setMethod(safeValue(request.getMethod())); +// logEntry.setQueryString(safeValue(request.getQueryString())); +// +// // Time Information +// logEntry.setRequestTime(LocalDateTime.now()); +// +// // Additional Information +// logEntry.setTransactionId(transactionId); +// +// // Status 초기 설정 (요청만 기록한 상태) +// logEntry.setStatus("IN_PROGRESS"); +// +// logRepository.save(logEntry); +// +// } catch (Exception e) { +// log.error("Failed to log request info", e); +// } +// } +// +// @AfterReturning("within(@org.springframework.web.bind.annotation.RestController *)") +// public void logRequestSuccess(JoinPoint joinPoint) { +// String transactionId = currentTransactionId.get(); +// if (transactionId == null) return; +// +// try { +// Log logEntry = logRepository.findByTransactionId(transactionId); +// if (logEntry != null) { +// logEntry.setStatus("SUCCESS"); +// logRepository.save(logEntry); +// } +// } catch (Exception e) { +// log.error("Failed to update log status to SUCCESS", e); +// } finally { +// currentTransactionId.remove(); +// } +// } +// +// +// public void logRequestFailure(String message) { +// String transactionId = currentTransactionId.get(); +// if (transactionId == null) return; +// +// try { +// Log logEntry = logRepository.findByTransactionId(transactionId); +// if (logEntry != null) { +// logEntry.setStatus("FAILURE"); +// logEntry.setErrorMessage(message); +// logRepository.save(logEntry); +// } +// } catch (Exception ex) { +// log.error("Failed to update log status to FAILURE", ex); +// } finally { +// currentTransactionId.remove(); +// } +// } +// +// private String getClientIp(HttpServletRequest request) { +// String[] headers = { +// "X-Forwarded-For", +// "Proxy-Client-IP", +// "WL-Proxy-Client-IP", +// "HTTP_CLIENT_IP", +// "HTTP_X_FORWARDED_FOR" +// }; +// +// for (String header : headers) { +// String ip = request.getHeader(header); +// if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) { +// return ip.split(",")[0]; +// } +// } +// return request.getRemoteAddr(); +// } +// +// private String safeValue(String value) { +// return value != null ? value : "N/A"; +// } +//} diff --git a/src/main/java/stanl_2/final_backend/global/log/repository/LogRepository.java b/src/main/java/stanl_2/final_backend/domain/log/command/repository/LogRepository.java similarity index 52% rename from src/main/java/stanl_2/final_backend/global/log/repository/LogRepository.java rename to src/main/java/stanl_2/final_backend/domain/log/command/repository/LogRepository.java index 93f9119a..4979af51 100644 --- a/src/main/java/stanl_2/final_backend/global/log/repository/LogRepository.java +++ b/src/main/java/stanl_2/final_backend/domain/log/command/repository/LogRepository.java @@ -1,9 +1,10 @@ -package stanl_2.final_backend.global.log.repository; +package stanl_2.final_backend.domain.log.command.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import stanl_2.final_backend.global.log.aggregate.Log; +import stanl_2.final_backend.domain.log.command.aggregate.Log; @Repository public interface LogRepository extends JpaRepository { + Log findByTransactionId(String transactionId); } \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/log/common/exception/LogCommonException.java b/src/main/java/stanl_2/final_backend/domain/log/common/exception/LogCommonException.java new file mode 100644 index 00000000..aa672853 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/log/common/exception/LogCommonException.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.log.common.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class LogCommonException extends RuntimeException { + private final LogErrorCode errorCode; + + // 에러 발생시 ErroCode 별 메시지 + @Override + public String getMessage() { + return this.errorCode.getMsg(); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/log/common/exception/LogErrorCode.java b/src/main/java/stanl_2/final_backend/domain/log/common/exception/LogErrorCode.java new file mode 100644 index 00000000..9570bf1e --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/log/common/exception/LogErrorCode.java @@ -0,0 +1,53 @@ +package stanl_2.final_backend.domain.log.common.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum LogErrorCode { + + /** + * 400(Bad Request) + * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. + */ + + + + /** + * 401(Unauthorized) + * 비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만, + * 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다. + * 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다. + */ + + + /** + * 403(Forbidden) + * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. + * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다. + */ + + + + /** + * 404(Not Found) + * 서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다. + * 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있습니다. + * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. + * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. + */ + + + /** + * 500(Internal Server Error) + * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. + */ + INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); + + + private final Integer code; + private final HttpStatus httpStatus; + private final String msg; +} diff --git a/src/main/java/stanl_2/final_backend/domain/log/common/exception/LogExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/log/common/exception/LogExceptionResponse.java new file mode 100644 index 00000000..d3714677 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/log/common/exception/LogExceptionResponse.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.log.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class LogExceptionResponse { + private final Integer code; + private final String msg; + private final HttpStatus httpStatus; + + public LogExceptionResponse(LogErrorCode errorCode) { + this.code = errorCode.getCode(); + this.msg = errorCode.getMsg(); + this.httpStatus = errorCode.getHttpStatus(); + } + + public static LogExceptionResponse of(LogErrorCode errorCode) { + return new LogExceptionResponse(errorCode); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/log/common/response/LogResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/log/common/response/LogResponseMessage.java new file mode 100644 index 00000000..b1533339 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/log/common/response/LogResponseMessage.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.log.common.response; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class LogResponseMessage { + private int httpStatus; + private String msg; + private Object result; +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/config/MybatisConfiguration.java b/src/main/java/stanl_2/final_backend/domain/log/query/config/MybatisConfiguration.java new file mode 100644 index 00000000..6358f4bc --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/log/query/config/MybatisConfiguration.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.log.query.config; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Configuration; + +@Configuration("logMybatisConfiguration") +@MapperScan(basePackages = "stanl_2.final_backend.domain.log.query.repository") +public class MybatisConfiguration { +} diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java b/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java new file mode 100644 index 00000000..25c9cfbd --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java @@ -0,0 +1,48 @@ +package stanl_2.final_backend.domain.log.query.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; +import stanl_2.final_backend.domain.log.common.response.LogResponseMessage; +import stanl_2.final_backend.domain.log.query.dto.LogDTO; +import stanl_2.final_backend.domain.log.query.service.LogQueryService; + +import java.util.List; + +@RestController(value = "queryLogController") +@RequestMapping("/api/v1/log") +public class LogController { + + private final LogQueryService logQueryService; + + @Autowired + public LogController(LogQueryService logQueryService) { + this.logQueryService = logQueryService; + } + + @Operation(summary = "로그 조회(관리자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("") + public ResponseEntity getLog(){ + + List log = logQueryService.selectLog(); + + return ResponseEntity.ok(LogResponseMessage.builder() + .httpStatus(200) + .result(log) + .build()); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogDTO.java b/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogDTO.java new file mode 100644 index 00000000..5ab50be0 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogDTO.java @@ -0,0 +1,37 @@ +package stanl_2.final_backend.domain.log.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class LogDTO { + + private Long logId; + + // User Information + private String sessionId; + private String userAgent; + + // Network Information + private String ipAddress; + private String hostName; + private Integer remotePort; + + // Request Information + private String uri; + private String method; + private String queryString; + + // Time Information + private LocalDateTime requestTime; + + // Additional Information + private String transactionId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/repository/LogMapper.java b/src/main/java/stanl_2/final_backend/domain/log/query/repository/LogMapper.java new file mode 100644 index 00000000..5a7739ff --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/log/query/repository/LogMapper.java @@ -0,0 +1,11 @@ +package stanl_2.final_backend.domain.log.query.repository; + +import org.apache.ibatis.annotations.Mapper; +import stanl_2.final_backend.domain.log.query.dto.LogDTO; + +import java.util.List; + +@Mapper +public interface LogMapper { + List findLog(); +} diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryService.java b/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryService.java new file mode 100644 index 00000000..eaeea15b --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryService.java @@ -0,0 +1,9 @@ +package stanl_2.final_backend.domain.log.query.service; + +import stanl_2.final_backend.domain.log.query.dto.LogDTO; + +import java.util.List; + +public interface LogQueryService { + List selectLog(); +} diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java new file mode 100644 index 00000000..9215af1e --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java @@ -0,0 +1,31 @@ +package stanl_2.final_backend.domain.log.query.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.log.query.dto.LogDTO; +import stanl_2.final_backend.domain.log.query.repository.LogMapper; + +import java.util.List; + +@Slf4j +@Service(value = "queryLogService") +public class LogQueryServiceImpl implements LogQueryService { + + private final LogMapper logMapper; + + @Autowired + public LogQueryServiceImpl(LogMapper logMapper) { + this.logMapper = logMapper; + } + + @Override + @Transactional(readOnly = true) + public List selectLog() { + + List log = logMapper.findLog(); + + return log; + } +} diff --git a/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java b/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java index a0a111fd..85126f87 100644 --- a/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java @@ -3,8 +3,10 @@ import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.JwtException; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingServletRequestParameterException; @@ -12,86 +14,216 @@ import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import org.springframework.web.servlet.NoHandlerFoundException; +import stanl_2.final_backend.domain.log.command.aggregate.Log; +import stanl_2.final_backend.domain.log.command.repository.LogRepository; +import stanl_2.final_backend.global.log.LoggingFilter; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; @Slf4j // 모든 rest컨트롤러에서 발생하는 예외 처리 @RestControllerAdvice(basePackages = "stanl_2.final_backend") public class GlobalExceptionHandler { - // 지원되지 않는 HTTP 메소드를 사용할 때 발생하는 예외 - @ExceptionHandler(value = {NoHandlerFoundException.class, HttpRequestMethodNotSupportedException.class}) - public ResponseEntity handleNoPageFoundException(Exception e) { - log.error("지원되지 않는 HTTP 메소드 요청: {}", e.getMessage()); - GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.WRONG_ENTRY_POINT).getErrorCode()); - return ResponseEntity.status(response.getHttpStatus()).body(response); + @Autowired + private LogRepository logRepository; // 로그를 저장하기 위한 리포지토리 주입 + + private void saveErrorLog(String message, Exception e) { + String transactionId = LoggingFilter.getCurrentTransactionId(); // ThreadLocal에서 가져옴 + if (transactionId == null) { + transactionId = UUID.randomUUID().toString(); // 누락 시 새로운 ID 생성 + } +// Log log = new Log(); +// log.setTransactionId(transactionId); +// log.setStatus("ERROR"); +// log.setErrorMessage(e.getMessage()); +// logRepository.save(log); + + try { + Log log = new Log(); + log.setStatus(message); + log.setErrorMessage(e.getMessage()); + log.setRequestTime(LocalDateTime.now()); + logRepository.save(log); // 로그 저장 + } catch (Exception ex) { + log.error("로그 저장 중 오류 발생: {}", ex.getMessage(), ex); + } } - // 메소드의 인자 타입이 일치하지 않을 때 발생하는 예외 - @ExceptionHandler(value = {MethodArgumentTypeMismatchException.class}) - public ResponseEntity handleArgumentNotValidException(MethodArgumentTypeMismatchException e) { - log.error("메소드 인자 타입 불일치: {}" - , e.getMessage()); - GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.INVALID_PARAMETER_FORMAT).getErrorCode()); +// // 지원되지 않는 HTTP 메소드를 사용할 때 발생하는 예외 +// @ExceptionHandler(value = {NoHandlerFoundException.class, HttpRequestMethodNotSupportedException.class}) +// public ResponseEntity handleNoPageFoundException(Exception e) { +// log.error("지원되지 않는 HTTP 메소드 요청: {}", e.getMessage()); +// saveErrorLog(GlobalErrorCode.WRONG_ENTRY_POINT.getMsg(), e); +// GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.WRONG_ENTRY_POINT).getErrorCode()); +//// loggingAspect.logRequestFailure(GlobalErrorCode.WRONG_ENTRY_POINT.getMsg()); +// return ResponseEntity.status(response.getHttpStatus()).body(response); +// } +// +// // 메소드의 인자 타입이 일치하지 않을 때 발생하는 예외 +// @ExceptionHandler(value = {MethodArgumentTypeMismatchException.class}) +// public ResponseEntity handleArgumentNotValidException(MethodArgumentTypeMismatchException e) { +// log.error("메소드 인자 타입 불일치: {}" +// , e.getMessage()); +// saveErrorLog(GlobalErrorCode.INVALID_PARAMETER_FORMAT.getMsg(), e); +// GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.INVALID_PARAMETER_FORMAT).getErrorCode()); +//// loggingAspect.logRequestFailure(GlobalErrorCode.INVALID_PARAMETER_FORMAT.getMsg()); +// return ResponseEntity.status(response.getHttpStatus()).body(response); +// } +// +// // 필수 파라미터가 누락되었을 때 발생하는 예외 +// @ExceptionHandler(value = {MissingServletRequestParameterException.class}) +// public ResponseEntity handleArgumentNotValidException(MissingServletRequestParameterException e) { +// log.error("필수 파라미터 누락: {}" +// , e.getMessage()); +// saveErrorLog(GlobalErrorCode.MISSING_REQUEST_PARAMETER.getMsg(), e); +// GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.MISSING_REQUEST_PARAMETER).getErrorCode()); +//// loggingAspect.logRequestFailure(GlobalErrorCode.MISSING_REQUEST_PARAMETER.getMsg()); +// return ResponseEntity.status(response.getHttpStatus()).body(response); +// } +// +// // 사용자 정의 예외 처리 +// @ExceptionHandler(value = {GlobalCommonException.class}) +// public ResponseEntity> handleCustomException(GlobalCommonException e) { +// log.error("사용자 예외처리: {}", e.getMessage()); +// saveErrorLog(e.getErrorCode().getMsg(), e); +//// GlobalExceptionResponse response = new GlobalExceptionResponse(e.getErrorCode()); +//// loggingAspect.logRequestFailure(e.getErrorCode().getMsg()); +// +// Map response = new HashMap<>(); +// response.put("message", e.getErrorCode().getMsg()); +// response.put("code", e.getErrorCode().getCode()); +// return new ResponseEntity<>(response, e.getErrorCode().getHttpStatus()); +// +//// return ResponseEntity.status(response.getHttpStatus()).body(response); +// } +//// +//// @ExceptionHandler(GlobalCommonException.class) +//// public ResponseEntity> handleGlobalCommonException(GlobalCommonException ex) { +//// // 로그 저장 +//// log.error("Error occurred: {}", ex.getErrorCode()); +//// Map response = new HashMap<>(); +//// response.put("message", ex.getErrorCode().getMsg()); +//// response.put("code", ex.getErrorCode().getCode()); +//// return new ResponseEntity<>(response, ex.getErrorCode().getHttpStatus()); +//// } +// +// // 서버 내부 오류시 작동 +// @ExceptionHandler(value = {Exception.class}) +// public ResponseEntity handleServerException(Exception e) { +// log.info("서버 내부 오류 발생: {}", e.getMessage()); +// e.printStackTrace(); +// saveErrorLog(GlobalErrorCode.INTERNAL_SERVER_ERROR.getMsg(), e); +// GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.INTERNAL_SERVER_ERROR).getErrorCode()); +//// loggingAspect.logRequestFailure(GlobalErrorCode.INTERNAL_SERVER_ERROR.getMsg()); +// return ResponseEntity.status(response.getHttpStatus()).body(response); +// } +// +// // 데이터 무결성 위반 예외 처리기 추가 +// @ExceptionHandler(value = {DataIntegrityViolationException.class}) +// public ResponseEntity handleDataIntegrityViolationException(DataIntegrityViolationException e) { +// log.error("데이터 무결성 위반: {}", e.getMessage()); +// saveErrorLog(GlobalErrorCode.DATA_INTEGRITY_VIOLATION.getMsg(), e); +// GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.DATA_INTEGRITY_VIOLATION).getErrorCode()); +//// loggingAspect.logRequestFailure(GlobalErrorCode.DATA_INTEGRITY_VIOLATION.getMsg()); +// return ResponseEntity.status(response.getHttpStatus()).body(response); +// } +// +// // 유효성 검사 실패 예외 +// @ExceptionHandler(value = {MethodArgumentNotValidException.class}) +// public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { +// log.error("유효성 검사 실패: {}", e.getMessage()); +// saveErrorLog(GlobalErrorCode.VALIDATION_FAIL.getMsg(), e); +// GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.VALIDATION_FAIL).getErrorCode()); +//// loggingAspect.logRequestFailure(GlobalErrorCode.VALIDATION_FAIL.getMsg()); +// return ResponseEntity.status(response.getHttpStatus()).body(response); +// } +// +// // JWT 토큰 만료 예외 처리 +// @ExceptionHandler(ExpiredJwtException.class) +// public ResponseEntity handleExpiredJwtException(ExpiredJwtException e) { +// log.error("만료된 JWT 토큰: {}", e.getMessage()); +// saveErrorLog(GlobalErrorCode.JWT_EXPIRED.getMsg(), e); +// GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.JWT_EXPIRED).getErrorCode()); +//// loggingAspect.logRequestFailure(GlobalErrorCode.JWT_EXPIRED.getMsg()); +// return ResponseEntity.status(response.getHttpStatus()).body(response); +// } +// +// // JWT 토큰 인증 실패 예외 처리 +// @ExceptionHandler(JwtException.class) +// public ResponseEntity handleJwtException(JwtException e) { +// log.error("유효하지 않은 JWT 토큰: {}", e.getMessage()); +// saveErrorLog(GlobalErrorCode.INVALID_TOKEN_ERROR.getMsg(), e); +// GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.INVALID_TOKEN_ERROR).getErrorCode()); +//// loggingAspect.logRequestFailure(GlobalErrorCode.INVALID_TOKEN_ERROR.getMsg()); +// return ResponseEntity.status(response.getHttpStatus()).body(response); +// } + + private ResponseEntity createErrorResponse(GlobalErrorCode errorCode, Exception e) { + saveErrorLog(errorCode.getMsg(), e); + GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(errorCode).getErrorCode()); return ResponseEntity.status(response.getHttpStatus()).body(response); } - // 필수 파라미터가 누락되었을 때 발생하는 예외 - @ExceptionHandler(value = {MissingServletRequestParameterException.class}) - public ResponseEntity handleArgumentNotValidException(MissingServletRequestParameterException e) { - log.error("필수 파라미터 누락: {}" - , e.getMessage()); - GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.MISSING_REQUEST_PARAMETER).getErrorCode()); - return ResponseEntity.status(response.getHttpStatus()).body(response); + @ExceptionHandler({NoHandlerFoundException.class, HttpRequestMethodNotSupportedException.class}) + public ResponseEntity handleNoPageFoundException(Exception e) { + log.error("지원되지 않는 요청: {}", e.getMessage()); + return createErrorResponse(GlobalErrorCode.WRONG_ENTRY_POINT, e); } - // 사용자 정의 예외 처리 - @ExceptionHandler(value = {GlobalCommonException.class}) - public ResponseEntity handleCustomException(GlobalCommonException e) { - log.error("사용자 예외처리: {}", e.getMessage()); - GlobalExceptionResponse response = new GlobalExceptionResponse(e.getErrorCode()); + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public ResponseEntity handleArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) { + log.error("인자 타입 불일치: {}", e.getMessage()); + return createErrorResponse(GlobalErrorCode.INVALID_PARAMETER_FORMAT, e); + } - return ResponseEntity.status(response.getHttpStatus()).body(response); + @ExceptionHandler(MissingServletRequestParameterException.class) + public ResponseEntity handleMissingRequestParameterException(MissingServletRequestParameterException e) { + log.error("필수 파라미터 누락: {}", e.getMessage()); + return createErrorResponse(GlobalErrorCode.MISSING_REQUEST_PARAMETER, e); + } + + @ExceptionHandler(GlobalCommonException.class) + public ResponseEntity> handleCustomException(GlobalCommonException e) { + log.error("사용자 예외 처리: {}", e.getMessage()); + saveErrorLog(e.getErrorCode().getMsg(), e); + Map response = new HashMap<>(); + response.put("message", e.getErrorCode().getMsg()); + response.put("code", e.getErrorCode().getCode()); + return new ResponseEntity<>(response, e.getErrorCode().getHttpStatus()); } - // 서버 내부 오류시 작동 - @ExceptionHandler(value = {Exception.class}) + @ExceptionHandler(Exception.class) public ResponseEntity handleServerException(Exception e) { - log.info("서버 내부 오류 발생: {}", e.getMessage()); - e.printStackTrace(); - GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.INTERNAL_SERVER_ERROR).getErrorCode()); - return ResponseEntity.status(response.getHttpStatus()).body(response); + log.error("서버 내부 오류 발생: {}", e.getMessage()); + return createErrorResponse(GlobalErrorCode.INTERNAL_SERVER_ERROR, e); } - // 데이터 무결성 위반 예외 처리기 추가 - @ExceptionHandler(value = {DataIntegrityViolationException.class}) + @ExceptionHandler(DataIntegrityViolationException.class) public ResponseEntity handleDataIntegrityViolationException(DataIntegrityViolationException e) { log.error("데이터 무결성 위반: {}", e.getMessage()); - GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.DATA_INTEGRITY_VIOLATION).getErrorCode()); - return ResponseEntity.status(response.getHttpStatus()).body(response); + return createErrorResponse(GlobalErrorCode.DATA_INTEGRITY_VIOLATION, e); } - // 유효성 검사 실패 예외 - @ExceptionHandler(value = {MethodArgumentNotValidException.class}) + @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { log.error("유효성 검사 실패: {}", e.getMessage()); - GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.VALIDATION_FAIL).getErrorCode()); - return ResponseEntity.status(response.getHttpStatus()).body(response); + return createErrorResponse(GlobalErrorCode.VALIDATION_FAIL, e); } - // JWT 토큰 만료 예외 처리 @ExceptionHandler(ExpiredJwtException.class) public ResponseEntity handleExpiredJwtException(ExpiredJwtException e) { log.error("만료된 JWT 토큰: {}", e.getMessage()); - GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.JWT_EXPIRED).getErrorCode()); - return ResponseEntity.status(response.getHttpStatus()).body(response); + return createErrorResponse(GlobalErrorCode.JWT_EXPIRED, e); } - // JWT 토큰 인증 실패 예외 처리 @ExceptionHandler(JwtException.class) public ResponseEntity handleJwtException(JwtException e) { log.error("유효하지 않은 JWT 토큰: {}", e.getMessage()); - GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.INVALID_TOKEN_ERROR).getErrorCode()); - return ResponseEntity.status(response.getHttpStatus()).body(response); + return createErrorResponse(GlobalErrorCode.INVALID_TOKEN_ERROR, e); } } diff --git a/src/main/java/stanl_2/final_backend/global/log/LoggingAspect.java b/src/main/java/stanl_2/final_backend/global/log/LoggingAspect.java deleted file mode 100644 index 3675871d..00000000 --- a/src/main/java/stanl_2/final_backend/global/log/LoggingAspect.java +++ /dev/null @@ -1,121 +0,0 @@ -package stanl_2.final_backend.global.log; - -import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.servlet.http.HttpServletRequest; -import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Before; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; -import stanl_2.final_backend.global.log.repository.LogRepository; -import stanl_2.final_backend.global.log.aggregate.Log; - -import java.time.LocalDateTime; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -@Slf4j -@Aspect -@Component -public class LoggingAspect { - - private final ObjectMapper objectMapper; - private final LogRepository logRepository; - - @Autowired - public LoggingAspect(LogRepository logRepository) { - this.objectMapper = new ObjectMapper(); - this.logRepository = logRepository; - } - - @Before("within(@org.springframework.web.bind.annotation.RestController *)") - public void logRequestInfo(JoinPoint joinPoint) { - ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); - if (attributes == null) return; - - HttpServletRequest request = attributes.getRequest(); - - Map logData = new HashMap<>(); - - // 사용자 정보 - Map userData = new HashMap<>(); - userData.put("session_id", safeValue(request.getRequestedSessionId())); - userData.put("user_agent", safeValue(request.getHeader("User-Agent"))); - logData.put("user", userData); - - // 네트워크 정보 - Map networkData = new HashMap<>(); - networkData.put("ip_address", safeValue(getClientIp(request))); - networkData.put("host_name", safeValue(request.getRemoteHost())); - networkData.put("remote_port", request.getRemotePort()); - logData.put("network", networkData); - - // 요청 정보 - Map requestData = new HashMap<>(); - requestData.put("uri", safeValue(request.getRequestURI())); - requestData.put("method", safeValue(request.getMethod())); - requestData.put("query_string", safeValue(request.getQueryString())); - requestData.put("headers", getHeaders(request)); - logData.put("request", requestData); - - // 시간 정보 - Map timeData = new HashMap<>(); - timeData.put("request_time", LocalDateTime.now().toString()); - logData.put("time", timeData); - - // 추가 정보 - logData.put("transaction_id", UUID.randomUUID().toString()); - - try { - log.info("Request Info: {}", objectMapper.writeValueAsString(logData)); - - // 로그 엔티티 저장 - Log logEntry = new Log(); - logEntry.setSessionId(safeValue(request.getRequestedSessionId())); - logEntry.setUserAgent(safeValue(request.getHeader("User-Agent"))); - logEntry.setIpAddress(safeValue(getClientIp(request))); - logEntry.setUri(safeValue(request.getRequestURI())); - logEntry.setMethod(safeValue(request.getMethod())); - logEntry.setQueryString(safeValue(request.getQueryString())); - logEntry.setRequestTime(LocalDateTime.now()); - - logRepository.save(logEntry); - } catch (Exception e) { - log.error("Failed to log request info", e); - } - } - - private String getClientIp(HttpServletRequest request) { - String[] headers = { - "X-Forwarded-For", - "Proxy-Client-IP", - "WL-Proxy-Client-IP", - "HTTP_CLIENT_IP", - "HTTP_X_FORWARDED_FOR" - }; - - for (String header : headers) { - String ip = request.getHeader(header); - if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) { - return ip.split(",")[0]; - } - } - return request.getRemoteAddr(); - } - - private Map getHeaders(HttpServletRequest request) { - Map headers = new HashMap<>(); - request.getHeaderNames().asIterator().forEachRemaining(headerName -> - headers.put(headerName, safeValue(request.getHeader(headerName))) - ); - return headers; - } - - private String safeValue(String value) { - return value != null ? value : "N/A"; - } -} diff --git a/src/main/java/stanl_2/final_backend/global/log/LoggingFilter.java b/src/main/java/stanl_2/final_backend/global/log/LoggingFilter.java new file mode 100644 index 00000000..e52f1563 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/log/LoggingFilter.java @@ -0,0 +1,193 @@ +package stanl_2.final_backend.global.log; + +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import stanl_2.final_backend.domain.log.command.aggregate.Log; +import stanl_2.final_backend.domain.log.command.repository.LogRepository; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@Slf4j +@Component +public class LoggingFilter implements Filter { + + private final ObjectMapper objectMapper; + private final LogRepository logRepository; + private static final ThreadLocal currentTransactionId = new ThreadLocal<>(); + + @Autowired + public LoggingFilter(LogRepository logRepository) { + this.objectMapper = new ObjectMapper(); + this.logRepository = logRepository; + } + + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + // 필터 초기화 로직 (필요 시 구현) + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + if (!(request instanceof HttpServletRequest)) { + chain.doFilter(request, response); + return; + } + + HttpServletRequest httpRequest = (HttpServletRequest) request; + Map logData = new HashMap<>(); + + // 사용자 정보 + Map userData = new HashMap<>(); + userData.put("session_id", safeValue(httpRequest.getRequestedSessionId())); + userData.put("user_agent", safeValue(httpRequest.getHeader("User-Agent"))); + logData.put("user", userData); + + // 네트워크 정보 + Map networkData = new HashMap<>(); + networkData.put("ip_address", safeValue(getClientIp(httpRequest))); + networkData.put("host_name", safeValue(httpRequest.getRemoteHost())); + networkData.put("remote_port", httpRequest.getRemotePort()); + logData.put("network", networkData); + + // 요청 정보 + Map requestData = new HashMap<>(); + requestData.put("uri", safeValue(httpRequest.getRequestURI())); + requestData.put("method", safeValue(httpRequest.getMethod())); + requestData.put("query_string", safeValue(httpRequest.getQueryString())); + logData.put("request", requestData); + + // 시간 정보 + Map timeData = new HashMap<>(); + timeData.put("request_time", LocalDateTime.now().toString()); + logData.put("time", timeData); + + // 추가 정보 + String transactionId = UUID.randomUUID().toString(); + currentTransactionId.set(transactionId); + logData.put("transaction_id", transactionId); + + try { + log.info("Request Info: {}", objectMapper.writeValueAsString(logData)); + + // 로그 엔티티 저장 + Log logEntry = new Log(); + + // User Information + logEntry.setSessionId(safeValue(httpRequest.getRequestedSessionId())); + logEntry.setUserAgent(safeValue(httpRequest.getHeader("User-Agent"))); + + // Network Information + logEntry.setIpAddress(safeValue(getClientIp(httpRequest))); + logEntry.setHostName(safeValue(httpRequest.getRemoteHost())); + logEntry.setRemotePort(httpRequest.getRemotePort()); + + // Request Information + logEntry.setUri(safeValue(httpRequest.getRequestURI())); + logEntry.setMethod(safeValue(httpRequest.getMethod())); + logEntry.setQueryString(safeValue(httpRequest.getQueryString())); + + // Time Information + logEntry.setRequestTime(LocalDateTime.now()); + + // Additional Information + logEntry.setTransactionId(transactionId); + + // Status 초기 설정 (요청만 기록한 상태) + logEntry.setStatus("IN_PROGRESS"); + + logRepository.save(logEntry); + + } catch (Exception e) { + log.error("Failed to log request info", e); + } + + try { + chain.doFilter(request, response); + logRequestSuccess(); + } catch (Exception e) { + logRequestFailure(e); + throw e; + } finally { + currentTransactionId.remove(); + } + } + + @Override + public void destroy() { + // 필터 해제 로직 (필요 시 구현) + } + + public static String getCurrentTransactionId() { + return currentTransactionId.get(); + } + + private void logRequestSuccess() { + String transactionId = currentTransactionId.get(); + if (transactionId == null) return; + + try { + Log logEntry = logRepository.findByTransactionId(transactionId); + if (logEntry != null) { + logEntry.setStatus("SUCCESS"); + logRepository.save(logEntry); + } + } catch (Exception e) { + log.error("Failed to update log status to SUCCESS", e); + } + } + + private void logRequestFailure(Throwable e) { + String transactionId = currentTransactionId.get(); + if (transactionId == null) return; + + try { + Log logEntry = logRepository.findByTransactionId(transactionId); + if (logEntry != null) { + logEntry = new Log(); + logEntry.setTransactionId(transactionId); + } + logEntry.setStatus("FAILURE"); + logEntry.setErrorMessage(e.getMessage()); + logRepository.save(logEntry); + } catch (Exception ex) { + log.error("Failed to update log status to FAILURE", ex); + } + } + + private String getClientIp(HttpServletRequest request) { + String[] headers = { + "X-Forwarded-For", + "Proxy-Client-IP", + "WL-Proxy-Client-IP", + "HTTP_CLIENT_IP", + "HTTP_X_FORWARDED_FOR" + }; + + for (String header : headers) { + String ip = request.getHeader(header); + if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) { + return ip.split(",")[0]; + } + } + return request.getRemoteAddr(); + } + + private String safeValue(String value) { + return value != null ? value : "N/A"; + } + + private void ensureTransactionId() { + if (currentTransactionId.get() == null) { + currentTransactionId.set(UUID.randomUUID().toString()); + } + } +} diff --git a/src/main/java/stanl_2/final_backend/global/log/aggregate/Log.java b/src/main/java/stanl_2/final_backend/global/log/aggregate/Log.java deleted file mode 100644 index f0e6fd52..00000000 --- a/src/main/java/stanl_2/final_backend/global/log/aggregate/Log.java +++ /dev/null @@ -1,29 +0,0 @@ -package stanl_2.final_backend.global.log.aggregate; - -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import lombok.Getter; -import lombok.Setter; - -import java.time.LocalDateTime; - -@Getter -@Setter -@Entity -public class Log { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String sessionId; - private String userAgent; - private String ipAddress; - private String uri; - private String method; - private String queryString; - private LocalDateTime requestTime; -} - - diff --git a/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java index d7ed8e7d..634dafa4 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.global.security.config; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -17,6 +18,7 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; +import stanl_2.final_backend.domain.log.command.repository.LogRepository; import stanl_2.final_backend.global.security.filter.JWTTokenValidatorFilter; import java.util.Arrays; @@ -34,6 +36,13 @@ public class DevSecurityConfig { @Value("${jwt.header}") private String jwtHeader; + private final LogRepository logRepository; + + @Autowired + public DevSecurityConfig(LogRepository logRepository) { + this.logRepository = logRepository; + } + @Bean SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { @@ -54,7 +63,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Excepti // [Example] member는 ADMIN 권한만 접근 가능 설정 예시 .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasRole("ADMIN") .anyRequest().authenticated()) - .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey), BasicAuthenticationFilter.class) + .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, logRepository), BasicAuthenticationFilter.class) .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()); http.formLogin(withDefaults()); http.httpBasic(withDefaults()); diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index 3ba77ab0..ec89766f 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -1,10 +1,16 @@ package stanl_2.final_backend.global.security.config; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; +import org.springframework.core.Ordered; import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.password.CompromisedPasswordChecker; @@ -13,11 +19,16 @@ import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.HttpStatusEntryPoint; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.password.HaveIBeenPwnedRestApiPasswordChecker; import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; +import stanl_2.final_backend.domain.log.command.repository.LogRepository; +import stanl_2.final_backend.global.exception.GlobalCommonException; +import stanl_2.final_backend.global.exception.GlobalErrorCode; +import stanl_2.final_backend.global.log.LoggingFilter; import stanl_2.final_backend.global.security.filter.JWTTokenValidatorFilter; import java.util.Arrays; @@ -25,6 +36,7 @@ import static org.springframework.security.config.Customizer.withDefaults; +@Slf4j @Configuration @Profile("prod") public class ProdSecurityConfig { @@ -32,6 +44,9 @@ public class ProdSecurityConfig { @Value("${jwt.secret-key}") private String jwtSecretKey; + @Autowired + private LogRepository logRepository; // logRepository 주입 + @Bean SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception { @@ -55,8 +70,18 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasAnyRole("ADMIN", "MEMBER") .anyRequest().authenticated()) // 필터 순서: JWT 검증 -> CSRF - .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey), UsernamePasswordAuthenticationFilter.class) - .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()); + .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, logRepository), UsernamePasswordAuthenticationFilter.class) + .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()) + // 인증 및 권한 예외를 처리 + .exceptionHandling(ex -> ex + .authenticationEntryPoint((request, response, authException) -> { + log.error("Authentication error: {}", authException.getMessage()); + throw new GlobalCommonException(GlobalErrorCode.LOGIN_FAILURE); + }) + .accessDeniedHandler((request, response, accessDeniedException) -> { + log.error("Access denied: {}", accessDeniedException.getMessage()); + throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + })); http.formLogin(withDefaults()); http.httpBasic(withDefaults()); return http.build(); @@ -96,4 +121,13 @@ public AuthenticationManager authenticationManager(UserDetailsService userDetail providerManager.setEraseCredentialsAfterAuthentication(false); return providerManager; } + + @Bean(name = "prodLoggingFilter") + public FilterRegistrationBean loggingFilter(){ + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new LoggingFilter(logRepository)); + registrationBean.addUrlPatterns("/*"); + registrationBean.setOrder(Ordered.LOWEST_PRECEDENCE); // 필터 체인의 가장 마지막에 위치시킵니다. + return registrationBean; + } } diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java index d3c5a094..06be752e 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java @@ -1,6 +1,7 @@ package stanl_2.final_backend.global.security.filter; import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.JwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; @@ -16,6 +17,8 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.filter.OncePerRequestFilter; +import stanl_2.final_backend.domain.log.command.aggregate.Log; +import stanl_2.final_backend.domain.log.command.repository.LogRepository; import stanl_2.final_backend.global.exception.GlobalCommonException; import stanl_2.final_backend.global.exception.GlobalErrorCode; @@ -24,15 +27,18 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; +import java.util.UUID; import java.util.stream.Collectors; @Slf4j public class JWTTokenValidatorFilter extends OncePerRequestFilter { private final String jwtSecretKey; + private final LogRepository logRepository; // 로그 저장소 - public JWTTokenValidatorFilter(String jwtSecretKey) { + public JWTTokenValidatorFilter(String jwtSecretKey, LogRepository logRepository) { this.jwtSecretKey = jwtSecretKey; + this.logRepository = logRepository; // DI 주입 } @Override @@ -55,37 +61,88 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse String username = claims.get("username", String.class); String authorities = claims.get("authorities", String.class); - // 예외 처리: 토큰에 유효한 권한이 없을 경우 if (username == null || authorities == null || authorities.isEmpty()) { - throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + log.warn("토큰에 필수 정보가 누락되었습니다. username: {}, authorities: {}", username, authorities); + handleInvalidToken(response, "토큰에 필수 정보가 없습니다.", new JwtException("Missing required claims")); + return; } List grantedAuthorities = Arrays.stream(authorities.split(",")) .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); - // SecurityContext에 인증 정보 설정 - if (username != null) { - Authentication authentication = new UsernamePasswordAuthenticationToken( - username, null, grantedAuthorities); - SecurityContextHolder.getContext().setAuthentication(authentication); - } + Authentication authentication = new UsernamePasswordAuthenticationToken( + username, null, grantedAuthorities); + SecurityContextHolder.getContext().setAuthentication(authentication); + } catch (JwtException e) { - log.error("유효하지 않은 토큰입니다: {}", e.getMessage()); - throw new GlobalCommonException(GlobalErrorCode.INVALID_TOKEN_ERROR); + log.error("JWT 파싱 실패: {}", e.getMessage()); + handleInvalidToken(response, "유효하지 않은 토큰입니다.", e); + return; } - }else { - // 토큰이 없을 경우 예외 처리 - throw new GlobalCommonException(GlobalErrorCode.LOGIN_FAILURE); + } else if (isExemptedPath(request)) { + log.debug("예외 경로 요청. JWT 검증 생략. 요청 URL: {}", request.getRequestURI()); + } else { + log.warn("Authorization 헤더가 없거나 올바르지 않은 형식입니다."); + handleInvalidToken(response, "인증 정보가 없습니다.", new JwtException("Missing Authorization Header")); + return; } filterChain.doFilter(request, response); } + private void handleInvalidToken(HttpServletResponse response, String message, Exception e) throws IOException { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.setContentType("application/json"); + response.getWriter().write("{\"error\": \"" + message + "\"}"); + log.error("요청 거부됨: {}", message); + + // 로그 저장 호출 + saveErrorLog(message, e); + } + + private void saveErrorLog(String message, Exception e) { + try { + Log logEntity = new Log(); + logEntity.setTransactionId(UUID.randomUUID().toString()); + logEntity.setStatus("ERROR"); + logEntity.setErrorMessage(message + " - " + e.getMessage()); + logRepository.save(logEntity); + } catch (Exception ex) { + log.error("로그 저장 실패: {}", ex.getMessage()); + } + } + + private void validateAndSetAuthentication(String token) { + SecretKey secretKey = Keys.hmacShaKeyFor(jwtSecretKey.getBytes(StandardCharsets.UTF_8)); + Claims claims = Jwts.parserBuilder() + .setSigningKey(secretKey) + .build() + .parseClaimsJws(token) + .getBody(); + + String username = claims.get("username", String.class); + String authorities = claims.get("authorities", String.class); + + if (username == null || authorities == null || authorities.isEmpty()) { + log.warn("토큰에 필수 정보가 누락되었습니다. username: {}, authorities: {}", username, authorities); + throw new JwtException("토큰에 필수 정보가 없습니다."); + } + + List grantedAuthorities = Arrays.stream(authorities.split(",")) + .map(SimpleGrantedAuthority::new) + .collect(Collectors.toList()); + + Authentication authentication = new UsernamePasswordAuthenticationToken(username, null, grantedAuthorities); + SecurityContextHolder.getContext().setAuthentication(authentication); + } @Override protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { - // 특정 경로에서는 필터를 적용하지 않음 + return isExemptedPath(request); + } + + private boolean isExemptedPath(HttpServletRequest request) { String path = request.getServletPath(); return path.equals("/api/v1/auth/signin") || path.equals("/api/v1/auth/signup") || @@ -93,6 +150,7 @@ protected boolean shouldNotFilter(HttpServletRequest request) throws ServletExce path.startsWith("/v3/api-docs") || path.startsWith("/swagger-resources") || path.startsWith("/webjars") || - path.equals("/api/v1/auth"); // 권한 부여때문에(일단 열어둠) + path.equals("/api/v1/auth"); } + } diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 520817c3..8668c185 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -5,4 +5,4 @@ spring: logging: level: - org.springframework.security: DEBUG \ No newline at end of file + org.springframework.security: TRACE \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml b/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml new file mode 100644 index 00000000..faf37fd7 --- /dev/null +++ b/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 14a34488ce88548bda9d40038c6c02a844933798 Mon Sep 17 00:00:00 2001 From: giuseog Date: Wed, 20 Nov 2024 21:48:06 +0900 Subject: [PATCH 242/563] =?UTF-8?q?feat:=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=EC=A4=91(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/aggregate/entity/Center.java | 2 +- .../query/controller/CenterController.java | 10 ++-- .../center/query/repository/CenterMapper.java | 12 +++-- .../center/query/service/CenterService.java | 4 +- .../query/service/CenterServiceImpl.java | 40 ++++++++++------ .../member/query/repository/MemberMapper.java | 7 +++ .../query/service/MemberQueryService.java | 4 ++ .../query/service/MemberQueryServiceImpl.java | 17 +++++++ .../controller/SalesHistoryController.java | 47 +++++++++++++++++-- src/main/resources/application-prod.yml | 2 +- src/main/resources/application.yml | 2 +- .../center/query/repository/CenterMapper.xml | 40 +++++++++------- .../member/query/repository/MemberMapper.xml | 22 +++++++++ .../query/repository/SalesHistoryMapper.xml | 32 ++++++++++++- 14 files changed, 188 insertions(+), 53 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java b/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java index 0f82cbbd..70daaae1 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java @@ -25,7 +25,7 @@ public class Center { @GeneratedValue(generator = "PrefixGeneratorConfig") @GenericGenerator(name = "PrefixGeneratorConfig", type = PrefixGeneratorConfig.class, - parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "CENT") + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "CEN") ) @Column(name ="CENT_ID") private String id; diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java b/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java index c4de4913..aeffb3a9 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java @@ -8,10 +8,10 @@ import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.center.common.response.ResponseMessage; import stanl_2.final_backend.domain.center.query.dto.CenterSearchRequestDTO; +import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectIdDTO; import stanl_2.final_backend.domain.center.query.service.CenterService; -import java.util.HashMap; import java.util.Map; @RestController("queryCenterController") @@ -28,7 +28,7 @@ public CenterController(CenterService centerService) { @GetMapping("") public ResponseEntity getCenterAll(@PageableDefault(size = 20) Pageable pageable){ - Page> responseCenters = centerService.selectAll(pageable); + Page responseCenters = centerService.selectAll(pageable); return ResponseEntity.ok(ResponseMessage.builder() .httpStatus(200) @@ -59,11 +59,7 @@ public ResponseEntity getCenterBySearch(@RequestParam Map par centerSearchRequestDTO.setName(params.get("name")); centerSearchRequestDTO.setAddress(params.get("address")); - Map paramMap = new HashMap<>(); - paramMap.put("centerSearchRequestDTO", centerSearchRequestDTO); - paramMap.put("pageable", pageable); - - Page> responseCenters = centerService.selectBySearch(paramMap); + Page responseCenters = centerService.selectBySearch(centerSearchRequestDTO, pageable); return ResponseEntity.ok(ResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java b/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java index 66fc6cbe..ae6f0ebd 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java @@ -1,10 +1,13 @@ package stanl_2.final_backend.domain.center.query.repository; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.springframework.data.domain.Page; import stanl_2.final_backend.domain.center.common.util.RequestList; import stanl_2.final_backend.domain.center.query.dto.CenterSearchRequestDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectIdDTO; +import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySearchDTO; import java.util.List; import java.util.Map; @@ -13,12 +16,15 @@ public interface CenterMapper { CenterSelectIdDTO findCenterById(String id); - List> findCenterAll(RequestList requestList); + List findCenterAll(@Param("size") int size + , @Param("offset") int offset); Integer findCenterCount(); - Integer findCenterBySearchCount(Map params); + Integer findCenterBySearchCount(@Param("centerSearchRequestDTO") CenterSearchRequestDTO centerSearchRequestDTO); - List> findCenterBySearch(Map params); + List findCenterBySearch(@Param("size") int size + , @Param("offset") int offset + , @Param("centerSearchRequestDTO") CenterSearchRequestDTO centerSearchRequestDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterService.java b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterService.java index 6f4087e1..170a4020 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterService.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterService.java @@ -14,7 +14,7 @@ public interface CenterService { CenterSelectIdDTO selectByCenterId(String id); - Page> selectAll(Pageable pageable); + Page selectAll(Pageable pageable); - Page> selectBySearch(Map params); + Page selectBySearch(CenterSearchRequestDTO centerSearchRequestDTO, Pageable pageable); } diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterServiceImpl.java index 44a5ad22..7a18eced 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterServiceImpl.java @@ -5,13 +5,17 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.A_sample.common.exception.SampleCommonException; +import stanl_2.final_backend.domain.A_sample.common.exception.SampleErrorCode; import stanl_2.final_backend.domain.center.common.util.RequestList; import stanl_2.final_backend.domain.center.query.dto.CenterSearchRequestDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectIdDTO; import stanl_2.final_backend.domain.center.query.repository.CenterMapper; +import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; import java.util.List; import java.util.Map; @@ -21,10 +25,12 @@ public class CenterServiceImpl implements CenterService{ private final CenterMapper centerMapper; + private final RedisTemplate redisTemplate; @Autowired - public CenterServiceImpl(CenterMapper centerMapper) { + public CenterServiceImpl(CenterMapper centerMapper, RedisTemplate redisTemplate) { this.centerMapper = centerMapper; + this.redisTemplate = redisTemplate; } @Override @@ -38,13 +44,11 @@ public CenterSelectIdDTO selectByCenterId(String id) { @Override @Transactional - public Page> selectAll(Pageable pageable) { + public Page selectAll(Pageable pageable) { - RequestList requestList = RequestList.builder() - .pageable(pageable) - .build(); - - List> centerList = centerMapper.findCenterAll(requestList); + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); + List centerList = centerMapper.findCenterAll(size, offset); int total = centerMapper.findCenterCount(); @@ -53,13 +57,21 @@ public Page> selectAll(Pageable pageable) { @Override @Transactional - public Page> selectBySearch(Map params){ - - Pageable pageable = (Pageable) params.get("pageable"); - - List> centerList = centerMapper.findCenterBySearch(params); - - int total = centerMapper.findCenterBySearchCount(params); + public Page selectBySearch(CenterSearchRequestDTO centerSearchRequestDTO, Pageable pageable){ + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); + +// String cacheKey = "myCache::center::offset=" + offset + "::size=" + size; +// +// List centerList = (List) redisTemplate.opsForValue().get(cacheKey); +// if (centerList == null) { +// System.out.println("여기까진 들어가나?"); +// centerList = centerMapper.findCenterBySearch(size, offset, centerSearchRequestDTO); +// redisTemplate.opsForValue().set(cacheKey, centerList); +// } + + List centerList = centerMapper.findCenterBySearch(size, offset, centerSearchRequestDTO); + int total = centerMapper.findCenterBySearchCount(centerSearchRequestDTO); return new PageImpl<>(centerList, pageable, total); } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java b/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java index 3cc8a550..728e7e7f 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java @@ -2,9 +2,16 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; +import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; +import java.util.List; + @Mapper public interface MemberMapper { MemberDTO findMemberInfoById(@Param("loginId") String loginId); + + List findMembersByCenterId(@Param("centerId") String centerId); + + List findMembersByCenterList(@Param("centerList") List centerList); } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java index 83f28f3c..2ebd0763 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java @@ -1,6 +1,7 @@ package stanl_2.final_backend.domain.member.query.service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; import java.security.GeneralSecurityException; @@ -11,5 +12,8 @@ public interface MemberQueryService { List selectMemberByRole(String role); + List selectMemberByCenterId(String centerId); + + List selectMemberByCenterList(List centerList); // MemberDetailDTO selectMemberDetail(String name) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index 581cfe54..1aa874ad 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -3,6 +3,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; import stanl_2.final_backend.domain.member.common.exception.MemberCommonException; import stanl_2.final_backend.domain.member.common.exception.MemberErrorCode; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; @@ -63,4 +64,20 @@ public List selectMemberByRole(String role){ return memberList; } + + @Override + @Transactional(readOnly = true) + public List selectMemberByCenterId(String centerId){ + + List memberList = memberMapper.findMembersByCenterId(centerId); + + return memberList; + } + + @Override + public List selectMemberByCenterList(List centerList) { + List memberList = memberMapper.findMembersByCenterList(centerList); + + return memberList; + } } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java index 0bc2a7b9..b46ff24e 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java @@ -34,13 +34,21 @@ public SalesHistoryController(SalesHistoryQueryService salesHistoryQueryService) /* 설명. * - 사원 -> 일주일별, 월별, 연별(조회기간별) - 본인 판매내역 조회 0 + -> 사원 랭킹(윈도우 함수 이용) - 본인 판매내역 상세 0 +* default: 월별 판매내역 List +* + 1. (지역: 센터 검색 조회(NULL 혹은 '지역') - 본인 실적,수당, 매출액 합 0 + 2. (매장명: NULL 혹은 1) + -1- NULL일 시 1번 결과를 다시 받는다. -> 그 행의 지역 검색결과(centerList)을 바탕으로 mem_id 조회 + -2- 1일 시 selectCenterById를 통해서 반환 값을 member에 where =cent_id)인 값 반환 - 본인 검색 + 3. (이름:null 혹은 1) + - null 일 시-2-의 리스트를 다시 받는다. -> select윈도우 함수 이용해서 멤버 결과 뿌려주기 + - 1 일시 하나의 멤버 + + 4. 분류에 따라서 뿌려줌 * */ @Operation(summary = "사원 판매내역 조회") @@ -207,7 +215,35 @@ public ResponseEntity getStatisticsSearchYearByEmpl salesHistorySearchDTO.setStartDate(params.get("startDate")); salesHistorySearchDTO.setEndDate(params.get("endDate")); - SalesHistoryStatisticsDTO responseSalesHistory = salesHistoryQueryService.selectStatisticsSearchYearBb yEmployee(salesHistorySearchDTO); + SalesHistoryStatisticsDTO responseSalesHistory = salesHistoryQueryService.selectStatisticsSearchYearByEmployee(salesHistorySearchDTO); + + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("사원 통계(실적,수당,매출액) 월 별 검색 성공") + .result(responseSalesHistory) + .build()); + } + + @Operation(summary = "사원 통계(실적,수당,매출액) 연도 별 검색") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("employee/statistics/search/year") + public ResponseEntity getStatisticsSearchYearByEmployee(@RequestParam Map params + ,Principal principal, + @PageableDefault(size = 20) Pageable pageable){ + + SalesHistorySearchDTO salesHistorySearchDTO = new SalesHistorySearchDTO(); + + /* 설명. 2024 식으로 와야함 !!!!! */ + salesHistorySearchDTO.setSearcherName(principal.getName()); + salesHistorySearchDTO.setStartDate(params.get("startDate")); + salesHistorySearchDTO.setEndDate(params.get("endDate")); + + SalesHistoryStatisticsDTO responseSalesHistory = salesHistoryQueryService.selectStatisticsSearchYearByEmployee(salesHistorySearchDTO); return ResponseEntity.ok(SalesHistoryResponseMessage.builder() .httpStatus(200) @@ -216,4 +252,5 @@ public ResponseEntity getStatisticsSearchYearByEmpl .build()); } + } diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 60bf5a48..23faa6a8 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: update + ddl-auto: create logging: level: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index eee7170c..6618043e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -23,7 +23,7 @@ spring: jpa: show-sql: true hibernate: - ddl-auto: update + ddl-auto: create properties: hibernate.format_sql: true diff --git a/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml b/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml index 68269c22..c974a3cc 100644 --- a/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml @@ -46,7 +46,7 @@ WHERE a.cent_id = #{id} AND a.active = TRUE - SELECT a.cent_id , a.cent_name @@ -61,10 +61,10 @@ FROM tb_center a WHERE a.active = TRUE ORDER BY a.created_at DESC - OFFSET #{pageable.offset} ROWS FETCH NEXT #{pageable.pageSize} ROWS ONLY; + LIMIT #{size} OFFSET #{offset} - SELECT a.cent_id , a.cent_name @@ -77,18 +77,20 @@ , a.deleted_at , a.active FROM tb_center a - WHERE a.active = TRUE + + a.active = TRUE AND a.cent_id LIKE #{centerSearchRequestDTO.id} - AND a.cent_name LIKE #{centerSearchRequestDTO.name} + AND a.cent_name LIKE CONCAT('%', #{centerSearchRequestDTO.name}, '%') - AND a.cent_adr LIKE #{centerSearchRequestDTO.address} + AND a.cent_adr LIKE CONCAT('%', #{centerSearchRequestDTO.address}, '%') + ORDER BY a.created_at DESC - OFFSET #{pageable.offset} ROWS FETCH NEXT #{pageable.pageSize} ROWS ONLY; + LIMIT #{size} OFFSET #{offset} - SELECT COUNT(*) AS cnt FROM tb_center a - WHERE a.active = TRUE - - AND a.cent_id like #{centerSearchRequestDTO.id} - - - AND a.cent_name like #{centerSearchRequestDTO.name} - - - AND a.cent_adr like #{centerSearchRequestDTO.address} - + + a.active = TRUE + + AND a.cent_id LIKE #{centerSearchRequestDTO.id} + + + AND a.cent_name LIKE CONCAT('%', #{centerSearchRequestDTO.name}, '%') + + + AND a.cent_adr LIKE CONCAT('%', #{centerSearchRequestDTO.address}, '%') + + diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml index bdc3d46e..e854e8c3 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml @@ -46,4 +46,26 @@ WHERE a.mem_login_id = #{ loginId } + + + + + + \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml index 4b9458ca..64ee9a26 100644 --- a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml @@ -130,7 +130,7 @@ ORDER BY MONTH ASC - SELECT LEFT(a.created_at, 4) AS YEAR, SUM(a.sal_hist_ince) AS INCENTIVE, @@ -146,4 +146,34 @@ GROUP BY LEFT(a.created_at, 4) ORDER BY YEAR ASC + + + SELECT + mem_id, + sal_hist_ince, + sal_hist_no_of_veh, + sal_hist_tota_sale, + created_at, + ROW_NUMBER() OVER (PARTITION BY mem_id ORDER BY sal_hist_ince DESC) AS rank_ince, + ROW_NUMBER() OVER (PARTITION BY mem_id ORDER BY sal_hist_no_of_veh DESC) AS rank_perf, + ROW_NUMBER() OVER (PARTITION BY mem_id ORDER BY sal_hist_tota_sale DESC) AS rank_sales + FROM tb_sales_history + WHERE active = TRUE + AND mem_id IN + + #{memId} + + + + + From 846b7065a1e8bef5153bce8db685af20ed06d94d Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Wed, 20 Nov 2024 22:16:17 +0900 Subject: [PATCH 243/563] =?UTF-8?q?feat:=20=EC=9D=BC=EC=A0=95,=20=EA=B3=B5?= =?UTF-8?q?=EC=A7=80=EC=82=AC=ED=95=AD=20=EC=95=8C=EB=A6=BC=20=EC=A0=84?= =?UTF-8?q?=EC=86=A1=20=EB=B0=8F=20=EC=A1=B0=ED=9A=8C=20=EC=99=84=EB=A3=8C?= =?UTF-8?q?=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/AlarmCommandServiceImpl.java | 2 + .../query/controller/AlarmController.java | 52 ++++++++++++++----- .../query/dto/AlarmSelectAllDetailDTO.java | 17 ------ ...DetailDTO.java => AlarmSelectReadDTO.java} | 2 +- .../alarm/query/dto/AlarmSelectUnreadDTO.java | 18 +++++++ .../alarm/query/repository/AlarmMapper.java | 18 ++++--- .../query/service/AlarmQueryService.java | 11 ++-- .../query/service/AlarmQueryServiceImpl.java | 51 ++++++++++-------- .../alarm/query/repository/AlarmMapper.xml | 20 ++++++- 9 files changed, 123 insertions(+), 68 deletions(-) delete mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectAllDetailDTO.java rename src/main/java/stanl_2/final_backend/domain/alarm/query/dto/{AlarmSelectDetailDTO.java => AlarmSelectReadDTO.java} (89%) create mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectUnreadDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java index 5e91179e..caa13b1a 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java @@ -113,6 +113,7 @@ public void send(String memberId, String message, String redirectUrl, String typ } @Override + @Transactional public Alarm createAlarm(String memberId, String message, String redirectUrl, String type, String createdAt) { Alarm alarm = new Alarm(); @@ -127,6 +128,7 @@ public Alarm createAlarm(String memberId, String message, String redirectUrl, St } @Override + @Transactional public void sendNoticeAlarm(NoticeAlarmDTO noticeAlarmDTO){ List memberIdList = memberQueryService.selectMemberByRole(noticeAlarmDTO.getTag()); diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/controller/AlarmController.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/controller/AlarmController.java index 528cb680..ed367928 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/controller/AlarmController.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/controller/AlarmController.java @@ -15,14 +15,13 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import stanl_2.final_backend.domain.alarm.common.response.AlarmResponseMessage; -import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectAllDetailDTO; -import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectDetailDTO; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectReadDTO; import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectTypeDTO; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectUnreadDTO; import stanl_2.final_backend.domain.alarm.query.service.AlarmQueryService; import stanl_2.final_backend.domain.schedule.common.response.ScheduleResponseMessage; import java.security.Principal; -import java.util.List; @RestController("queryAlarmController") @RequestMapping("/api/v1/alarm") @@ -54,28 +53,53 @@ public ResponseEntity selectMemberAlarmType(Principal prin .build()); } - @Operation(summary = "회원 알림 상세 조회") + @Operation(summary = "회원 읽은 알림 상세 조회") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "회원 알림 상세 조회 완료", + @ApiResponse(responseCode = "200", description = "회원 읽은 알림 상세 조회 완료", content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) }) - @GetMapping("/detail/{type}") - public ResponseEntity selectDetailAlarm(Principal principal, + @GetMapping("/read/{type}") + public ResponseEntity selectReadAlarm(Principal principal, + @PathVariable String type, + @PageableDefault(size = 8) Pageable pageable){ + + String memberLoginId = principal.getName(); + AlarmSelectReadDTO alarmSelectReadDTO = new AlarmSelectReadDTO(); + alarmSelectReadDTO.setMemberLoginId(memberLoginId); + alarmSelectReadDTO.setType(type); + + Page allReadAlarms + = alarmQueryService.selectReadAlarmByType(alarmSelectReadDTO , pageable); + + return ResponseEntity.ok(AlarmResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(allReadAlarms) + .build()); + } + + @Operation(summary = "회원 읽지 않은 알림 상세 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "회원 읽은 알림 상세 조회 완료", + content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) + }) + @GetMapping("/unread/{type}") + public ResponseEntity selectUnreadAlarm(Principal principal, @PathVariable String type, @PageableDefault(size = 8) Pageable pageable){ - String memberLoginId = principal.getName(); - AlarmSelectDetailDTO alarmSelectDetailDTO = new AlarmSelectDetailDTO(); - alarmSelectDetailDTO.setMemberLoginId(memberLoginId); - alarmSelectDetailDTO.setType(type); + AlarmSelectUnreadDTO alarmSelectUnreadDTO = new AlarmSelectUnreadDTO(); + alarmSelectUnreadDTO.setMemberLoginId(memberLoginId); + alarmSelectUnreadDTO.setType(type); - Page alarmSelectAllDetailDTO - = alarmQueryService.selectDetailAlarmByType(alarmSelectDetailDTO , pageable); + Page allUnreadAlarms + = alarmQueryService.selectUnreadAlarmByType(alarmSelectUnreadDTO, pageable); return ResponseEntity.ok(AlarmResponseMessage.builder() .httpStatus(200) .msg("성공") - .result(alarmSelectAllDetailDTO) + .result(allUnreadAlarms) .build()); } + } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectAllDetailDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectAllDetailDTO.java deleted file mode 100644 index 75439cb8..00000000 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectAllDetailDTO.java +++ /dev/null @@ -1,17 +0,0 @@ -package stanl_2.final_backend.domain.alarm.query.dto; - -import lombok.*; - -import java.util.List; - -@AllArgsConstructor -@NoArgsConstructor -@Getter -@Setter -@ToString -public class AlarmSelectAllDetailDTO { - - List readAlarmList; - List notReadAlarmList; - -} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDetailDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectReadDTO.java similarity index 89% rename from src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDetailDTO.java rename to src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectReadDTO.java index 6ca2d602..5f10d825 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDetailDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectReadDTO.java @@ -7,7 +7,7 @@ @Getter @Setter @ToString -public class AlarmSelectDetailDTO { +public class AlarmSelectReadDTO { private String message; private String type; diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectUnreadDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectUnreadDTO.java new file mode 100644 index 00000000..2bafaf17 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectUnreadDTO.java @@ -0,0 +1,18 @@ +package stanl_2.final_backend.domain.alarm.query.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString +public class AlarmSelectUnreadDTO { + + private String message; + private String type; + private String redirectUrl; + private Boolean readStatus; + + private String memberLoginId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.java index 683707a4..7762738d 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.java @@ -2,23 +2,27 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; -import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectDetailDTO; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectUnreadDTO; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectReadDTO; import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectTypeDTO; import java.util.List; -import java.util.Map; @Mapper public interface AlarmMapper { AlarmSelectTypeDTO findNumberOfAlarmsByType(String memberId); - List findReadAlarmsByType(@Param("offset") Integer offset, - @Param("pageSize") Integer pageSize, - @Param("memberId") String memberId, - @Param("type") String type); + List findReadAlarmsByType(@Param("offset") Integer offset, + @Param("pageSize") Integer pageSize, + @Param("memberId") String memberId, + @Param("type") String type); - List findNotReadAlarmsByType(@Param("offset") Integer offset, + List findUnReadAlarmsByType(@Param("offset") Integer offset, @Param("pageSize") Integer pageSize, @Param("memberId") String memberId, @Param("type") String type); + + Integer findReadAlarmsCountByMemberId(String memberId); + + Integer findUnreadAlarmsCountByMemberId(String memberId); } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryService.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryService.java index e0350e9e..8818ae12 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryService.java @@ -1,14 +1,15 @@ package stanl_2.final_backend.domain.alarm.query.service; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectAllDetailDTO; -import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectDetailDTO; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectReadDTO; import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectTypeDTO; - -import java.util.List; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectUnreadDTO; public interface AlarmQueryService { AlarmSelectTypeDTO selectMemberByAlarmType(String memberLoginId); - AlarmSelectAllDetailDTO selectDetailAlarmByType(AlarmSelectDetailDTO alarmSelectDetailDTO, Pageable pageable); + Page selectReadAlarmByType(AlarmSelectReadDTO alarmSelectReadDTO, Pageable pageable); + + Page selectUnreadAlarmByType(AlarmSelectUnreadDTO alarmSelectUnreadDTO, Pageable pageable); } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryServiceImpl.java index 31112e66..4fad874f 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryServiceImpl.java @@ -6,17 +6,16 @@ import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; -import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectAllDetailDTO; -import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectDetailDTO; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.alarm.common.exception.AlarmCommonException; +import stanl_2.final_backend.domain.alarm.common.exception.AlarmErrorCode; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectReadDTO; import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectTypeDTO; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectUnreadDTO; import stanl_2.final_backend.domain.alarm.query.repository.AlarmMapper; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; -import stanl_2.final_backend.domain.order.common.exception.OrderCommonException; -import stanl_2.final_backend.domain.order.common.exception.OrderErrorCode; -import java.util.HashMap; import java.util.List; -import java.util.Map; @Slf4j @Service @@ -32,6 +31,7 @@ public AlarmQueryServiceImpl(AlarmMapper alarmMapper, AuthQueryService authQuery } @Override + @Transactional(readOnly = true) public AlarmSelectTypeDTO selectMemberByAlarmType(String memberLoginId) { String memberId = authQueryService.selectMemberIdByLoginId(memberLoginId); @@ -42,31 +42,38 @@ public AlarmSelectTypeDTO selectMemberByAlarmType(String memberLoginId) { } @Override - public Page selectDetailAlarmByType(AlarmSelectDetailDTO alarmSelectDetailDTO, Pageable pageable) { + @Transactional(readOnly = true) + public Page selectReadAlarmByType(AlarmSelectReadDTO alarmSelectReadDTO, Pageable pageable) { Integer offset = Math.toIntExact(pageable.getOffset()); Integer pageSize = pageable.getPageSize(); - String memberId = authQueryService.selectMemberIdByLoginId(alarmSelectDetailDTO.getMemberLoginId()); + String memberId = authQueryService.selectMemberIdByLoginId(alarmSelectReadDTO.getMemberLoginId()); - List readAlarmList - = alarmMapper.findReadAlarmsByType(offset, pageSize, memberId, alarmSelectDetailDTO.getType()); + List readAlarmList + = alarmMapper.findReadAlarmsByType(offset, pageSize, memberId, alarmSelectReadDTO.getType()); - List notReadAlarmList - = alarmMapper.findNotReadAlarmsByType(offset, pageSize, memberId, alarmSelectDetailDTO.getType()); + Integer count = alarmMapper.findReadAlarmsCountByMemberId(memberId); + int totalOrder = (count != null) ? count : 0; - if (readAlarmList == null || readAlarmList.isEmpty()) { - throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); - } + return new PageImpl<>(readAlarmList, pageable, totalOrder); + } + + @Override + @Transactional(readOnly = true) + public Page selectUnreadAlarmByType(AlarmSelectUnreadDTO alarmSelectUnreadDTO, Pageable pageable) { + + Integer offset = Math.toIntExact(pageable.getOffset()); + Integer pageSize = pageable.getPageSize(); + + String memberId = authQueryService.selectMemberIdByLoginId(alarmSelectUnreadDTO.getMemberLoginId()); - if (notReadAlarmList == null || notReadAlarmList.isEmpty()) { - throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); - } + List unReadAlarmList + = alarmMapper.findUnReadAlarmsByType(offset, pageSize, memberId, alarmSelectUnreadDTO.getType()); - AlarmSelectAllDetailDTO alarmSelectAllDetailDTO = new AlarmSelectAllDetailDTO(); - alarmSelectAllDetailDTO.setReadAlarmList(readAlarmList); - alarmSelectAllDetailDTO.setNotReadAlarmList(notReadAlarmList); + Integer count = alarmMapper.findUnreadAlarmsCountByMemberId(memberId); + int totalOrder = (count != null) ? count : 0; - return new PageImpl<>(alarmSelectAllDetailDTO, pageable, 1); + return new PageImpl<>(unReadAlarmList, pageable, totalOrder); } } diff --git a/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml b/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml index 838839b2..9999d591 100644 --- a/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml @@ -17,7 +17,7 @@ WHERE a.mem_id = #{memberId} - + @@ -34,7 +34,7 @@ AND a.alr_type = #{type} AND a.alr_read_stat = true - SELECT a.alr_msg, a.alr_type, @@ -45,4 +45,20 @@ AND a.alr_type = #{type} AND a.alr_read_stat = false + + + + \ No newline at end of file From 1a8ddd44c1a37978f60b6896adf0dbd3f03b9db5 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Wed, 20 Nov 2024 23:31:30 +0900 Subject: [PATCH 244/563] =?UTF-8?q?feat:=20pr=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20Comment=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EC=99=84=EB=A3=8C=20(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/alarm/command/application/dto/AlarmRegistDTO.java | 1 - .../domain/alarm/query/dto/AlarmSelectReadDTO.java | 1 - .../domain/alarm/query/dto/AlarmSelectTypeDTO.java | 1 - .../domain/alarm/query/dto/AlarmSelectUnreadDTO.java | 1 - .../final_backend/domain/alarm/scheduler/AlarmScheduler.java | 2 +- .../domain/notices/command/application/dto/NoticeAlarmDTO.java | 1 - .../domain/alarm/query/repository/AlarmMapper.xml | 3 ++- 7 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/application/dto/AlarmRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/dto/AlarmRegistDTO.java index 947ea30b..b8aca5bb 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/application/dto/AlarmRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/dto/AlarmRegistDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Getter @Setter -@ToString public class AlarmRegistDTO { private String memberId; diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectReadDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectReadDTO.java index 5f10d825..a18d0c02 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectReadDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectReadDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Getter @Setter -@ToString public class AlarmSelectReadDTO { private String message; diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectTypeDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectTypeDTO.java index 0baeed6d..af77e787 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectTypeDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectTypeDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Getter @Setter -@ToString public class AlarmSelectTypeDTO { private Integer scheduleAlarmCount; diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectUnreadDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectUnreadDTO.java index 2bafaf17..b488f5df 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectUnreadDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectUnreadDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Getter @Setter -@ToString public class AlarmSelectUnreadDTO { private String message; diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java index 1b7de03f..21d1518d 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java @@ -14,7 +14,7 @@ import java.util.List; -@Service +@Service("AlarmSchdulerService") public class AlarmScheduler { private final AlarmCommandService alarmCommandService; diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeAlarmDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeAlarmDTO.java index 67202faa..ea2ec01a 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeAlarmDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeAlarmDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Setter @Getter -@ToString public class NoticeAlarmDTO { private String noticeId; diff --git a/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml b/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml index 9999d591..1b9bf3ea 100644 --- a/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml @@ -13,8 +13,9 @@ SUM(CASE WHEN a.alr_type = 'SCHEDULE' THEN 1 ELSE 0 END) AS SCH_ALR_CNT, SUM(CASE WHEN a.alr_type = 'NOTICE' THEN 1 ELSE 0 END) AS NOT_ALR_CNT, SUM(CASE WHEN a.alr_type = 'CONTRACT' THEN 1 ELSE 0 END) AS CONR_ALR_CNT - FROM tb_alarm a + FROM tb_alarm a WHERE a.mem_id = #{memberId} + AND a.alr_read_stat = false From d53c42b81037be9641bc16b7028e53c81e60923a Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Thu, 21 Nov 2024 00:15:38 +0900 Subject: [PATCH 245/563] =?UTF-8?q?refactor:=20=EA=B3=84=EC=95=BD=EC=84=9C?= =?UTF-8?q?=20CRUD=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=8A=B9=EC=9D=B8?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=EB=B3=80=EA=B2=BD=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?(#112)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ContractController.java | 5 +- .../dto/ContractStatusModifyDTO.java | 1 - .../service/ContractCommandService.java | 6 +- .../domain/aggregate/entity/Contract.java | 3 +- .../service/ContractCommandServiceImpl.java | 80 ++++--- .../query/controller/ContractController.java | 145 ++++++++++--- .../contract/query/dto/ContractSearchDTO.java | 23 -- .../query/dto/ContractSelectAllDTO.java | 5 - .../query/dto/ContractSeletIdDTO.java | 5 - .../query/repository/ContractMapper.java | 24 +- .../query/service/ContractQueryService.java | 14 +- .../service/ContractQueryServiceImpl.java | 205 +++++++----------- .../service/CustomerCommandServiceImpl.java | 1 + .../service/CustomerQueryServiceImpl.java | 6 +- .../order/query/repository/OrderMapper.java | 3 - .../service/ProductCommandService.java | 4 +- .../service/ProductCommandServiceImpl.java | 9 + .../query/repository/PurchaseOrderMapper.java | 3 +- .../PurchaseOrderQueryServiceImpl.java | 7 +- src/main/resources/application-dev.yml | 2 +- src/main/resources/application-prod.yml | 2 +- .../query/repository/ContractMapper.xml | 92 ++++---- .../query/repository/PurchaseOrderMapper.xml | 64 +++--- 23 files changed, 379 insertions(+), 330 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java index 9bf81a2a..9050e636 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java @@ -15,6 +15,7 @@ import stanl_2.final_backend.domain.contract.command.application.service.ContractCommandService; import stanl_2.final_backend.domain.contract.common.response.ContractResponseMessage; +import java.security.GeneralSecurityException; import java.security.Principal; @RestController("contractController") @@ -35,7 +36,7 @@ public ContractController(ContractCommandService contractCommandService) { }) @PostMapping("") public ResponseEntity postTest(@RequestBody ContractRegistDTO contractRegistRequestDTO, - Principal principal) { + Principal principal) throws GeneralSecurityException { contractRegistRequestDTO.setMemberId(principal.getName()); contractCommandService.registerContract(contractRegistRequestDTO); @@ -55,7 +56,7 @@ public ResponseEntity postTest(@RequestBody ContractReg @PutMapping("{contractId}") public ResponseEntity putContract(@PathVariable String contractId, @RequestBody ContractModifyDTO contractModifyRequestDTO, - Principal principal) { + Principal principal) throws GeneralSecurityException { contractModifyRequestDTO.setContractId(contractId); contractModifyRequestDTO.setMemberId(principal.getName()); diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractStatusModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractStatusModifyDTO.java index d7ffce7b..8c479cca 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractStatusModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractStatusModifyDTO.java @@ -4,7 +4,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import java.util.List; @AllArgsConstructor @NoArgsConstructor diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java index 5e050d58..2b32ac17 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java @@ -5,10 +5,12 @@ import stanl_2.final_backend.domain.contract.command.application.dto.ContractRegistDTO; import stanl_2.final_backend.domain.contract.command.application.dto.ContractStatusModifyDTO; +import java.security.GeneralSecurityException; + public interface ContractCommandService { - void registerContract(ContractRegistDTO contractRegistRequestDTO); + void registerContract(ContractRegistDTO contractRegistRequestDTO) throws GeneralSecurityException; - ContractModifyDTO modifyContract(ContractModifyDTO contractModifyRequestDTO); + ContractModifyDTO modifyContract(ContractModifyDTO contractModifyRequestDTO) throws GeneralSecurityException; void deleteContract(ContractDeleteDTO contractDeleteDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java index b8546750..317497ba 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java @@ -94,8 +94,7 @@ public class Contract { private String carName; @Column(name = "CONR_STAT", nullable = false) - @ColumnDefault("'WAIT'") - private String status; + private String status = "WAIT"; @Column(name = "CONR_NO_OF_VEH", nullable = false) @ColumnDefault("1") diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index abb6f172..ac2a2381 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -12,6 +12,7 @@ import stanl_2.final_backend.domain.contract.command.domain.repository.ContractRepository; import stanl_2.final_backend.domain.contract.common.exception.ContractCommonException; import stanl_2.final_backend.domain.contract.common.exception.ContractErrorCode; +import stanl_2.final_backend.domain.customer.command.application.dto.CustomerModifyDTO; import stanl_2.final_backend.domain.customer.command.application.dto.CustomerRegistDTO; import stanl_2.final_backend.domain.customer.command.application.service.CustomerCommandService; import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; @@ -23,8 +24,7 @@ import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; import stanl_2.final_backend.domain.product.query.service.ProductService; import stanl_2.final_backend.domain.sales_history.command.application.service.SalesHistoryCommandService; -import stanl_2.final_backend.global.exception.GlobalCommonException; -import stanl_2.final_backend.global.exception.GlobalErrorCode; +import stanl_2.final_backend.global.utils.AESUtils; import java.security.GeneralSecurityException; import java.time.ZoneId; @@ -43,8 +43,9 @@ public class ContractCommandServiceImpl implements ContractCommandService { private final ProductCommandService productCommandService; private final SalesHistoryCommandService salesHistoryCommandService; private final ModelMapper modelMapper; + private final AESUtils aesUtils; - public ContractCommandServiceImpl(ContractRepository contractRepository, AuthQueryService authQueryService, CustomerQueryService customerQueryService, MemberQueryService memberQueryService, CustomerCommandService customerCommandService, ProductService productService, ProductCommandService productCommandService, SalesHistoryCommandService salesHistoryCommandService, ModelMapper modelMapper) { + public ContractCommandServiceImpl(ContractRepository contractRepository, AuthQueryService authQueryService, CustomerQueryService customerQueryService, MemberQueryService memberQueryService, CustomerCommandService customerCommandService, ProductService productService, ProductCommandService productCommandService, SalesHistoryCommandService salesHistoryCommandService, ModelMapper modelMapper, AESUtils aesUtils) { this.contractRepository = contractRepository; this.authQueryService = authQueryService; this.customerQueryService = customerQueryService; @@ -54,6 +55,7 @@ public ContractCommandServiceImpl(ContractRepository contractRepository, AuthQue this.productCommandService = productCommandService; this.salesHistoryCommandService = salesHistoryCommandService; this.modelMapper = modelMapper; + this.aesUtils = aesUtils; } private String getCurrentTime() { @@ -84,7 +86,7 @@ private String handleCustomerInfo(ContractRegistDTO contractRegistRequestDTO, St @Override @Transactional - public void registerContract(ContractRegistDTO contractRegistRequestDTO) { + public void registerContract(ContractRegistDTO contractRegistRequestDTO) throws GeneralSecurityException { // 영업사원 번호 String memberId = authQueryService.selectMemberIdByLoginId(contractRegistRequestDTO.getMemberId()); @@ -97,19 +99,11 @@ public void registerContract(ContractRegistDTO contractRegistRequestDTO) { String centerId = null; // 매장 ID 변수 선언 // 고객전화번호로 고객테이블 찾아서 고객이 있으면 넘어가고, 고객이 없으면 고객테이블에 고객 정보 넣기 - try { - customerId = handleCustomerInfo(contractRegistRequestDTO, memberId); - } catch (GeneralSecurityException e) { - throw new RuntimeException("고객 정보 처리 중 문제가 발생했습니다.", e); - } + customerId = handleCustomerInfo(contractRegistRequestDTO, memberId); // 회원의 영업 매장번호 - try { - MemberDTO memberDTO = memberQueryService.selectMemberInfo(contractRegistRequestDTO.getMemberId()); - centerId = memberDTO.getCenterId(); - } catch (Exception e) { - throw new RuntimeException("회원 정보 조회 중 문제가 발생했습니다.", e); - } + MemberDTO memberDTO = memberQueryService.selectMemberInfo(contractRegistRequestDTO.getMemberId()); + centerId = memberDTO.getCenterId(); Contract contract = modelMapper.map(contractRegistRequestDTO, Contract.class); @@ -117,35 +111,51 @@ public void registerContract(ContractRegistDTO contractRegistRequestDTO) { contract.setCenterId(centerId); // 회원의 매장번호 넣기 contract.setProductId(productId); // 제품 번호 넣기 contract.setCustomerId(customerId); + contract.setStatus("WAIT"); + + contract.setCustomerPhone(aesUtils.encrypt(contractRegistRequestDTO.getCustomerPhone())); + contract.setCustomerEmail(aesUtils.encrypt(contractRegistRequestDTO.getCustomerEmail())); + contract.setCustomerAddrress(aesUtils.encrypt(contractRegistRequestDTO.getCustomerAddrress())); + contract.setCustomerIdentifiNo(aesUtils.encrypt(contractRegistRequestDTO.getCustomerIdentifiNo())); contractRepository.save(contract); } @Override @Transactional - public ContractModifyDTO modifyContract(ContractModifyDTO contractModifyRequestDTO) { + public ContractModifyDTO modifyContract(ContractModifyDTO contractModifyRequestDTO) throws GeneralSecurityException { String memberId = authQueryService.selectMemberIdByLoginId(contractModifyRequestDTO.getMemberId()); - // 일련번호로 제품테이블의 총식별번호 찾아서 제품 가져오기 -// ProductSelectIdDTO productSelectIdDTO = productService.selectByProductSerialNumber(contractModifyRequestDTO.getSerialNum()); -// String productId = productSelectIdDTO.getId(); - - // 판매내역 수정 - - // 고객전화번호로 고객테이블 찾아서 가져오기 - // 가져온 고객 정보에 수정된 값 넣기 + CustomerModifyDTO customerModifyDTO = new CustomerModifyDTO(); + customerModifyDTO.setName(contractModifyRequestDTO.getCustomerName()); + customerModifyDTO.setAge(contractModifyRequestDTO.getCustomerAge()); + customerModifyDTO.setSex(contractModifyRequestDTO.getCustomerSex()); + customerModifyDTO.setPhone(contractModifyRequestDTO.getCustomerPhone()); + customerModifyDTO.setEmail(contractModifyRequestDTO.getCustomerEmail()); + customerModifyDTO.setCustomerId(contractModifyRequestDTO.getCustomerId()); + customerModifyDTO.setMemberId(memberId); + + customerCommandService.modifyCustomerInfo(customerModifyDTO); Contract contract = (Contract) contractRepository.findByContractIdAndMemberId(contractModifyRequestDTO.getContractId(), memberId) .orElseThrow(() -> new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND)); + // 계약서 업데이트 Contract updateContract = modelMapper.map(contractModifyRequestDTO, Contract.class); updateContract.setCreatedAt(contract.getCreatedAt()); updateContract.setUpdatedAt(contract.getUpdatedAt()); updateContract.setActive(contract.isActive()); updateContract.setCenterId(contract.getCenterId()); updateContract.setCreatedUrl(contract.getCreatedUrl()); + updateContract.setCustomerSex(contract.getCustomerSex()); + updateContract.setStatus("WAIT"); + + contract.setCustomerPhone(aesUtils.encrypt(contractModifyRequestDTO.getCustomerPhone())); + contract.setCustomerEmail(aesUtils.encrypt(contractModifyRequestDTO.getCustomerEmail())); + contract.setCustomerAddrress(aesUtils.encrypt(contractModifyRequestDTO.getCustomerAddrress())); + contract.setCustomerIdentifiNo(aesUtils.encrypt(contractModifyRequestDTO.getCustomerIdentifiNo())); contractRepository.save(updateContract); @@ -166,8 +176,6 @@ public void deleteContract(ContractDeleteDTO contractDeleteDTO) { contract.setActive(false); contract.setDeletedAt(getCurrentTime()); - // 판매내역도 false 해서 -1 - contractRepository.save(contract); } @@ -189,10 +197,20 @@ public void modifyContractStatus(ContractStatusModifyDTO contractStatusModifyDTO contractRepository.save(contract); -// ProductSelectIdDTO productSelectIdDTO = productService.selectByProductSerialNumber(contractRegistRequestDTO.getSerialNum()); -// String productId = productSelectIdDTO.getId(); -// -// // 제품 재고 수 줄이기 -// productCommandService.modifyProductStock(productId); + if (contractStatusModifyDTO.getStatus().equals("APPROVED")) { + // 판매 내역 등록 + salesHistoryCommandService.registerSalesHistory(contract.getContractId()); + + // 제품 재고 수 줄이기 + ProductSelectIdDTO productSelectIdDTO = productService.selectByProductSerialNumber(contract.getSerialNum()); + String productId = productSelectIdDTO.getId(); + productCommandService.modifyProductStock(productId); + } else if (contractStatusModifyDTO.getStatus().equals("CANCLED")) { + salesHistoryCommandService.deleteSalesHistory(contract.getContractId()); + + ProductSelectIdDTO productSelectIdDTO = productService.selectByProductSerialNumber(contract.getSerialNum()); + String productId = productSelectIdDTO.getId(); + productCommandService.deleteProductStock(productId); + } } } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java index fb7cc9af..1b93154a 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java @@ -10,7 +10,6 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; -import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.contract.common.response.ContractResponseMessage; import stanl_2.final_backend.domain.contract.query.dto.ContractSearchDTO; @@ -18,6 +17,8 @@ import stanl_2.final_backend.domain.contract.query.dto.ContractSeletIdDTO; import stanl_2.final_backend.domain.contract.query.service.ContractQueryService; +import java.security.Principal; + @Slf4j @RestController("queryContractController") @RequestMapping("/api/v1/contract") @@ -29,23 +30,20 @@ public ContractController(ContractQueryService contractQueryService) { this.contractQueryService = contractQueryService; } - /** - * [GET] http://localhost:8080/api/v1/contract/MEM_000000001?page=0&size=10 - * */ - @Operation(summary = "계약서 전체 조회") + // 영업사원 조회 + @Operation(summary = "계약서 전체 조회(영업사원)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "계약서 전체 조회 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) - @GetMapping("") - public ResponseEntity getAllContract(@PageableDefault(size = 10) Pageable pageable, - Authentication authentication) { + @GetMapping("employee") + public ResponseEntity getAllContractEmployee(@PageableDefault(size = 10) Pageable pageable, + Principal principal) { ContractSelectAllDTO contractSelectAllDTO = new ContractSelectAllDTO(); - contractSelectAllDTO.setMemberId(authentication.getName()); - contractSelectAllDTO.setRoles(authentication.getAuthorities()); + contractSelectAllDTO.setMemberId(principal.getName()); - Page responseContracts = contractQueryService.selectAll(contractSelectAllDTO, pageable); + Page responseContracts = contractQueryService.selectAllContractEmployee(contractSelectAllDTO, pageable); return ResponseEntity.ok(ContractResponseMessage.builder() .httpStatus(200) @@ -54,24 +52,20 @@ public ResponseEntity getAllContract(@PageableDefault(s .build()); } - /** - * [GET] http://localhost:8080/api/v1/contract/CON_000000001/MEM_000000001 - * */ - @Operation(summary = "계약서 상세 조회") + @Operation(summary = "계약서 상세 조회(영업사원)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "계약서 상세 조회 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) - @GetMapping("{id}") - public ResponseEntity getDetailContract(@PathVariable("id") String id, - Authentication authentication) { + @GetMapping("employee/{contractId}") + public ResponseEntity getDetailContractEmployee(@PathVariable String contractId, + Principal principal) { ContractSeletIdDTO contractSeletIdDTO = new ContractSeletIdDTO(); - contractSeletIdDTO.setContractId(id); - contractSeletIdDTO.setMemberId(authentication.getName()); - contractSeletIdDTO.setRoles(authentication.getAuthorities()); + contractSeletIdDTO.setContractId(contractId); + contractSeletIdDTO.setMemberId(principal.getName()); - ContractSeletIdDTO responseContract = contractQueryService.selectDetailContract(contractSeletIdDTO); + ContractSeletIdDTO responseContract = contractQueryService.selectDetailContractEmployee(contractSeletIdDTO); return ResponseEntity.ok(ContractResponseMessage.builder() .httpStatus(200) @@ -80,16 +74,13 @@ public ResponseEntity getDetailContract(@PathVariable(" .build()); } - /** 수정예정 - * [GET] http://localhost:8080/api/v1/contract/search?memId=MEM_000000001 - * */ - @Operation(summary = "계약서 검색 조회") + @Operation(summary = "계약서 검색 조회(영업사원)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "계약서 검색 조회 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) - @GetMapping("/search") - public ResponseEntity getContractBySearch(Authentication authentication, + @GetMapping("employee/search") + public ResponseEntity getContractBySearchEmployee(Principal principal, @RequestParam(required = false) String searchMemberId, @RequestParam(required = false) String centerId, @RequestParam(required = false) String title, @@ -103,10 +94,21 @@ public ResponseEntity getContractBySearch(Authenticatio @RequestParam(required = false) String customerPurchaseCondition, @PageableDefault(size = 10) Pageable pageable) { - String memberId = authentication.getName(); - ContractSearchDTO contractSearchDTO = new ContractSearchDTO(memberId, searchMemberId, centerId, title, startAt, endAt, - customerName, customerClassifcation, productId, status, companyName, customerPurchaseCondition, authentication.getAuthorities()); - Page responseContracts = contractQueryService.selectBySearch(contractSearchDTO, pageable); + ContractSearchDTO contractSearchDTO = new ContractSearchDTO(); + contractSearchDTO.setMemberId(principal.getName()); + contractSearchDTO.setSearchMemberId(searchMemberId); + contractSearchDTO.setCenterId(centerId); + contractSearchDTO.setTitle(title); + contractSearchDTO.setStartAt(startAt); + contractSearchDTO.setEndAt(endAt); + contractSearchDTO.setCustomerName(customerName); + contractSearchDTO.setCustomerClassifcation(customerClassifcation); + contractSearchDTO.setProductId(productId); + contractSearchDTO.setStatus(status); + contractSearchDTO.setCompanyName(companyName); + contractSearchDTO.setCustomerPurchaseCondition(customerPurchaseCondition); + + Page responseContracts = contractQueryService.selectBySearchEmployee(contractSearchDTO, pageable); return ResponseEntity.ok(ContractResponseMessage.builder() .httpStatus(200) @@ -115,5 +117,84 @@ public ResponseEntity getContractBySearch(Authenticatio .build()); } + // 영업담당자, 관리자 조회 + @Operation(summary = "계약서 전체 조회(영업관리자, 담당자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "계약서 전체 조회 성공", + content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) + }) + @GetMapping("") + public ResponseEntity getAllContract(@PageableDefault(size = 10) Pageable pageable) { + + ContractSelectAllDTO contractSelectAllDTO = new ContractSelectAllDTO(); + + Page responseContracts = contractQueryService.selectAllContract(contractSelectAllDTO, pageable); + + return ResponseEntity.ok(ContractResponseMessage.builder() + .httpStatus(200) + .msg("계약서 전체 조회 성공") + .result(responseContracts) + .build()); + } + + @Operation(summary = "계약서 상세 조회(영업사원)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "계약서 상세 조회 성공", + content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) + }) + @GetMapping("{contractId}") + public ResponseEntity getDetailContract(@PathVariable String contractId) { + ContractSeletIdDTO contractSeletIdDTO = new ContractSeletIdDTO(); + contractSeletIdDTO.setContractId(contractId); + + ContractSeletIdDTO responseContract = contractQueryService.selectDetailContract(contractSeletIdDTO); + + return ResponseEntity.ok(ContractResponseMessage.builder() + .httpStatus(200) + .msg("계약서 상세조회 성공") + .result(responseContract) + .build()); + } + + @Operation(summary = "계약서 검색 조회(영업사원)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "계약서 검색 조회 성공", + content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) + }) + @GetMapping("search") + public ResponseEntity getContractBySearch(@RequestParam(required = false) String searchMemberId, + @RequestParam(required = false) String centerId, + @RequestParam(required = false) String title, + @RequestParam(required = false) String startAt, + @RequestParam(required = false) String endAt, + @RequestParam(required = false) String customerName, + @RequestParam(required = false) String customerClassifcation, + @RequestParam(required = false) String productId, + @RequestParam(required = false) String status, + @RequestParam(required = false) String companyName, + @RequestParam(required = false) String customerPurchaseCondition, + @PageableDefault(size = 10) Pageable pageable) { + + ContractSearchDTO contractSearchDTO = new ContractSearchDTO(); + contractSearchDTO.setSearchMemberId(searchMemberId); + contractSearchDTO.setCenterId(centerId); + contractSearchDTO.setTitle(title); + contractSearchDTO.setStartAt(startAt); + contractSearchDTO.setEndAt(endAt); + contractSearchDTO.setCustomerName(customerName); + contractSearchDTO.setCustomerClassifcation(customerClassifcation); + contractSearchDTO.setProductId(productId); + contractSearchDTO.setStatus(status); + contractSearchDTO.setCompanyName(companyName); + contractSearchDTO.setCustomerPurchaseCondition(customerPurchaseCondition); + + Page responseContracts = contractQueryService.selectBySearch(contractSearchDTO, pageable); + + return ResponseEntity.ok(ContractResponseMessage.builder() + .httpStatus(200) + .msg("계약서 검색 조회 성공") + .result(responseContracts) + .build()); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java index 20d295d8..5ad874b1 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java @@ -1,15 +1,11 @@ package stanl_2.final_backend.domain.contract.query.dto; import lombok.*; -import org.springframework.security.core.GrantedAuthority; - -import java.util.Collection; @AllArgsConstructor @NoArgsConstructor @Getter @Setter -@ToString public class ContractSearchDTO { private String contractId; @@ -47,23 +43,4 @@ public class ContractSearchDTO { private String startAt; private String endAt; private String carName; - private Collection roles; - - public ContractSearchDTO(String memberId, String searchMemberId, String centerId, String title, String startAt, String endAt, - String customerName, String customerClassifcation, String productId, String status, - String companyName, String customerPurchaseCondition, Collection roles) { - this.memberId = memberId; - this.searchMemberId = searchMemberId; - this.centerId = centerId; - this.title = title; - this.startAt = startAt; - this.endAt = endAt; - this.customerName = customerName; - this.customerClassifcation = customerClassifcation; - this.productId = productId; - this.status = status; - this.companyName = companyName; - this.customerPurchaseCondition = customerPurchaseCondition; - this.roles = roles; - } } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java index 3aff0a4d..d29af5be 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java @@ -1,15 +1,11 @@ package stanl_2.final_backend.domain.contract.query.dto; import lombok.*; -import org.springframework.security.core.GrantedAuthority; - -import java.util.Collection; @AllArgsConstructor @NoArgsConstructor @Getter @Setter -@ToString public class ContractSelectAllDTO { private String contractId; @@ -25,5 +21,4 @@ public class ContractSelectAllDTO { private String customerId; private String productId; private String carName; - private Collection roles; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java index 38d19932..b42ab15d 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java @@ -1,15 +1,11 @@ package stanl_2.final_backend.domain.contract.query.dto; import lombok.*; -import org.springframework.security.core.GrantedAuthority; - -import java.util.Collection; @AllArgsConstructor @NoArgsConstructor @Getter @Setter -@ToString public class ContractSeletIdDTO { private String contractId; @@ -46,5 +42,4 @@ public class ContractSeletIdDTO { private String centerId; private String customerId; private String productId; - private Collection roles; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java index 7ad187f8..13a0345d 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java @@ -1,32 +1,40 @@ package stanl_2.final_backend.domain.contract.query.repository; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; import stanl_2.final_backend.domain.contract.query.dto.ContractSearchDTO; import stanl_2.final_backend.domain.contract.query.dto.ContractSelectAllDTO; import stanl_2.final_backend.domain.contract.query.dto.ContractSeletIdDTO; import java.util.List; -import java.util.Map; @Mapper public interface ContractMapper { - ContractSeletIdDTO findContractByIdAndMemId(ContractSeletIdDTO contractDTO); + ContractSeletIdDTO findContractByIdAndMemId(@Param("purchaseOrderId") String purchaseOrderId, + @Param("memberId") String memberId); - List findContractBySearchAndMemberId(Map map); + List findContractBySearchAndMemberId(@Param("offset") int offset, + @Param("pageSize") int pageSize, + @Param("contractSearchDTO") ContractSearchDTO contractSearchDTO); - int findContractBySearchAndMemberIdCount(Map map); + int findContractBySearchAndMemberIdCount(ContractSearchDTO contractSearchDTO); - List findContractAllByMemId(Map map); + List findContractAllByMemId(@Param("offset") int offset, + @Param("pageSize") int pageSize, + @Param("memberId") String memberId); int findContractCountByMemId(String memId); - List findContractAll(Map params); + List findContractAll(@Param("offset") int offset, + @Param("pageSize") int pageSize); int findContractCount(); ContractSeletIdDTO findContractById(String contractId); - List findContractBySearch(Map map); + List findContractBySearch(@Param("offset") int offset, + @Param("pageSize") int pageSize, + @Param("contractSearchDTO") ContractSearchDTO contractSearchDTO); - int findContractBySearchCount(Map map); + int findContractBySearchCount(ContractSearchDTO contractSearchDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java index 1fb39b8d..0d1e7ba3 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java @@ -2,19 +2,23 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.contract.query.dto.ContractSearchDTO; import stanl_2.final_backend.domain.contract.query.dto.ContractSelectAllDTO; import stanl_2.final_backend.domain.contract.query.dto.ContractSeletIdDTO; -import java.util.Map; - public interface ContractQueryService { + + Page selectAllContract(ContractSelectAllDTO contractSelectAllDTO, Pageable pageable); + ContractSeletIdDTO selectDetailContract(ContractSeletIdDTO contractDTO); Page selectBySearch(ContractSearchDTO contractSearchDTO, Pageable pageable); - Page selectAll(ContractSelectAllDTO contractSelectAllDTO, Pageable pageable); + Page selectAllContractEmployee(ContractSelectAllDTO contractSelectAllDTO, Pageable pageable); + + ContractSeletIdDTO selectDetailContractEmployee(ContractSeletIdDTO contractSeletIdDTO); + + Page selectBySearchEmployee(ContractSearchDTO contractSearchDTO, Pageable pageable); + - ContractSeletIdDTO selectContractByIdAndMemberId(String contractId, String memberId); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java index d717eeec..65749787 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java @@ -3,9 +3,11 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringEscapeUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.contract.common.exception.ContractCommonException; @@ -15,12 +17,8 @@ import stanl_2.final_backend.domain.contract.query.dto.ContractSeletIdDTO; import stanl_2.final_backend.domain.contract.query.repository.ContractMapper; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; -import stanl_2.final_backend.global.exception.GlobalCommonException; -import stanl_2.final_backend.global.exception.GlobalErrorCode; -import java.util.HashMap; import java.util.List; -import java.util.Map; @Slf4j @Service("queryContractService") @@ -28,184 +26,139 @@ public class ContractQueryServiceImpl implements ContractQueryService { private final ContractMapper contractMapper; private final AuthQueryService authQueryService; + private final RedisTemplate redisTemplate; @Autowired - public ContractQueryServiceImpl(ContractMapper contractMapper, AuthQueryService authQueryService) { + public ContractQueryServiceImpl(ContractMapper contractMapper, AuthQueryService authQueryService, @Qualifier("redisTemplate") RedisTemplate redisTemplate) { this.contractMapper = contractMapper; this.authQueryService = authQueryService; + this.redisTemplate = redisTemplate; } + // 영업사원 조회 // 계약서 전체조회 @Override @Transactional(readOnly = true) - public Page selectAll(ContractSelectAllDTO contractSelectAllDTO, Pageable pageable) { + public Page selectAllContractEmployee(ContractSelectAllDTO contractSelectAllDTO, Pageable pageable) { - if(contractSelectAllDTO.getRoles().stream() - .anyMatch(role -> "ROLE_EMPLOYEE".equals(role.getAuthority()))){ + String memberId = authQueryService.selectMemberIdByLoginId(contractSelectAllDTO.getMemberId()); - String memberId = authQueryService.selectMemberIdByLoginId(contractSelectAllDTO.getMemberId()); + int offset = Math.toIntExact(pageable.getOffset()); + int pageSize = pageable.getPageSize(); - Map params = new HashMap<>(); - params.put("memId", memberId); - params.put("offset", pageable.getOffset()); - params.put("pageSize", pageable.getPageSize()); + String caschKey = "myCache::contracts::offset=" + offset + "::pageSize=" + pageSize; - List contractList = contractMapper.findContractAllByMemId(params); + List contracts = (List) redisTemplate.opsForValue().get(caschKey); - if (contractList == null || contractList.isEmpty()) { + if (contracts == null) { + contracts = contractMapper.findContractAllByMemId(offset, pageSize, memberId); + + if (contracts == null) { throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); } - int total = contractMapper.findContractCountByMemId(memberId); + redisTemplate.opsForValue().set(caschKey, contracts); + } - return new PageImpl<>(contractList, pageable, total); + Integer count = contractMapper.findContractCountByMemId(memberId); + int totalContract = (count != null) ? count : 0; - } else if (contractSelectAllDTO.getRoles().stream() - .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()) || "ROLE_REPRESENTATIVE".equals(role.getAuthority()))) { + return new PageImpl<>(contracts, pageable, totalContract); + } - Map params = new HashMap<>(); - params.put("offset", pageable.getOffset()); - params.put("pageSize", pageable.getPageSize()); + // 계약서 상세조회 + @Override + @Transactional(readOnly = true) + public ContractSeletIdDTO selectDetailContractEmployee(ContractSeletIdDTO contractSeletIdDTO) { - List contractList = contractMapper.findContractAll(params); + String memberId = authQueryService.selectMemberIdByLoginId(contractSeletIdDTO.getMemberId()); - if (contractList == null || contractList.isEmpty()) { - throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); - } + ContractSeletIdDTO responseContract = contractMapper.findContractByIdAndMemId(contractSeletIdDTO.getContractId(), memberId); - int total = contractMapper.findContractCount(); + // 이스케이프된 HTML 제거 + String unescapedHtml = StringEscapeUtils.unescapeJson(responseContract.getCreatedUrl()); + responseContract.setCreatedUrl(unescapedHtml); - return new PageImpl<>(contractList, pageable, total); - } else { - throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); - } + return responseContract; } - // 계약서 상세조회 @Override @Transactional(readOnly = true) - public ContractSeletIdDTO selectDetailContract(ContractSeletIdDTO contractSeletIdDTO) { + public Page selectBySearchEmployee(ContractSearchDTO contractSearchDTO, Pageable pageable) { + String memberId = authQueryService.selectMemberIdByLoginId(contractSearchDTO.getMemberId()); + contractSearchDTO.setMemberId(memberId); - if(contractSeletIdDTO.getRoles().stream() - .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()))){ + int offset = Math.toIntExact(pageable.getOffset()); + int pageSize = pageable.getPageSize(); + List contracts = contractMapper.findContractBySearchAndMemberId(offset, pageSize, contractSearchDTO); - String memberId = authQueryService.selectMemberIdByLoginId(contractSeletIdDTO.getMemberId()); - contractSeletIdDTO.setMemberId(memberId); + if (contracts == null) { + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); + } - ContractSeletIdDTO responseContract = contractMapper.findContractByIdAndMemId(contractSeletIdDTO); + Integer count = contractMapper.findContractBySearchAndMemberIdCount(contractSearchDTO); + int totalContract = (count != null) ? count : 0; - if (responseContract == null) { - throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); - } + return new PageImpl<>(contracts, pageable, totalContract); + } - // 이스케이프된 HTML 제거 - String unescapedHtml = StringEscapeUtils.unescapeJson(responseContract.getCreatedUrl()); - responseContract.setCreatedUrl(unescapedHtml); + // 영업담당자, 관리자 조회 + @Override + @Transactional(readOnly = true) + public Page selectAllContract(ContractSelectAllDTO contractSelectAllDTO, Pageable pageable) { - return responseContract; + int offset = Math.toIntExact(pageable.getOffset()); + int pageSize = pageable.getPageSize(); - } else if (contractSeletIdDTO.getRoles().stream() - .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()) || "ROLE_REPRESENTATIVE".equals(role.getAuthority()))) { + String caschKey = "myCache::contracts::offset=" + offset + "::pageSize=" + pageSize; - ContractSeletIdDTO responseContract = contractMapper.findContractById(contractSeletIdDTO.getContractId()); + List contracts = (List) redisTemplate.opsForValue().get(caschKey); - if (responseContract == null) { + if (contracts == null) { + contracts = contractMapper.findContractAll(offset, pageSize); + + if (contracts == null) { throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); } - // 이스케이프된 HTML 제거 - String unescapedHtml = StringEscapeUtils.unescapeJson(responseContract.getCreatedUrl()); - responseContract.setCreatedUrl(unescapedHtml); + redisTemplate.opsForValue().set(caschKey, contracts); + } - return responseContract; + Integer count = contractMapper.findContractCount(); + int totalContract = (count != null) ? count : 0; - } else { - throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); - } + return new PageImpl<>(contracts, pageable, totalContract); } + // 계약서 상세조회 @Override @Transactional(readOnly = true) - public Page selectBySearch(ContractSearchDTO contractSearchDTO, Pageable pageable) { - - if (contractSearchDTO.getRoles().stream() - .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()))) { - - String memberId = authQueryService.selectMemberIdByLoginId(contractSearchDTO.getMemberId()); - - Map map = new HashMap<>(); - map.put("memberId", memberId); - map.put("centerId", contractSearchDTO.getCenterId()); - map.put("title", contractSearchDTO.getTitle()); - map.put("startAt", contractSearchDTO.getStartAt()); - map.put("endAt", contractSearchDTO.getEndAt()); - map.put("customerName", contractSearchDTO.getCustomerName()); - map.put("customerClassifcation", contractSearchDTO.getCustomerClassifcation()); - map.put("productId", contractSearchDTO.getProductId()); - map.put("status", contractSearchDTO.getStatus()); - map.put("companyName", contractSearchDTO.getCompanyName()); - map.put("customerPurchaseCondition", contractSearchDTO.getCustomerPurchaseCondition()); - map.put("pageSize", pageable.getPageSize()); - map.put("offset", pageable.getOffset()); - - List contracts = contractMapper.findContractBySearchAndMemberId(map); - - if (contracts == null || contracts.isEmpty()) { - throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); - } - - int total = contractMapper.findContractBySearchAndMemberIdCount(map); - - return new PageImpl<>(contracts, pageable, total); - - } else if (contractSearchDTO.getRoles().stream() - .anyMatch(role -> "ROLE_MANAGER".equals(role.getAuthority()) || "ROLE_REPRESENTATIVE".equals(role.getAuthority()))) { - - Map map = new HashMap<>(); - map.put("searchMemberId", contractSearchDTO.getSearchMemberId()); - map.put("centerId", contractSearchDTO.getCenterId()); - map.put("title", contractSearchDTO.getTitle()); - map.put("startAt", contractSearchDTO.getStartAt()); - map.put("endAt", contractSearchDTO.getEndAt()); - map.put("customerName", contractSearchDTO.getCustomerName()); - map.put("customerClassifcation", contractSearchDTO.getCustomerClassifcation()); - map.put("productId", contractSearchDTO.getProductId()); - map.put("status", contractSearchDTO.getStatus()); - map.put("companyName", contractSearchDTO.getCompanyName()); - map.put("customerPurchaseCondition", contractSearchDTO.getCustomerPurchaseCondition()); - map.put("pageSize", pageable.getPageSize()); - map.put("offset", pageable.getOffset()); - - List contracts = contractMapper.findContractBySearch(map); - - if (contracts == null || contracts.isEmpty()) { - throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); - } + public ContractSeletIdDTO selectDetailContract(ContractSeletIdDTO contractSeletIdDTO) { - int total = contractMapper.findContractBySearchCount(map); + ContractSeletIdDTO responseContract = contractMapper.findContractById(contractSeletIdDTO.getContractId()); - return new PageImpl<>(contracts, pageable, total); + // 이스케이프된 HTML 제거 + String unescapedHtml = StringEscapeUtils.unescapeJson(responseContract.getCreatedUrl()); + responseContract.setCreatedUrl(unescapedHtml); - } else { - throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); - } + return responseContract; } - @Override @Transactional(readOnly = true) - public ContractSeletIdDTO selectContractByIdAndMemberId(String contractId, String memberId) { - - ContractSeletIdDTO contractDTO = new ContractSeletIdDTO(); - contractDTO.setContractId(contractId); - contractDTO.setMemberId(memberId); + public Page selectBySearch(ContractSearchDTO contractSearchDTO, Pageable pageable) { - ContractSeletIdDTO contractSelectDto = contractMapper.findContractByIdAndMemId(contractDTO); + int offset = Math.toIntExact(pageable.getOffset()); + int pageSize = pageable.getPageSize(); + List contracts = contractMapper.findContractBySearch(offset, pageSize, contractSearchDTO); - if (contractSelectDto == null) { + if (contracts == null) { throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); } - return contractSelectDto; + Integer count = contractMapper.findContractBySearchCount(contractSearchDTO); + int totalContract = (count != null) ? count : 0; + + return new PageImpl<>(contracts, pageable, totalContract); } -} +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java index e88a5ae1..39a3b57e 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java @@ -50,6 +50,7 @@ public void registerCustomerInfo(CustomerRegistDTO customerRegistDTO) throws Gen @Transactional public void modifyCustomerInfo(CustomerModifyDTO customerModifyDTO) throws GeneralSecurityException { + System.out.println("아아아: " + customerModifyDTO.getCustomerId()); Customer customer = customerRepository.findById(customerModifyDTO.getCustomerId()) .orElseThrow(() -> new CustomerCommonException(CustomerErrorCode.CUSTOMER_NOT_FOUND)); diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java index 86521df3..297f1e7d 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java @@ -97,7 +97,11 @@ public Page findCustomerByCondition(Pageable pageable, CustomerSear @Transactional(readOnly = true) public CustomerDTO selectCustomerInfoByPhone(String customerPhone) throws GeneralSecurityException { - CustomerDTO customerInfoDTO = customerMapper.selectCustomerInfoByPhone(customerPhone); + String encryptedPhone = aesUtils.encrypt(customerPhone); + + CustomerDTO customerInfoDTO = customerMapper.selectCustomerInfoByPhone(encryptedPhone); + + System.out.println("쿼리쪽 고객정보: " + customerInfoDTO); if (customerInfoDTO == null) { return customerInfoDTO; diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java b/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java index 90c0a072..238694d1 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java @@ -7,7 +7,6 @@ import stanl_2.final_backend.domain.order.query.dto.OrderSelectSearchDTO; import java.util.List; -import java.util.Map; @Mapper public interface OrderMapper { @@ -23,8 +22,6 @@ List findSearchOrderByMemberId(@Param("offset") int offset int findOrderSearchCountByMemberId(OrderSelectSearchDTO orderSelectSearchDTO); - List findSearchOrderByMemberId(Map map); - List findAllOrder(int offset, int pageSize); int findOrderCount(); diff --git a/src/main/java/stanl_2/final_backend/domain/product/command/application/command/service/ProductCommandService.java b/src/main/java/stanl_2/final_backend/domain/product/command/application/command/service/ProductCommandService.java index b08a5329..4c973b51 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/command/application/command/service/ProductCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/product/command/application/command/service/ProductCommandService.java @@ -1,7 +1,7 @@ package stanl_2.final_backend.domain.product.command.application.command.service; -import stanl_2.final_backend.domain.contract.command.application.dto.ContractDeleteDTO; - public interface ProductCommandService { void modifyProductStock(String productId); + + void deleteProductStock(String productId); } diff --git a/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/service/ProductCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/service/ProductCommandServiceImpl.java index a02fd6bb..65b0cca3 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/service/ProductCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/service/ProductCommandServiceImpl.java @@ -25,4 +25,13 @@ public void modifyProductStock(String productId) { productRepository.save(product); } + + @Override + public void deleteProductStock(String productId) { + Product product = productRepository.findById(productId) + .orElseThrow(() -> new ProductCommonException(ProductErrorCode.PRODUCT_NOT_FOUND)); + + product.setStock(product.getStock() + 1); + productRepository.save(product); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java index af63e466..728dc983 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java @@ -30,7 +30,8 @@ List findSearchPurchaseOrder(@Param("offset") int PurchaseOrderSelectIdDTO findPurchaseOrderByPurchaseOrderId(String purchaseOrderId); - List findAllPurchaseOrder(int offset, int pageSize); + List findAllPurchaseOrder(@Param("pageSize") int offset, + @Param("pageSize") int pageSize); Integer findAllPurchaseOrderCount(); diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java index c020f5a9..146e6d24 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.purchase_order.query.service; +import org.apache.commons.lang3.StringEscapeUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; @@ -41,6 +42,10 @@ public PurchaseOrderSelectIdDTO selectDetailPurchaseOrderAdmin(PurchaseOrderSele if (purchaseOrder == null) { throw new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND); } + + String unescapedUrl = StringEscapeUtils.unescapeJson(purchaseOrder.getContent()); + purchaseOrder.setContent(unescapedUrl); + return purchaseOrder; } @@ -53,7 +58,7 @@ public Page selectAllPurchaseOrderAdmin(Pageable page int offset = Math.toIntExact(pageable.getOffset()); int pageSize = pageable.getPageSize(); - String cacheKey = "myCache::purchaseOrders::offset=" + offset + "::size=" + pageSize; + String cacheKey = "myCache::purchaseOrders::offset=" + offset + "::pageSize=" + pageSize; // 캐시 조회 List purchaseOrders = (List) redisTemplate.opsForValue().get(cacheKey); diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index e9390662..aabe59aa 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: create + ddl-auto: update logging: level: diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 6ff598fc..520817c3 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: create + ddl-auto: update logging: level: diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index e0d1e918..6d861024 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -80,7 +80,7 @@ - SELECT a.conr_id, a.conr_ttl, @@ -159,7 +159,7 @@ AND a.active = TRUE; - SELECT a.conr_id, a.conr_ttl, @@ -176,7 +176,7 @@ LIMIT #{pageSize} OFFSET #{offset}; - SELECT a.conr_id, a.conr_cust_name, @@ -194,7 +194,7 @@ LIMIT #{pageSize} OFFSET #{offset}; - SELECT a.conr_id, a.conr_cust_name, @@ -210,40 +210,40 @@ a.active FROM tb_contract a - a.mem_id = #{memberId} + a.mem_id = #{contractSearchDTO.memberId} AND a.active = TRUE - - AND a.cent_id = #{centerId} + + AND a.cent_id = #{contractSearchDTO.centerId} - - AND a.conr_name LIKE CONCAT('%', #{title}, '%') + + AND a.conr_name LIKE CONCAT('%', #{contractSearchDTO.title}, '%') - - AND a.conr_cust_name LIKE CONCAT('%', #{customerName}, '%') + + AND a.conr_cust_name LIKE CONCAT('%', #{contractSearchDTO.customerName}, '%') - - AND a.conr_cust_cla LIKE CONCAT('%', #{customerClassifcation}, '%') + + AND a.conr_cust_cla LIKE CONCAT('%', #{contractSearchDTO.customerClassifcation}, '%') - - AND a.conr_stat = #{status} + + AND a.conr_stat = #{contractSearchDTO.status} - - AND a.conr_comp_name LIKE CONCAT('%', #{companyName}, '%') + + AND a.conr_comp_name LIKE CONCAT('%', #{contractSearchDTO.companyName}, '%') - - AND a.conr_cust_pur_cond LIKE CONCAT('%', #{customerPurchaseCondition}, '%') + + AND a.conr_cust_pur_cond LIKE CONCAT('%', #{contractSearchDTO.customerPurchaseCondition}, '%') - - AND a.created_at BETWEEN #{startAt} AND #{endAt} + + AND a.created_at BETWEEN #{contractSearchDTO.startAt} AND #{contractSearchDTO.endAt} - - AND b.prod_name LIKE CONCAT('%', #{productId}, '%') + + AND b.prod_name LIKE CONCAT('%', #{contractSearchDTO.productId}, '%') ORDER BY a.created_at DESC LIMIT #{pageSize} OFFSET #{offset}; - SELECT a.conr_id, a.conr_cust_name, @@ -260,39 +260,39 @@ FROM tb_contract a a.active = TRUE - - AND a.cent_id = #{centerId} + + AND a.cent_id = #{contractSearchDTO.centerId} - - AND a.conr_name LIKE CONCAT('%', #{title}, '%') + + AND a.conr_name LIKE CONCAT('%', #{contractSearchDTO.title}, '%') - - AND a.conr_cust_name LIKE CONCAT('%', #{customerName}, '%') + + AND a.conr_cust_name LIKE CONCAT('%', #{contractSearchDTO.customerName}, '%') - - AND a.conr_cust_cla LIKE CONCAT('%', #{customerClassifcation}, '%') + + AND a.conr_cust_cla LIKE CONCAT('%', #{contractSearchDTO.customerClassifcation}, '%') - - AND a.conr_stat = #{status} + + AND a.conr_stat = #{contractSearchDTO.status} - - AND a.conr_comp_name LIKE CONCAT('%', #{companyName}, '%') + + AND a.conr_comp_name LIKE CONCAT('%', #{contractSearchDTO.companyName}, '%') - - AND a.conr_cust_pur_cond LIKE CONCAT('%', #{customerPurchaseCondition}, '%') + + AND a.conr_cust_pur_cond LIKE CONCAT('%', #{contractSearchDTO.customerPurchaseCondition}, '%') - - AND a.created_at BETWEEN #{startAt} AND #{endAt} + + AND a.created_at BETWEEN #{contractSearchDTO.startAt} AND #{contractSearchDTO.endAt} - - AND b.prod_name LIKE CONCAT('%', #{productId}, '%') + + AND b.prod_name LIKE CONCAT('%', #{contractSearchDTO.productId}, '%') ORDER BY a.created_at DESC LIMIT #{pageSize} OFFSET #{offset}; - SELECT COUNT(*) AS cnt FROM tb_contract a @@ -307,11 +307,11 @@ WHERE a.active = TRUE; - SELECT COUNT(*) FROM tb_contract a - a.mem_id = #{memId} + a.mem_id = #{memberId} AND a.active = TRUE AND a.cent_id = #{centerId} @@ -343,7 +343,7 @@ ; - SELECT COUNT(*) FROM tb_contract a diff --git a/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml b/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml index d51523ca..bc1684fe 100644 --- a/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml @@ -103,22 +103,22 @@ LEFT JOIN tb_product f ON e.prod_id = f.prod_id - a.mem_id = #{purchaseOrderSelectSearchDTOtitle.memberId} + a.mem_id = #{purchaseOrderSelectSearchDTO.memberId} a.active = TRUE - - AND a.pur_ord_ttl LIKE CONCAT('%', #{purchaseOrderSelectSearchDTOtitle}, '%') + + AND a.pur_ord_ttl LIKE CONCAT('%', #{purchaseOrderSelectSearchDTO.title}, '%') - - AND a.pur_ord_stat = #{purchaseOrderSelectSearchDTOstatus} + + AND a.pur_ord_stat = #{purchaseOrderSelectSearchDTO.status} - - AND a.admin_id = #{purchaseOrderSelectSearchDTOadminId} + + AND a.admin_id = #{purchaseOrderSelectSearchDTO.adminId} - - AND a.mem_id = #{purchaseOrderSelectSearchDTOsearchMemberId} + + AND a.mem_id = #{purchaseOrderSelectSearchDTO.searchMemberId} - - AND a.created_at BETWEEN #{purchaseOrderSelectSearchDTOstartDate} AND #{purchaseOrderSelectSearchDTOendDate} + + AND a.created_at BETWEEN #{purchaseOrderSelectSearchDTO.startDate} AND #{purchaseOrderSelectSearchDTO.endDate} ORDER BY a.created_at DESC @@ -130,22 +130,22 @@ COUNT(*) AS cnt FROM tb_purchase_order a - a.mem_id = #{purchaseOrderSelectSearchDTOtitle.memberId} + a.mem_id = #{purchaseOrderSelectSearchDTO.memberId} AND a.active = TRUE - - AND a.ord_ttl LIKE CONCAT('%', #{title}, '%') + + AND a.ord_ttl LIKE CONCAT('%', #{purchaseOrderSelectSearchDTO.title}, '%') - - AND a.ord_stat = #{status} + + AND a.ord_stat = #{purchaseOrderSelectSearchDTO.status} - - AND a.admin_id = #{adminId} + + AND a.admin_id = #{purchaseOrderSelectSearchDTO.adminId} - - AND a.mem_id = #{searchMemberId} + + AND a.mem_id = #{purchaseOrderSelectSearchDTO.searchMemberId} - - AND a.created_at BETWEEN #{startDate} AND #{endDate} + + AND a.created_at BETWEEN #{purchaseOrderSelectSearchDTO.startDate} AND #{purchaseOrderSelectSearchDTO.endDate} @@ -223,20 +223,20 @@ ON e.prod_id = f.prod_id a.active = TRUE - - AND a.pur_ord_ttl LIKE CONCAT('%', #{purchaseOrderSelectSearchDTOtitle}, '%') + + AND a.pur_ord_ttl LIKE CONCAT('%', #{purchaseOrderSelectSearchDTO.title}, '%') - - AND a.pur_ord_stat = #{purchaseOrderSelectSearchDTOstatus} + + AND a.pur_ord_stat = #{purchaseOrderSelectSearchDTO.status} - - AND a.admin_id = #{purchaseOrderSelectSearchDTOadminId} + + AND a.admin_id = #{purchaseOrderSelectSearchDTO.adminId} - - AND a.mem_id = #{purchaseOrderSelectSearchDTOsearchMemberId} + + AND a.mem_id = #{purchaseOrderSelectSearchDTO.searchMemberId} - - AND a.created_at BETWEEN #{purchaseOrderSelectSearchDTOstartDate} AND #{purchaseOrderSelectSearchDTOendDate} + + AND a.created_at BETWEEN #{purchaseOrderSelectSearchDTO.startDate} AND #{purchaseOrderSelectSearchDTO.endDate} ORDER BY a.created_at DESC From 944eda6804d17806dc24a1d119840596f5580992 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 21 Nov 2024 00:58:59 +0900 Subject: [PATCH 246/563] =?UTF-8?q?feat:=20=EB=AC=B8=EC=A0=9C=20=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=A1=B0=ED=9A=8C=20=EA=B4=80=EB=A0=A8(#62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/aggregate/entity/Problem.java | 2 +- .../query/controller/ProblemController.java | 32 ++--- .../domain/problem/query/dto/ProblemDTO.java | 10 +- .../problem/query/dto/ProblemSearchDTO.java | 21 ++- .../query/repository/ProblemMapper.java | 1 - .../problem/query/service/ProblemService.java | 4 +- .../query/service/ProblemServiceImpl.java | 1 - .../query/controller/PromotionController.java | 2 - .../query/repository/ProblemMapper.xml | 126 ++++++++---------- 9 files changed, 100 insertions(+), 99 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java index 5d3dbda6..3f604990 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java @@ -46,7 +46,7 @@ public class Problem { @Column(name = "ACTIVE", nullable = false) private Boolean active = true; - @Column(name = "CUST_ID", nullable = false) + @Column(name = "CST_ID", nullable = false) private String customerId; @Column(name = "MEM_ID", nullable = false) diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/controller/ProblemController.java b/src/main/java/stanl_2/final_backend/domain/problem/query/controller/ProblemController.java index c227d1c7..050e4dc3 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/query/controller/ProblemController.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/controller/ProblemController.java @@ -11,12 +11,12 @@ import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.problem.common.response.ProblemResponseMessage; +import stanl_2.final_backend.domain.problem.query.dto.ProblemDTO; +import stanl_2.final_backend.domain.problem.query.dto.ProblemSearchDTO; import stanl_2.final_backend.domain.problem.query.service.ProblemService; -import stanl_2.final_backend.domain.promotion.common.response.PromotionResponseMessage; -import stanl_2.final_backend.domain.promotion.query.dto.PromotionDTO; -import stanl_2.final_backend.domain.promotion.query.dto.PromotionSearchDTO; -@RestController("queryPromotionController") +@RestController("queryProblemController") @RequestMapping("/api/v1/problem") public class ProblemController { @@ -30,33 +30,35 @@ public ProblemController(ProblemService problemService) { @Operation(summary = "문제사항 조건별 조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = PromotionResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = ProblemResponseMessage.class))}) }) @GetMapping - public ResponseEntity> getPromotions( + public ResponseEntity> getProblems( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(required = false) String title, @RequestParam(required = false) String memberId, + @RequestParam(required = false) String productId, + @RequestParam(required = false) String customerId, @RequestParam(required = false) String startDate, @RequestParam(required = false) String endDate ) { Pageable pageable = PageRequest.of(page, size); - PromotionSearchDTO promotionsearchDTO = new PromotionSearchDTO(title, memberId, startDate, endDate); - Page promotionDTOPage = promotionService.findPromotions(pageable,promotionsearchDTO); + ProblemSearchDTO problemsearchDTO = new ProblemSearchDTO(title, memberId, productId, customerId, startDate, endDate); + Page problemDTOPage = problemService.findProblems(pageable, problemsearchDTO); - return ResponseEntity.ok(promotionDTOPage); + return ResponseEntity.ok(problemDTOPage); } - @Operation(summary = "프로모션 Id로 조회") + @Operation(summary = "문제사항 Id로 조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = PromotionResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = ProblemResponseMessage.class))}) }) - @GetMapping("{promotionId}") - public ResponseEntity getPromotion(@PathVariable String promotionId){ - PromotionDTO promotionDTO = promotionService.findPromotion(promotionId); - return ResponseEntity.ok(promotionDTO); + @GetMapping("{problemId}") + public ResponseEntity getProblem(@PathVariable String problemId){ + ProblemDTO problemDTO = problemService.findProblem(problemId); + return ResponseEntity.ok(problemDTO); } } diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemDTO.java b/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemDTO.java index a818b24a..29f4745b 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemDTO.java @@ -1,7 +1,15 @@ package stanl_2.final_backend.domain.problem.query.dto; -import jakarta.persistence.Column; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter public class ProblemDTO { private String problemId; diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemSearchDTO.java index a27391e7..99bf78c1 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemSearchDTO.java @@ -1,8 +1,18 @@ package stanl_2.final_backend.domain.problem.query.dto; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter public class ProblemSearchDTO { - private String promotionId; + private String problemId; private String title; + private String content; private String memberId; private String productId; private String customerId; @@ -11,4 +21,13 @@ public class ProblemSearchDTO { private String deletedAt; private String startDate; private String endDate; + + public ProblemSearchDTO(String title, String memberId, String productId, String customerId, String startDate, String endDate) { + this.title = title; + this.memberId = memberId; + this.productId = productId; + this.customerId = customerId; + this.startDate = startDate; + this.endDate = endDate; + } } diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.java b/src/main/java/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.java index aba48127..5ff4e1e2 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.java @@ -4,7 +4,6 @@ import org.springframework.data.repository.query.Param; import stanl_2.final_backend.domain.problem.query.dto.ProblemDTO; import stanl_2.final_backend.domain.problem.query.dto.ProblemSearchDTO; -import stanl_2.final_backend.domain.promotion.query.dto.PromotionDTO; import java.util.List; diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemService.java b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemService.java index 5ea78365..4db72ae3 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemService.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemService.java @@ -2,12 +2,10 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import stanl_2.final_backend.domain.notices.query.dto.SearchDTO; import stanl_2.final_backend.domain.problem.query.dto.ProblemDTO; import stanl_2.final_backend.domain.problem.query.dto.ProblemSearchDTO; public interface ProblemService { - Page findProblems(Pageable pageable, ProblemSearchDTO problemSearchDTO); - ProblemDTO findProblem(String noticeId); + ProblemDTO findProblem(String problemId); } diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java index 4136e7f4..923c7fbb 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java @@ -8,7 +8,6 @@ import stanl_2.final_backend.domain.problem.query.dto.ProblemDTO; import stanl_2.final_backend.domain.problem.query.dto.ProblemSearchDTO; import stanl_2.final_backend.domain.problem.query.repository.ProblemMapper; -import stanl_2.final_backend.domain.promotion.query.dto.PromotionDTO; import java.util.List; diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/controller/PromotionController.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/controller/PromotionController.java index 3b8b5b73..7e1039a6 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/query/controller/PromotionController.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/controller/PromotionController.java @@ -11,8 +11,6 @@ import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.notices.common.response.NoticeResponseMessage; -import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; import stanl_2.final_backend.domain.promotion.common.response.PromotionResponseMessage; import stanl_2.final_backend.domain.promotion.query.dto.PromotionDTO; import stanl_2.final_backend.domain.promotion.query.dto.PromotionSearchDTO; diff --git a/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml b/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml index 1167f852..74a4ecdb 100644 --- a/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml @@ -12,118 +12,96 @@ - + - - - - - + + + + + + + + + - - SELECT - A.prob_id, - A.prob_ttl, - A.prob_cont, - A.created_at, - A.updated_at, - A.deleted_at, - A.active, - A.cust_id, - A.mem_id, - A.prod_id - FROM tb_problem A + a.prob_id, + a.prob_ttl, + a.prob_cont, + a.created_at, + a.updated_at, + a.deleted_at, + a.cst_id, + a.mem_id, + a.prod_id, + a.active + FROM tb_problem a a.active != 'false' - AND A.NOT_TTL LIKE CONCAT('%', #{searchDTO.title}, '%') + AND a.prob_ttl LIKE CONCAT('%', #{problemSearchDTO.title}, '%') - AND A.MEM_ID = #{searchDTO.memberId} + AND a.mem_id = #{problemSearchDTO.memberId} - AND A.PROD_ID = #{searchDTO.productId} + AND a.prod_id = #{problemSearchDTO.productId} - AND A.CUST_ID = #{searchDTO.customerId} + AND a.cst_id = #{problemSearchDTO.customerId} - - AND A.CREATED_AT BETWEEN #{searchDTO.startDate} AND #{searchDTO.endDate} + + AND a.created_at BETWEEN #{problemSearchDTO.startDate} AND #{problemSearchDTO.endDate} - ORDER BY A.CREATED_AT DESC + ORDER BY a.created_at DESC LIMIT #{size} OFFSET #{offset} - - SELECT COUNT(*) AS CNT FROM tb_problem a a.active != 'false' - - AND A.NOT_TTL LIKE CONCAT('%', #{searchDTO.title}, '%') + + AND a.prob_ttl LIKE CONCAT('%', #{title}, '%') - - AND A.MEM_ID = #{searchDTO.memberId} + + AND a.mem_id = #{memberId} - - AND A.PROD_ID = #{searchDTO.productId} + + AND a.prod_id = #{productId} - - AND A.CUST_ID = #{searchDTO.customerId} + + AND a.cst_id = #{customerId} - - AND A.CREATED_AT BETWEEN #{searchDTO.startDate} AND #{searchDTO.endDate} + + AND a.created_at BETWEEN #{startDate} AND #{endDate} - - - \ No newline at end of file + From 29a6df02152923d9de275cb109cb4b46daf81339 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 21 Nov 2024 00:59:32 +0900 Subject: [PATCH 247/563] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EB=AA=A8?= =?UTF-8?q?=EC=85=98=20=EC=A1=B0=ED=9A=8C=20=EA=B4=80=EB=A0=A8(#61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/promotion/query/dto/PromotionSearchDTO.java | 2 +- .../domain/promotion/query/repository/PromotionMapper.java | 2 -- .../domain/promotion/query/service/PromotionService.java | 2 +- .../promotion/query/service/PromotionServiceImpl.java | 2 +- .../domain/promotion/query/repository/PromotionMapper.xml | 7 ++++--- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionSearchDTO.java index 16a0320f..67ea887d 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionSearchDTO.java @@ -1,6 +1,5 @@ package stanl_2.final_backend.domain.promotion.query.dto; -import jakarta.persistence.Column; import lombok.*; @AllArgsConstructor @@ -10,6 +9,7 @@ public class PromotionSearchDTO { private String promotionId; private String title; + private String content; private String memberId; private String createdAt; private String updatedAt; diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.java index b82fa0b5..34c8bad1 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.java @@ -2,8 +2,6 @@ import org.apache.ibatis.annotations.Mapper; import org.springframework.data.repository.query.Param; -import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; -import stanl_2.final_backend.domain.notices.query.dto.SearchDTO; import stanl_2.final_backend.domain.promotion.query.dto.PromotionDTO; import stanl_2.final_backend.domain.promotion.query.dto.PromotionSearchDTO; diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionService.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionService.java index 79c67512..654fd787 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionService.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionService.java @@ -8,5 +8,5 @@ public interface PromotionService { Page findPromotions(Pageable pageable, PromotionSearchDTO PromotionSearchDTO); - PromotionDTO findPromotion(String promotion); + PromotionDTO findPromotion(String promotionId); } diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java index 272d6ef6..a78a506c 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java @@ -12,7 +12,7 @@ import java.util.List; -@Service("queryPromotionServiceImpl") +@Service("queryPromoteServiceImpl") public class PromotionServiceImpl implements PromotionService{ private final PromotionMapper promotionMapper; @Autowired diff --git a/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml b/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml index c921d04e..6d5bd5cb 100644 --- a/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml @@ -15,9 +15,10 @@ - + + @@ -30,11 +31,11 @@ a.prm_id, a.prm_ttl, a.prm_cont, + a.mem_id, a.created_at, a.updated_at, a.deleted_at, - a.active, - a.mem_id + a.active FROM tb_promotion a a.active != 'false' From 382a521647ca0ac073c05070243c2c306dffeb79 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 21 Nov 2024 09:53:28 +0900 Subject: [PATCH 248/563] =?UTF-8?q?feat:=20=EC=98=88=EC=99=B8=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EA=B4=80=EB=A0=A8(#62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/NoticeCommandServiceImpl.java | 5 +- .../common/exception/NoticeErrorCode.java | 4 +- .../controller/ProblemController.java | 12 +-- .../application/dto/ProblemModifyDTO.java | 1 + .../service/ProblemCommandService.java | 8 +- .../aggregate/service/ProblemServiceImpl.java | 76 ++++++++++++++----- .../common/exception/ProblemErrorCode.java | 4 + .../query/repository/ProblemMapper.java | 4 +- .../query/service/ProblemServiceImpl.java | 6 +- 9 files changed, 83 insertions(+), 37 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java index a24f3cef..599ec8b1 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java @@ -69,7 +69,6 @@ public void registerNotice(NoticeRegistDTO noticeRegistDTO, @Transactional public NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO,Principal principal) { String memberId= principal.getName(); - noticeModifyDTO.setMemberId(memberId); Notice notice = noticeRepository.findById(id) .orElseThrow(() -> new NoticeCommonException(NoticeErrorCode.NOTICE_NOT_FOUND)); @@ -121,10 +120,10 @@ public void deleteNotice(NoticeDeleteDTO noticeDeleteDTO, Principal principal) { noticeRepository.save(notice); } catch (DataIntegrityViolationException e) { // 데이터 무결성 위반 예외 처리 - throw new ScheduleCommonException(ScheduleErrorCode.DATA_INTEGRITY_VIOLATION); + throw new NoticeCommonException(NoticeErrorCode.DATA_INTEGRITY_VIOLATION); } catch (Exception e) { // 서버 오류 - throw new ScheduleCommonException(ScheduleErrorCode.INTERNAL_SERVER_ERROR); + throw new NoticeCommonException(NoticeErrorCode.INTERNAL_SERVER_ERROR); } } } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/common/exception/NoticeErrorCode.java b/src/main/java/stanl_2/final_backend/domain/notices/common/exception/NoticeErrorCode.java index 6f90d956..0f833657 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/common/exception/NoticeErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/common/exception/NoticeErrorCode.java @@ -40,9 +40,9 @@ public enum NoticeErrorCode { */ NOTICE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "notice 데이터를 찾지 못했습니다"), - DATA_INTEGRITY_VIOLATION(40002, HttpStatus.BAD_REQUEST, "데이터 무결 위반하였습니다."), + DATA_INTEGRITY_VIOLATION(40402, HttpStatus.BAD_REQUEST, "데이터 무결 위반하였습니다."), - AUTHORIZATION_VIOLATION(40002, HttpStatus.BAD_REQUEST, "접근 권한이 없습니다."), + AUTHORIZATION_VIOLATION(40403, HttpStatus.BAD_REQUEST, "접근 권한이 없습니다."), /** * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java index 9f97d4b7..a186c5bb 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java @@ -33,10 +33,10 @@ public ProblemController(ProblemCommandService problemCommandService, AuthQueryS content = {@Content(schema = @Schema(implementation = ProblemResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postNotice(@RequestBody ProblemRegistDTO problemRegistDTO, Principal principal){ + public ResponseEntity postProblem(@RequestBody ProblemRegistDTO problemRegistDTO, Principal principal){ String memberId = authQueryService.selectMemberIdByLoginId(principal.getName()); problemRegistDTO.setMemberId(memberId); - problemCommandService.registerProblem(problemRegistDTO); + problemCommandService.registerProblem(problemRegistDTO,principal); return ResponseEntity.ok(ProblemResponseMessage.builder() .httpStatus(200) .msg("성공") @@ -51,9 +51,9 @@ public ResponseEntity postNotice(@RequestBody ProblemReg }) @PutMapping("{problemId}") public ResponseEntity modifyProblem(@PathVariable String problemId, - @RequestBody ProblemModifyDTO problemModifyRequestDTO){ + @RequestBody ProblemModifyDTO problemModifyRequestDTO, Principal principal){ - ProblemModifyDTO problemModifyDTO = problemCommandService.modifyProblem(problemId,problemModifyRequestDTO); + ProblemModifyDTO problemModifyDTO = problemCommandService.modifyProblem(problemId,problemModifyRequestDTO,principal); return ResponseEntity.ok(ProblemResponseMessage.builder() .httpStatus(200) @@ -68,9 +68,9 @@ public ResponseEntity modifyProblem(@PathVariable String content = {@Content(schema = @Schema(implementation = ProblemResponseMessage.class))}) }) @DeleteMapping("{problemId}") - public ResponseEntity deleteProblem(@PathVariable String problemId) { + public ResponseEntity deleteProblem(@PathVariable String problemId, Principal principal) { - problemCommandService.deleteProblem(problemId); + problemCommandService.deleteProblem(problemId,principal); return ResponseEntity.ok(ProblemResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java index 70f02406..f647082f 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java @@ -14,5 +14,6 @@ public class ProblemModifyDTO { private String title; private String content; + private String memberId; } diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/service/ProblemCommandService.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/service/ProblemCommandService.java index e9fa685b..dd112d3e 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/application/service/ProblemCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/service/ProblemCommandService.java @@ -3,11 +3,13 @@ import stanl_2.final_backend.domain.problem.command.application.dto.ProblemModifyDTO; import stanl_2.final_backend.domain.problem.command.application.dto.ProblemRegistDTO; +import java.security.Principal; + public interface ProblemCommandService { - void registerProblem(ProblemRegistDTO problemRegistDTO); + void registerProblem(ProblemRegistDTO problemRegistDTO, Principal principal); - ProblemModifyDTO modifyProblem(String problemId, ProblemModifyDTO problemModifyDTO); + ProblemModifyDTO modifyProblem(String problemId, ProblemModifyDTO problemModifyDTO, Principal principal); - void deleteProblem(String problemId); + void deleteProblem(String problemId, Principal principal); } diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java index b4ba3233..2706cc0e 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java @@ -2,8 +2,11 @@ import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.notices.common.exception.NoticeCommonException; +import stanl_2.final_backend.domain.notices.common.exception.NoticeErrorCode; import stanl_2.final_backend.domain.problem.command.application.dto.ProblemModifyDTO; import stanl_2.final_backend.domain.problem.command.application.dto.ProblemRegistDTO; import stanl_2.final_backend.domain.problem.command.application.service.ProblemCommandService; @@ -13,7 +16,10 @@ import stanl_2.final_backend.domain.problem.common.exception.ProblemErrorCode; import stanl_2.final_backend.domain.promotion.common.exception.PromotionCommonException; import stanl_2.final_backend.domain.promotion.common.exception.PromotionErrorCode; +import stanl_2.final_backend.domain.schedule.common.exception.ScheduleCommonException; +import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; +import java.security.Principal; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -37,41 +43,75 @@ private String getCurrentTimestamp() { @Transactional @Override - public void registerProblem(ProblemRegistDTO problemRegistDTO) { - Problem problem =modelMapper.map(problemRegistDTO,Problem.class); - problemRepository.save(problem); + public void registerProblem(ProblemRegistDTO problemRegistDTO, Principal principal) { + String memberId =principal.getName(); + problemRegistDTO.setMemberId(memberId); + try { + Problem problem = modelMapper.map(problemRegistDTO, Problem.class); + problemRepository.save(problem); + } catch (DataIntegrityViolationException e){ + throw new ProblemCommonException(ProblemErrorCode.DATA_INTEGRITY_VIOLATION); + }catch (Exception e) { + // 서버 오류 + throw new NoticeCommonException(NoticeErrorCode.INTERNAL_SERVER_ERROR); + } } @Transactional @Override - public ProblemModifyDTO modifyProblem(String problemId, ProblemModifyDTO problemModifyDTO) { + public ProblemModifyDTO modifyProblem(String problemId, ProblemModifyDTO problemModifyDTO,Principal principal) { + String memberId= principal.getName(); Problem problem = problemRepository.findById(problemId) .orElseThrow(() -> new ProblemCommonException(ProblemErrorCode.PROBLEM_NOT_FOUND)); + if(!problemModifyDTO.getMemberId().equals(memberId)){ + throw new ProblemCommonException(ProblemErrorCode.AUTHORIZATION_VIOLATION); + } + try { + Problem updateProblem = modelMapper.map(problemModifyDTO, Problem.class); + updateProblem.setProblemId(problem.getProblemId()); + updateProblem.setMemberId(problem.getMemberId()); + updateProblem.setCreatedAt(problem.getCreatedAt()); + updateProblem.setActive(problem.getActive()); + updateProblem.setCustomerId(problem.getCustomerId()); + updateProblem.setProductId(problem.getProductId()); - Problem updateProblem = modelMapper.map(problemModifyDTO, Problem.class); - updateProblem.setProblemId(problem.getProblemId()); - updateProblem.setMemberId(problem.getMemberId()); - updateProblem.setCreatedAt(problem.getCreatedAt()); - updateProblem.setActive(problem.getActive()); - updateProblem.setCustomerId(problem.getCustomerId()); - updateProblem.setProductId(problem.getProductId()); + problemRepository.save(updateProblem); - problemRepository.save(updateProblem); + ProblemModifyDTO problemModify = modelMapper.map(updateProblem,ProblemModifyDTO.class); - ProblemModifyDTO problemModify = modelMapper.map(updateProblem,ProblemModifyDTO.class); - - return problemModify; + return problemModify; + } catch (DataIntegrityViolationException e) { + // 데이터 무결성 위반 예외 처리 + throw new ProblemCommonException(ProblemErrorCode.DATA_INTEGRITY_VIOLATION); + } catch (Exception e) { + // 서버 오류 + throw new ProblemCommonException(ProblemErrorCode.INTERNAL_SERVER_ERROR); + } } @Transactional @Override - public void deleteProblem(String problemId) { + public void deleteProblem(String problemId, Principal principal) { + String memberId= principal.getName(); Problem problem = problemRepository.findById(problemId) - .orElseThrow(()-> new PromotionCommonException(PromotionErrorCode.PROMOTION_NOT_FOUND)); + .orElseThrow(()-> new ProblemCommonException(ProblemErrorCode.PROBLEM_NOT_FOUND)); + + if(!memberId.equals(memberId)){ + // 권한 오류 + throw new ProblemCommonException(ProblemErrorCode.AUTHORIZATION_VIOLATION); + } problem.setActive(false); problem.setDeletedAt(getCurrentTimestamp()); - problemRepository.save(problem); + try { + problemRepository.save(problem); + } catch (DataIntegrityViolationException e) { + // 데이터 무결성 위반 예외 처리 + throw new ProblemCommonException(ProblemErrorCode.DATA_INTEGRITY_VIOLATION); + } catch (Exception e) { + // 서버 오류 + throw new ProblemCommonException(ProblemErrorCode.INTERNAL_SERVER_ERROR); + } } } diff --git a/src/main/java/stanl_2/final_backend/domain/problem/common/exception/ProblemErrorCode.java b/src/main/java/stanl_2/final_backend/domain/problem/common/exception/ProblemErrorCode.java index 50606191..4b54f853 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/common/exception/ProblemErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/common/exception/ProblemErrorCode.java @@ -39,6 +39,10 @@ public enum ProblemErrorCode { * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. */ PROBLEM_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "problem 데이터를 찾지 못했습니다"), + DATA_INTEGRITY_VIOLATION(40402, HttpStatus.BAD_REQUEST, "데이터 무결 위반하였습니다."), + + AUTHORIZATION_VIOLATION(40403, HttpStatus.BAD_REQUEST, "접근 권한이 없습니다."), + /** * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. */ diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.java b/src/main/java/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.java index 5ff4e1e2..bfff97b1 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.java @@ -10,8 +10,8 @@ @Mapper public interface ProblemMapper { List findProblems( - @Param("offset") int offset, - @Param("size") int size, + @Param("offset") Integer offset, + @Param("size") Integer size, @Param("problemDTO") ProblemSearchDTO problemSearchDTO ); Integer findProblemsCount(@Param("problemSearchDTO") ProblemSearchDTO problemSearchDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java index 923c7fbb..139502e6 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java @@ -21,11 +21,11 @@ public ProblemServiceImpl(ProblemMapper problemMapper) { @Override public Page findProblems(Pageable pageable, ProblemSearchDTO problemSearchDTO) { - int offset = Math.toIntExact(pageable.getOffset()); - int size = pageable.getPageSize(); + Integer offset = Math.toIntExact(pageable.getOffset()); + Integer size = pageable.getPageSize(); List problems = problemMapper.findProblems(offset,size,problemSearchDTO); Integer count = problemMapper.findProblemsCount(problemSearchDTO); - int totalCount = (count != null) ? problemMapper.findProblemsCount(problemSearchDTO) : 0; + Integer totalCount = (count != null) ? problemMapper.findProblemsCount(problemSearchDTO) : 0; return new PageImpl<>(problems, pageable, totalCount); } From 364f80ab2088647289c00c2f5951dd5024f86b14 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Thu, 21 Nov 2024 10:11:10 +0900 Subject: [PATCH 249/563] =?UTF-8?q?fix:=20=EB=A1=9C=EA=B7=B8=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EC=99=84=EB=A3=8C(#110)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/log/command/aggregate/Log.java | 5 - .../domain/log/command/aop/LoggingAspect.java | 296 ++++++++---------- .../log/command/repository/LogRepository.java | 1 - .../exception/GlobalExceptionHandler.java | 241 +++++--------- .../global/log/LoggingFilter.java | 193 ------------ .../security/config/DevSecurityConfig.java | 19 +- .../security/config/ProdSecurityConfig.java | 21 +- .../filter/JWTTokenValidatorFilter.java | 87 ++--- 8 files changed, 282 insertions(+), 581 deletions(-) delete mode 100644 src/main/java/stanl_2/final_backend/global/log/LoggingFilter.java diff --git a/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java b/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java index a69c0448..db5a9cca 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java +++ b/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java @@ -21,14 +21,12 @@ public class Log { @Column(name = "LOG_ID", nullable = false) private String logId; - // User Information @Column(name = "SESSION_ID") private String sessionId; @Column(name = "USER_AGENT") private String userAgent; - // Network Information @Column(name = "IP_ADDRESS") private String ipAddress; @@ -38,7 +36,6 @@ public class Log { @Column(name = "REMOTE_PORT") private Integer remotePort; - // Request Information @Column(name = "URI") private String uri; @@ -48,11 +45,9 @@ public class Log { @Column(name = "QUERY_STRING") private String queryString; - // Time Information @Column(name = "REQUEST_TIME") private LocalDateTime requestTime; - // Additional Information @Column(name = "TRANSACTION_ID") private String transactionId; diff --git a/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java b/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java index 8b0b2b78..918f5f04 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java +++ b/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java @@ -1,170 +1,126 @@ -//package stanl_2.final_backend.domain.log.command.aop; -// -//import com.fasterxml.jackson.databind.ObjectMapper; -//import jakarta.servlet.http.HttpServletRequest; -//import lombok.extern.slf4j.Slf4j; -//import org.aspectj.lang.JoinPoint; -//import org.aspectj.lang.annotation.AfterReturning; -//import org.aspectj.lang.annotation.Aspect; -//import org.aspectj.lang.annotation.Before; -//import org.springframework.beans.factory.annotation.Autowired; -//import org.springframework.stereotype.Component; -//import org.springframework.web.context.request.RequestContextHolder; -//import org.springframework.web.context.request.ServletRequestAttributes; -//import stanl_2.final_backend.domain.log.command.aggregate.Log; -//import stanl_2.final_backend.domain.log.command.repository.LogRepository; -// -//import java.time.LocalDateTime; -//import java.util.HashMap; -//import java.util.Map; -//import java.util.UUID; -// -//@Slf4j -//@Aspect -//@Component -//public class LoggingAspect { -// -// private final ObjectMapper objectMapper; -// private final LogRepository logRepository; -// private static final ThreadLocal currentTransactionId = new ThreadLocal<>(); -// -// @Autowired -// public LoggingAspect(LogRepository logRepository) { -// this.objectMapper = new ObjectMapper(); -// this.logRepository = logRepository; -// } -// -// @Before("within(@org.springframework.web.bind.annotation.RestController *)") -// public void logRequestInfo(JoinPoint joinPoint) { -// ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); -// if (attributes == null) return; -// -// HttpServletRequest request = attributes.getRequest(); -// -// Map logData = new HashMap<>(); -// -// // 사용자 정보 -// Map userData = new HashMap<>(); -// userData.put("session_id", safeValue(request.getRequestedSessionId())); -// userData.put("user_agent", safeValue(request.getHeader("User-Agent"))); -// logData.put("user", userData); -// -// // 네트워크 정보 -// Map networkData = new HashMap<>(); -// networkData.put("ip_address", safeValue(getClientIp(request))); -// networkData.put("host_name", safeValue(request.getRemoteHost())); -// networkData.put("remote_port", request.getRemotePort()); -// logData.put("network", networkData); -// -// // 요청 정보 -// Map requestData = new HashMap<>(); -// requestData.put("uri", safeValue(request.getRequestURI())); -// requestData.put("method", safeValue(request.getMethod())); -// requestData.put("query_string", safeValue(request.getQueryString())); -// logData.put("request", requestData); -// -// // 시간 정보 -// Map timeData = new HashMap<>(); -// timeData.put("request_time", LocalDateTime.now().toString()); -// logData.put("time", timeData); -// -// // 추가 정보 -// String transactionId = UUID.randomUUID().toString(); -// currentTransactionId.set(transactionId); -// logData.put("transaction_id", transactionId); -// -// try { -// log.info("Request Info: {}", objectMapper.writeValueAsString(logData)); -// -// // 로그 엔티티 저장 -// Log logEntry = new Log(); -// -// // User Information -// logEntry.setSessionId(safeValue(request.getRequestedSessionId())); -// logEntry.setUserAgent(safeValue(request.getHeader("User-Agent"))); -// -// // Network Information -// logEntry.setIpAddress(safeValue(getClientIp(request))); -// logEntry.setHostName(safeValue(request.getRemoteHost())); -// logEntry.setRemotePort(request.getRemotePort()); -// -// // Request Information -// logEntry.setUri(safeValue(request.getRequestURI())); -// logEntry.setMethod(safeValue(request.getMethod())); -// logEntry.setQueryString(safeValue(request.getQueryString())); -// -// // Time Information -// logEntry.setRequestTime(LocalDateTime.now()); -// -// // Additional Information -// logEntry.setTransactionId(transactionId); -// -// // Status 초기 설정 (요청만 기록한 상태) -// logEntry.setStatus("IN_PROGRESS"); -// -// logRepository.save(logEntry); -// -// } catch (Exception e) { -// log.error("Failed to log request info", e); -// } -// } -// -// @AfterReturning("within(@org.springframework.web.bind.annotation.RestController *)") -// public void logRequestSuccess(JoinPoint joinPoint) { -// String transactionId = currentTransactionId.get(); -// if (transactionId == null) return; -// -// try { -// Log logEntry = logRepository.findByTransactionId(transactionId); -// if (logEntry != null) { -// logEntry.setStatus("SUCCESS"); -// logRepository.save(logEntry); -// } -// } catch (Exception e) { -// log.error("Failed to update log status to SUCCESS", e); -// } finally { -// currentTransactionId.remove(); -// } -// } -// -// -// public void logRequestFailure(String message) { -// String transactionId = currentTransactionId.get(); -// if (transactionId == null) return; -// -// try { -// Log logEntry = logRepository.findByTransactionId(transactionId); -// if (logEntry != null) { -// logEntry.setStatus("FAILURE"); -// logEntry.setErrorMessage(message); -// logRepository.save(logEntry); -// } -// } catch (Exception ex) { -// log.error("Failed to update log status to FAILURE", ex); -// } finally { -// currentTransactionId.remove(); -// } -// } -// -// private String getClientIp(HttpServletRequest request) { -// String[] headers = { -// "X-Forwarded-For", -// "Proxy-Client-IP", -// "WL-Proxy-Client-IP", -// "HTTP_CLIENT_IP", -// "HTTP_X_FORWARDED_FOR" -// }; -// -// for (String header : headers) { -// String ip = request.getHeader(header); -// if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) { -// return ip.split(",")[0]; -// } -// } -// return request.getRemoteAddr(); -// } -// -// private String safeValue(String value) { -// return value != null ? value : "N/A"; -// } -//} +package stanl_2.final_backend.domain.log.command.aop; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import stanl_2.final_backend.domain.log.command.aggregate.Log; +import stanl_2.final_backend.domain.log.command.repository.LogRepository; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@Slf4j +@Aspect +@Component +public class LoggingAspect { + + private final LogRepository logRepository; + private static final ThreadLocal currentTransactionId = new ThreadLocal<>(); + + @Autowired + public LoggingAspect(LogRepository logRepository) { + this.logRepository = logRepository; + } + + @AfterReturning("within(@org.springframework.web.bind.annotation.RestController *)") + public void logRequestSuccess(JoinPoint joinPoint) { + + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if (attributes == null) return; + + HttpServletRequest request = attributes.getRequest(); + + Map logData = new HashMap<>(); + + // 사용자 정보 + Map userData = new HashMap<>(); + userData.put("session_id", safeValue(request.getRequestedSessionId())); + userData.put("user_agent", safeValue(request.getHeader("User-Agent"))); + logData.put("user", userData); + + // 네트워크 정보 + Map networkData = new HashMap<>(); + networkData.put("ip_address", safeValue(getClientIp(request))); + networkData.put("host_name", safeValue(request.getRemoteHost())); + networkData.put("remote_port", request.getRemotePort()); + logData.put("network", networkData); + + // 요청 정보 + Map requestData = new HashMap<>(); + requestData.put("uri", safeValue(request.getRequestURI())); + requestData.put("method", safeValue(request.getMethod())); + requestData.put("query_string", safeValue(request.getQueryString())); + logData.put("request", requestData); + + // 시간 정보 + Map timeData = new HashMap<>(); + timeData.put("request_time", LocalDateTime.now().toString()); + logData.put("time", timeData); + + // 추가 정보 + String transactionId = UUID.randomUUID().toString(); + currentTransactionId.set(transactionId); + logData.put("transaction_id", transactionId); + + try { + // 로그 엔티티 저장 + Log logEntry = new Log(); + + // 유저 정보 + logEntry.setSessionId(safeValue(request.getRequestedSessionId())); + logEntry.setUserAgent(safeValue(request.getHeader("User-Agent"))); + + // 네트워크 정보 + logEntry.setIpAddress(safeValue(getClientIp(request))); + logEntry.setHostName(safeValue(request.getRemoteHost())); + logEntry.setRemotePort(request.getRemotePort()); + + // 요청 정보 + logEntry.setUri(safeValue(request.getRequestURI())); + logEntry.setMethod(safeValue(request.getMethod())); + logEntry.setQueryString(safeValue(request.getQueryString())); + + // 시간 정보 + logEntry.setRequestTime(LocalDateTime.now()); + + // 추가적인 정보 + logEntry.setTransactionId(transactionId); + + logEntry.setStatus("SUCCESS"); + logRepository.save(logEntry); + + } catch (Exception e) { + log.error("Failed to log request info", e); + } + } + + private String getClientIp(HttpServletRequest request) { + String[] headers = { + "X-Forwarded-For", + "Proxy-Client-IP", + "WL-Proxy-Client-IP", + "HTTP_CLIENT_IP", + "HTTP_X_FORWARDED_FOR" + }; + + for (String header : headers) { + String ip = request.getHeader(header); + if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) { + return ip.split(",")[0]; + } + } + return request.getRemoteAddr(); + } + + private String safeValue(String value) { + return value != null ? value : "N/A"; + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/log/command/repository/LogRepository.java b/src/main/java/stanl_2/final_backend/domain/log/command/repository/LogRepository.java index 4979af51..00a2b826 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/command/repository/LogRepository.java +++ b/src/main/java/stanl_2/final_backend/domain/log/command/repository/LogRepository.java @@ -6,5 +6,4 @@ @Repository public interface LogRepository extends JpaRepository { - Log findByTransactionId(String transactionId); } \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java b/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java index 85126f87..872ad417 100644 --- a/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java @@ -2,11 +2,11 @@ import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.JwtException; +import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.ResponseEntity; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingServletRequestParameterException; @@ -16,7 +16,6 @@ import org.springframework.web.servlet.NoHandlerFoundException; import stanl_2.final_backend.domain.log.command.aggregate.Log; import stanl_2.final_backend.domain.log.command.repository.LogRepository; -import stanl_2.final_backend.global.log.LoggingFilter; import java.time.LocalDateTime; import java.util.HashMap; @@ -24,172 +23,38 @@ import java.util.UUID; @Slf4j -// 모든 rest컨트롤러에서 발생하는 예외 처리 @RestControllerAdvice(basePackages = "stanl_2.final_backend") public class GlobalExceptionHandler { - @Autowired - private LogRepository logRepository; // 로그를 저장하기 위한 리포지토리 주입 - - private void saveErrorLog(String message, Exception e) { - String transactionId = LoggingFilter.getCurrentTransactionId(); // ThreadLocal에서 가져옴 - if (transactionId == null) { - transactionId = UUID.randomUUID().toString(); // 누락 시 새로운 ID 생성 - } -// Log log = new Log(); -// log.setTransactionId(transactionId); -// log.setStatus("ERROR"); -// log.setErrorMessage(e.getMessage()); -// logRepository.save(log); - - try { - Log log = new Log(); - log.setStatus(message); - log.setErrorMessage(e.getMessage()); - log.setRequestTime(LocalDateTime.now()); - logRepository.save(log); // 로그 저장 - } catch (Exception ex) { - log.error("로그 저장 중 오류 발생: {}", ex.getMessage(), ex); - } - } + private LogRepository logRepository; -// // 지원되지 않는 HTTP 메소드를 사용할 때 발생하는 예외 -// @ExceptionHandler(value = {NoHandlerFoundException.class, HttpRequestMethodNotSupportedException.class}) -// public ResponseEntity handleNoPageFoundException(Exception e) { -// log.error("지원되지 않는 HTTP 메소드 요청: {}", e.getMessage()); -// saveErrorLog(GlobalErrorCode.WRONG_ENTRY_POINT.getMsg(), e); -// GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.WRONG_ENTRY_POINT).getErrorCode()); -//// loggingAspect.logRequestFailure(GlobalErrorCode.WRONG_ENTRY_POINT.getMsg()); -// return ResponseEntity.status(response.getHttpStatus()).body(response); -// } -// -// // 메소드의 인자 타입이 일치하지 않을 때 발생하는 예외 -// @ExceptionHandler(value = {MethodArgumentTypeMismatchException.class}) -// public ResponseEntity handleArgumentNotValidException(MethodArgumentTypeMismatchException e) { -// log.error("메소드 인자 타입 불일치: {}" -// , e.getMessage()); -// saveErrorLog(GlobalErrorCode.INVALID_PARAMETER_FORMAT.getMsg(), e); -// GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.INVALID_PARAMETER_FORMAT).getErrorCode()); -//// loggingAspect.logRequestFailure(GlobalErrorCode.INVALID_PARAMETER_FORMAT.getMsg()); -// return ResponseEntity.status(response.getHttpStatus()).body(response); -// } -// -// // 필수 파라미터가 누락되었을 때 발생하는 예외 -// @ExceptionHandler(value = {MissingServletRequestParameterException.class}) -// public ResponseEntity handleArgumentNotValidException(MissingServletRequestParameterException e) { -// log.error("필수 파라미터 누락: {}" -// , e.getMessage()); -// saveErrorLog(GlobalErrorCode.MISSING_REQUEST_PARAMETER.getMsg(), e); -// GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.MISSING_REQUEST_PARAMETER).getErrorCode()); -//// loggingAspect.logRequestFailure(GlobalErrorCode.MISSING_REQUEST_PARAMETER.getMsg()); -// return ResponseEntity.status(response.getHttpStatus()).body(response); -// } -// -// // 사용자 정의 예외 처리 -// @ExceptionHandler(value = {GlobalCommonException.class}) -// public ResponseEntity> handleCustomException(GlobalCommonException e) { -// log.error("사용자 예외처리: {}", e.getMessage()); -// saveErrorLog(e.getErrorCode().getMsg(), e); -//// GlobalExceptionResponse response = new GlobalExceptionResponse(e.getErrorCode()); -//// loggingAspect.logRequestFailure(e.getErrorCode().getMsg()); -// -// Map response = new HashMap<>(); -// response.put("message", e.getErrorCode().getMsg()); -// response.put("code", e.getErrorCode().getCode()); -// return new ResponseEntity<>(response, e.getErrorCode().getHttpStatus()); -// -//// return ResponseEntity.status(response.getHttpStatus()).body(response); -// } -//// -//// @ExceptionHandler(GlobalCommonException.class) -//// public ResponseEntity> handleGlobalCommonException(GlobalCommonException ex) { -//// // 로그 저장 -//// log.error("Error occurred: {}", ex.getErrorCode()); -//// Map response = new HashMap<>(); -//// response.put("message", ex.getErrorCode().getMsg()); -//// response.put("code", ex.getErrorCode().getCode()); -//// return new ResponseEntity<>(response, ex.getErrorCode().getHttpStatus()); -//// } -// -// // 서버 내부 오류시 작동 -// @ExceptionHandler(value = {Exception.class}) -// public ResponseEntity handleServerException(Exception e) { -// log.info("서버 내부 오류 발생: {}", e.getMessage()); -// e.printStackTrace(); -// saveErrorLog(GlobalErrorCode.INTERNAL_SERVER_ERROR.getMsg(), e); -// GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.INTERNAL_SERVER_ERROR).getErrorCode()); -//// loggingAspect.logRequestFailure(GlobalErrorCode.INTERNAL_SERVER_ERROR.getMsg()); -// return ResponseEntity.status(response.getHttpStatus()).body(response); -// } -// -// // 데이터 무결성 위반 예외 처리기 추가 -// @ExceptionHandler(value = {DataIntegrityViolationException.class}) -// public ResponseEntity handleDataIntegrityViolationException(DataIntegrityViolationException e) { -// log.error("데이터 무결성 위반: {}", e.getMessage()); -// saveErrorLog(GlobalErrorCode.DATA_INTEGRITY_VIOLATION.getMsg(), e); -// GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.DATA_INTEGRITY_VIOLATION).getErrorCode()); -//// loggingAspect.logRequestFailure(GlobalErrorCode.DATA_INTEGRITY_VIOLATION.getMsg()); -// return ResponseEntity.status(response.getHttpStatus()).body(response); -// } -// -// // 유효성 검사 실패 예외 -// @ExceptionHandler(value = {MethodArgumentNotValidException.class}) -// public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { -// log.error("유효성 검사 실패: {}", e.getMessage()); -// saveErrorLog(GlobalErrorCode.VALIDATION_FAIL.getMsg(), e); -// GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.VALIDATION_FAIL).getErrorCode()); -//// loggingAspect.logRequestFailure(GlobalErrorCode.VALIDATION_FAIL.getMsg()); -// return ResponseEntity.status(response.getHttpStatus()).body(response); -// } -// -// // JWT 토큰 만료 예외 처리 -// @ExceptionHandler(ExpiredJwtException.class) -// public ResponseEntity handleExpiredJwtException(ExpiredJwtException e) { -// log.error("만료된 JWT 토큰: {}", e.getMessage()); -// saveErrorLog(GlobalErrorCode.JWT_EXPIRED.getMsg(), e); -// GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.JWT_EXPIRED).getErrorCode()); -//// loggingAspect.logRequestFailure(GlobalErrorCode.JWT_EXPIRED.getMsg()); -// return ResponseEntity.status(response.getHttpStatus()).body(response); -// } -// -// // JWT 토큰 인증 실패 예외 처리 -// @ExceptionHandler(JwtException.class) -// public ResponseEntity handleJwtException(JwtException e) { -// log.error("유효하지 않은 JWT 토큰: {}", e.getMessage()); -// saveErrorLog(GlobalErrorCode.INVALID_TOKEN_ERROR.getMsg(), e); -// GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(GlobalErrorCode.INVALID_TOKEN_ERROR).getErrorCode()); -//// loggingAspect.logRequestFailure(GlobalErrorCode.INVALID_TOKEN_ERROR.getMsg()); -// return ResponseEntity.status(response.getHttpStatus()).body(response); -// } - - private ResponseEntity createErrorResponse(GlobalErrorCode errorCode, Exception e) { - saveErrorLog(errorCode.getMsg(), e); - GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(errorCode).getErrorCode()); - return ResponseEntity.status(response.getHttpStatus()).body(response); + @Autowired + public GlobalExceptionHandler(LogRepository logRepository) { + this.logRepository = logRepository; } @ExceptionHandler({NoHandlerFoundException.class, HttpRequestMethodNotSupportedException.class}) - public ResponseEntity handleNoPageFoundException(Exception e) { + public ResponseEntity handleNoPageFoundException(Exception e, HttpServletRequest request) { log.error("지원되지 않는 요청: {}", e.getMessage()); - return createErrorResponse(GlobalErrorCode.WRONG_ENTRY_POINT, e); + return createErrorResponse(GlobalErrorCode.WRONG_ENTRY_POINT, e, request); } @ExceptionHandler(MethodArgumentTypeMismatchException.class) - public ResponseEntity handleArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) { + public ResponseEntity handleArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) { log.error("인자 타입 불일치: {}", e.getMessage()); - return createErrorResponse(GlobalErrorCode.INVALID_PARAMETER_FORMAT, e); + return createErrorResponse(GlobalErrorCode.INVALID_PARAMETER_FORMAT, e, request); } @ExceptionHandler(MissingServletRequestParameterException.class) - public ResponseEntity handleMissingRequestParameterException(MissingServletRequestParameterException e) { + public ResponseEntity handleMissingRequestParameterException(MissingServletRequestParameterException e, HttpServletRequest request) { log.error("필수 파라미터 누락: {}", e.getMessage()); - return createErrorResponse(GlobalErrorCode.MISSING_REQUEST_PARAMETER, e); + return createErrorResponse(GlobalErrorCode.MISSING_REQUEST_PARAMETER, e, request); } @ExceptionHandler(GlobalCommonException.class) - public ResponseEntity> handleCustomException(GlobalCommonException e) { + public ResponseEntity> handleCustomException(GlobalCommonException e, HttpServletRequest request) { log.error("사용자 예외 처리: {}", e.getMessage()); - saveErrorLog(e.getErrorCode().getMsg(), e); + saveErrorLog(e.getErrorCode().getMsg(), e, request); Map response = new HashMap<>(); response.put("message", e.getErrorCode().getMsg()); response.put("code", e.getErrorCode().getCode()); @@ -197,33 +62,93 @@ public ResponseEntity> handleCustomException(GlobalCommonExc } @ExceptionHandler(Exception.class) - public ResponseEntity handleServerException(Exception e) { + public ResponseEntity handleServerException(Exception e, HttpServletRequest request) { log.error("서버 내부 오류 발생: {}", e.getMessage()); - return createErrorResponse(GlobalErrorCode.INTERNAL_SERVER_ERROR, e); + return createErrorResponse(GlobalErrorCode.INTERNAL_SERVER_ERROR, e, request); } @ExceptionHandler(DataIntegrityViolationException.class) - public ResponseEntity handleDataIntegrityViolationException(DataIntegrityViolationException e) { + public ResponseEntity handleDataIntegrityViolationException(DataIntegrityViolationException e, HttpServletRequest request) { log.error("데이터 무결성 위반: {}", e.getMessage()); - return createErrorResponse(GlobalErrorCode.DATA_INTEGRITY_VIOLATION, e); + return createErrorResponse(GlobalErrorCode.DATA_INTEGRITY_VIOLATION, e, request); } @ExceptionHandler(MethodArgumentNotValidException.class) - public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { + public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) { log.error("유효성 검사 실패: {}", e.getMessage()); - return createErrorResponse(GlobalErrorCode.VALIDATION_FAIL, e); + return createErrorResponse(GlobalErrorCode.VALIDATION_FAIL, e, request); } @ExceptionHandler(ExpiredJwtException.class) - public ResponseEntity handleExpiredJwtException(ExpiredJwtException e) { + public ResponseEntity handleExpiredJwtException(ExpiredJwtException e, HttpServletRequest request) { log.error("만료된 JWT 토큰: {}", e.getMessage()); - return createErrorResponse(GlobalErrorCode.JWT_EXPIRED, e); + return createErrorResponse(GlobalErrorCode.JWT_EXPIRED, e, request); } @ExceptionHandler(JwtException.class) - public ResponseEntity handleJwtException(JwtException e) { + public ResponseEntity handleJwtException(JwtException e, HttpServletRequest request) { log.error("유효하지 않은 JWT 토큰: {}", e.getMessage()); - return createErrorResponse(GlobalErrorCode.INVALID_TOKEN_ERROR, e); + return createErrorResponse(GlobalErrorCode.INVALID_TOKEN_ERROR, e, request); + } + + private ResponseEntity createErrorResponse(GlobalErrorCode errorCode, Exception e, HttpServletRequest request) { + saveErrorLog(errorCode.getMsg(), e, request); + GlobalExceptionResponse response = new GlobalExceptionResponse(new GlobalCommonException(errorCode).getErrorCode()); + return ResponseEntity.status(response.getHttpStatus()).body(response); } + private void saveErrorLog(String message, Exception e, HttpServletRequest request) { + try { + Log logEntry = new Log(); + + // 에러 정보 + logEntry.setStatus("ERROR"); + logEntry.setErrorMessage(e.getMessage()); + + // 요청 정보 + logEntry.setUri(safeValue(request.getRequestURI())); + logEntry.setMethod(safeValue(request.getMethod())); + logEntry.setQueryString(safeValue(request.getQueryString())); + logEntry.setRequestTime(LocalDateTime.now()); + + // 유저 정보 + logEntry.setSessionId(safeValue(request.getRequestedSessionId())); + logEntry.setUserAgent(safeValue(request.getHeader("User-Agent"))); + + // 네트워크 정보 + logEntry.setIpAddress(safeValue(getClientIp(request))); + logEntry.setHostName(safeValue(request.getRemoteHost())); + logEntry.setRemotePort(request.getRemotePort()); + + // Transaction ID + String transactionId = UUID.randomUUID().toString(); + logEntry.setTransactionId(transactionId); + + logRepository.save(logEntry); + } catch (Exception ex) { + log.error("로그 저장 중 오류 발생: {}", ex.getMessage(), ex); + } + } + + private String getClientIp(HttpServletRequest request) { + String[] headers = { + "X-Forwarded-For", + "Proxy-Client-IP", + "WL-Proxy-Client-IP", + "HTTP_CLIENT_IP", + "HTTP_X_FORWARDED_FOR" + }; + + for (String header : headers) { + String ip = request.getHeader(header); + if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) { + return ip.split(",")[0]; + } + } + return request.getRemoteAddr(); + } + + private String safeValue(String value) { + return value != null ? value : "N/A"; + } } diff --git a/src/main/java/stanl_2/final_backend/global/log/LoggingFilter.java b/src/main/java/stanl_2/final_backend/global/log/LoggingFilter.java deleted file mode 100644 index e52f1563..00000000 --- a/src/main/java/stanl_2/final_backend/global/log/LoggingFilter.java +++ /dev/null @@ -1,193 +0,0 @@ -package stanl_2.final_backend.global.log; - -import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.servlet.*; -import jakarta.servlet.http.HttpServletRequest; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import stanl_2.final_backend.domain.log.command.aggregate.Log; -import stanl_2.final_backend.domain.log.command.repository.LogRepository; - -import java.io.IOException; -import java.time.LocalDateTime; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -@Slf4j -@Component -public class LoggingFilter implements Filter { - - private final ObjectMapper objectMapper; - private final LogRepository logRepository; - private static final ThreadLocal currentTransactionId = new ThreadLocal<>(); - - @Autowired - public LoggingFilter(LogRepository logRepository) { - this.objectMapper = new ObjectMapper(); - this.logRepository = logRepository; - } - - - @Override - public void init(FilterConfig filterConfig) throws ServletException { - // 필터 초기화 로직 (필요 시 구현) - } - - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - if (!(request instanceof HttpServletRequest)) { - chain.doFilter(request, response); - return; - } - - HttpServletRequest httpRequest = (HttpServletRequest) request; - Map logData = new HashMap<>(); - - // 사용자 정보 - Map userData = new HashMap<>(); - userData.put("session_id", safeValue(httpRequest.getRequestedSessionId())); - userData.put("user_agent", safeValue(httpRequest.getHeader("User-Agent"))); - logData.put("user", userData); - - // 네트워크 정보 - Map networkData = new HashMap<>(); - networkData.put("ip_address", safeValue(getClientIp(httpRequest))); - networkData.put("host_name", safeValue(httpRequest.getRemoteHost())); - networkData.put("remote_port", httpRequest.getRemotePort()); - logData.put("network", networkData); - - // 요청 정보 - Map requestData = new HashMap<>(); - requestData.put("uri", safeValue(httpRequest.getRequestURI())); - requestData.put("method", safeValue(httpRequest.getMethod())); - requestData.put("query_string", safeValue(httpRequest.getQueryString())); - logData.put("request", requestData); - - // 시간 정보 - Map timeData = new HashMap<>(); - timeData.put("request_time", LocalDateTime.now().toString()); - logData.put("time", timeData); - - // 추가 정보 - String transactionId = UUID.randomUUID().toString(); - currentTransactionId.set(transactionId); - logData.put("transaction_id", transactionId); - - try { - log.info("Request Info: {}", objectMapper.writeValueAsString(logData)); - - // 로그 엔티티 저장 - Log logEntry = new Log(); - - // User Information - logEntry.setSessionId(safeValue(httpRequest.getRequestedSessionId())); - logEntry.setUserAgent(safeValue(httpRequest.getHeader("User-Agent"))); - - // Network Information - logEntry.setIpAddress(safeValue(getClientIp(httpRequest))); - logEntry.setHostName(safeValue(httpRequest.getRemoteHost())); - logEntry.setRemotePort(httpRequest.getRemotePort()); - - // Request Information - logEntry.setUri(safeValue(httpRequest.getRequestURI())); - logEntry.setMethod(safeValue(httpRequest.getMethod())); - logEntry.setQueryString(safeValue(httpRequest.getQueryString())); - - // Time Information - logEntry.setRequestTime(LocalDateTime.now()); - - // Additional Information - logEntry.setTransactionId(transactionId); - - // Status 초기 설정 (요청만 기록한 상태) - logEntry.setStatus("IN_PROGRESS"); - - logRepository.save(logEntry); - - } catch (Exception e) { - log.error("Failed to log request info", e); - } - - try { - chain.doFilter(request, response); - logRequestSuccess(); - } catch (Exception e) { - logRequestFailure(e); - throw e; - } finally { - currentTransactionId.remove(); - } - } - - @Override - public void destroy() { - // 필터 해제 로직 (필요 시 구현) - } - - public static String getCurrentTransactionId() { - return currentTransactionId.get(); - } - - private void logRequestSuccess() { - String transactionId = currentTransactionId.get(); - if (transactionId == null) return; - - try { - Log logEntry = logRepository.findByTransactionId(transactionId); - if (logEntry != null) { - logEntry.setStatus("SUCCESS"); - logRepository.save(logEntry); - } - } catch (Exception e) { - log.error("Failed to update log status to SUCCESS", e); - } - } - - private void logRequestFailure(Throwable e) { - String transactionId = currentTransactionId.get(); - if (transactionId == null) return; - - try { - Log logEntry = logRepository.findByTransactionId(transactionId); - if (logEntry != null) { - logEntry = new Log(); - logEntry.setTransactionId(transactionId); - } - logEntry.setStatus("FAILURE"); - logEntry.setErrorMessage(e.getMessage()); - logRepository.save(logEntry); - } catch (Exception ex) { - log.error("Failed to update log status to FAILURE", ex); - } - } - - private String getClientIp(HttpServletRequest request) { - String[] headers = { - "X-Forwarded-For", - "Proxy-Client-IP", - "WL-Proxy-Client-IP", - "HTTP_CLIENT_IP", - "HTTP_X_FORWARDED_FOR" - }; - - for (String header : headers) { - String ip = request.getHeader(header); - if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) { - return ip.split(",")[0]; - } - } - return request.getRemoteAddr(); - } - - private String safeValue(String value) { - return value != null ? value : "N/A"; - } - - private void ensureTransactionId() { - if (currentTransactionId.get() == null) { - currentTransactionId.set(UUID.randomUUID().toString()); - } - } -} diff --git a/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java index 634dafa4..7ebd1b01 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.global.security.config; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -14,11 +15,14 @@ import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.password.HaveIBeenPwnedRestApiPasswordChecker; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import stanl_2.final_backend.domain.log.command.repository.LogRepository; +import stanl_2.final_backend.global.exception.GlobalCommonException; +import stanl_2.final_backend.global.exception.GlobalErrorCode; import stanl_2.final_backend.global.security.filter.JWTTokenValidatorFilter; import java.util.Arrays; @@ -26,6 +30,7 @@ import static org.springframework.security.config.Customizer.withDefaults; +@Slf4j @Configuration @Profile("dev") public class DevSecurityConfig { @@ -63,8 +68,18 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Excepti // [Example] member는 ADMIN 권한만 접근 가능 설정 예시 .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasRole("ADMIN") .anyRequest().authenticated()) - .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, logRepository), BasicAuthenticationFilter.class) - .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()); + .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, logRepository), UsernamePasswordAuthenticationFilter.class) + .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()) + .exceptionHandling(ex -> ex + .authenticationEntryPoint((request, response, authException) -> { + log.error("Authentication error: {}", authException.getMessage()); + throw new GlobalCommonException(GlobalErrorCode.LOGIN_FAILURE); + }) + .accessDeniedHandler((request, response, accessDeniedException) -> { + log.error("Access denied: {}", accessDeniedException.getMessage()); + throw new GlobalCommonException(GlobalErrorCode.UNAUTHORIZED); + })); + http.formLogin(withDefaults()); http.httpBasic(withDefaults()); return http.build(); diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index ec89766f..99ff3b1e 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -1,16 +1,12 @@ package stanl_2.final_backend.global.security.config; -import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; -import org.springframework.core.Ordered; import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.password.CompromisedPasswordChecker; @@ -19,7 +15,6 @@ import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.HttpStatusEntryPoint; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.password.HaveIBeenPwnedRestApiPasswordChecker; import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; @@ -28,7 +23,6 @@ import stanl_2.final_backend.domain.log.command.repository.LogRepository; import stanl_2.final_backend.global.exception.GlobalCommonException; import stanl_2.final_backend.global.exception.GlobalErrorCode; -import stanl_2.final_backend.global.log.LoggingFilter; import stanl_2.final_backend.global.security.filter.JWTTokenValidatorFilter; import java.util.Arrays; @@ -44,8 +38,12 @@ public class ProdSecurityConfig { @Value("${jwt.secret-key}") private String jwtSecretKey; + private LogRepository logRepository; + @Autowired - private LogRepository logRepository; // logRepository 주입 + public ProdSecurityConfig(LogRepository logRepository) { + this.logRepository = logRepository; + } @Bean SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception { @@ -121,13 +119,4 @@ public AuthenticationManager authenticationManager(UserDetailsService userDetail providerManager.setEraseCredentialsAfterAuthentication(false); return providerManager; } - - @Bean(name = "prodLoggingFilter") - public FilterRegistrationBean loggingFilter(){ - FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); - registrationBean.setFilter(new LoggingFilter(logRepository)); - registrationBean.addUrlPatterns("/*"); - registrationBean.setOrder(Ordered.LOWEST_PRECEDENCE); // 필터 체인의 가장 마지막에 위치시킵니다. - return registrationBean; - } } diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java index 06be752e..9955c79a 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java @@ -1,7 +1,6 @@ package stanl_2.final_backend.global.security.filter; import io.jsonwebtoken.Claims; -import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.JwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; @@ -19,12 +18,11 @@ import org.springframework.web.filter.OncePerRequestFilter; import stanl_2.final_backend.domain.log.command.aggregate.Log; import stanl_2.final_backend.domain.log.command.repository.LogRepository; -import stanl_2.final_backend.global.exception.GlobalCommonException; -import stanl_2.final_backend.global.exception.GlobalErrorCode; import javax.crypto.SecretKey; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; import java.util.Arrays; import java.util.List; import java.util.UUID; @@ -63,7 +61,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse if (username == null || authorities == null || authorities.isEmpty()) { log.warn("토큰에 필수 정보가 누락되었습니다. username: {}, authorities: {}", username, authorities); - handleInvalidToken(response, "토큰에 필수 정보가 없습니다.", new JwtException("Missing required claims")); + handleInvalidToken(response, "토큰에 필수 정보가 없습니다.", new JwtException("Missing required claims"), request); return; } @@ -77,66 +75,83 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse } catch (JwtException e) { log.error("JWT 파싱 실패: {}", e.getMessage()); - handleInvalidToken(response, "유효하지 않은 토큰입니다.", e); + handleInvalidToken(response, "유효하지 않은 토큰입니다.", e, request); return; } } else if (isExemptedPath(request)) { log.debug("예외 경로 요청. JWT 검증 생략. 요청 URL: {}", request.getRequestURI()); } else { log.warn("Authorization 헤더가 없거나 올바르지 않은 형식입니다."); - handleInvalidToken(response, "인증 정보가 없습니다.", new JwtException("Missing Authorization Header")); + handleInvalidToken(response, "인증 정보가 없습니다.", new JwtException("Missing Authorization Header"), request); return; } filterChain.doFilter(request, response); } - private void handleInvalidToken(HttpServletResponse response, String message, Exception e) throws IOException { + private void handleInvalidToken(HttpServletResponse response, String message, Exception e, HttpServletRequest request) throws IOException { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.setContentType("application/json"); response.getWriter().write("{\"error\": \"" + message + "\"}"); - log.error("요청 거부됨: {}", message); + log.error("요청 거부됨: {}. Error: {}. Request URI: {}, Method: {}, Client IP: {}", + message, e.getMessage(), request.getRequestURI(), request.getMethod(), getClientIp(request)); - // 로그 저장 호출 - saveErrorLog(message, e); + saveErrorLog(message, e, request); // Save detailed error log } - private void saveErrorLog(String message, Exception e) { + + private void saveErrorLog(String message, Exception e, HttpServletRequest request) { try { - Log logEntity = new Log(); - logEntity.setTransactionId(UUID.randomUUID().toString()); - logEntity.setStatus("ERROR"); - logEntity.setErrorMessage(message + " - " + e.getMessage()); - logRepository.save(logEntity); + Log logEntry = new Log(); + + // 에러 정보 + logEntry.setStatus("ERROR"); + logEntry.setErrorMessage(e.getMessage()); + + // 요청 정보 + logEntry.setUri(safeValue(request.getRequestURI())); + logEntry.setMethod(safeValue(request.getMethod())); + logEntry.setQueryString(safeValue(request.getQueryString())); + logEntry.setRequestTime(LocalDateTime.now()); + + // 유저 정보 + logEntry.setSessionId(safeValue(request.getRequestedSessionId())); + logEntry.setUserAgent(safeValue(request.getHeader("User-Agent"))); + + // 네트워크 정보 + logEntry.setIpAddress(safeValue(getClientIp(request))); + logEntry.setHostName(safeValue(request.getRemoteHost())); + logEntry.setRemotePort(request.getRemotePort()); + + // Transaction ID + String transactionId = UUID.randomUUID().toString(); + logEntry.setTransactionId(transactionId); + + logRepository.save(logEntry); + } catch (Exception ex) { log.error("로그 저장 실패: {}", ex.getMessage()); } } - private void validateAndSetAuthentication(String token) { - SecretKey secretKey = Keys.hmacShaKeyFor(jwtSecretKey.getBytes(StandardCharsets.UTF_8)); - Claims claims = Jwts.parserBuilder() - .setSigningKey(secretKey) - .build() - .parseClaimsJws(token) - .getBody(); - - String username = claims.get("username", String.class); - String authorities = claims.get("authorities", String.class); - - if (username == null || authorities == null || authorities.isEmpty()) { - log.warn("토큰에 필수 정보가 누락되었습니다. username: {}, authorities: {}", username, authorities); - throw new JwtException("토큰에 필수 정보가 없습니다."); + private String getClientIp(HttpServletRequest request) { + String[] headers = {"X-Forwarded-For", "Proxy-Client-IP", "WL-Proxy-Client-IP", + "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"}; + for (String header : headers) { + String ip = request.getHeader(header); + if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) { + return ip.split(",")[0]; + } } + return request.getRemoteAddr(); + } - List grantedAuthorities = Arrays.stream(authorities.split(",")) - .map(SimpleGrantedAuthority::new) - .collect(Collectors.toList()); - - Authentication authentication = new UsernamePasswordAuthenticationToken(username, null, grantedAuthorities); - SecurityContextHolder.getContext().setAuthentication(authentication); + private String safeValue(String value) { + return value != null ? value : "N/A"; } + + @Override protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { return isExemptedPath(request); From ab6df9e0010d62d975fc207c92f94565eae3252b Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 21 Nov 2024 10:19:21 +0900 Subject: [PATCH 250/563] =?UTF-8?q?feat:=20=EC=98=88=EC=99=B8=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EA=B4=80=EB=A0=A8(#61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PromotionController.java | 10 +-- .../application/dto/PromotionModifyDTO.java | 2 +- .../service/PromotionCommandService.java | 8 +- .../service/PromotionServiceImpl.java | 76 ++++++++++++++----- .../common/exception/PromotionErrorCode.java | 3 + 5 files changed, 71 insertions(+), 28 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java index 2c97992e..7a487fd4 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java @@ -37,7 +37,7 @@ public PromotionController(PromotionCommandService promotionCommandService, Auth public ResponseEntity postNotice(@RequestBody PromotionRegistDTO prmotionRegistDTO, Principal principal){ String memberId = authQueryService.selectMemberIdByLoginId(principal.getName()); prmotionRegistDTO.setMemberId(memberId); - promotionCommandService.registerPromotion(prmotionRegistDTO); + promotionCommandService.registerPromotion(prmotionRegistDTO,principal); return ResponseEntity.ok(PromotionResponseMessage.builder() .httpStatus(200) .msg("성공") @@ -52,9 +52,9 @@ public ResponseEntity postNotice(@RequestBody Promotio }) @PutMapping("{promotionId}") public ResponseEntity modifyNotice(@PathVariable String promotionId, - @RequestBody PromotionModifyDTO promotionModifyRequestDTO){ + @RequestBody PromotionModifyDTO promotionModifyRequestDTO, Principal principal){ - PromotionModifyDTO promotionModifyDTO = promotionCommandService.modifyPromotion(promotionId,promotionModifyRequestDTO); + PromotionModifyDTO promotionModifyDTO = promotionCommandService.modifyPromotion(promotionId,promotionModifyRequestDTO,principal); return ResponseEntity.ok(PromotionResponseMessage.builder() .httpStatus(200) @@ -69,9 +69,9 @@ public ResponseEntity modifyNotice(@PathVariable Strin content = {@Content(schema = @Schema(implementation = PromotionResponseMessage.class))}) }) @DeleteMapping("{promotionId}") - public ResponseEntity deleteNotice(@PathVariable String promotionId) { + public ResponseEntity deleteNotice(@PathVariable String promotionId, Principal principal) { - promotionCommandService.deletePromotion(promotionId); + promotionCommandService.deletePromotion(promotionId,principal); return ResponseEntity.ok(PromotionResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java index a6c5cdd6..cb68c8c2 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java @@ -12,7 +12,7 @@ public class PromotionModifyDTO { private String title; - + private String memberId; private String content; } diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/service/PromotionCommandService.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/service/PromotionCommandService.java index 46d6e76b..a8abdc19 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/service/PromotionCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/service/PromotionCommandService.java @@ -3,11 +3,13 @@ import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionModifyDTO; import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionRegistDTO; +import java.security.Principal; + public interface PromotionCommandService { - void registerPromotion(PromotionRegistDTO promotionRegistDTO); + void registerPromotion(PromotionRegistDTO promotionRegistDTO, Principal principal); - PromotionModifyDTO modifyPromotion(String promotionId, PromotionModifyDTO promotionModifyDTO); + PromotionModifyDTO modifyPromotion(String promotionId, PromotionModifyDTO promotionModifyDTO, Principal principal); - void deletePromotion(String promotionId); + void deletePromotion(String promotionId, Principal principal); } diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java index 396b6882..04f23920 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java @@ -2,8 +2,13 @@ import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.notices.common.exception.NoticeCommonException; +import stanl_2.final_backend.domain.notices.common.exception.NoticeErrorCode; +import stanl_2.final_backend.domain.problem.common.exception.ProblemCommonException; +import stanl_2.final_backend.domain.problem.common.exception.ProblemErrorCode; import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionModifyDTO; import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionRegistDTO; import stanl_2.final_backend.domain.promotion.command.application.service.PromotionCommandService; @@ -12,6 +17,7 @@ import stanl_2.final_backend.domain.promotion.common.exception.PromotionCommonException; import stanl_2.final_backend.domain.promotion.common.exception.PromotionErrorCode; +import java.security.Principal; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -35,41 +41,73 @@ private String getCurrentTimestamp() { @Transactional @Override - public void registerPromotion(PromotionRegistDTO promotionRegistDTO) { - Promotion promotion =modelMapper.map(promotionRegistDTO,Promotion.class); - promotionRepository.save(promotion); + public void registerPromotion(PromotionRegistDTO promotionRegistDTO, Principal principal) { + String memberId =principal.getName(); + promotionRegistDTO.setMemberId(memberId); + try { + Promotion promotion =modelMapper.map(promotionRegistDTO,Promotion.class); + promotionRepository.save(promotion); + } catch (DataIntegrityViolationException e){ + throw new PromotionCommonException(PromotionErrorCode.DATA_INTEGRITY_VIOLATION); + }catch (Exception e) { + // 서버 오류 + throw new NoticeCommonException(NoticeErrorCode.INTERNAL_SERVER_ERROR); + } } @Transactional @Override - public PromotionModifyDTO modifyPromotion(String promotionId, PromotionModifyDTO promotionModifyDTO) { + public PromotionModifyDTO modifyPromotion(String promotionId, PromotionModifyDTO promotionModifyDTO, Principal principal) { + String memberId= principal.getName(); Promotion promotion = promotionRepository.findById(promotionId) .orElseThrow(() -> new PromotionCommonException(PromotionErrorCode.PROMOTION_NOT_FOUND)); - - Promotion updatePromotion = modelMapper.map(promotionModifyDTO, Promotion.class); - updatePromotion.setPromotionId(promotion.getPromotionId()); - updatePromotion.setMemberId(promotion.getMemberId()); - updatePromotion.setCreatedAt(promotion.getCreatedAt()); - updatePromotion.setActive(promotion.getActive()); - - promotionRepository.save(updatePromotion); - - PromotionModifyDTO promotionModify = modelMapper.map(updatePromotion,PromotionModifyDTO.class); - - return promotionModify; + if(!promotionModifyDTO.getMemberId().equals(memberId)){ + throw new ProblemCommonException(ProblemErrorCode.AUTHORIZATION_VIOLATION); + } + try { + Promotion updatePromotion = modelMapper.map(promotionModifyDTO, Promotion.class); + updatePromotion.setPromotionId(promotion.getPromotionId()); + updatePromotion.setMemberId(promotion.getMemberId()); + updatePromotion.setCreatedAt(promotion.getCreatedAt()); + updatePromotion.setActive(promotion.getActive()); + + promotionRepository.save(updatePromotion); + + PromotionModifyDTO promotionModify = modelMapper.map(updatePromotion,PromotionModifyDTO.class); + + return promotionModify; + } catch (DataIntegrityViolationException e) { + // 데이터 무결성 위반 예외 처리 + throw new PromotionCommonException(PromotionErrorCode.DATA_INTEGRITY_VIOLATION); + } catch (Exception e) { + // 서버 오류 + throw new PromotionCommonException(PromotionErrorCode.INTERNAL_SERVER_ERROR); + } } @Transactional @Override - public void deletePromotion(String promotionId) { - + public void deletePromotion(String promotionId, Principal principal) { + String memberId= principal.getName(); Promotion promotion = promotionRepository.findById(promotionId) .orElseThrow(()-> new PromotionCommonException(PromotionErrorCode.PROMOTION_NOT_FOUND)); + if(!memberId.equals(memberId)){ + // 권한 오류 + throw new PromotionCommonException(PromotionErrorCode.AUTHORIZATION_VIOLATION); + } promotion.setActive(false); promotion.setDeletedAt(getCurrentTimestamp()); - promotionRepository.save(promotion); + try { + promotionRepository.save(promotion); + } catch (DataIntegrityViolationException e) { + // 데이터 무결성 위반 예외 처리 + throw new PromotionCommonException(PromotionErrorCode.DATA_INTEGRITY_VIOLATION); + } catch (Exception e) { + // 서버 오류 + throw new PromotionCommonException(PromotionErrorCode.INTERNAL_SERVER_ERROR); + } } } diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/common/exception/PromotionErrorCode.java b/src/main/java/stanl_2/final_backend/domain/promotion/common/exception/PromotionErrorCode.java index 2e72fdf9..e853787d 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/common/exception/PromotionErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/common/exception/PromotionErrorCode.java @@ -39,6 +39,9 @@ public enum PromotionErrorCode { * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. */ PROMOTION_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "promotion 데이터를 찾지 못했습니다"), + DATA_INTEGRITY_VIOLATION(40402, HttpStatus.BAD_REQUEST, "데이터 무결 위반하였습니다."), + + AUTHORIZATION_VIOLATION(40403, HttpStatus.BAD_REQUEST, "접근 권한이 없습니다."), /** * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. */ From 2bb936ae50c20cca6ebc20e62cd59304597373ae Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Thu, 21 Nov 2024 10:40:55 +0900 Subject: [PATCH 251/563] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C(#110)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/log/command/aggregate/Log.java | 35 ++++++++++++---- .../domain/log/command/aop/LoggingAspect.java | 7 ---- .../log/query/controller/LogController.java | 2 +- .../domain/log/query/dto/LogDTO.java | 17 ++------ .../global/exception/GlobalErrorCode.java | 3 +- .../exception/GlobalExceptionHandler.java | 2 - .../filter/JWTTokenValidatorFilter.java | 19 ++++++--- .../domain/log/query/repository/LogMapper.xml | 40 +++++++++++++------ 8 files changed, 75 insertions(+), 50 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java b/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java index db5a9cca..de9786a5 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java +++ b/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java @@ -1,16 +1,24 @@ package stanl_2.final_backend.domain.log.command.aggregate; import jakarta.persistence.*; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; import org.hibernate.annotations.GenericGenerator; import stanl_2.final_backend.global.config.PrefixGeneratorConfig; import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +@NoArgsConstructor +@AllArgsConstructor @Getter @Setter @Entity +@Table(name = "tb_log") public class Log { @Id @GeneratedValue(generator = "PrefixGeneratorConfig") @@ -24,10 +32,10 @@ public class Log { @Column(name = "SESSION_ID") private String sessionId; - @Column(name = "USER_AGENT") + @Column(name = "USER_AGENT", length = 500) private String userAgent; - @Column(name = "IP_ADDRESS") + @Column(name = "IP_ADDRESS", length = 45) private String ipAddress; @Column(name = "HOST_NAME") @@ -36,26 +44,37 @@ public class Log { @Column(name = "REMOTE_PORT") private Integer remotePort; - @Column(name = "URI") + @Column(name = "URI", length = 2048) private String uri; @Column(name = "METHOD") private String method; - @Column(name = "QUERY_STRING") + @Column(name = "QUERY_STRING", length = 2048) private String queryString; - @Column(name = "REQUEST_TIME") - private LocalDateTime requestTime; + @Column(name = "REQUEST_TIME", nullable = false, updatable = false) + private String requestTime; @Column(name = "TRANSACTION_ID") private String transactionId; - @Column(name = "STATUS") + @Column(name = "STATUS", nullable = false, length = 50) private String status; - @Column(name = "ERROR_MESSAGE") + @Column(name = "ERROR_MESSAGE", columnDefinition = "TEXT") private String errorMessage; + + + @PrePersist + private void prePersist() { + this.requestTime = getCurrentTime(); + } + + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java b/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java index 918f5f04..0f06ad99 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java +++ b/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java @@ -60,10 +60,6 @@ public void logRequestSuccess(JoinPoint joinPoint) { requestData.put("query_string", safeValue(request.getQueryString())); logData.put("request", requestData); - // 시간 정보 - Map timeData = new HashMap<>(); - timeData.put("request_time", LocalDateTime.now().toString()); - logData.put("time", timeData); // 추가 정보 String transactionId = UUID.randomUUID().toString(); @@ -88,9 +84,6 @@ public void logRequestSuccess(JoinPoint joinPoint) { logEntry.setMethod(safeValue(request.getMethod())); logEntry.setQueryString(safeValue(request.getQueryString())); - // 시간 정보 - logEntry.setRequestTime(LocalDateTime.now()); - // 추가적인 정보 logEntry.setTransactionId(transactionId); diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java b/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java index 25c9cfbd..97a81300 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java @@ -28,7 +28,7 @@ public LogController(LogQueryService logQueryService) { this.logQueryService = logQueryService; } - @Operation(summary = "로그 조회(관리자)") + @Operation(summary = "로그 조회(시스템 관리자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogDTO.java b/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogDTO.java index 5ab50be0..ff3e746d 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogDTO.java @@ -12,26 +12,17 @@ @Setter @Getter public class LogDTO { - - private Long logId; - - // User Information + private String logId; private String sessionId; private String userAgent; - - // Network Information private String ipAddress; private String hostName; private Integer remotePort; - - // Request Information private String uri; private String method; private String queryString; - - // Time Information - private LocalDateTime requestTime; - - // Additional Information + private String requestTime; private String transactionId; + private String status; + private String errorMessage; } diff --git a/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java b/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java index 81e6c27d..1b19f1aa 100644 --- a/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java +++ b/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java @@ -57,7 +57,8 @@ public enum GlobalErrorCode { * 500(Internal Server Error) * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. */ - INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); + INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."), + FAIL_LOG_SAVE(50001, HttpStatus.INTERNAL_SERVER_ERROR, "로그내용을 저장할 수 없습니다."); private final Integer code; diff --git a/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java b/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java index 872ad417..fc09ed0b 100644 --- a/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java @@ -17,7 +17,6 @@ import stanl_2.final_backend.domain.log.command.aggregate.Log; import stanl_2.final_backend.domain.log.command.repository.LogRepository; -import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -109,7 +108,6 @@ private void saveErrorLog(String message, Exception e, HttpServletRequest reques logEntry.setUri(safeValue(request.getRequestURI())); logEntry.setMethod(safeValue(request.getMethod())); logEntry.setQueryString(safeValue(request.getQueryString())); - logEntry.setRequestTime(LocalDateTime.now()); // 유저 정보 logEntry.setSessionId(safeValue(request.getRequestedSessionId())); diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java index 9955c79a..62de2954 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java @@ -18,11 +18,12 @@ import org.springframework.web.filter.OncePerRequestFilter; import stanl_2.final_backend.domain.log.command.aggregate.Log; import stanl_2.final_backend.domain.log.command.repository.LogRepository; +import stanl_2.final_backend.global.exception.GlobalCommonException; +import stanl_2.final_backend.global.exception.GlobalErrorCode; import javax.crypto.SecretKey; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.time.LocalDateTime; import java.util.Arrays; import java.util.List; import java.util.UUID; @@ -91,12 +92,20 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse private void handleInvalidToken(HttpServletResponse response, String message, Exception e, HttpServletRequest request) throws IOException { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - response.setContentType("application/json"); - response.getWriter().write("{\"error\": \"" + message + "\"}"); + response.setContentType("application/json;charset=UTF-8"); + + String jsonResponse = String.format( + "{\"code\": 40100, \"msg\": \"%s\", \"httpStatus\": \"UNAUTHORIZED\"}", + message + ); + + response.getWriter().write(jsonResponse); + log.error("요청 거부됨: {}. Error: {}. Request URI: {}, Method: {}, Client IP: {}", message, e.getMessage(), request.getRequestURI(), request.getMethod(), getClientIp(request)); - saveErrorLog(message, e, request); // Save detailed error log + + saveErrorLog(message, e, request); } @@ -112,7 +121,6 @@ private void saveErrorLog(String message, Exception e, HttpServletRequest reques logEntry.setUri(safeValue(request.getRequestURI())); logEntry.setMethod(safeValue(request.getMethod())); logEntry.setQueryString(safeValue(request.getQueryString())); - logEntry.setRequestTime(LocalDateTime.now()); // 유저 정보 logEntry.setSessionId(safeValue(request.getRequestedSessionId())); @@ -131,6 +139,7 @@ private void saveErrorLog(String message, Exception e, HttpServletRequest reques } catch (Exception ex) { log.error("로그 저장 실패: {}", ex.getMessage()); + throw new GlobalCommonException(GlobalErrorCode.FAIL_LOG_SAVE); } } diff --git a/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml b/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml index faf37fd7..2523b03b 100644 --- a/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml @@ -4,23 +4,37 @@ "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> - - - - - - - - - - - + + + + + + + + + + + + + + \ No newline at end of file From cb42ce47bada9a8d3ea3287b9ab190e79dabe20d Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 21 Nov 2024 11:03:00 +0900 Subject: [PATCH 252/563] =?UTF-8?q?fix:=20=EC=88=98=EC=A0=95=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EC=88=98=EC=A0=95=20(#119)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/domain/service/NoticeCommandServiceImpl.java | 2 +- .../command/domain/aggregate/service/ProblemServiceImpl.java | 4 ++-- .../domain/aggregate/service/PromotionServiceImpl.java | 4 ++-- src/main/resources/application-prod.yml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java index 4285f7c4..78f44fed 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java @@ -82,7 +82,7 @@ public NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO,P Notice notice = noticeRepository.findById(id) .orElseThrow(() -> new NoticeCommonException(NoticeErrorCode.NOTICE_NOT_FOUND)); - if(!noticeModifyDTO.getMemberId().equals(memberId)){ + if(!notice.getMemberId().equals(memberId)){ // 권한 오류 throw new NoticeCommonException(NoticeErrorCode.AUTHORIZATION_VIOLATION); } diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java index 2706cc0e..62399dd8 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java @@ -63,7 +63,7 @@ public ProblemModifyDTO modifyProblem(String problemId, ProblemModifyDTO problem String memberId= principal.getName(); Problem problem = problemRepository.findById(problemId) .orElseThrow(() -> new ProblemCommonException(ProblemErrorCode.PROBLEM_NOT_FOUND)); - if(!problemModifyDTO.getMemberId().equals(memberId)){ + if(!problem.getMemberId().equals(memberId)){ throw new ProblemCommonException(ProblemErrorCode.AUTHORIZATION_VIOLATION); } try { @@ -96,7 +96,7 @@ public void deleteProblem(String problemId, Principal principal) { Problem problem = problemRepository.findById(problemId) .orElseThrow(()-> new ProblemCommonException(ProblemErrorCode.PROBLEM_NOT_FOUND)); - if(!memberId.equals(memberId)){ + if(!problem.getMemberId().equals(memberId)){ // 권한 오류 throw new ProblemCommonException(ProblemErrorCode.AUTHORIZATION_VIOLATION); } diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java index 04f23920..29dca26c 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java @@ -61,7 +61,7 @@ public PromotionModifyDTO modifyPromotion(String promotionId, PromotionModifyDTO String memberId= principal.getName(); Promotion promotion = promotionRepository.findById(promotionId) .orElseThrow(() -> new PromotionCommonException(PromotionErrorCode.PROMOTION_NOT_FOUND)); - if(!promotionModifyDTO.getMemberId().equals(memberId)){ + if(!promotion.getMemberId().equals(memberId)){ throw new ProblemCommonException(ProblemErrorCode.AUTHORIZATION_VIOLATION); } try { @@ -92,7 +92,7 @@ public void deletePromotion(String promotionId, Principal principal) { Promotion promotion = promotionRepository.findById(promotionId) .orElseThrow(()-> new PromotionCommonException(PromotionErrorCode.PROMOTION_NOT_FOUND)); - if(!memberId.equals(memberId)){ + if(!promotion.getMemberId().equals(memberId)){ // 권한 오류 throw new PromotionCommonException(PromotionErrorCode.AUTHORIZATION_VIOLATION); } diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 6ff598fc..520817c3 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: create + ddl-auto: update logging: level: From a974937da301ad5448ed80af2a9256a38b9dfd15 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 21 Nov 2024 11:04:46 +0900 Subject: [PATCH 253/563] =?UTF-8?q?fix:=20=EC=88=98=EC=A0=95=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EC=88=98=EC=A0=95=20(#119)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-prod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 520817c3..6ff598fc 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: update + ddl-auto: create logging: level: From 0446009d8d71f274e9b747db5a3678b61a1b6cac Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Thu, 21 Nov 2024 11:10:36 +0900 Subject: [PATCH 254/563] =?UTF-8?q?fix:=20nullable=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/log/command/aggregate/Log.java | 20 +++++++++---------- .../filter/JWTTokenValidatorFilter.java | 1 - 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java b/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java index de9786a5..a80f2afe 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java +++ b/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java @@ -29,37 +29,37 @@ public class Log { @Column(name = "LOG_ID", nullable = false) private String logId; - @Column(name = "SESSION_ID") + @Column(name = "SESSION_ID", nullable = false) private String sessionId; - @Column(name = "USER_AGENT", length = 500) + @Column(name = "USER_AGENT", length = 500, nullable = false) private String userAgent; - @Column(name = "IP_ADDRESS", length = 45) + @Column(name = "IP_ADDRESS", nullable = false) private String ipAddress; - @Column(name = "HOST_NAME") + @Column(name = "HOST_NAME", nullable = false) private String hostName; - @Column(name = "REMOTE_PORT") + @Column(name = "REMOTE_PORT", nullable = false) private Integer remotePort; - @Column(name = "URI", length = 2048) + @Column(name = "URI", length = 2048, nullable = false) private String uri; - @Column(name = "METHOD") + @Column(name = "METHOD", nullable = false) private String method; - @Column(name = "QUERY_STRING", length = 2048) + @Column(name = "QUERY_STRING", length = 2048, nullable = false) private String queryString; @Column(name = "REQUEST_TIME", nullable = false, updatable = false) private String requestTime; - @Column(name = "TRANSACTION_ID") + @Column(name = "TRANSACTION_ID", nullable = false) private String transactionId; - @Column(name = "STATUS", nullable = false, length = 50) + @Column(name = "STATUS", nullable = false) private String status; @Column(name = "ERROR_MESSAGE", columnDefinition = "TEXT") diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java index 62de2954..81586cd5 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java @@ -104,7 +104,6 @@ private void handleInvalidToken(HttpServletResponse response, String message, Ex log.error("요청 거부됨: {}. Error: {}. Request URI: {}, Method: {}, Client IP: {}", message, e.getMessage(), request.getRequestURI(), request.getMethod(), getClientIp(request)); - saveErrorLog(message, e, request); } From 8fcda47ce7e5d6f2addbca51592da46741379c73 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 21 Nov 2024 11:23:57 +0900 Subject: [PATCH 255/563] =?UTF-8?q?prod:=20ddl-auto=20update=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-prod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 0d0b92a3..083491db 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: create + ddl-auto: update logging: level: From bcfb6570b9bba48f5ba966397a44ba5520fb7afb Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Thu, 21 Nov 2024 11:29:49 +0900 Subject: [PATCH 256/563] =?UTF-8?q?feat:=20S3=20=EC=84=B8=ED=8C=85=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B8=B0=EB=8A=A5=20=EC=84=B8=ED=8C=85=20(#121)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 +- .../domain/s3/S3FileService.java | 26 ++++ .../domain/s3/S3FileServiceImpl.java | 115 ++++++++++++++++++ .../common/exception/S3CommonException.java | 16 +++ .../s3/common/exception/S3ErrorCode.java | 52 ++++++++ .../common/exception/S3ExceptionResponse.java | 22 ++++ .../common/response/OrderResponseMessage.java | 14 +++ .../final_backend/global/config/S3Config.java | 28 +++++ src/main/resources/application.yml | 14 ++- 9 files changed, 288 insertions(+), 2 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/s3/S3FileService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/s3/S3FileServiceImpl.java create mode 100644 src/main/java/stanl_2/final_backend/domain/s3/common/exception/S3CommonException.java create mode 100644 src/main/java/stanl_2/final_backend/domain/s3/common/exception/S3ErrorCode.java create mode 100644 src/main/java/stanl_2/final_backend/domain/s3/common/exception/S3ExceptionResponse.java create mode 100644 src/main/java/stanl_2/final_backend/domain/s3/common/response/OrderResponseMessage.java create mode 100644 src/main/java/stanl_2/final_backend/global/config/S3Config.java diff --git a/build.gradle b/build.gradle index d3510cba..ec125d82 100644 --- a/build.gradle +++ b/build.gradle @@ -62,7 +62,8 @@ dependencies { //hibernate core implementation 'org.hibernate.orm:hibernate-core:6.5.2.Final' - + // S3 + implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' diff --git a/src/main/java/stanl_2/final_backend/domain/s3/S3FileService.java b/src/main/java/stanl_2/final_backend/domain/s3/S3FileService.java new file mode 100644 index 00000000..456f590c --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/s3/S3FileService.java @@ -0,0 +1,26 @@ +package stanl_2.final_backend.domain.s3; + +import org.springframework.web.multipart.MultipartFile; + +public interface S3FileService { + + // 파일 업로드(단일) + String uploadOneFile(MultipartFile file); + + // 파일 업로드(다중) + + + // 파일 삭제 + void deleteFile(String fileName); + + // 파일 URL 조회 + String getFileUrl(String fileName); + + // 파일 다운로드 + byte[] downloadFile(String fileName); + + // 폴더 조회 +// String getFileFolder(FileForder fileFolder); + + +} diff --git a/src/main/java/stanl_2/final_backend/domain/s3/S3FileServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/s3/S3FileServiceImpl.java new file mode 100644 index 00000000..aa1a817d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/s3/S3FileServiceImpl.java @@ -0,0 +1,115 @@ +package stanl_2.final_backend.domain.s3; + +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.DeleteObjectRequest; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.S3ObjectInputStream; +import com.amazonaws.util.IOUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import stanl_2.final_backend.domain.s3.common.exception.S3CommonException; +import stanl_2.final_backend.domain.s3.common.exception.S3ErrorCode; +import stanl_2.final_backend.global.config.S3Config; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.UUID; + +@Component +@Service +public class S3FileServiceImpl implements S3FileService { + + private final AmazonS3Client amazonS3Client; + + @Autowired + public S3FileServiceImpl(AmazonS3Client amazonS3Client) { + this.amazonS3Client = amazonS3Client; + } + + @Value("${cloud.aws.s3.bucket}") + private String bucket; + + @Value("${cloud.aws.region.static}") + private String region; + + // 단일 파일 업로드 + @Override + public String uploadOneFile(MultipartFile file) { + + String fileName = createFileName(file.getOriginalFilename()); + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentType(file.getContentType()); + metadata.setContentLength(file.getSize()); + + try(InputStream inputStream = file.getInputStream()) { + amazonS3Client.putObject(bucket, fileName, inputStream, metadata); + + return "https://" + bucket + ".s3." + region + ".amazonaws.com/" + fileName; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + // 파일 삭제 + @Override + public void deleteFile(String fileName) { + amazonS3Client.deleteObject(new DeleteObjectRequest(bucket, fileName)); + } + + // AWS S3에 저장된 파일 이름에 해당하는 URL(경로)를 알아내는 함수 + @Override + public String getFileUrl(String fileName) { + return amazonS3Client.getUrl(bucket, fileName).toString(); + } + + // 파일 다운로드 + @Override + public byte[] downloadFile(String fileName) { + + // 파일 유무 확인 + validateFileExists(fileName); + + S3Object s3Object = amazonS3Client.getObject(bucket, fileName); + S3ObjectInputStream s3ObjectContent = s3Object.getObjectContent(); + + try { + return IOUtils.toByteArray(s3ObjectContent); + } catch (IOException e) { + throw new S3CommonException(S3ErrorCode.FILE_NOT_FOUND); + } + } + + private void validateFileExists(String fileName) { + if (!amazonS3Client.doesObjectExist(bucket, fileName)) { + throw new S3CommonException(S3ErrorCode.FILE_NOT_FOUND); + } + } + + private String createFileName(String fileName) { + return UUID.randomUUID().toString().concat(getFileExtension(fileName)); + } + + private String getFileExtension(String fileName) { + if (fileName.length() == 0) { + throw new S3CommonException(S3ErrorCode.FILE_NOT_FOUND); + } + + ArrayList fileValidate = new ArrayList<>(); + fileValidate.add(".jpg"); + fileValidate.add(".jpeg"); + fileValidate.add(".png"); + fileValidate.add(".JPG"); + fileValidate.add(".JPEG"); + fileValidate.add(".PNG"); + String idxFileName = fileName.substring(fileName.lastIndexOf(".")); + if(!fileValidate.contains(idxFileName)) { + throw new S3CommonException(S3ErrorCode.FILE_NOT_FOUND); + } + return fileName.substring(fileName.lastIndexOf(".")); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/s3/common/exception/S3CommonException.java b/src/main/java/stanl_2/final_backend/domain/s3/common/exception/S3CommonException.java new file mode 100644 index 00000000..aac705cb --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/s3/common/exception/S3CommonException.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.s3.common.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class S3CommonException extends RuntimeException { + private final S3ErrorCode sampleErrorCode; + + // 에러 발생시 ErroCode 별 메시지 + @Override + public String getMessage() { + return this.sampleErrorCode.getMsg(); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/s3/common/exception/S3ErrorCode.java b/src/main/java/stanl_2/final_backend/domain/s3/common/exception/S3ErrorCode.java new file mode 100644 index 00000000..48675e58 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/s3/common/exception/S3ErrorCode.java @@ -0,0 +1,52 @@ +package stanl_2.final_backend.domain.s3.common.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum S3ErrorCode { + + /** + * 400(Bad Request) + * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. + */ + + + + /** + * 401(Unauthorized) + * 비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만, + * 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다. + * 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다. + */ + + + /** + * 403(Forbidden) + * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다. + * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다. + */ + + + + /** + * 404(Not Found) + * 서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다. + * 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있습니다. + * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. + * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. + */ + FILE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "파일이 존재하지 않습니다."), + + /** + * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. + */ + INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); + + + private final Integer code; + private final HttpStatus httpStatus; + private final String msg; +} diff --git a/src/main/java/stanl_2/final_backend/domain/s3/common/exception/S3ExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/s3/common/exception/S3ExceptionResponse.java new file mode 100644 index 00000000..26548574 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/s3/common/exception/S3ExceptionResponse.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.s3.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class S3ExceptionResponse { + private final Integer code; + private final String msg; + private final HttpStatus httpStatus; + + public S3ExceptionResponse(S3ErrorCode sampleErrorCode) { + this.code = sampleErrorCode.getCode(); + this.msg = sampleErrorCode.getMsg(); + this.httpStatus = sampleErrorCode.getHttpStatus(); + } + + public static S3ExceptionResponse of(S3ErrorCode sampleErrorCode) { + return new S3ExceptionResponse(sampleErrorCode); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/s3/common/response/OrderResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/s3/common/response/OrderResponseMessage.java new file mode 100644 index 00000000..53e2eb79 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/s3/common/response/OrderResponseMessage.java @@ -0,0 +1,14 @@ +package stanl_2.final_backend.domain.s3.common.response; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Getter +@Setter +public class OrderResponseMessage { + private int httpStatus; + private String msg; + private Object result; +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/global/config/S3Config.java b/src/main/java/stanl_2/final_backend/global/config/S3Config.java new file mode 100644 index 00000000..069b1e89 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/config/S3Config.java @@ -0,0 +1,28 @@ +package stanl_2.final_backend.global.config; + +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class S3Config { + @Value("${cloud.aws.credentials.access-key}") + private String accessKey; + @Value("${cloud.aws.credential.secret-key}") + private String secretKey; + @Value("${cloud.aws.region.static") + private String region; + + @Bean + public AmazonS3Client amazonS3Client() { + BasicAWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey); + return (AmazonS3Client) AmazonS3ClientBuilder.standard() + .withRegion(region) + .withCredentials(new AWSStaticCredentialsProvider(awsCredentials)) + .build(); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 6618043e..1ba7515c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -43,4 +43,16 @@ logging: level: org: springframework: - security: ${SPRING_SECURITY_LOG_LEVEL:TRACE} \ No newline at end of file + security: ${SPRING_SECURITY_LOG_LEVEL:TRACE} + +cloud: + aws: + credentials: + access-key: ${S3_ACCESSKEY} + secret-key: ${S3_SECRETKEY} + s3: + bucket: ${S3_BUCKETNAME} + region: + static: ${S3_REGION} + stack: + auto: false From 4e546c198e2c92b2557cc9ae33f215458a20192f Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Thu, 21 Nov 2024 11:32:20 +0900 Subject: [PATCH 257/563] =?UTF-8?q?refactor:=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#112)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-dev.yml | 2 +- src/main/resources/application-prod.yml | 2 +- src/main/resources/application.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index aabe59aa..e9390662 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: update + ddl-auto: create logging: level: diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 083491db..0d0b92a3 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: update + ddl-auto: create logging: level: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index eee7170c..6618043e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -23,7 +23,7 @@ spring: jpa: show-sql: true hibernate: - ddl-auto: update + ddl-auto: create properties: hibernate.format_sql: true From fadbfdea17ee154d4836e9f7fe1436499bcd8519 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 21 Nov 2024 11:47:52 +0900 Subject: [PATCH 258/563] =?UTF-8?q?Fix:=20Redis=20=EC=84=B8=ED=8C=85=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/service/NoticeServiceImpl.java | 10 ++++-- .../global/{config => redis}/RedisConfig.java | 2 +- .../global/redis/RedisService.java | 31 +++++++++++++++++++ 3 files changed, 40 insertions(+), 3 deletions(-) rename src/main/java/stanl_2/final_backend/global/{config => redis}/RedisConfig.java (97%) create mode 100644 src/main/java/stanl_2/final_backend/global/redis/RedisService.java diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java index 379bf290..6f91c9ee 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java @@ -10,6 +10,7 @@ import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; import stanl_2.final_backend.domain.notices.query.dto.SearchDTO; import stanl_2.final_backend.domain.notices.query.repository.NoticeMapper; +import stanl_2.final_backend.global.redis.RedisService; import java.util.List; @@ -18,11 +19,12 @@ public class NoticeServiceImpl implements NoticeService{ private final NoticeMapper noticeMapper; private final RedisTemplate redisTemplate; - + private final RedisService redisService; @Autowired - public NoticeServiceImpl(NoticeMapper noticeMapper, RedisTemplate redisTemplate) { + public NoticeServiceImpl(NoticeMapper noticeMapper, RedisTemplate redisTemplate, RedisService redisService) { this.noticeMapper = noticeMapper; this.redisTemplate = redisTemplate; + this.redisService =redisService; } @Transactional @@ -39,6 +41,10 @@ public Page findAllNotices(Pageable pageable) { System.out.println("데이터베이스에서 데이터 조회 중..."); notices = noticeMapper.findAllNotices(offset, size); redisTemplate.opsForValue().set(cacheKey, notices); + String key = "NoticePage"+offset; + Object value = notices; + long ttlInSeconds = 30*60; + redisService.setKeyWithTTL(key, value, ttlInSeconds); } else { System.out.println("캐시에서 데이터 조회 중..."); } diff --git a/src/main/java/stanl_2/final_backend/global/config/RedisConfig.java b/src/main/java/stanl_2/final_backend/global/redis/RedisConfig.java similarity index 97% rename from src/main/java/stanl_2/final_backend/global/config/RedisConfig.java rename to src/main/java/stanl_2/final_backend/global/redis/RedisConfig.java index cfbc911f..43c962d8 100644 --- a/src/main/java/stanl_2/final_backend/global/config/RedisConfig.java +++ b/src/main/java/stanl_2/final_backend/global/redis/RedisConfig.java @@ -1,4 +1,4 @@ -package stanl_2.final_backend.global.config; +package stanl_2.final_backend.global.redis; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.annotation.Bean; diff --git a/src/main/java/stanl_2/final_backend/global/redis/RedisService.java b/src/main/java/stanl_2/final_backend/global/redis/RedisService.java new file mode 100644 index 00000000..f4d2b5e0 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/redis/RedisService.java @@ -0,0 +1,31 @@ +package stanl_2.final_backend.global.redis; + +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import java.time.Duration; + +@Service +public class RedisService { + + private final RedisTemplate redisTemplate; + + public RedisService(RedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + } + + // 키와 값을 저장하며 TTL 설정 + public void setKeyWithTTL(String key, Object value, long ttlInSeconds) { + redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(ttlInSeconds)); + } + + // 키 조회 + public Object getKey(String key) { + return redisTemplate.opsForValue().get(key); + } + + // TTL 설정 (기존 키에 대해) + public boolean setTTL(String key, long ttlInSeconds) { + return redisTemplate.expire(key, Duration.ofSeconds(ttlInSeconds)); + } +} From f4b8cb01c0989a1f3428e4ff87f748264828b977 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Thu, 21 Nov 2024 11:49:30 +0900 Subject: [PATCH 259/563] =?UTF-8?q?fix:=20pr=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95(#112)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/service/ContractCommandServiceImpl.java | 6 +++--- .../query/controller/ProductController.java | 14 +++++++------- ...roductService.java => ProductQueryService.java} | 2 +- ...rviceImpl.java => ProductQueryServiceImpl.java} | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) rename src/main/java/stanl_2/final_backend/domain/product/query/service/{ProductService.java => ProductQueryService.java} (94%) rename src/main/java/stanl_2/final_backend/domain/product/query/service/{ProductServiceImpl.java => ProductQueryServiceImpl.java} (94%) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index ac2a2381..73de5969 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -22,7 +22,7 @@ import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import stanl_2.final_backend.domain.product.command.application.command.service.ProductCommandService; import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; -import stanl_2.final_backend.domain.product.query.service.ProductService; +import stanl_2.final_backend.domain.product.query.service.ProductQueryService; import stanl_2.final_backend.domain.sales_history.command.application.service.SalesHistoryCommandService; import stanl_2.final_backend.global.utils.AESUtils; @@ -39,13 +39,13 @@ public class ContractCommandServiceImpl implements ContractCommandService { private final CustomerQueryService customerQueryService; private final MemberQueryService memberQueryService; private final CustomerCommandService customerCommandService; - private final ProductService productService; + private final ProductQueryService productService; private final ProductCommandService productCommandService; private final SalesHistoryCommandService salesHistoryCommandService; private final ModelMapper modelMapper; private final AESUtils aesUtils; - public ContractCommandServiceImpl(ContractRepository contractRepository, AuthQueryService authQueryService, CustomerQueryService customerQueryService, MemberQueryService memberQueryService, CustomerCommandService customerCommandService, ProductService productService, ProductCommandService productCommandService, SalesHistoryCommandService salesHistoryCommandService, ModelMapper modelMapper, AESUtils aesUtils) { + public ContractCommandServiceImpl(ContractRepository contractRepository, AuthQueryService authQueryService, CustomerQueryService customerQueryService, MemberQueryService memberQueryService, CustomerCommandService customerCommandService, ProductQueryService productService, ProductCommandService productCommandService, SalesHistoryCommandService salesHistoryCommandService, ModelMapper modelMapper, AESUtils aesUtils) { this.contractRepository = contractRepository; this.authQueryService = authQueryService; this.customerQueryService = customerQueryService; diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java b/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java index bbd63f6c..4bd45100 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java @@ -9,7 +9,7 @@ import stanl_2.final_backend.domain.product.common.response.ProductResponseMessage; import stanl_2.final_backend.domain.product.query.dto.ProductSearchRequestDTO; import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; -import stanl_2.final_backend.domain.product.query.service.ProductService; +import stanl_2.final_backend.domain.product.query.service.ProductQueryService; import java.util.HashMap; import java.util.Map; @@ -18,17 +18,17 @@ @RequestMapping("/api/v1/product") public class ProductController { - private final ProductService productService; + private final ProductQueryService productQueryService; @Autowired - public ProductController(ProductService productService) { - this.productService = productService; + public ProductController(ProductQueryService productQueryService) { + this.productQueryService = productQueryService; } @GetMapping("") public ResponseEntity getProductAll(@PageableDefault(size = 20) Pageable pageable){ - Page> responseProducts = productService.selectAll(pageable); + Page> responseProducts = productQueryService.selectAll(pageable); return ResponseEntity.ok(ProductResponseMessage.builder() .httpStatus(200) @@ -40,7 +40,7 @@ public ResponseEntity getProductAll(@PageableDefault(siz @GetMapping("{id}") public ResponseEntity getProductById(@PathVariable("id") String id){ - ProductSelectIdDTO productSelectIdDTO = productService.selectByProductId(id); + ProductSelectIdDTO productSelectIdDTO = productQueryService.selectByProductId(id); return ResponseEntity.ok(ProductResponseMessage.builder() .httpStatus(200) @@ -62,7 +62,7 @@ public ResponseEntity getProductBySearch(@RequestParam M paramMap.put("productSearchRequestDTO", productSearchRequestDTO); paramMap.put("pageable", pageable); - Page> responseProducts = productService.selectProductBySearch(paramMap); + Page> responseProducts = productQueryService.selectProductBySearch(paramMap); return ResponseEntity.ok(ProductResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductService.java b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryService.java similarity index 94% rename from src/main/java/stanl_2/final_backend/domain/product/query/service/ProductService.java rename to src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryService.java index 634f51b8..0d2faf49 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductService.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryService.java @@ -9,7 +9,7 @@ import java.util.Map; @Service -public interface ProductService { +public interface ProductQueryService { Page> selectAll(Pageable pageable); ProductSelectIdDTO selectByProductId(String id); diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java similarity index 94% rename from src/main/java/stanl_2/final_backend/domain/product/query/service/ProductServiceImpl.java rename to src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java index 780a246a..9145422e 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java @@ -14,12 +14,12 @@ import java.util.Map; @Service -public class ProductServiceImpl implements ProductService{ +public class ProductQueryServiceImpl implements ProductQueryService { private final ProductMapper productMapper; @Autowired - public ProductServiceImpl(ProductMapper productMapper) { + public ProductQueryServiceImpl(ProductMapper productMapper) { this.productMapper = productMapper; } From bba6a829eef0c7897d141ef18cfa8b5bb0ea1490 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 21 Nov 2024 12:11:59 +0900 Subject: [PATCH 260/563] =?UTF-8?q?Fix:=20=EC=A0=84=EC=B2=B4=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=A3=BC=EC=84=9D=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/NoticeController.java | 36 ++++++++-------- .../notices/query/service/NoticeService.java | 2 +- .../query/service/NoticeServiceImpl.java | 41 ++++++++++++------- 3 files changed, 45 insertions(+), 34 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java index 18b9f277..837bf764 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java @@ -26,24 +26,24 @@ public NoticeController(NoticeService noticeService) { this.noticeService = noticeService; } - @Operation(summary = "공지사항 전체 조회") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = NoticeResponseMessage.class))}) - }) - @GetMapping("/all") - public ResponseEntity> getAllNotices( - @RequestParam(defaultValue = "0") int page, - @RequestParam(defaultValue = "10") int size - ) { - // PageRequest를 사용하여 페이지 번호와 페이지 크기를 설정 - Pageable pageable = PageRequest.of(page, size); - - // 공지사항 목록 조회 - Page noticeDTOPage = noticeService.findAllNotices(pageable); - - return ResponseEntity.ok(noticeDTOPage); - } +// @Operation(summary = "공지사항 전체 조회") +// @ApiResponses(value = { +// @ApiResponse(responseCode = "200", description = "성공", +// content = {@Content(schema = @Schema(implementation = NoticeResponseMessage.class))}) +// }) +// @GetMapping("/all") +// public ResponseEntity> getAllNotices( +// @RequestParam(defaultValue = "0") int page, +// @RequestParam(defaultValue = "10") int size +// ) { +// // PageRequest를 사용하여 페이지 번호와 페이지 크기를 설정 +// Pageable pageable = PageRequest.of(page, size); +// +// // 공지사항 목록 조회 +// Page noticeDTOPage = noticeService.findAllNotices(pageable); +// +// return ResponseEntity.ok(noticeDTOPage); +// } @Operation(summary = "공지사항 조건별 조회") @ApiResponses(value = { diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java index 5117c080..b3014f84 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java @@ -9,7 +9,7 @@ public interface NoticeService { - Page findAllNotices(Pageable pageable); +// Page findAllNotices(Pageable pageable); Page findNotices(Pageable pageable, SearchDTO searchDTO); NoticeDTO findNotice(String noticeId); diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java index 6f91c9ee..f27ffecc 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java @@ -27,16 +27,39 @@ public NoticeServiceImpl(NoticeMapper noticeMapper, RedisTemplate redisTemplate, this.redisService =redisService; } +// @Transactional +// @Override +// public Page findAllNotices(Pageable pageable) { +// int offset = Math.toIntExact(pageable.getOffset()); +// int size = pageable.getPageSize(); +// String cacheKey = "myCache::notices::offset=" + offset + "::size=" + size; +// +// List notices = (List) redisTemplate.opsForValue().get(cacheKey); +// +// // 캐시에 데이터가 없다면 DB에서 조회하고 캐시에 저장 +// if (notices == null) { +// System.out.println("데이터베이스에서 데이터 조회 중..."); +// notices = noticeMapper.findAllNotices(offset, size); +// redisTemplate.opsForValue().set(cacheKey, notices); +// String key = "NoticePage"+offset; +// Object value = notices; +// long ttlInSeconds = 30*60; +// redisService.setKeyWithTTL(key, value, ttlInSeconds); +// } else { +// System.out.println("캐시에서 데이터 조회 중..."); +// } +// int totalElements = noticeMapper.findNoticeCount(); // 총 개수 조회 +// return new PageImpl<>(notices, pageable, totalElements); +// } + @Transactional @Override - public Page findAllNotices(Pageable pageable) { + public Page findNotices(Pageable pageable, SearchDTO searchDTO) { int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); String cacheKey = "myCache::notices::offset=" + offset + "::size=" + size; List notices = (List) redisTemplate.opsForValue().get(cacheKey); - - // 캐시에 데이터가 없다면 DB에서 조회하고 캐시에 저장 if (notices == null) { System.out.println("데이터베이스에서 데이터 조회 중..."); notices = noticeMapper.findAllNotices(offset, size); @@ -52,18 +75,6 @@ public Page findAllNotices(Pageable pageable) { return new PageImpl<>(notices, pageable, totalElements); } - @Transactional - @Override - public Page findNotices(Pageable pageable, SearchDTO searchDTO) { - int offset = Math.toIntExact(pageable.getOffset()); - int size = pageable.getPageSize(); - List notices = noticeMapper.findNotices(offset,size,searchDTO); - Integer count = noticeMapper.findNoticesCount(searchDTO); - int noticeCount = (count != null) ? noticeMapper.findNoticesCount(searchDTO) : 0; - - return new PageImpl<>(notices, pageable, noticeCount); - } - @Override public NoticeDTO findNotice(String noticeId) { NoticeDTO notice = noticeMapper.findNotice(noticeId); From 33ff9c7ca03bcf6fa860ca3d194f6862a80b9a2f Mon Sep 17 00:00:00 2001 From: giuseog Date: Thu, 21 Nov 2024 12:15:55 +0900 Subject: [PATCH 261/563] =?UTF-8?q?fix:=20cetner=20api=20operatation=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80(#6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CenterController.java | 2 +- ...eption.java => CenterCommonException.java} | 6 +-- .../{ErrorCode.java => CenterErrorCode.java} | 7 +-- .../exception/CenterExceptionResponse.java | 22 ++++++++ .../common/exception/ExceptionResponse.java | 22 -------- ...essage.java => CenterResponseMessage.java} | 2 +- .../query/controller/CenterController.java | 41 +++++++++++--- .../controller/SalesHistoryController.java | 54 +++++++++---------- 8 files changed, 89 insertions(+), 67 deletions(-) rename src/main/java/stanl_2/final_backend/domain/center/common/exception/{CommonException.java => CenterCommonException.java} (62%) rename src/main/java/stanl_2/final_backend/domain/center/common/exception/{ErrorCode.java => CenterErrorCode.java} (93%) create mode 100644 src/main/java/stanl_2/final_backend/domain/center/common/exception/CenterExceptionResponse.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/center/common/exception/ExceptionResponse.java rename src/main/java/stanl_2/final_backend/domain/center/common/response/{ResponseMessage.java => CenterResponseMessage.java} (85%) diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java index e1f25a58..3a38684e 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java @@ -62,7 +62,7 @@ public ResponseEntity putTest(@PathVariable("id") String id, .build()); } - @Operation(summary = "샘플 삭제 테스트") + @Operation(summary = "매장 삭제") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) diff --git a/src/main/java/stanl_2/final_backend/domain/center/common/exception/CommonException.java b/src/main/java/stanl_2/final_backend/domain/center/common/exception/CenterCommonException.java similarity index 62% rename from src/main/java/stanl_2/final_backend/domain/center/common/exception/CommonException.java rename to src/main/java/stanl_2/final_backend/domain/center/common/exception/CenterCommonException.java index 6f9598f6..5b227b11 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/common/exception/CommonException.java +++ b/src/main/java/stanl_2/final_backend/domain/center/common/exception/CenterCommonException.java @@ -5,12 +5,12 @@ @Getter @RequiredArgsConstructor -public class CommonException extends RuntimeException { - private final ErrorCode errorCode; +public class CenterCommonException extends RuntimeException { + private final CenterErrorCode centerErrorCode; // 에러 발생시 ErroCode 별 메시지 @Override public String getMessage() { - return this.errorCode.getMsg(); + return this.centerErrorCode.getMsg(); } } diff --git a/src/main/java/stanl_2/final_backend/domain/center/common/exception/ErrorCode.java b/src/main/java/stanl_2/final_backend/domain/center/common/exception/CenterErrorCode.java similarity index 93% rename from src/main/java/stanl_2/final_backend/domain/center/common/exception/ErrorCode.java rename to src/main/java/stanl_2/final_backend/domain/center/common/exception/CenterErrorCode.java index 8353f359..12175476 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/common/exception/ErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/center/common/exception/CenterErrorCode.java @@ -6,7 +6,7 @@ @Getter @AllArgsConstructor -public enum ErrorCode { +public enum CenterErrorCode { /** * 400(Bad Request) @@ -38,11 +38,8 @@ public enum ErrorCode { * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. */ - - - + CENTER_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "CENTER 데이터를 찾지 못했습니다"), /** - * 500(Internal Server Error) * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. */ INTERNAL_SERVER_ERROR(50000, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부 오류입니다."); diff --git a/src/main/java/stanl_2/final_backend/domain/center/common/exception/CenterExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/center/common/exception/CenterExceptionResponse.java new file mode 100644 index 00000000..4c5000d5 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/center/common/exception/CenterExceptionResponse.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.center.common.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class CenterExceptionResponse { + private final Integer code; + private final String msg; + private final HttpStatus httpStatus; + + public CenterExceptionResponse(CenterErrorCode centerErrorCode) { + this.code = centerErrorCode.getCode(); + this.msg = centerErrorCode.getMsg(); + this.httpStatus = centerErrorCode.getHttpStatus(); + } + + public static CenterExceptionResponse of(CenterErrorCode centerErrorCode) { + return new CenterExceptionResponse(centerErrorCode); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/center/common/exception/ExceptionResponse.java b/src/main/java/stanl_2/final_backend/domain/center/common/exception/ExceptionResponse.java deleted file mode 100644 index d7ebb1c1..00000000 --- a/src/main/java/stanl_2/final_backend/domain/center/common/exception/ExceptionResponse.java +++ /dev/null @@ -1,22 +0,0 @@ -package stanl_2.final_backend.domain.center.common.exception; - -import lombok.Getter; -import org.springframework.http.HttpStatus; - -@Getter -public class ExceptionResponse { - private final Integer code; - private final String msg; - private final HttpStatus httpStatus; - - public ExceptionResponse(ErrorCode errorCode) { - this.code = errorCode.getCode(); - this.msg = errorCode.getMsg(); - this.httpStatus = errorCode.getHttpStatus(); - } - - public static ExceptionResponse of(ErrorCode errorCode) { - return new ExceptionResponse(errorCode); - } - -} diff --git a/src/main/java/stanl_2/final_backend/domain/center/common/response/ResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/center/common/response/CenterResponseMessage.java similarity index 85% rename from src/main/java/stanl_2/final_backend/domain/center/common/response/ResponseMessage.java rename to src/main/java/stanl_2/final_backend/domain/center/common/response/CenterResponseMessage.java index 60766c56..840dc755 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/common/response/ResponseMessage.java +++ b/src/main/java/stanl_2/final_backend/domain/center/common/response/CenterResponseMessage.java @@ -7,7 +7,7 @@ @Builder @Getter @Setter -public class ResponseMessage { +public class CenterResponseMessage { private int httpStatus; private String msg; private Object result; diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java b/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java index aeffb3a9..856459f5 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java @@ -1,12 +1,17 @@ package stanl_2.final_backend.domain.center.query.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.center.common.response.ResponseMessage; +import stanl_2.final_backend.domain.center.common.response.CenterResponseMessage; import stanl_2.final_backend.domain.center.query.dto.CenterSearchRequestDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectIdDTO; @@ -25,33 +30,53 @@ public CenterController(CenterService centerService) { this.centerService = centerService; } + @Operation(summary = "영업매장 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = CenterResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) @GetMapping("") - public ResponseEntity getCenterAll(@PageableDefault(size = 20) Pageable pageable){ + public ResponseEntity getCenterAll(@PageableDefault(size = 20) Pageable pageable){ Page responseCenters = centerService.selectAll(pageable); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(CenterResponseMessage.builder() .httpStatus(200) .msg("조회 성공") .result(responseCenters) .build()); } - + @Operation(summary = "영업매장 상세 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = CenterResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) @GetMapping("{id}") - public ResponseEntity getCenterById(@PathVariable("id") String id){ + public ResponseEntity getCenterById(@PathVariable("id") String id){ CenterSelectIdDTO centerSelectIdDTO = centerService.selectByCenterId(id); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(CenterResponseMessage.builder() .httpStatus(200) .msg("상세 조회 성공") .result(centerSelectIdDTO) .build()); } + @Operation(summary = "영업매장 검색") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = CenterResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) @GetMapping("/search") - public ResponseEntity getCenterBySearch(@RequestParam Map params + public ResponseEntity getCenterBySearch(@RequestParam Map params ,@PageableDefault(size = 20) Pageable pageable){ CenterSearchRequestDTO centerSearchRequestDTO = new CenterSearchRequestDTO(); @@ -61,7 +86,7 @@ public ResponseEntity getCenterBySearch(@RequestParam Map par Page responseCenters = centerService.selectBySearch(centerSearchRequestDTO, pageable); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(CenterResponseMessage.builder() .httpStatus(200) .msg("검색 조회 성공") .result(responseCenters) diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java index b46ff24e..9b136d92 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java @@ -224,33 +224,33 @@ public ResponseEntity getStatisticsSearchYearByEmpl .build()); } - @Operation(summary = "사원 통계(실적,수당,매출액) 연도 별 검색") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), - @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", - content = @Content(mediaType = "application/json")) - }) - @GetMapping("employee/statistics/search/year") - public ResponseEntity getStatisticsSearchYearByEmployee(@RequestParam Map params - ,Principal principal, - @PageableDefault(size = 20) Pageable pageable){ - - SalesHistorySearchDTO salesHistorySearchDTO = new SalesHistorySearchDTO(); - - /* 설명. 2024 식으로 와야함 !!!!! */ - salesHistorySearchDTO.setSearcherName(principal.getName()); - salesHistorySearchDTO.setStartDate(params.get("startDate")); - salesHistorySearchDTO.setEndDate(params.get("endDate")); - - SalesHistoryStatisticsDTO responseSalesHistory = salesHistoryQueryService.selectStatisticsSearchYearByEmployee(salesHistorySearchDTO); - - return ResponseEntity.ok(SalesHistoryResponseMessage.builder() - .httpStatus(200) - .msg("사원 통계(실적,수당,매출액) 월 별 검색 성공") - .result(responseSalesHistory) - .build()); - } +// @Operation(summary = "사원 통계(실적,수당,매출액) 연도 별 검색") +// @ApiResponses(value = { +// @ApiResponse(responseCode = "200", description = "성공", +// content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), +// @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", +// content = @Content(mediaType = "application/json")) +// }) +// @GetMapping("employee/statistics/search/year") +// public ResponseEntity getStatisticsSearchYearByEmployee(@RequestParam Map params +// ,Principal principal, +// @PageableDefault(size = 20) Pageable pageable){ +// +// SalesHistorySearchDTO salesHistorySearchDTO = new SalesHistorySearchDTO(); +// +// /* 설명. 2024 식으로 와야함 !!!!! */ +// salesHistorySearchDTO.setSearcherName(principal.getName()); +// salesHistorySearchDTO.setStartDate(params.get("startDate")); +// salesHistorySearchDTO.setEndDate(params.get("endDate")); +// +// SalesHistoryStatisticsDTO responseSalesHistory = salesHistoryQueryService.selectStatisticsSearchYearByEmployee(salesHistorySearchDTO); +// +// return ResponseEntity.ok(SalesHistoryResponseMessage.builder() +// .httpStatus(200) +// .msg("사원 통계(실적,수당,매출액) 월 별 검색 성공") +// .result(responseSalesHistory) +// .build()); +// } } From 89a63b339621f1728ecf5fba25457b62d3e20536 Mon Sep 17 00:00:00 2001 From: giuseog Date: Thu, 21 Nov 2024 12:25:50 +0900 Subject: [PATCH 262/563] =?UTF-8?q?fix:=20=EC=97=90=EB=9F=AC=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AlarmController.java | 1 - .../controller/CenterController.java | 21 +++++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/application/controller/AlarmController.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/controller/AlarmController.java index 8d3b75af..a6df8811 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/application/controller/AlarmController.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/controller/AlarmController.java @@ -14,7 +14,6 @@ import stanl_2.final_backend.domain.alarm.command.application.dto.AlarmRegistDTO; import stanl_2.final_backend.domain.alarm.command.application.service.AlarmCommandService; import stanl_2.final_backend.domain.alarm.common.response.AlarmResponseMessage; -import stanl_2.final_backend.domain.center.common.response.ResponseMessage; import stanl_2.final_backend.domain.schedule.common.response.ScheduleResponseMessage; import java.security.Principal; diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java index 3a38684e..b9ef59f4 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java @@ -8,11 +8,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterModifyRequestDTO; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistRequestDTO; import stanl_2.final_backend.domain.center.command.application.service.CenterCommandService; -import stanl_2.final_backend.domain.center.common.response.ResponseMessage; +import stanl_2.final_backend.domain.center.common.response.CenterResponseMessage; @RestController("commandCenterController") @RequestMapping("/api/v1/center") @@ -28,16 +27,16 @@ public CenterController(CenterCommandService centerCommandService) { @Operation(summary = "매장 등록") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = CenterResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postTest(@RequestBody CenterRegistRequestDTO centerRegistRequestDTO){ + public ResponseEntity postTest(@RequestBody CenterRegistRequestDTO centerRegistRequestDTO){ /* 설명. memberId 토큰으로 받는 것 고려 */ centerCommandService.registCenter(centerRegistRequestDTO); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(CenterResponseMessage.builder() .httpStatus(200) .msg("등록 성공") .result(null) @@ -47,15 +46,15 @@ public ResponseEntity postTest(@RequestBody CenterRegistRequestDTO centerRegi @Operation(summary = "매장 수정") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = CenterResponseMessage.class))}) }) @PutMapping("{id}") - public ResponseEntity putTest(@PathVariable("id") String id, + public ResponseEntity putTest(@PathVariable("id") String id, @RequestBody CenterModifyRequestDTO centerModifyRequestDTO){ centerCommandService.modifyCenter(id, centerModifyRequestDTO); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(CenterResponseMessage.builder() .httpStatus(200) .msg("수정 성공") .result(null) @@ -65,14 +64,14 @@ public ResponseEntity putTest(@PathVariable("id") String id, @Operation(summary = "매장 삭제") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = CenterResponseMessage.class))}) }) @DeleteMapping("{id}") - public ResponseEntity deleteTest(@PathVariable("id") String id){ + public ResponseEntity deleteTest(@PathVariable("id") String id){ centerCommandService.deleteCenter(id); - return ResponseEntity.ok(ResponseMessage.builder() + return ResponseEntity.ok(CenterResponseMessage.builder() .httpStatus(200) .msg("성공") .result(null) From 63bd62f32f5ec9e66c295e089c4d96eca720ff7f Mon Sep 17 00:00:00 2001 From: giuseog Date: Thu, 21 Nov 2024 12:30:57 +0900 Subject: [PATCH 263/563] =?UTF-8?q?fix:=20=EC=88=98=EC=A0=95=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EB=B0=98=EC=98=81=20&=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=9E=A1=EA=B8=B0(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/service/MemberQueryServiceImpl.java | 1 + .../query/dto/SalesHistoryStatisticsDTO.java | 1 - .../service/SalesHistoryQueryServiceImpl.java | 1 + .../member/query/repository/MemberMapper.xml | 2 +- .../query/repository/SalesHistoryMapper.xml | 54 +++++++++---------- 5 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index 52303021..a2adee7e 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -73,6 +73,7 @@ public List selectMemberByCenterId(String centerId){ } @Override + @Transactional(readOnly = true) public List selectMemberByCenterList(List centerList) { List memberList = memberMapper.findMembersByCenterList(centerList); diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsDTO.java index 72d6c106..6debab66 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Getter @Setter -@ToString public class SalesHistoryStatisticsDTO { private int incentive; private int performance; diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java index 684681b2..4b59dd3b 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java @@ -127,6 +127,7 @@ public SalesHistoryStatisticsDTO selectStatisticsSearchMonthByEmployee(SalesHist } @Override + @Transactional(readOnly = true) public SalesHistoryStatisticsDTO selectStatisticsSearchYearByEmployee(SalesHistorySearchDTO salesHistorySearchDTO) { salesHistorySearchDTO.setSearcherName(authQueryService.selectMemberIdByLoginId(salesHistorySearchDTO.getSearcherName())); diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml index 96e3aea4..4c710ed3 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml @@ -54,7 +54,7 @@ WHERE a.active = true and a.centerId = #{centerId} - SELECT a.mem_id, a.login_id diff --git a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml index 64ee9a26..d3dc4609 100644 --- a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml @@ -147,33 +147,33 @@ ORDER BY YEAR ASC - - SELECT - mem_id, - sal_hist_ince, - sal_hist_no_of_veh, - sal_hist_tota_sale, - created_at, - ROW_NUMBER() OVER (PARTITION BY mem_id ORDER BY sal_hist_ince DESC) AS rank_ince, - ROW_NUMBER() OVER (PARTITION BY mem_id ORDER BY sal_hist_no_of_veh DESC) AS rank_perf, - ROW_NUMBER() OVER (PARTITION BY mem_id ORDER BY sal_hist_tota_sale DESC) AS rank_sales - FROM tb_sales_history - WHERE active = TRUE - AND mem_id IN - - #{memId} - - + + + + + + + + + + + + + + + + + - + + + + + + + + + + From ea7514e972254f74fc8b51eede9dafb2d8c75903 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Thu, 21 Nov 2024 12:31:14 +0900 Subject: [PATCH 264/563] =?UTF-8?q?feat:=20DB=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=97=91=EC=85=80=ED=8C=8C=EC=9D=BC=20=EB=82=B4=EB=B3=B4?= =?UTF-8?q?=EB=82=B4=EA=B8=B0=20=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C=20?= =?UTF-8?q?&=20Sampel=EC=97=90=20=EC=A0=81=EC=9A=A9=20(#115)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 + .../query/controller/SampleController.java | 19 ++- .../query/dto/SampleExcelDownload.java | 22 +++ .../query/repository/SampleMapper.java | 5 + .../query/service/SampleQueryService.java | 3 + .../query/service/SampleQueryServiceImpl.java | 17 +- .../global/excel/ExcelColumnName.java | 13 ++ .../global/excel/ExcelSupportV1.java | 10 ++ .../global/excel/ExcelUtilsV1.java | 154 ++++++++++++++++++ src/main/resources/application-dev.yml | 2 +- src/main/resources/application-prod.yml | 2 +- src/main/resources/application.yml | 2 +- .../query/repository/SampleMapper.xml | 16 ++ 13 files changed, 264 insertions(+), 5 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/A_sample/query/dto/SampleExcelDownload.java create mode 100644 src/main/java/stanl_2/final_backend/global/excel/ExcelColumnName.java create mode 100644 src/main/java/stanl_2/final_backend/global/excel/ExcelSupportV1.java create mode 100644 src/main/java/stanl_2/final_backend/global/excel/ExcelUtilsV1.java diff --git a/build.gradle b/build.gradle index 818414e9..d05e329c 100644 --- a/build.gradle +++ b/build.gradle @@ -68,6 +68,10 @@ dependencies { // implementation 'ch.qos.logback:logback-classic:1.2.11' // implementation 'ch.qos.logback:logback-core:1.2.11' + // excel을 위한 poi 라이브러리 + implementation 'org.apache.poi:poi-ooxml:5.2.2' + implementation 'org.apache.poi:poi:5.2.2' + } diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java b/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java index 8dfda062..25b378ee 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java @@ -5,13 +5,16 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.A_sample.query.dto.SampleDTO; +import stanl_2.final_backend.domain.A_sample.query.dto.SampleExcelDownload; import stanl_2.final_backend.domain.A_sample.query.service.SampleQueryService; +import stanl_2.final_backend.global.excel.ExcelUtilsV1; import java.security.Principal; @@ -21,10 +24,12 @@ public class SampleController { private final SampleQueryService sampleQueryService; + private final ExcelUtilsV1 excelUtilsV1; @Autowired - public SampleController(SampleQueryService sampleQueryService) { + public SampleController(SampleQueryService sampleQueryService, ExcelUtilsV1 excelUtilsV1) { this.sampleQueryService = sampleQueryService; + this.excelUtilsV1 = excelUtilsV1; } /** @@ -75,4 +80,16 @@ public ResponseEntity getDetailTest(@PathVariable String .build()); } + @Operation(summary = "샘플 엑셀 다운 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "샘플 엑설 다운 테스트 성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/excel") + public void exportSample(HttpServletResponse response){ + + sampleQueryService.exportSamplesToExcel(response); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/query/dto/SampleExcelDownload.java b/src/main/java/stanl_2/final_backend/domain/A_sample/query/dto/SampleExcelDownload.java new file mode 100644 index 00000000..458ddb4e --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/query/dto/SampleExcelDownload.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.A_sample.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import stanl_2.final_backend.global.excel.ExcelColumnName; + +@Getter +@AllArgsConstructor +public class SampleExcelDownload { + + @ExcelColumnName(name = "이름") + private String name; + + @ExcelColumnName(name = "갯수") + private String num; + + @ExcelColumnName(name = "판매상태") + private Boolean active; + + @ExcelColumnName(name = "생성일자") + private String createdAt; +} diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/query/repository/SampleMapper.java b/src/main/java/stanl_2/final_backend/domain/A_sample/query/repository/SampleMapper.java index 5ccb65cd..a10bde45 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/query/repository/SampleMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/query/repository/SampleMapper.java @@ -3,10 +3,15 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import stanl_2.final_backend.domain.A_sample.query.dto.SampleDTO; +import stanl_2.final_backend.domain.A_sample.query.dto.SampleExcelDownload; + +import java.util.List; @Mapper public interface SampleMapper { String selectNameById(@Param("id") String id); SampleDTO selectById(@Param("id") String id); + + List findSamplesForExcel(); } diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryService.java b/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryService.java index df522286..c4538594 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryService.java @@ -1,9 +1,12 @@ package stanl_2.final_backend.domain.A_sample.query.service; +import jakarta.servlet.http.HttpServletResponse; import stanl_2.final_backend.domain.A_sample.query.dto.SampleDTO; public interface SampleQueryService { String selectSampleName(String id); SampleDTO selectSampleInfo(String id); + + void exportSamplesToExcel(HttpServletResponse response); } diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryServiceImpl.java index 04874bbf..90d7103f 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryServiceImpl.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.A_sample.query.service; +import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -7,17 +8,23 @@ import stanl_2.final_backend.domain.A_sample.common.exception.SampleCommonException; import stanl_2.final_backend.domain.A_sample.common.exception.SampleErrorCode; import stanl_2.final_backend.domain.A_sample.query.dto.SampleDTO; +import stanl_2.final_backend.domain.A_sample.query.dto.SampleExcelDownload; import stanl_2.final_backend.domain.A_sample.query.repository.SampleMapper; +import stanl_2.final_backend.global.excel.ExcelUtilsV1; + +import java.util.List; @Slf4j @Service public class SampleQueryServiceImpl implements SampleQueryService { private final SampleMapper sampleMapper; + private final ExcelUtilsV1 excelUtilsV1; @Autowired - public SampleQueryServiceImpl(SampleMapper sampleMapper) { + public SampleQueryServiceImpl(SampleMapper sampleMapper, ExcelUtilsV1 excelUtilsV1) { this.sampleMapper = sampleMapper; + this.excelUtilsV1 = excelUtilsV1; } @Override @@ -46,5 +53,13 @@ public SampleDTO selectSampleInfo(String id) { return sampleDTO; } + @Override + public void exportSamplesToExcel(HttpServletResponse response) { + + List sampleList = sampleMapper.findSamplesForExcel(); + + excelUtilsV1.download(SampleExcelDownload.class, sampleList, "sampleExcel", response); + } + } diff --git a/src/main/java/stanl_2/final_backend/global/excel/ExcelColumnName.java b/src/main/java/stanl_2/final_backend/global/excel/ExcelColumnName.java new file mode 100644 index 00000000..17fd2905 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/excel/ExcelColumnName.java @@ -0,0 +1,13 @@ +package stanl_2.final_backend.global.excel; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExcelColumnName { + + String name() default ""; +} diff --git a/src/main/java/stanl_2/final_backend/global/excel/ExcelSupportV1.java b/src/main/java/stanl_2/final_backend/global/excel/ExcelSupportV1.java new file mode 100644 index 00000000..35741c31 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/excel/ExcelSupportV1.java @@ -0,0 +1,10 @@ +package stanl_2.final_backend.global.excel; + +import jakarta.servlet.http.HttpServletResponse; + +import java.util.List; + +public interface ExcelSupportV1 { + // 메타데이터 타입, 메타 데이터, 파일이름, 응답 + void download(Class clazz, List data, String fileName, HttpServletResponse response); +} diff --git a/src/main/java/stanl_2/final_backend/global/excel/ExcelUtilsV1.java b/src/main/java/stanl_2/final_backend/global/excel/ExcelUtilsV1.java new file mode 100644 index 00000000..4e22d714 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/excel/ExcelUtilsV1.java @@ -0,0 +1,154 @@ +package stanl_2.final_backend.global.excel; + +import com.sun.nio.sctp.IllegalReceiveException; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@Component +public class ExcelUtilsV1 implements ExcelSupportV1{ + + private static final Integer MAX_ROW = 5000; + + // 메타데이터 타입, 메타 데이터, 파일이름, 응답 + @Override + public void download(Class clazz, List data, String fileName, HttpServletResponse response){ + + try(SXSSFWorkbook workbook = new SXSSFWorkbook()){ + + Integer loop = 1; + Integer listSize = data.size(); + + // 하나의 sheet당 10000개의 데이터를 그려줌 + for (Integer start = 0; start < listSize; start+= MAX_ROW) { + + Integer nextPage = MAX_ROW * loop; + if(nextPage > listSize){ + nextPage = listSize - 1; + } + + List datas = new ArrayList<>(data.subList(start, nextPage)); + getWorkBook(clazz, workbook, start, findHeaderNames(clazz), datas, listSize); + datas.clear(); + loop++; + } + + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xlsx"); + + ServletOutputStream outputStream = response.getOutputStream(); + workbook.write(outputStream); + outputStream.flush(); + outputStream.close(); + } catch(IOException | IllegalAccessException e){ + log.error("Excel 다운로드 Error 메세지: {}", e.getMessage()); + throw new RuntimeException(e); + } + + } + + private SXSSFWorkbook getWorkBook(Class clazz, SXSSFWorkbook workbook, Integer start, + List headerNames, List datas, Integer listSize) + throws IllegalAccessException, IOException{ + + // 각 sheet당 MAX_ROW개씩 생성 + String sheetName = "Sheet" + (start / MAX_ROW + 1); + + Sheet sheet = ObjectUtils.isEmpty(workbook.getSheet(sheetName)) + ? workbook.createSheet(sheetName) : workbook.getSheet(sheetName); + sheet.setDefaultColumnWidth((short) 300); // 디폴트 너비 설정 + sheet.setDefaultRowHeight((short) 500); // 디폴트 높이 설정 + + Row row = null; + Cell cell = null; + Integer rowNo = start % listSize; // 0, 10000, 20000, 30000 -> 10000(maxSize)씩 증가 + + row = sheet.createRow(0); + createHeaders(workbook, row, cell, headerNames); // sheet의 헤더 생성 + createBody(clazz, datas, sheet, row, cell, start); // sheet의 body 생성 + + // 주기적으로 flush 진행 + if (rowNo % MAX_ROW == 0){ + ((SXSSFSheet) sheet).flushRows(MAX_ROW); + } + + return workbook; + } + + private void createBody(Class clazz, List datas, Sheet sheet, Row row, Cell cell, Integer start) + throws IllegalAccessException, IOException{ + + Integer startRow = 0; + + for (Object o : datas){ + List fields = findFieldValue(clazz, o); + row = sheet.createRow(++startRow); + for (Integer i = 0, fieldSize = fields.size() ; i < fieldSize; i++) { + cell = row.createCell(i); + cell.setCellValue(String.valueOf(fields.get(i))); + } + + // 주기적인 flush 진행 + if (start % MAX_ROW == 0){ + ((SXSSFSheet) sheet).flushRows(MAX_ROW); + } + } + } + + // 데이터의 값을 추출하는 메소드 + private List findFieldValue(Class clazz, Object o) throws IllegalAccessException { + List result = new ArrayList<>(); + for (Field field: clazz.getDeclaredFields()){ + field.setAccessible(true); + result.add(field.get(o)); + } + + return result; + } + + private void createHeaders(SXSSFWorkbook workbook, Row row, Cell cell, List headerNames) { + + // header의 폰트 스타일 + Font font = workbook.createFont(); + font.setBold(true); // 글자 굵게 + + // header의 cell 스타일 + CellStyle headerCellStyle = workbook.createCellStyle(); + headerCellStyle.setAlignment(HorizontalAlignment.CENTER); // 가로 가운데 정렬 + headerCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 세로 가운데 정렬 + + // cell 테두리 설정 + headerCellStyle.setBorderLeft(BorderStyle.MEDIUM); + headerCellStyle.setBorderRight(BorderStyle.MEDIUM); + headerCellStyle.setBorderTop(BorderStyle.MEDIUM); + headerCellStyle.setBorderBottom(BorderStyle.MEDIUM); + headerCellStyle.setFont(font); + + for (Integer i = 0, size = headerNames.size() ; i < size; i++) { + cell = row.createCell(i); + cell.setCellStyle(headerCellStyle); + cell.setCellValue(headerNames.get(i)); + } + } + + // 엑셀의 헤더 이름을 찾는 로직 + private List findHeaderNames(Class clazz) { + return Arrays.stream(clazz.getDeclaredFields()) + .filter(field -> field.isAnnotationPresent(ExcelColumnName.class)) + .map(field -> field.getAnnotation(ExcelColumnName.class).name()) + .collect(Collectors.toList()); + } +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index e9390662..aabe59aa 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: create + ddl-auto: update logging: level: diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 6ff598fc..520817c3 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: create + ddl-auto: update logging: level: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 6618043e..eee7170c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -23,7 +23,7 @@ spring: jpa: show-sql: true hibernate: - ddl-auto: create + ddl-auto: update properties: hibernate.format_sql: true diff --git a/src/main/resources/stanl_2/final_backend/domain/A_sample/query/repository/SampleMapper.xml b/src/main/resources/stanl_2/final_backend/domain/A_sample/query/repository/SampleMapper.xml index 6e8020d5..9b005431 100644 --- a/src/main/resources/stanl_2/final_backend/domain/A_sample/query/repository/SampleMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/A_sample/query/repository/SampleMapper.xml @@ -34,4 +34,20 @@ WHERE A.SAM_ID = #{ id } + + + + + + + + + \ No newline at end of file From ee4c52cc97f9fd73c4ff4e302d25a7b7d737f5df Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Thu, 21 Nov 2024 14:02:51 +0900 Subject: [PATCH 265/563] =?UTF-8?q?feat:=20excel=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EB=82=B4=EB=B3=B4=EB=82=B4=EA=B8=B0=20=EC=B5=9C=EC=A2=85=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=99=84=EB=A3=8C=20(#115)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/A_sample/query/controller/SampleController.java | 6 +----- .../stanl_2/final_backend/global/excel/ExcelUtilsV1.java | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java b/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java index 25b378ee..f0228da6 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java @@ -12,9 +12,7 @@ import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.A_sample.query.dto.SampleDTO; -import stanl_2.final_backend.domain.A_sample.query.dto.SampleExcelDownload; import stanl_2.final_backend.domain.A_sample.query.service.SampleQueryService; -import stanl_2.final_backend.global.excel.ExcelUtilsV1; import java.security.Principal; @@ -24,12 +22,10 @@ public class SampleController { private final SampleQueryService sampleQueryService; - private final ExcelUtilsV1 excelUtilsV1; @Autowired - public SampleController(SampleQueryService sampleQueryService, ExcelUtilsV1 excelUtilsV1) { + public SampleController(SampleQueryService sampleQueryService) { this.sampleQueryService = sampleQueryService; - this.excelUtilsV1 = excelUtilsV1; } /** diff --git a/src/main/java/stanl_2/final_backend/global/excel/ExcelUtilsV1.java b/src/main/java/stanl_2/final_backend/global/excel/ExcelUtilsV1.java index 4e22d714..e69bfd30 100644 --- a/src/main/java/stanl_2/final_backend/global/excel/ExcelUtilsV1.java +++ b/src/main/java/stanl_2/final_backend/global/excel/ExcelUtilsV1.java @@ -69,8 +69,8 @@ private SXSSFWorkbook getWorkBook(Class clazz, SXSSFWorkbook workbook, Intege Sheet sheet = ObjectUtils.isEmpty(workbook.getSheet(sheetName)) ? workbook.createSheet(sheetName) : workbook.getSheet(sheetName); - sheet.setDefaultColumnWidth((short) 300); // 디폴트 너비 설정 - sheet.setDefaultRowHeight((short) 500); // 디폴트 높이 설정 + sheet.setDefaultColumnWidth(10); // 디폴트 너비 설정 +// sheet.setDefaultRowHeight((short) 500); // 디폴트 높이 설정 Row row = null; Cell cell = null; From cd889570d2c3e7863ddc3cb642a5625ac4b25ff9 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 21 Nov 2024 14:10:24 +0900 Subject: [PATCH 266/563] =?UTF-8?q?=EC=9E=AC=EB=B0=B0=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-prod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 083491db..0d0b92a3 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: update + ddl-auto: create logging: level: From d2b095b818311eddaf195cca2c30af8a74bb9989 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 21 Nov 2024 14:24:35 +0900 Subject: [PATCH 267/563] =?UTF-8?q?=EC=9E=AC=EB=B0=B0=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-dev.yml | 2 +- src/main/resources/application-prod.yml | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 8668c185..43ec1fd9 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: update + ddl-auto: create logging: level: diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 0d0b92a3..a1515a69 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,8 +1,19 @@ spring: + data: + redis: + host: ${REDIS_HOST:redis} + port: ${REDIS_PORT:6379} + lettuce: + pool: + max-active: 10 + max-idle: 5 + min-idle: 2 + timeout: 5000ms jpa: hibernate: ddl-auto: create + logging: level: - org.springframework.security: WARN \ No newline at end of file + org.springframework.security: DEBUG \ No newline at end of file From b748b69236c3ff2a1605bfef244acbb6ea6a2f3c Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 21 Nov 2024 14:41:24 +0900 Subject: [PATCH 268/563] =?UTF-8?q?=EC=9E=AC=EB=B0=B0=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/NoticeCommandServiceImpl.java | 2 -- .../query/service/NoticeServiceImpl.java | 26 ------------------- src/main/resources/application-prod.yml | 2 +- 3 files changed, 1 insertion(+), 29 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java index 78f44fed..1aafaac5 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java @@ -17,8 +17,6 @@ import stanl_2.final_backend.domain.notices.command.domain.repository.NoticeRepository; import stanl_2.final_backend.domain.notices.common.exception.NoticeCommonException; import stanl_2.final_backend.domain.notices.common.exception.NoticeErrorCode; -import stanl_2.final_backend.domain.schedule.common.exception.ScheduleCommonException; -import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; import java.security.Principal; import java.time.ZoneId; diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java index f27ffecc..ba226b0b 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java @@ -27,31 +27,6 @@ public NoticeServiceImpl(NoticeMapper noticeMapper, RedisTemplate redisTemplate, this.redisService =redisService; } -// @Transactional -// @Override -// public Page findAllNotices(Pageable pageable) { -// int offset = Math.toIntExact(pageable.getOffset()); -// int size = pageable.getPageSize(); -// String cacheKey = "myCache::notices::offset=" + offset + "::size=" + size; -// -// List notices = (List) redisTemplate.opsForValue().get(cacheKey); -// -// // 캐시에 데이터가 없다면 DB에서 조회하고 캐시에 저장 -// if (notices == null) { -// System.out.println("데이터베이스에서 데이터 조회 중..."); -// notices = noticeMapper.findAllNotices(offset, size); -// redisTemplate.opsForValue().set(cacheKey, notices); -// String key = "NoticePage"+offset; -// Object value = notices; -// long ttlInSeconds = 30*60; -// redisService.setKeyWithTTL(key, value, ttlInSeconds); -// } else { -// System.out.println("캐시에서 데이터 조회 중..."); -// } -// int totalElements = noticeMapper.findNoticeCount(); // 총 개수 조회 -// return new PageImpl<>(notices, pageable, totalElements); -// } - @Transactional @Override public Page findNotices(Pageable pageable, SearchDTO searchDTO) { @@ -63,7 +38,6 @@ public Page findNotices(Pageable pageable, SearchDTO searchDTO) { if (notices == null) { System.out.println("데이터베이스에서 데이터 조회 중..."); notices = noticeMapper.findAllNotices(offset, size); - redisTemplate.opsForValue().set(cacheKey, notices); String key = "NoticePage"+offset; Object value = notices; long ttlInSeconds = 30*60; diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index a1515a69..360698af 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -16,4 +16,4 @@ spring: logging: level: - org.springframework.security: DEBUG \ No newline at end of file + org.springframework.security: WARN \ No newline at end of file From 81a24461b026a5549a0ddeb33bbdaa55344f47a0 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 21 Nov 2024 15:04:51 +0900 Subject: [PATCH 269/563] =?UTF-8?q?=EC=9E=AC=EB=B0=B0=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 3 ++- src/main/resources/application-prod.yml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 20b46941..8694057b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,8 @@ COPY build.gradle settings.gradle ./ # gradlew 실행 권한 추가 및 의존성 설치 RUN chmod +x gradlew RUN ./gradlew dependencies --no-daemon - +# libfreetype6 설치 +RUN apk update && apk add --no-cache libfreetype libfreetype6 fontconfig ttf-dejavu # 소스 코드 복사 및 빌드 COPY . . RUN ./gradlew clean build -x test --no-daemon diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 360698af..d632afdd 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -11,7 +11,7 @@ spring: timeout: 5000ms jpa: hibernate: - ddl-auto: create + ddl-auto: update logging: From 040e5ddfe09ed745a8d4f85625fecf3b174019de Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 21 Nov 2024 15:08:48 +0900 Subject: [PATCH 270/563] =?UTF-8?q?=EC=9E=AC=EB=B0=B0=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8694057b..c53b3d41 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,8 +10,7 @@ COPY build.gradle settings.gradle ./ # gradlew 실행 권한 추가 및 의존성 설치 RUN chmod +x gradlew RUN ./gradlew dependencies --no-daemon -# libfreetype6 설치 -RUN apk update && apk add --no-cache libfreetype libfreetype6 fontconfig ttf-dejavu + # 소스 코드 복사 및 빌드 COPY . . RUN ./gradlew clean build -x test --no-daemon @@ -22,6 +21,12 @@ RUN ls -la build/libs FROM openjdk:17-jdk-slim WORKDIR /app +# libfreetype6 설치 +RUN apt-get update && apt-get install -y \ + libfreetype6 fontconfig ttf-dejavu --no-install-recommends \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + + COPY --from=build /app/build/libs/*.jar app.jar # 포트 노출 From 548432a22f37ec56d5619264b5901556d88d7d6a Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 21 Nov 2024 15:12:57 +0900 Subject: [PATCH 271/563] =?UTF-8?q?=EC=9E=AC=EB=B0=B0=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index c53b3d41..6ce25d42 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,6 +9,7 @@ COPY build.gradle settings.gradle ./ # gradlew 실행 권한 추가 및 의존성 설치 RUN chmod +x gradlew + RUN ./gradlew dependencies --no-daemon # 소스 코드 복사 및 빌드 From 4e0ce983b6db2937a594083e2b7463db5af7cb3c Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 21 Nov 2024 15:15:15 +0900 Subject: [PATCH 272/563] =?UTF-8?q?=EC=9E=AC=EB=B0=B0=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 6ce25d42..3323dd06 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ WORKDIR /app # libfreetype6 설치 RUN apt-get update && apt-get install -y \ - libfreetype6 fontconfig ttf-dejavu --no-install-recommends \ + libfreetype6 fontconfig fonts-dejavu-core --no-install-recommends \ && apt-get clean && rm -rf /var/lib/apt/lists/* From 14c2069289b2749d7226ff5ecd4062d208573b83 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 21 Nov 2024 15:52:32 +0900 Subject: [PATCH 273/563] =?UTF-8?q?feat:=20Redis=20=EC=84=B8=ED=8C=85=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/NoticeCommandServiceImpl.java | 2 -- .../query/controller/NoticeController.java | 19 ------------ .../notices/query/service/NoticeService.java | 3 -- .../query/service/NoticeServiceImpl.java | 29 ++++++----------- .../global/{config => redis}/RedisConfig.java | 4 +-- .../global/redis/RedisService.java | 31 +++++++++++++++++++ src/main/resources/application-prod.yml | 13 +++++++- 7 files changed, 55 insertions(+), 46 deletions(-) rename src/main/java/stanl_2/final_backend/global/{config => redis}/RedisConfig.java (97%) create mode 100644 src/main/java/stanl_2/final_backend/global/redis/RedisService.java diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java index a24f3cef..daa8c9dd 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java @@ -14,8 +14,6 @@ import stanl_2.final_backend.domain.notices.command.domain.repository.NoticeRepository; import stanl_2.final_backend.domain.notices.common.exception.NoticeCommonException; import stanl_2.final_backend.domain.notices.common.exception.NoticeErrorCode; -import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleDeleteDTO; -import stanl_2.final_backend.domain.schedule.command.domain.aggregate.entity.Schedule; import stanl_2.final_backend.domain.schedule.common.exception.ScheduleCommonException; import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java index 18b9f277..9c0c19c8 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java @@ -26,25 +26,6 @@ public NoticeController(NoticeService noticeService) { this.noticeService = noticeService; } - @Operation(summary = "공지사항 전체 조회") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = NoticeResponseMessage.class))}) - }) - @GetMapping("/all") - public ResponseEntity> getAllNotices( - @RequestParam(defaultValue = "0") int page, - @RequestParam(defaultValue = "10") int size - ) { - // PageRequest를 사용하여 페이지 번호와 페이지 크기를 설정 - Pageable pageable = PageRequest.of(page, size); - - // 공지사항 목록 조회 - Page noticeDTOPage = noticeService.findAllNotices(pageable); - - return ResponseEntity.ok(noticeDTOPage); - } - @Operation(summary = "공지사항 조건별 조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java index 5117c080..f69e5244 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java @@ -1,6 +1,5 @@ package stanl_2.final_backend.domain.notices.query.service; - import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; @@ -9,8 +8,6 @@ public interface NoticeService { - Page findAllNotices(Pageable pageable); - Page findNotices(Pageable pageable, SearchDTO searchDTO); NoticeDTO findNotice(String noticeId); diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java index 379bf290..eaa335f4 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java @@ -10,6 +10,7 @@ import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; import stanl_2.final_backend.domain.notices.query.dto.SearchDTO; import stanl_2.final_backend.domain.notices.query.repository.NoticeMapper; +import stanl_2.final_backend.global.redis.RedisService; import java.util.List; @@ -18,27 +19,29 @@ public class NoticeServiceImpl implements NoticeService{ private final NoticeMapper noticeMapper; private final RedisTemplate redisTemplate; - + private final RedisService redisService; @Autowired - public NoticeServiceImpl(NoticeMapper noticeMapper, RedisTemplate redisTemplate) { + public NoticeServiceImpl(NoticeMapper noticeMapper, RedisTemplate redisTemplate, RedisService redisService) { this.noticeMapper = noticeMapper; this.redisTemplate = redisTemplate; + this.redisService =redisService; } @Transactional @Override - public Page findAllNotices(Pageable pageable) { + public Page findNotices(Pageable pageable, SearchDTO searchDTO) { int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); String cacheKey = "myCache::notices::offset=" + offset + "::size=" + size; List notices = (List) redisTemplate.opsForValue().get(cacheKey); - - // 캐시에 데이터가 없다면 DB에서 조회하고 캐시에 저장 if (notices == null) { System.out.println("데이터베이스에서 데이터 조회 중..."); notices = noticeMapper.findAllNotices(offset, size); - redisTemplate.opsForValue().set(cacheKey, notices); + String key = "NoticePage"+offset; + Object value = notices; + long ttlInSeconds = 30*60; + redisService.setKeyWithTTL(key, value, ttlInSeconds); } else { System.out.println("캐시에서 데이터 조회 중..."); } @@ -46,22 +49,10 @@ public Page findAllNotices(Pageable pageable) { return new PageImpl<>(notices, pageable, totalElements); } - @Transactional - @Override - public Page findNotices(Pageable pageable, SearchDTO searchDTO) { - int offset = Math.toIntExact(pageable.getOffset()); - int size = pageable.getPageSize(); - List notices = noticeMapper.findNotices(offset,size,searchDTO); - Integer count = noticeMapper.findNoticesCount(searchDTO); - int noticeCount = (count != null) ? noticeMapper.findNoticesCount(searchDTO) : 0; - - return new PageImpl<>(notices, pageable, noticeCount); - } - @Override public NoticeDTO findNotice(String noticeId) { NoticeDTO notice = noticeMapper.findNotice(noticeId); return notice; } -} +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/global/config/RedisConfig.java b/src/main/java/stanl_2/final_backend/global/redis/RedisConfig.java similarity index 97% rename from src/main/java/stanl_2/final_backend/global/config/RedisConfig.java rename to src/main/java/stanl_2/final_backend/global/redis/RedisConfig.java index 705c5488..43c962d8 100644 --- a/src/main/java/stanl_2/final_backend/global/config/RedisConfig.java +++ b/src/main/java/stanl_2/final_backend/global/redis/RedisConfig.java @@ -1,4 +1,4 @@ -package stanl_2.final_backend.global.config; +package stanl_2.final_backend.global.redis; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.annotation.Bean; @@ -41,4 +41,4 @@ public void testRedisConnection() { System.err.println("❌ Failed to connect to Redis: " + e.getMessage()); } } -} +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/global/redis/RedisService.java b/src/main/java/stanl_2/final_backend/global/redis/RedisService.java new file mode 100644 index 00000000..b41f0bd1 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/redis/RedisService.java @@ -0,0 +1,31 @@ +package stanl_2.final_backend.global.redis; + +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import java.time.Duration; + +@Service +public class RedisService { + + private final RedisTemplate redisTemplate; + + public RedisService(RedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + } + + // 키와 값을 저장하며 TTL 설정 + public void setKeyWithTTL(String key, Object value, long ttlInSeconds) { + redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(ttlInSeconds)); + } + + // 키 조회 + public Object getKey(String key) { + return redisTemplate.opsForValue().get(key); + } + + // TTL 설정 (기존 키에 대해) + public boolean setTTL(String key, long ttlInSeconds) { + return redisTemplate.expire(key, Duration.ofSeconds(ttlInSeconds)); + } +} \ No newline at end of file diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 520817c3..d632afdd 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,8 +1,19 @@ spring: + data: + redis: + host: ${REDIS_HOST:redis} + port: ${REDIS_PORT:6379} + lettuce: + pool: + max-active: 10 + max-idle: 5 + min-idle: 2 + timeout: 5000ms jpa: hibernate: ddl-auto: update + logging: level: - org.springframework.security: DEBUG \ No newline at end of file + org.springframework.security: WARN \ No newline at end of file From 87581bf28b584e082b444cb30d8de92733262d98 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Thu, 21 Nov 2024 17:55:35 +0900 Subject: [PATCH 274/563] =?UTF-8?q?feat:=20=EC=BB=A4=EC=8A=A4=ED=84=B0?= =?UTF-8?q?=EB=A7=88=EC=9D=B4=EC=A7=95=20=EA=B6=8C=ED=95=9C(#126)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/config/CorsConfig.java | 21 --- .../security/config/DevSecurityConfig.java | 16 +- .../security/config/ProdSecurityConfig.java | 22 +-- .../security/config/RequestMatcherConfig.java | 176 ++++++++++++++++++ 4 files changed, 182 insertions(+), 53 deletions(-) delete mode 100644 src/main/java/stanl_2/final_backend/global/security/config/CorsConfig.java create mode 100644 src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java diff --git a/src/main/java/stanl_2/final_backend/global/security/config/CorsConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/CorsConfig.java deleted file mode 100644 index 32871306..00000000 --- a/src/main/java/stanl_2/final_backend/global/security/config/CorsConfig.java +++ /dev/null @@ -1,21 +0,0 @@ -package stanl_2.final_backend.global.security.config; - -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; - -import java.util.Arrays; -import java.util.Collections; - -public class CorsConfig { - - public CorsConfigurationSource corsConfigurationSource() { - CorsConfiguration config = new CorsConfiguration(); - config.setAllowedOrigins(Collections.singletonList("http://localhost:5173")); - config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); - config.setAllowCredentials(true); - config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "X-XSRF-TOKEN")); - config.setExposedHeaders(Collections.singletonList("Authorization")); - config.setMaxAge(3600L); - return request -> config; - } -} diff --git a/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java index 7ebd1b01..71205379 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/DevSecurityConfig.java @@ -6,7 +6,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; -import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.password.CompromisedPasswordChecker; @@ -17,7 +16,6 @@ import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.password.HaveIBeenPwnedRestApiPasswordChecker; -import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import stanl_2.final_backend.domain.log.command.repository.LogRepository; @@ -55,19 +53,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Excepti http.cors(corsConfig -> corsConfig.configurationSource(corsConfigurationSource())) .authorizeHttpRequests(auth -> auth // 인증 없이 접근 가능한 API 설정 - .requestMatchers( - "/swagger-ui/**", - "/v3/api-docs/**", - "/swagger-resources/**", - "/webjars/**", - "/api/v1/auth/signin", - "/api/v1/auth/signup", - "/api/v1/auth/refresh", - "/api/v1/auth" // 권한 부여때문에(일단 열어둠) - ).permitAll() - // [Example] member는 ADMIN 권한만 접근 가능 설정 예시 - .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasRole("ADMIN") - .anyRequest().authenticated()) + .anyRequest().permitAll()) .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, logRepository), UsernamePasswordAuthenticationFilter.class) .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()) .exceptionHandling(ex -> ex diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index 99ff3b1e..3ad2d625 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -6,7 +6,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; -import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.password.CompromisedPasswordChecker; @@ -51,22 +50,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication CsrfTokenRequestAttributeHandler csrfTokenRequestAttributeHandler = new CsrfTokenRequestAttributeHandler(); http.cors(corsConfig -> corsConfig.configurationSource(corsConfigurationSource())) .csrf(csrf -> csrf.disable()) - .authorizeHttpRequests(auth -> auth - // 인증 없이 접근 가능한 API 설정 - .requestMatchers( - "/swagger-ui/**", - "/v3/api-docs/**", - "/swagger-resources/**", - "/webjars/**", - "/api/v1/auth/signin", - "/api/v1/auth/signup", - "/api/v1/auth/refresh", - "/api/v1/auth" // 권한 부여때문에(일단 열어둠) - ).permitAll() - // [Example] member는 ADMIN 권한만 접근 가능 설정 예시 -// .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasRole("ADMIN") - .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasAnyRole("ADMIN", "MEMBER") - .anyRequest().authenticated()) + // 필터 순서: JWT 검증 -> CSRF .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, logRepository), UsernamePasswordAuthenticationFilter.class) .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()) @@ -82,6 +66,10 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication })); http.formLogin(withDefaults()); http.httpBasic(withDefaults()); + + // 접근 제어 + RequestMatcherConfig.configureRequestMatchers(http); + return http.build(); } diff --git a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java new file mode 100644 index 00000000..1fd3416f --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java @@ -0,0 +1,176 @@ +package stanl_2.final_backend.global.security.config; + +import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; + +public class RequestMatcherConfig { + + public static void configureRequestMatchers(HttpSecurity http) throws Exception { + http.authorizeHttpRequests(auth -> auth + // 인증 없이 접근 가능한 API 설정 + .requestMatchers( + "/swagger-ui/**", + "/v3/api-docs/**", + "/swagger-resources/**", + "/webjars/**", + "/api/v1/auth/signin", + "/api/v1/auth/signup", + "/api/v1/auth/refresh" + ).permitAll() + + // Alarm API + .requestMatchers(HttpMethod.GET, "/api/v1/alarm").hasAnyRole("alarm-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 회원 알림창 전체 조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/alarm/read/**").hasAnyRole("alarm-read-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 회원 읽은 알림 상세 조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/alarm/unread/**").hasAnyRole("alarm-unread-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 회원 읽지 않은 알림 상세 조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/alarm/connect").hasAnyRole("alarm-connect-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // sse 연결 (전체) + .requestMatchers(HttpMethod.PUT, "/api/v1/alarm/**").hasAnyRole("alarm-*-put", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 회원 알림 읽음 처리 (전체) + + // Auth API + .requestMatchers(HttpMethod.POST, "/api/v1/auth").hasAnyRole("auth-post", "GOD", "DIRECTOR", "ADMIN") // 권한 부여 (전체 except 영업사원) + + // Career API + .requestMatchers(HttpMethod.GET, "/api/v1/career").hasAnyRole("career-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 경력 조회(접속중인 사용자) (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/career/other/**").hasAnyRole("career-other-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 경력 조회(with 사번) (전체) + .requestMatchers(HttpMethod.POST, "/api/v1/career").hasAnyRole("career-post", "GOD", "DIRECTOR", "ADMIN") // 경력 등록 (영업관리자, 영업담당자) + + // Certification API + .requestMatchers(HttpMethod.GET, "/api/v1/certification").hasAnyRole("certification-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 자격증/외국어 조회(접속중인 사용자) (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/certification/other/**").hasAnyRole("certification-other-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 자격증/외국어 조회(with 사번) (전체) + .requestMatchers(HttpMethod.POST, "/api/v1/certification").hasAnyRole("certification-post", "GOD", "DIRECTOR", "ADMIN") // 자격증/외국어 등록 (영업 관리자, 영업담당자) + + // Contract API + .requestMatchers(HttpMethod.GET, "/api/v1/contract/{contractId}").hasAnyRole("contract-id-get", "GOD", "DIRECTOR", "ADMIN") // 계약서 상세조회 (영업관리자, 영업담당자) + .requestMatchers(HttpMethod.PUT, "/api/v1/contract/{contractId}").hasAnyRole("contract-id-put", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 수정 (전체) + .requestMatchers(HttpMethod.DELETE, "/api/v1/contract/{contractId}").hasAnyRole("contract-id-delete", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 삭제 (전체) + .requestMatchers(HttpMethod.PUT, "/api/v1/contract/status/**").hasAnyRole("contract-status-put", "GOD", "ADMIN") // 계약서 승인상태 수정 (관리자) + .requestMatchers(HttpMethod.GET, "/api/v1/contract").hasAnyRole("contract-get", "GOD", "DIRECTOR", "ADMIN") // 계약서 전체 조회 (영업관리자, 영업담당자) + .requestMatchers(HttpMethod.POST, "/api/v1/contract").hasAnyRole("contract-post", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 등록 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/contract/search").hasAnyRole("contract-search-get", "GOD", "DIRECTOR", "ADMIN") // 계약서 검색 조회 (영업관리자, 영업담당자) + .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee").hasAnyRole("contract-employee-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 전체 조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee/**").hasAnyRole("contract-employee-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 상세조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee/search").hasAnyRole("contract-employee-search-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 검색 조회 (전체) + + // Customer API + .requestMatchers(HttpMethod.DELETE, "/api/v1/customer/{customerId}").hasAnyRole("customer-id-delete", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 고객정보 삭제(자기 고객만) (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/customer/{customerId}").hasAnyRole("customer-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 고객정보 상세조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/customer/list").hasAnyRole("customer-list-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 고객번호로 전체 목록 조회 (전체) + .requestMatchers(HttpMethod.PUT, "/api/v1/customer/{customerId}").hasAnyRole("customer-id-put", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 고객정보 수정(자기 고객만) (전체) + .requestMatchers(HttpMethod.POST, "/api/v1/customer").hasAnyRole("customer-post", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 고객정보 등록 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/customer/search").hasAnyRole("customer-search-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 고객정보 조건별 조회 (전체) + + // Education API + .requestMatchers(HttpMethod.GET, "/api/v1/education").hasAnyRole("education-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 학력 조회(접속중인 사용자) (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/education/other/**").hasAnyRole("education-other-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 학력 조회(with 사번) (전체) + + // Evaluation API + .requestMatchers(HttpMethod.DELETE, "/api/v1/evaluation/{id}").hasAnyRole("evaluation-id-delete", "GOD", "DIRECTOR", "ADMIN") // 평가서 삭제 (영업 관리자, 영업담당자) + .requestMatchers(HttpMethod.GET, "/api/v1/evaluation/manager").hasAnyRole("evaluation-manager-get", "GOD", "ADMIN") // 평가서 관리자 전체 조회 (영업 관리자) + .requestMatchers(HttpMethod.GET, "/api/v1/evaluation/representative").hasAnyRole("evaluation-representative-get", "GOD", "DIRECTOR") // 평가서 담당자 전체 조회 (영업담당자) + .requestMatchers(HttpMethod.GET, "/api/v1/evaluation/representative/search").hasAnyRole("evaluation-representative-search-get", "GOD", "DIRECTOR") // 평가서 담당자 검색 (영업담당자) + .requestMatchers(HttpMethod.GET, "/api/v1/evaluation/manager/search").hasAnyRole("evaluation-manager-search-get", "GOD", "ADMIN") // 평가서 관리자 검색 (영업 관리자) + .requestMatchers(HttpMethod.GET, "/api/v1/evaluation/{id}").hasAnyRole("evaluation-id-get", "GOD", "DIRECTOR", "ADMIN") // 평가서 상세 조회 (영업 관리자, 영업담당자) + .requestMatchers(HttpMethod.POST, "/api/v1/evaluation").hasAnyRole("evaluation-post", "GOD", "DIRECTOR", "ADMIN") // 평가서 등록 (영업 관리자, 영업담당자) + .requestMatchers(HttpMethod.PUT, "/api/v1/evaluation/{id}").hasAnyRole("evaluation-id-put", "GOD", "DIRECTOR", "ADMIN") // 평가서 수정 (영업 관리자, 영업담당자) + + // Family API + .requestMatchers(HttpMethod.GET, "/api/v1/family").hasAnyRole("family-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 가족 구성원 조회(접속중인 사용자) (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/family/other/{loginId}").hasAnyRole("family-other-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 가족 구성원 조회(with 사번) (전체) + + // Member API + .requestMatchers(HttpMethod.GET, "/api/v1/member/authorities").hasAnyRole("GOD") // CHECK (시스템관리자) + .requestMatchers(HttpMethod.GET, "/api/v1/member").hasAnyRole("member-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 회원 정보 조회 (전체) + + // Log API + .requestMatchers(HttpMethod.GET, "/api/v1/log").hasAnyRole("log-get", "GOD") // 로그 조회 (시스템 관리자) + + // Notice API + .requestMatchers(HttpMethod.DELETE, "/api/v1/notice/{noticeId}").hasAnyRole("notice-delete", "GOD", "DIRECTOR") // 공지사항 삭제 (영업담당자) + .requestMatchers(HttpMethod.GET, "/api/v1/notice/all").hasAnyRole("notice-all-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 공지사항 전체 조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/notice/{noticeId}").hasAnyRole("notice-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 공지사항 Id로 조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/notice").hasAnyRole("notice-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 공지사항 조건별 조회 (전체) + .requestMatchers(HttpMethod.PUT, "/api/v1/notice/{noticeId}").hasAnyRole("notice-update", "GOD", "DIRECTOR") // 공지사항 수정 (영업담당자) + .requestMatchers(HttpMethod.POST, "/api/v1/notice").hasAnyRole("notice-create", "GOD", "DIRECTOR") // 공지사항 작성 (영업담당자) + + // Order API + .requestMatchers(HttpMethod.DELETE, "/api/v1/order/{orderId}").hasAnyRole("order-delete", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 수주서 삭제 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/order").hasAnyRole("order-get", "GOD", "DIRECTOR", "ADMIN") // 수주서 전체 조회 (영업관리자, 영업담당자) + .requestMatchers(HttpMethod.GET, "/api/v1/order/employee").hasAnyRole("order-employee-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 수주서 전체 조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/order/{orderId}").hasAnyRole("order-id-get", "GOD", "DIRECTOR", "ADMIN") // 수주서 상세 조회 (영업관리자, 영업담당자) + .requestMatchers(HttpMethod.GET, "/api/v1/order/employee/{orderId}").hasAnyRole("order-employee-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 수주서 상세 조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/order/search").hasAnyRole("order-search", "GOD", "DIRECTOR", "ADMIN") // 수주서 검색 조회 (영업관리자, 영업담당자) + .requestMatchers(HttpMethod.GET, "/api/v1/order/employee/search").hasAnyRole("order-employee-search", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 수주서 검색 조회 (전체) + .requestMatchers(HttpMethod.POST, "/api/v1/order").hasAnyRole("order-create", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 수주서 등록 (전체) + .requestMatchers(HttpMethod.PUT, "/api/v1/order/{orderId}").hasAnyRole("order-update", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 수주서 수정 (전체) + .requestMatchers(HttpMethod.PUT, "/api/v1/order/status/{orderId}").hasAnyRole("order-status-update", "GOD", "ADMIN") // 수주서 승인상태 변경 (영업관리자) + + // Organization API + .requestMatchers(HttpMethod.GET, "/api/v1/organization").hasAnyRole("organization-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 사이드바 메뉴 조회 (전체) + + // Problem API + .requestMatchers(HttpMethod.DELETE, "/api/v1/problem/{problemId}").hasAnyRole("problem-delete", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 문제사항 삭제 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/problem/{problemId}").hasAnyRole("problem-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 문제사항 Id로 조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/problem").hasAnyRole("problem-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 문제사항 조건별 조회 (전체) + .requestMatchers(HttpMethod.PUT, "/api/v1/problem/{problemId}").hasAnyRole("problem-update", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 문제사항 수정 (전체) + .requestMatchers(HttpMethod.POST, "/api/v1/problem").hasAnyRole("problem-create", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 문제사항 작성 (전체) + + // Product API + .requestMatchers(HttpMethod.GET, "/api/v1/product").hasAnyRole("product-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 제품 조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/product/{id}").hasAnyRole("product-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 제품 상세 조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/product/search").hasAnyRole("product-search", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 제품 검색 (전체) + + // Promotion API + .requestMatchers(HttpMethod.DELETE, "/api/v1/promotion/{promotionId}").hasAnyRole("promotion-delete", "GOD", "DIRECTOR") // 프로모션 삭제 (영업담당자) + .requestMatchers(HttpMethod.GET, "/api/v1/promotion/{promotionId}").hasAnyRole("promotion-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 프로모션 Id로 조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/promotion").hasAnyRole("promotion-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 프로모션 조건별 조회 (전체) + .requestMatchers(HttpMethod.PUT, "/api/v1/promotion/{promotionId}").hasAnyRole("promotion-update", "GOD", "DIRECTOR") // 프로모션 수정 (영업담당자) + .requestMatchers(HttpMethod.POST, "/api/v1/promotion").hasAnyRole("promotion-create", "GOD", "DIRECTOR") // 프로모션 작성 (영업담당자) + + // Purchase-Order API + .requestMatchers(HttpMethod.DELETE, "/api/v1/purchase-order/{purchaseOrderId}").hasAnyRole("purchase-order-delete", "GOD", "ADMIN") // 발주서 삭제 (영업관리자) + .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order").hasAnyRole("purchase-order-get", "GOD", "DIRECTOR") // 발주서 전체 조회 (영업담당자) + .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order/admin").hasAnyRole("purchase-order-admin-get", "GOD", "ADMIN") // 발주서 전체 조회 (영업관리자) + .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order/{purchaseOrderId}").hasAnyRole("purchase-order-id-get", "GOD", "DIRECTOR") // 발주서 상세 조회 (영업담당자) + .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order/admin/{purchaseOrderId}").hasAnyRole("purchase-order-admin-id-get", "GOD", "ADMIN") // 발주서 상세 조회 (영업관리자) + .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order/search").hasAnyRole("purchase-order-search", "GOD", "DIRECTOR") // 발주서 검색 조회 (영업담당자) + .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order/admin/search").hasAnyRole("purchase-order-admin-search", "GOD", "ADMIN") // 발주서 검색 조회 (영업관리자) + .requestMatchers(HttpMethod.POST, "/api/v1/purchase-order").hasAnyRole("purchase-order-create", "GOD", "ADMIN") // 발주서 등록 (영업관리자) + .requestMatchers(HttpMethod.PUT, "/api/v1/purchase-order/{purchaseOrderId}").hasAnyRole("purchase-order-update", "GOD", "ADMIN") // 발주서 수정 (영업관리자) + .requestMatchers(HttpMethod.PUT, "/api/v1/purchase-order/stauts/{purchaseOrderId}").hasAnyRole("purchase-order-status-update", "GOD", "DIRECTOR") // 발주서 승인 상태 수정 (영업담당자) + + // Sample API + .requestMatchers(HttpMethod.DELETE, "/api/v1/sample/{id}").hasAnyRole("sample-delete", "GOD") // 샘플 삭제 테스트 (시스템관리자) + .requestMatchers(HttpMethod.GET, "/api/v1/sample/detail/{id}").hasAnyRole("sample-detail-get", "GOD") // 샘플 상세 조회 테스트 (시스템관리자) + .requestMatchers(HttpMethod.GET, "/api/v1/schedule/{scheduleId}").hasAnyRole("sample-schedule-get", "GOD") // 샘플 조회 테스트 (시스템관리자) + .requestMatchers(HttpMethod.POST, "/api/v1/sample").hasAnyRole("sample-create", "GOD") // 샘플 요청 테스트 (시스템관리자) + .requestMatchers(HttpMethod.PUT, "/api/v1/sample/{id}").hasAnyRole("sample-update", "GOD") // 샘플 수정 테스트 (시스템관리자) + + // Schedule API + .requestMatchers(HttpMethod.DELETE, "/api/v1/schedule/{scheduleId}").hasAnyRole("schedule-delete", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 일정 삭제 api (전체) + .requestMatchers(HttpMethod.PUT, "/api/v1/schedule/{scheduleId}").hasAnyRole("schedule-update", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 일정 수정 api (전체) + .requestMatchers(HttpMethod.POST, "/api/v1/schedule").hasAnyRole("schedule-create", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 일정 등록 api (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/schedule").hasAnyRole("schedule-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 일정 전체 조회 api (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/schedule/{scheduleId}").hasAnyRole("schedule-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 일정 상세 조회 api (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/schedule/{year}/{month}").hasAnyRole("schedule-month-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 일정 조건별(년&일) 전체 조회 api (전체) + + // Center API + .requestMatchers(HttpMethod.GET, "/api/v1/center/{id}").hasAnyRole("center-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 영업매장 상세조회 (전체) + .requestMatchers(HttpMethod.PUT, "/api/v1/center/{id}").hasAnyRole("center-update", "GOD", "ADMIN") // 매장 수정 (영업 관리자) + .requestMatchers(HttpMethod.DELETE, "/api/v1/center/{id}").hasAnyRole("center-delete", "GOD", "ADMIN") // 매장 삭제 (영업 관리자) + .requestMatchers(HttpMethod.GET, "/api/v1/center").hasAnyRole("center-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 영업매장 조회 (전체) + .requestMatchers(HttpMethod.POST, "/api/v1/center").hasAnyRole("center-create", "GOD", "ADMIN") // 매장 등록 (영업 관리자) + .requestMatchers(HttpMethod.GET, "/api/v1/center/search").hasAnyRole("center-search", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 영업매장 검색 (전체) + + // SalesHistory API + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory").hasAnyRole("salesHistory-get", "GOD", "DIRECTOR", "ADMIN") // 판매내역 조회 (영업관리자, 영업담당자) + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee").hasAnyRole("salesHistory-employee-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 판매내역 조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/{salesHistoryId}").hasAnyRole("salesHistory-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 판매내역 상세조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search").hasAnyRole("salesHistory-statistics-search", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 사원 통계 조회기간별 검색 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search/month").hasAnyRole("salesHistory-statistics-month-search", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 사원 통계 월별 검색 (전체) + + // 그 외 나머지 시스템 관리자만 접근 가능 + .anyRequest().hasRole("GOD") + ); + } +} + + From a8131d3ef1f603109aa955d164cc69d66bad931e Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Thu, 21 Nov 2024 17:57:18 +0900 Subject: [PATCH 275/563] =?UTF-8?q?feat:=20S3=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B3=84=EC=95=BD=EC=84=9C=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EB=82=B4=EC=97=AD=20=EA=B5=AC=ED=98=84=EC=A4=91=20(#121)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ContractController.java | 6 +-- .../application/dto/ContractModifyDTO.java | 6 +-- .../application/dto/ContractRegistDTO.java | 6 +-- .../dto/UpdateHistoryRegistDTO.java | 16 ++++++ .../service/ContractCommandService.java | 2 +- .../domain/aggregate/entity/Contract.java | 11 ++-- .../aggregate/entity/UpdateHistory.java | 51 +++++++++++++++++++ .../repository/UpdateHistoryRepository.java | 7 +++ .../service/ContractCommandServiceImpl.java | 47 +++++++++++------ .../contract/query/dto/ContractSearchDTO.java | 6 +-- .../query/dto/ContractSeletIdDTO.java | 6 +-- .../application/dto/CustomerModifyDTO.java | 6 +-- .../domain/aggregate/entity/Customer.java | 6 +-- .../domain/repository/CustomerRepository.java | 1 + .../service/CustomerCommandServiceImpl.java | 3 +- .../service/CustomerQueryServiceImpl.java | 2 + .../domain/s3/S3FileService.java | 2 + .../domain/s3/S3FileServiceImpl.java | 22 +++++++- .../s3/common/exception/S3ErrorCode.java | 2 +- .../final_backend/global/config/S3Config.java | 4 +- src/main/resources/application-dev.yml | 2 +- src/main/resources/application-prod.yml | 2 +- src/main/resources/application.yml | 2 +- .../query/repository/ProductMapper.xml | 14 ++--- 24 files changed, 170 insertions(+), 62 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/UpdateHistoryRegistDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/UpdateHistory.java create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/command/domain/repository/UpdateHistoryRepository.java diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java index 9050e636..c98ea6fc 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java @@ -35,7 +35,7 @@ public ContractController(ContractCommandService contractCommandService) { content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postTest(@RequestBody ContractRegistDTO contractRegistRequestDTO, + public ResponseEntity postContract(@RequestBody ContractRegistDTO contractRegistRequestDTO, Principal principal) throws GeneralSecurityException { contractRegistRequestDTO.setMemberId(principal.getName()); @@ -60,12 +60,12 @@ public ResponseEntity putContract(@PathVariable String contractModifyRequestDTO.setContractId(contractId); contractModifyRequestDTO.setMemberId(principal.getName()); - ContractModifyDTO contractModifyDTO = contractCommandService.modifyContract(contractModifyRequestDTO); + contractCommandService.modifyContract(contractModifyRequestDTO); return ResponseEntity.ok(ContractResponseMessage.builder() .httpStatus(200) .msg("계약서가 성공적으로 수정되었습니다.") - .result(contractModifyDTO) + .result(null) .build()); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java index 26048c2e..fbeb73b3 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java @@ -17,7 +17,7 @@ public class ContractModifyDTO { private String customerSex; private String customerIdentifiNo; private Integer customerAge; - private String customerAddrress; + private String customerAddress; private String customerEmail; private String customerPhone; private String companyName; @@ -31,8 +31,8 @@ public class ContractModifyDTO { private Integer remainderPayment; private Integer consignmentPayment; private Integer totalSales; - private String delveryDate; - private String delveryLocation; + private String deliveryDate; + private String deliveryLocation; private String status; private String numberOfVehicles; private String createdUrl; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java index 419f98ac..c87c4280 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java @@ -16,7 +16,7 @@ public class ContractRegistDTO { private String customerSex; private String customerIdentifiNo; private Integer customerAge; - private String customerAddrress; + private String customerAddress; private String customerEmail; private String customerPhone; private String companyName; @@ -30,8 +30,8 @@ public class ContractRegistDTO { private Integer consignmentPayment; private Integer totalSales; private String carName; - private String delveryDate; - private String delveryLocation; + private String deliveryDate; + private String deliveryLocation; private String status; private String numberOfVehicles; private String createdUrl; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/UpdateHistoryRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/UpdateHistoryRegistDTO.java new file mode 100644 index 00000000..330cf755 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/UpdateHistoryRegistDTO.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.contract.command.application.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class UpdateHistoryRegistDTO { + private String content; + private String contractId; + private String memberId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java index 2b32ac17..546f8e52 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java @@ -10,7 +10,7 @@ public interface ContractCommandService { void registerContract(ContractRegistDTO contractRegistRequestDTO) throws GeneralSecurityException; - ContractModifyDTO modifyContract(ContractModifyDTO contractModifyRequestDTO) throws GeneralSecurityException; + void modifyContract(ContractModifyDTO contractModifyRequestDTO) throws GeneralSecurityException; void deleteContract(ContractDeleteDTO contractDeleteDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java index 317497ba..59785006 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java @@ -1,10 +1,7 @@ package stanl_2.final_backend.domain.contract.command.domain.aggregate.entity; import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import lombok.*; import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.GenericGenerator; import stanl_2.final_backend.global.config.PrefixGeneratorConfig; @@ -47,7 +44,7 @@ public class Contract { private String customerAge; @Column(name = "CONR_CUST_ADR", nullable = false) - private String customerAddrress; + private String customerAddress; @Column(name = "CONR_CUST_EMA", nullable = false) private String customerEmail; @@ -85,10 +82,10 @@ public class Contract { private Integer consignmentPayment; @Column(name = "CONR_DELV_DATE") - private String delveryDate; + private String deliveryDate; @Column(name = "CONR_DELV_LOC") - private String delveryLocation; + private String deliveryLocation; @Column(name = "CONR_CAR_NAME") private String carName; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/UpdateHistory.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/UpdateHistory.java new file mode 100644 index 00000000..faaae2e7 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/UpdateHistory.java @@ -0,0 +1,51 @@ +package stanl_2.final_backend.domain.contract.command.domain.aggregate.entity; + +import jakarta.persistence.*; +import lombok.*; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "TB_UPDATE_HISTORY") +public class UpdateHistory { + + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "UPD_HIS") + ) + @Column(name = "UPD_ID", nullable = false) + private String updateHistoryId; + + @Column(name = "CREATED_AT", nullable = false) + private String createdAt; + + @Column(name = "UPD_CONT", nullable = false) + private String content; + + @Column(name = "CONR_ID", nullable = false) + private String contractId; + + @Column(name = "MEM_ID", nullable = false) + private String memberId; + + @PrePersist + private void prePersist() { + this.createdAt = getCurrentTime(); + } + + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } + +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/repository/UpdateHistoryRepository.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/repository/UpdateHistoryRepository.java new file mode 100644 index 00000000..c4e31684 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/repository/UpdateHistoryRepository.java @@ -0,0 +1,7 @@ +package stanl_2.final_backend.domain.contract.command.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import stanl_2.final_backend.domain.contract.command.domain.aggregate.entity.UpdateHistory; + +public interface UpdateHistoryRepository extends JpaRepository { +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index 73de5969..064bdb68 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -1,15 +1,16 @@ package stanl_2.final_backend.domain.contract.command.domain.service; +import lombok.extern.slf4j.Slf4j; import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.contract.command.application.dto.ContractDeleteDTO; -import stanl_2.final_backend.domain.contract.command.application.dto.ContractModifyDTO; -import stanl_2.final_backend.domain.contract.command.application.dto.ContractRegistDTO; -import stanl_2.final_backend.domain.contract.command.application.dto.ContractStatusModifyDTO; +import stanl_2.final_backend.domain.contract.command.application.dto.*; import stanl_2.final_backend.domain.contract.command.application.service.ContractCommandService; import stanl_2.final_backend.domain.contract.command.domain.aggregate.entity.Contract; +import stanl_2.final_backend.domain.contract.command.domain.aggregate.entity.UpdateHistory; import stanl_2.final_backend.domain.contract.command.domain.repository.ContractRepository; +import stanl_2.final_backend.domain.contract.command.domain.repository.UpdateHistoryRepository; import stanl_2.final_backend.domain.contract.common.exception.ContractCommonException; import stanl_2.final_backend.domain.contract.common.exception.ContractErrorCode; import stanl_2.final_backend.domain.customer.command.application.dto.CustomerModifyDTO; @@ -23,6 +24,7 @@ import stanl_2.final_backend.domain.product.command.application.command.service.ProductCommandService; import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; import stanl_2.final_backend.domain.product.query.service.ProductQueryService; +import stanl_2.final_backend.domain.s3.S3FileService; import stanl_2.final_backend.domain.sales_history.command.application.service.SalesHistoryCommandService; import stanl_2.final_backend.global.utils.AESUtils; @@ -31,31 +33,37 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +@Slf4j @Service("contractServiceImpl") public class ContractCommandServiceImpl implements ContractCommandService { private final ContractRepository contractRepository; + private final UpdateHistoryRepository updateHistoryRepository; private final AuthQueryService authQueryService; private final CustomerQueryService customerQueryService; private final MemberQueryService memberQueryService; private final CustomerCommandService customerCommandService; - private final ProductQueryService productService; + private final ProductQueryService productQueryService; private final ProductCommandService productCommandService; private final SalesHistoryCommandService salesHistoryCommandService; private final ModelMapper modelMapper; private final AESUtils aesUtils; + private final S3FileService s3FileService; - public ContractCommandServiceImpl(ContractRepository contractRepository, AuthQueryService authQueryService, CustomerQueryService customerQueryService, MemberQueryService memberQueryService, CustomerCommandService customerCommandService, ProductQueryService productService, ProductCommandService productCommandService, SalesHistoryCommandService salesHistoryCommandService, ModelMapper modelMapper, AESUtils aesUtils) { + @Autowired + public ContractCommandServiceImpl(ContractRepository contractRepository, UpdateHistoryRepository updateHistoryRepository, AuthQueryService authQueryService, CustomerQueryService customerQueryService, MemberQueryService memberQueryService, CustomerCommandService customerCommandService, ProductQueryService productQueryService, ProductCommandService productCommandService, SalesHistoryCommandService salesHistoryCommandService, ModelMapper modelMapper, AESUtils aesUtils, S3FileService s3FileService) { this.contractRepository = contractRepository; + this.updateHistoryRepository = updateHistoryRepository; this.authQueryService = authQueryService; this.customerQueryService = customerQueryService; this.memberQueryService = memberQueryService; this.customerCommandService = customerCommandService; - this.productService = productService; + this.productQueryService = productQueryService; this.productCommandService = productCommandService; this.salesHistoryCommandService = salesHistoryCommandService; this.modelMapper = modelMapper; this.aesUtils = aesUtils; + this.s3FileService = s3FileService; } private String getCurrentTime() { @@ -92,7 +100,7 @@ public void registerContract(ContractRegistDTO contractRegistRequestDTO) throws String memberId = authQueryService.selectMemberIdByLoginId(contractRegistRequestDTO.getMemberId()); // 일련번호로 제품테이블의 총식별번호 찾아서 제품 가져오기 - ProductSelectIdDTO productSelectIdDTO = productService.selectByProductSerialNumber(contractRegistRequestDTO.getSerialNum()); + ProductSelectIdDTO productSelectIdDTO = productQueryService.selectByProductSerialNumber(contractRegistRequestDTO.getSerialNum()); String productId = productSelectIdDTO.getId(); String customerId = null; // 고객 ID 변수 선언 @@ -115,7 +123,7 @@ public void registerContract(ContractRegistDTO contractRegistRequestDTO) throws contract.setCustomerPhone(aesUtils.encrypt(contractRegistRequestDTO.getCustomerPhone())); contract.setCustomerEmail(aesUtils.encrypt(contractRegistRequestDTO.getCustomerEmail())); - contract.setCustomerAddrress(aesUtils.encrypt(contractRegistRequestDTO.getCustomerAddrress())); + contract.setCustomerAddress(aesUtils.encrypt(contractRegistRequestDTO.getCustomerAddress())); contract.setCustomerIdentifiNo(aesUtils.encrypt(contractRegistRequestDTO.getCustomerIdentifiNo())); contractRepository.save(contract); @@ -123,10 +131,11 @@ public void registerContract(ContractRegistDTO contractRegistRequestDTO) throws @Override @Transactional - public ContractModifyDTO modifyContract(ContractModifyDTO contractModifyRequestDTO) throws GeneralSecurityException { + public void modifyContract(ContractModifyDTO contractModifyRequestDTO) throws GeneralSecurityException { String memberId = authQueryService.selectMemberIdByLoginId(contractModifyRequestDTO.getMemberId()); + // 가져온 고객 정보에 수정된 값 넣기 CustomerModifyDTO customerModifyDTO = new CustomerModifyDTO(); customerModifyDTO.setName(contractModifyRequestDTO.getCustomerName()); @@ -154,14 +163,22 @@ public ContractModifyDTO modifyContract(ContractModifyDTO contractModifyRequestD contract.setCustomerPhone(aesUtils.encrypt(contractModifyRequestDTO.getCustomerPhone())); contract.setCustomerEmail(aesUtils.encrypt(contractModifyRequestDTO.getCustomerEmail())); - contract.setCustomerAddrress(aesUtils.encrypt(contractModifyRequestDTO.getCustomerAddrress())); + contract.setCustomerAddress(aesUtils.encrypt(contractModifyRequestDTO.getCustomerAddress())); contract.setCustomerIdentifiNo(aesUtils.encrypt(contractModifyRequestDTO.getCustomerIdentifiNo())); contractRepository.save(updateContract); - ContractModifyDTO contractModifyDTO = modelMapper.map(updateContract, ContractModifyDTO.class); + String updatedS3Url = s3FileService.uploadHtml(contractModifyRequestDTO.getCreatedUrl(), contractModifyRequestDTO.getTitle()); + + UpdateHistoryRegistDTO updateHistoryRegistDTO = new UpdateHistoryRegistDTO(); + updateHistoryRegistDTO.setContent(updatedS3Url); + updateHistoryRegistDTO.setMemberId(memberId); + updateHistoryRegistDTO.setContractId(contractModifyRequestDTO.getContractId()); + + UpdateHistory updateHistory = modelMapper.map(updateHistoryRegistDTO, UpdateHistory.class); - return contractModifyDTO; + // 수정 내역 테이블에 저장 + updateHistoryRepository.save(updateHistory); } @Override @@ -202,13 +219,13 @@ public void modifyContractStatus(ContractStatusModifyDTO contractStatusModifyDTO salesHistoryCommandService.registerSalesHistory(contract.getContractId()); // 제품 재고 수 줄이기 - ProductSelectIdDTO productSelectIdDTO = productService.selectByProductSerialNumber(contract.getSerialNum()); + ProductSelectIdDTO productSelectIdDTO = productQueryService.selectByProductSerialNumber(contract.getSerialNum()); String productId = productSelectIdDTO.getId(); productCommandService.modifyProductStock(productId); } else if (contractStatusModifyDTO.getStatus().equals("CANCLED")) { salesHistoryCommandService.deleteSalesHistory(contract.getContractId()); - ProductSelectIdDTO productSelectIdDTO = productService.selectByProductSerialNumber(contract.getSerialNum()); + ProductSelectIdDTO productSelectIdDTO = productQueryService.selectByProductSerialNumber(contract.getSerialNum()); String productId = productSelectIdDTO.getId(); productCommandService.deleteProductStock(productId); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java index 5ad874b1..943083c8 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java @@ -12,7 +12,7 @@ public class ContractSearchDTO { private String title; private String customerName; private String customerIdentifiNo; - private String customerAddrress; + private String customerAddress; private String customerEmail; private String customerPhone; private String companyName; @@ -24,8 +24,8 @@ public class ContractSearchDTO { private Integer intermediatePayment; private Integer remainderPayment; private Integer consignmentPayment; - private String delveryDate; - private String delveryLocationLoc; + private String deliveryDate; + private String deliveryLocationLoc; private String status; private String numberOfVehicles; private String totalSales; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java index b42ab15d..71f2f1d6 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java @@ -14,7 +14,7 @@ public class ContractSeletIdDTO { private String customerSex; private String customerIdentifiNo; private Integer customerAge; - private String customerAddrress; + private String customerAddress; private String customerEmail; private String customerPhone; private String companyName; @@ -26,8 +26,8 @@ public class ContractSeletIdDTO { private Integer intermediatePayment; private Integer remainderPayment; private Integer consignmentPayment; - private String delveryDate; - private String delveryLocation; + private String deliveryDate; + private String deliveryLocation; private String status; private String NumberOfVehicles; private String totalSales; diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/application/dto/CustomerModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/customer/command/application/dto/CustomerModifyDTO.java index 937a5f8e..7e6bface 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/application/dto/CustomerModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/application/dto/CustomerModifyDTO.java @@ -1,14 +1,12 @@ package stanl_2.final_backend.domain.customer.command.application.dto; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import lombok.*; @AllArgsConstructor @NoArgsConstructor @Setter @Getter +@ToString public class CustomerModifyDTO { private String memberId; private String customerId; diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/aggregate/entity/Customer.java b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/aggregate/entity/Customer.java index 12d0052f..d8a18bd7 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/aggregate/entity/Customer.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/aggregate/entity/Customer.java @@ -1,10 +1,7 @@ package stanl_2.final_backend.domain.customer.command.domain.aggregate.entity; import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import lombok.*; import org.hibernate.annotations.GenericGenerator; import stanl_2.final_backend.global.config.PrefixGeneratorConfig; @@ -17,6 +14,7 @@ @Getter @Setter @Entity +@ToString @Table(name = "TB_CUSTOMER_INFO") public class Customer { @Id diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/repository/CustomerRepository.java b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/repository/CustomerRepository.java index de571e93..d2ad757f 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/repository/CustomerRepository.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/repository/CustomerRepository.java @@ -8,4 +8,5 @@ @Repository public interface CustomerRepository extends JpaRepository { + Customer findByCustomerId(String customerId); } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java index 39a3b57e..a4c29383 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/service/CustomerCommandServiceImpl.java @@ -50,7 +50,6 @@ public void registerCustomerInfo(CustomerRegistDTO customerRegistDTO) throws Gen @Transactional public void modifyCustomerInfo(CustomerModifyDTO customerModifyDTO) throws GeneralSecurityException { - System.out.println("아아아: " + customerModifyDTO.getCustomerId()); Customer customer = customerRepository.findById(customerModifyDTO.getCustomerId()) .orElseThrow(() -> new CustomerCommonException(CustomerErrorCode.CUSTOMER_NOT_FOUND)); @@ -74,4 +73,4 @@ public void deleteCustomerId(String customerId) { customerRepository.save(customer); } -} +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java index 297f1e7d..bae37c62 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java @@ -97,6 +97,8 @@ public Page findCustomerByCondition(Pageable pageable, CustomerSear @Transactional(readOnly = true) public CustomerDTO selectCustomerInfoByPhone(String customerPhone) throws GeneralSecurityException { + System.out.println("핸드폰 번호: " + customerPhone); + String encryptedPhone = aesUtils.encrypt(customerPhone); CustomerDTO customerInfoDTO = customerMapper.selectCustomerInfoByPhone(encryptedPhone); diff --git a/src/main/java/stanl_2/final_backend/domain/s3/S3FileService.java b/src/main/java/stanl_2/final_backend/domain/s3/S3FileService.java index 456f590c..a18cd262 100644 --- a/src/main/java/stanl_2/final_backend/domain/s3/S3FileService.java +++ b/src/main/java/stanl_2/final_backend/domain/s3/S3FileService.java @@ -9,6 +9,8 @@ public interface S3FileService { // 파일 업로드(다중) + // html 업로드 + String uploadHtml(String htmlContent, String fileName); // 파일 삭제 void deleteFile(String fileName); diff --git a/src/main/java/stanl_2/final_backend/domain/s3/S3FileServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/s3/S3FileServiceImpl.java index aa1a817d..46af4f4c 100644 --- a/src/main/java/stanl_2/final_backend/domain/s3/S3FileServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/s3/S3FileServiceImpl.java @@ -15,8 +15,10 @@ import stanl_2.final_backend.domain.s3.common.exception.S3ErrorCode; import stanl_2.final_backend.global.config.S3Config; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.UUID; @@ -51,7 +53,24 @@ public String uploadOneFile(MultipartFile file) { return "https://" + bucket + ".s3." + region + ".amazonaws.com/" + fileName; } catch (IOException e) { - throw new RuntimeException(e); + throw new S3CommonException(S3ErrorCode.FILE_UPLOAD_BAD_REQUEST); + } + } + + public String uploadHtml(String htmlContent, String fileName) { + + String fileKey = createFileName(fileName + ".html"); // 파일 이름 생성 (UUID로 고유화 가능) + + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentType("text/html; charset=UTF-8"); + metadata.setContentLength(htmlContent.getBytes(StandardCharsets.UTF_8).length); + + try (InputStream inputStream = new ByteArrayInputStream(htmlContent.getBytes(StandardCharsets.UTF_8))) { + amazonS3Client.putObject(bucket, fileKey, inputStream, metadata); + + return "https://" + bucket + ".s3." + region + ".amazonaws.com/" + fileKey; + } catch (IOException e) { + throw new S3CommonException(S3ErrorCode.FILE_UPLOAD_BAD_REQUEST); } } @@ -106,6 +125,7 @@ private String getFileExtension(String fileName) { fileValidate.add(".JPG"); fileValidate.add(".JPEG"); fileValidate.add(".PNG"); + fileValidate.add(".html"); String idxFileName = fileName.substring(fileName.lastIndexOf(".")); if(!fileValidate.contains(idxFileName)) { throw new S3CommonException(S3ErrorCode.FILE_NOT_FOUND); diff --git a/src/main/java/stanl_2/final_backend/domain/s3/common/exception/S3ErrorCode.java b/src/main/java/stanl_2/final_backend/domain/s3/common/exception/S3ErrorCode.java index 48675e58..a92ac193 100644 --- a/src/main/java/stanl_2/final_backend/domain/s3/common/exception/S3ErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/s3/common/exception/S3ErrorCode.java @@ -12,7 +12,7 @@ public enum S3ErrorCode { * 400(Bad Request) * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다. */ - + FILE_UPLOAD_BAD_REQUEST(400001, HttpStatus.BAD_REQUEST, "업로드에 실패했습니다."), /** diff --git a/src/main/java/stanl_2/final_backend/global/config/S3Config.java b/src/main/java/stanl_2/final_backend/global/config/S3Config.java index 069b1e89..e5af658a 100644 --- a/src/main/java/stanl_2/final_backend/global/config/S3Config.java +++ b/src/main/java/stanl_2/final_backend/global/config/S3Config.java @@ -12,9 +12,9 @@ public class S3Config { @Value("${cloud.aws.credentials.access-key}") private String accessKey; - @Value("${cloud.aws.credential.secret-key}") + @Value("${cloud.aws.credentials.secret-key}") private String secretKey; - @Value("${cloud.aws.region.static") + @Value("${cloud.aws.region.static}") private String region; @Bean diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index e9390662..aabe59aa 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: create + ddl-auto: update logging: level: diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 0d0b92a3..083491db 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: create + ddl-auto: update logging: level: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 1ba7515c..2ce1ff51 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -23,7 +23,7 @@ spring: jpa: show-sql: true hibernate: - ddl-auto: create + ddl-auto: update properties: hibernate.format_sql: true diff --git a/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml b/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml index affb849d..94b4e47d 100644 --- a/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml @@ -84,13 +84,13 @@ + diff --git a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml index d3dc4609..1e61f994 100644 --- a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml @@ -23,6 +23,14 @@ + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + From 5e2a303b8215d95ce2aa5ad9dcdc7ad8e47c8dc3 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Thu, 21 Nov 2024 19:05:41 +0900 Subject: [PATCH 277/563] feat: postconstructure Member(#128) --- .../command/application/dto/SignupDTO.java | 1 - .../global/dataloader/MemberInitializer.java | 135 ++++++++++++++++++ src/main/resources/application-prod.yml | 2 +- src/main/resources/application.yml | 7 - 4 files changed, 136 insertions(+), 9 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java index e31d1aad..6e27bc55 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java @@ -1,6 +1,5 @@ package stanl_2.final_backend.domain.member.command.application.dto; -import jakarta.persistence.Column; import lombok.*; @AllArgsConstructor diff --git a/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java b/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java new file mode 100644 index 00000000..2c2d7e7c --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java @@ -0,0 +1,135 @@ +package stanl_2.final_backend.global.dataloader; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; +import stanl_2.final_backend.domain.member.command.application.dto.GrantDTO; +import stanl_2.final_backend.domain.member.command.application.dto.SignupDTO; +import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; + +@Component +public class MemberInitializer implements ApplicationRunner { + + private final AuthCommandService authCommandService; + + @Autowired + public MemberInitializer(AuthCommandService authCommandService) { + this.authCommandService = authCommandService; + } + + @Override + public void run(ApplicationArguments args) throws Exception { + + // 영업 관리자(회원가입) + SignupDTO employee = new SignupDTO(); + employee.setLoginId("employee"); + employee.setPassword("employee"); + employee.setName("영업 관리자"); + employee.setEmail("employee@stanl.com"); + employee.setAge(3); + employee.setSex("FEMALE"); + employee.setIdentNo("000000-0000003"); + employee.setPhone("010-0000-0003"); + employee.setAddress("신대방삼거리"); + employee.setPosition("영업 관리자"); + employee.setGrade("고졸"); + employee.setJobType("백엔드 개발자"); + employee.setMilitary("미필"); + employee.setBankName("한국은행"); + employee.setAccount("000-0000-000000-00003"); + employee.setCenterId("CEN_000000001"); + employee.setOrganizationId("ORG_000000001"); + authCommandService.signup(employee); + + // 영업 관리자(권한 부여) + GrantDTO grandEmployee = new GrantDTO(); + grandEmployee.setLoginId("employee"); + grandEmployee.setRole("EMPLOYEE"); + authCommandService.grantAuthority(grandEmployee); + + + // 영업 관리자(회원가입) + SignupDTO admin = new SignupDTO(); + admin.setLoginId("admin"); + admin.setPassword("admin"); + admin.setName("영업 관리자"); + admin.setEmail("admin@stanl.com"); + admin.setAge(2); + admin.setSex("FEMALE"); + admin.setIdentNo("000000-0000002"); + admin.setPhone("010-0000-0002"); + admin.setAddress("신대방삼거리"); + admin.setPosition("영업 관리자"); + admin.setGrade("고졸"); + admin.setJobType("백엔드 개발자"); + admin.setMilitary("미필"); + admin.setBankName("한국은행"); + admin.setAccount("000-0000-000000-00000"); + admin.setCenterId("CEN_000000001"); + admin.setOrganizationId("ORG_000000001"); + authCommandService.signup(admin); + + // 영업 관리자(권한 부여) + GrantDTO grandAdmin = new GrantDTO(); + grandAdmin.setLoginId("admin"); + grandAdmin.setRole("ADMIN"); + authCommandService.grantAuthority(grandAdmin); + + + // 영업 담당자(회원가입) + SignupDTO director = new SignupDTO(); + director.setLoginId("director"); + director.setPassword("director"); + director.setName("영업 담당자"); + director.setEmail("director@stanl.com"); + director.setAge(1); + director.setSex("MALE"); + director.setIdentNo("000000-0000001"); + director.setPhone("010-0000-0001"); + director.setAddress("STANL2"); + director.setPosition("영업담당자"); + director.setGrade("고졸"); + director.setJobType("영업 담당자"); + director.setMilitary("미필"); + director.setBankName("한국은행"); + director.setAccount("000-0000-000000-00000"); + director.setCenterId("CEN_000000001"); + director.setOrganizationId("ORG_000000001"); + authCommandService.signup(director); + + // 영업 담당자(권한 부여) + GrantDTO grandDirector = new GrantDTO(); + grandDirector.setLoginId("director"); + grandDirector.setRole("DIRECTOR"); + authCommandService.grantAuthority(grandDirector); + + + // 시스템 관리자(회원가입) + SignupDTO god = new SignupDTO(); + god.setLoginId("god"); + god.setPassword("god"); + god.setName("시스템관리자"); + god.setEmail("god@stanl.com"); + god.setAge(0); + god.setSex("MALE"); + god.setIdentNo("000000-0000000"); + god.setPhone("010-0000-0000"); + god.setAddress("신대방삼거리"); + god.setPosition("시스템관리자"); + god.setGrade("중졸"); + god.setJobType("백엔드 개발자"); + god.setMilitary("미필"); + god.setBankName("한국은행"); + god.setAccount("000-0000-000000-00000"); + god.setCenterId("CEN_000000001"); + god.setOrganizationId("ORG_000000001"); + authCommandService.signup(god); + + // 시스템 관리자(권한 부여) + GrantDTO grandGOD = new GrantDTO(); + grandGOD.setLoginId("god"); + grandGOD.setRole("GOD"); + authCommandService.grantAuthority(grandGOD); + } +} diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 083491db..0d0b92a3 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: update + ddl-auto: create logging: level: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index eee7170c..169384fc 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -20,13 +20,6 @@ spring: restart: enabled: false - jpa: - show-sql: true - hibernate: - ddl-auto: update - properties: - hibernate.format_sql: true - jwt: secret-key: ${JWT_SECRET_KEY} header: ${JWT_HEADER} From 348bae9036898459afef6d0384982e340b1a828c Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Thu, 21 Nov 2024 19:10:25 +0900 Subject: [PATCH 278/563] =?UTF-8?q?fix:=20loginId=20unique=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/command/domain/aggregate/entity/Member.java | 2 +- src/main/resources/application-prod.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java index b6ca6889..5ba08b24 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java @@ -31,7 +31,7 @@ public class Member { @Column(name = "MEM_ID", nullable = false) private String memberId; - @Column(name = "MEM_LOGIN_ID", nullable = false) + @Column(name = "MEM_LOGIN_ID", nullable = false, unique = true) private String loginId; @Column(name = "MEM_PWD", nullable = false) diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 0d0b92a3..083491db 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: create + ddl-auto: update logging: level: From 74583927dfa75ee0e935473c31e085726b90259c Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Thu, 21 Nov 2024 19:17:52 +0900 Subject: [PATCH 279/563] =?UTF-8?q?fix:=20=EC=BB=A8=EB=B2=A4=EC=85=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/controller/CareerController.java | 3 +-- .../career/query/controller/CareerController.java | 5 ++--- .../controller/CertificationController.java | 3 +-- .../query/controller/CertificationController.java | 5 ++--- .../application/controller/CustomerController.java | 8 +++----- .../customer/query/controller/CustomerController.java | 7 +++---- .../query/controller/EducationController.java | 7 ++----- .../family/query/controller/FamilyController.java | 7 ++----- .../domain/log/command/aggregate/Log.java | 1 - .../domain/log/command/aop/LoggingAspect.java | 1 - .../domain/log/query/controller/LogController.java | 3 +-- .../application/controller/MemberController.java | 11 +++++++++++ .../member/query/controller/MemberController.java | 3 +-- 13 files changed, 29 insertions(+), 35 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java b/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java index 10d061a9..cecea16b 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java +++ b/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java @@ -9,7 +9,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.career.command.application.dto.CareerRegistDTO; import stanl_2.final_backend.domain.career.command.application.service.CareerCommandService; import stanl_2.final_backend.domain.career.common.response.CareerResponseMessage; @@ -35,7 +34,7 @@ public CareerController(CareerCommandService careerCommandService, @Operation(summary = "경력 등록") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = CareerResponseMessage.class))}) }) @PostMapping("") public ResponseEntity postCareer(@RequestBody CareerRegistDTO careerRegistDTO, diff --git a/src/main/java/stanl_2/final_backend/domain/career/query/controller/CareerController.java b/src/main/java/stanl_2/final_backend/domain/career/query/controller/CareerController.java index 61ffde26..f695ea3b 100644 --- a/src/main/java/stanl_2/final_backend/domain/career/query/controller/CareerController.java +++ b/src/main/java/stanl_2/final_backend/domain/career/query/controller/CareerController.java @@ -12,7 +12,6 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.career.common.response.CareerResponseMessage; import stanl_2.final_backend.domain.career.query.dto.CareerDTO; import stanl_2.final_backend.domain.career.query.service.CareerQueryService; @@ -35,7 +34,7 @@ public CareerController(CareerQueryService careerQueryService) { @Operation(summary = "경력 조회(with 사번)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + content = {@Content(schema = @Schema(implementation = CareerResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) @@ -54,7 +53,7 @@ public ResponseEntity getCareerByOther(@PathVariable Stri @Operation(summary = "경력 조회(접속중인 사용자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + content = {@Content(schema = @Schema(implementation = CareerResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) diff --git a/src/main/java/stanl_2/final_backend/domain/certification/command/application/controller/CertificationController.java b/src/main/java/stanl_2/final_backend/domain/certification/command/application/controller/CertificationController.java index 2d2a8ce1..a6547067 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/command/application/controller/CertificationController.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/command/application/controller/CertificationController.java @@ -11,7 +11,6 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.career.common.response.CareerResponseMessage; import stanl_2.final_backend.domain.certification.command.application.dto.CertificationRegisterDTO; import stanl_2.final_backend.domain.certification.command.application.service.CertificationCommandService; @@ -36,7 +35,7 @@ public CertificationController(CertificationCommandService certificationCommandS @Operation(summary = "자격증/외국어 등록") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = CareerResponseMessage.class))}) }) @PostMapping("") public ResponseEntity postCertification(@RequestBody CertificationRegisterDTO certificationRegisterDTO, diff --git a/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java b/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java index d38c2b7b..70ffe9c5 100644 --- a/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java +++ b/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java @@ -11,7 +11,6 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.career.common.response.CareerResponseMessage; import stanl_2.final_backend.domain.certification.query.dto.CertificationDTO; import stanl_2.final_backend.domain.certification.query.service.CertificationQueryService; @@ -33,7 +32,7 @@ public CertificationController(CertificationQueryService certificationQueryServi @Operation(summary = "자격증/외국어 조회(with 사번)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + content = {@Content(schema = @Schema(implementation = CareerResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) @@ -52,7 +51,7 @@ public ResponseEntity getCertificationByOther(@PathVariab @Operation(summary = "자격증/외국어 조회(접속중인 사용자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + content = {@Content(schema = @Schema(implementation = CareerResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java b/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java index d8582dd4..e209189a 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/application/controller/CustomerController.java @@ -5,12 +5,10 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.customer.command.application.dto.CustomerModifyDTO; import stanl_2.final_backend.domain.customer.command.application.dto.CustomerRegistDTO; import stanl_2.final_backend.domain.customer.command.application.service.CustomerCommandService; @@ -38,7 +36,7 @@ public CustomerController(CustomerCommandService customerCommandService, @Operation(summary = "고객정보 등록") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = CustomerResponseMessage.class))}) }) @PostMapping("") public ResponseEntity postCustomer(@RequestBody CustomerRegistDTO customerRegistDTO, @@ -60,7 +58,7 @@ public ResponseEntity postCustomer(@RequestBody Custome @Operation(summary = "고객정보 수정(자기 고객만)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = CustomerResponseMessage.class))}) }) @PutMapping("/{customerId}") public ResponseEntity postCustomer(@PathVariable String customerId, @@ -84,7 +82,7 @@ public ResponseEntity postCustomer(@PathVariable String @Operation(summary = "고객정보 삭제(자기 고객만)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = CustomerResponseMessage.class))}) }) @DeleteMapping("/{customerId}") public ResponseEntity deleteCustomer(@PathVariable String customerId) { diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java b/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java index 15fd9880..6f0bae8c 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java @@ -12,7 +12,6 @@ import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.customer.common.response.CustomerResponseMessage; import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; import stanl_2.final_backend.domain.customer.query.dto.CustomerSearchDTO; @@ -35,7 +34,7 @@ public CustomerController(CustomerQueryService customerQueryService) { @Operation(summary = "고객정보 상세조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + content = {@Content(schema = @Schema(implementation = CustomerResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) @@ -55,7 +54,7 @@ public ResponseEntity getCustomerInfo(@PathVariable Str @Operation(summary = "고객번호로 전체 목록 조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + content = {@Content(schema = @Schema(implementation = CustomerResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) @@ -79,7 +78,7 @@ public ResponseEntity getCustomers( @Operation(summary = "고객정보 조건별 조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + content = {@Content(schema = @Schema(implementation = CustomerResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) diff --git a/src/main/java/stanl_2/final_backend/domain/education/query/controller/EducationController.java b/src/main/java/stanl_2/final_backend/domain/education/query/controller/EducationController.java index 5eb7672d..1f3fe70a 100644 --- a/src/main/java/stanl_2/final_backend/domain/education/query/controller/EducationController.java +++ b/src/main/java/stanl_2/final_backend/domain/education/query/controller/EducationController.java @@ -11,9 +11,6 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; -import stanl_2.final_backend.domain.career.common.response.CareerResponseMessage; -import stanl_2.final_backend.domain.certification.query.dto.CertificationDTO; import stanl_2.final_backend.domain.education.common.response.EducationResponseMessage; import stanl_2.final_backend.domain.education.query.dto.EducationDTO; import stanl_2.final_backend.domain.education.query.service.EducationQueryService; @@ -35,7 +32,7 @@ public EducationController(EducationQueryService educationQueryService) { @Operation(summary = "학력 조회(with 사번)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + content = {@Content(schema = @Schema(implementation = EducationResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) @@ -54,7 +51,7 @@ public ResponseEntity getEducation(@PathVariable Strin @Operation(summary = "학력 조회(접속중인 사용자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + content = {@Content(schema = @Schema(implementation = EducationResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) diff --git a/src/main/java/stanl_2/final_backend/domain/family/query/controller/FamilyController.java b/src/main/java/stanl_2/final_backend/domain/family/query/controller/FamilyController.java index 32464b61..57da897e 100644 --- a/src/main/java/stanl_2/final_backend/domain/family/query/controller/FamilyController.java +++ b/src/main/java/stanl_2/final_backend/domain/family/query/controller/FamilyController.java @@ -11,9 +11,6 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; -import stanl_2.final_backend.domain.education.common.response.EducationResponseMessage; -import stanl_2.final_backend.domain.education.query.dto.EducationDTO; import stanl_2.final_backend.domain.family.common.response.FamilyResponseMessage; import stanl_2.final_backend.domain.family.query.dto.FamilyDTO; import stanl_2.final_backend.domain.family.query.service.FamilyQueryService; @@ -35,7 +32,7 @@ public FamilyController(FamilyQueryService familyQueryService) { @Operation(summary = "가족 구성원 조회(with 사번)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + content = {@Content(schema = @Schema(implementation = FamilyResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) @@ -54,7 +51,7 @@ public ResponseEntity getEducation(@PathVariable String l @Operation(summary = "가족 구성원 조회(접속중인 사용자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + content = {@Content(schema = @Schema(implementation = FamilyResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) diff --git a/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java b/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java index a80f2afe..9e19bf82 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java +++ b/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java @@ -8,7 +8,6 @@ import org.hibernate.annotations.GenericGenerator; import stanl_2.final_backend.global.config.PrefixGeneratorConfig; -import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java b/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java index 0f06ad99..e1bee917 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java +++ b/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java @@ -12,7 +12,6 @@ import stanl_2.final_backend.domain.log.command.aggregate.Log; import stanl_2.final_backend.domain.log.command.repository.LogRepository; -import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; import java.util.UUID; diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java b/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java index 97a81300..b7acf5b7 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java @@ -10,7 +10,6 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.log.common.response.LogResponseMessage; import stanl_2.final_backend.domain.log.query.dto.LogDTO; import stanl_2.final_backend.domain.log.query.service.LogQueryService; @@ -31,7 +30,7 @@ public LogController(LogQueryService logQueryService) { @Operation(summary = "로그 조회(시스템 관리자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + content = {@Content(schema = @Schema(implementation = LogResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java index c33a4755..b3867f0b 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/MemberController.java @@ -1,5 +1,10 @@ package stanl_2.final_backend.domain.member.command.application.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; @@ -21,6 +26,12 @@ public MemberController(MemberCommandService memberCommandService) { this.memberCommandService = memberCommandService; } + + @Operation(summary = "본인정보(Principal) 출력 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = MemberResponseMessage.class))}) + }) @GetMapping("/authorities") public ResponseEntity check(Principal principal) { diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java b/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java index 06203c0c..e21d4432 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java @@ -10,7 +10,6 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.member.common.response.MemberResponseMessage; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; import stanl_2.final_backend.domain.member.query.service.MemberQueryService; @@ -32,7 +31,7 @@ public MemberController(MemberQueryService memberQueryService) { @Operation(summary = "회원 정보 조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + content = {@Content(schema = @Schema(implementation = MemberResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) From f0cec2fa5b4f553f81762c79c4b247998611804f Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Thu, 21 Nov 2024 19:27:56 +0900 Subject: [PATCH 280/563] =?UTF-8?q?fix:=20postconstructure=20member=20?= =?UTF-8?q?=EC=88=98=EC=A0=95(#128)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/dataloader/MemberInitializer.java | 256 ++++++++++-------- 1 file changed, 146 insertions(+), 110 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java b/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java index 2c2d7e7c..f17fedae 100644 --- a/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java +++ b/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.global.dataloader; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; @@ -7,129 +8,164 @@ import stanl_2.final_backend.domain.member.command.application.dto.GrantDTO; import stanl_2.final_backend.domain.member.command.application.dto.SignupDTO; import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; +import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.Member; +import stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; +@Slf4j @Component public class MemberInitializer implements ApplicationRunner { private final AuthCommandService authCommandService; + private final MemberRepository memberRepository; @Autowired - public MemberInitializer(AuthCommandService authCommandService) { + public MemberInitializer(AuthCommandService authCommandService, + MemberRepository memberRepository) { this.authCommandService = authCommandService; + this.memberRepository = memberRepository; } @Override public void run(ApplicationArguments args) throws Exception { - // 영업 관리자(회원가입) - SignupDTO employee = new SignupDTO(); - employee.setLoginId("employee"); - employee.setPassword("employee"); - employee.setName("영업 관리자"); - employee.setEmail("employee@stanl.com"); - employee.setAge(3); - employee.setSex("FEMALE"); - employee.setIdentNo("000000-0000003"); - employee.setPhone("010-0000-0003"); - employee.setAddress("신대방삼거리"); - employee.setPosition("영업 관리자"); - employee.setGrade("고졸"); - employee.setJobType("백엔드 개발자"); - employee.setMilitary("미필"); - employee.setBankName("한국은행"); - employee.setAccount("000-0000-000000-00003"); - employee.setCenterId("CEN_000000001"); - employee.setOrganizationId("ORG_000000001"); - authCommandService.signup(employee); - - // 영업 관리자(권한 부여) - GrantDTO grandEmployee = new GrantDTO(); - grandEmployee.setLoginId("employee"); - grandEmployee.setRole("EMPLOYEE"); - authCommandService.grantAuthority(grandEmployee); - - - // 영업 관리자(회원가입) - SignupDTO admin = new SignupDTO(); - admin.setLoginId("admin"); - admin.setPassword("admin"); - admin.setName("영업 관리자"); - admin.setEmail("admin@stanl.com"); - admin.setAge(2); - admin.setSex("FEMALE"); - admin.setIdentNo("000000-0000002"); - admin.setPhone("010-0000-0002"); - admin.setAddress("신대방삼거리"); - admin.setPosition("영업 관리자"); - admin.setGrade("고졸"); - admin.setJobType("백엔드 개발자"); - admin.setMilitary("미필"); - admin.setBankName("한국은행"); - admin.setAccount("000-0000-000000-00000"); - admin.setCenterId("CEN_000000001"); - admin.setOrganizationId("ORG_000000001"); - authCommandService.signup(admin); - - // 영업 관리자(권한 부여) - GrantDTO grandAdmin = new GrantDTO(); - grandAdmin.setLoginId("admin"); - grandAdmin.setRole("ADMIN"); - authCommandService.grantAuthority(grandAdmin); - - - // 영업 담당자(회원가입) - SignupDTO director = new SignupDTO(); - director.setLoginId("director"); - director.setPassword("director"); - director.setName("영업 담당자"); - director.setEmail("director@stanl.com"); - director.setAge(1); - director.setSex("MALE"); - director.setIdentNo("000000-0000001"); - director.setPhone("010-0000-0001"); - director.setAddress("STANL2"); - director.setPosition("영업담당자"); - director.setGrade("고졸"); - director.setJobType("영업 담당자"); - director.setMilitary("미필"); - director.setBankName("한국은행"); - director.setAccount("000-0000-000000-00000"); - director.setCenterId("CEN_000000001"); - director.setOrganizationId("ORG_000000001"); - authCommandService.signup(director); - - // 영업 담당자(권한 부여) - GrantDTO grandDirector = new GrantDTO(); - grandDirector.setLoginId("director"); - grandDirector.setRole("DIRECTOR"); - authCommandService.grantAuthority(grandDirector); - - - // 시스템 관리자(회원가입) - SignupDTO god = new SignupDTO(); - god.setLoginId("god"); - god.setPassword("god"); - god.setName("시스템관리자"); - god.setEmail("god@stanl.com"); - god.setAge(0); - god.setSex("MALE"); - god.setIdentNo("000000-0000000"); - god.setPhone("010-0000-0000"); - god.setAddress("신대방삼거리"); - god.setPosition("시스템관리자"); - god.setGrade("중졸"); - god.setJobType("백엔드 개발자"); - god.setMilitary("미필"); - god.setBankName("한국은행"); - god.setAccount("000-0000-000000-00000"); - god.setCenterId("CEN_000000001"); - god.setOrganizationId("ORG_000000001"); - authCommandService.signup(god); + createOrUpdateMember( + "employee", + "employee", + "영업 관리자", + "employee@stanl.com", + 3, + "FEMALE", + "000000-0000003", + "010-0000-0003", + "신대방삼거리", + "영업 관리자", + "고졸", + "백엔드 개발자", + "미필", + "한국은행", + "000-0000-000000-00003", + "CEN_000000001", + "ORG_000000001", + "EMPLOYEE" + ); + + createOrUpdateMember( + "admin", + "admin", + "영업 관리자", + "admin@stanl.com", + 2, + "FEMALE", + "000000-0000002", + "010-0000-0002", + "신대방삼거리", + "영업 관리자", + "고졸", + "백엔드 개발자", + "미필", + "한국은행", + "000-0000-000000-00000", + "CEN_000000001", + "ORG_000000001", + "ADMIN" + ); + + createOrUpdateMember( + "director", + "director", + "영업 담당자", + "director@stanl.com", + 1, + "MALE", + "000000-0000001", + "010-0000-0001", + "STANL2", + "영업담당자", + "고졸", + "영업 담당자", + "미필", + "한국은행", + "000-0000-000000-00000", + "CEN_000000001", + "ORG_000000001", + "DIRECTOR" + ); + + createOrUpdateMember( + "god", + "god", + "시스템관리자", + "god@stanl.com", + 0, + "MALE", + "000000-0000000", + "010-0000-0000", + "신대방삼거리", + "시스템관리자", + "중졸", + "백엔드 개발자", + "미필", + "한국은행", + "000-0000-000000-00000", + "CEN_000000001", + "ORG_000000001", + "GOD" + ); + } - // 시스템 관리자(권한 부여) - GrantDTO grandGOD = new GrantDTO(); - grandGOD.setLoginId("god"); - grandGOD.setRole("GOD"); - authCommandService.grantAuthority(grandGOD); + private void createOrUpdateMember(String loginId, + String password, + String name, + String email, + int age, + String sex, + String identNo, + String phone, + String address, + String position, + String grade, + String jobType, + String military, + String bankName, + String account, + String centerId, + String organizationId, + String role) throws Exception { + + // db에 정보가 있는지 확인 + Member existingMember = memberRepository.findByLoginId(loginId); + if (existingMember == null) { + // Create the user + SignupDTO signupDTO = new SignupDTO(); + signupDTO.setLoginId(loginId); + signupDTO.setPassword(password); + signupDTO.setName(name); + signupDTO.setEmail(email); + signupDTO.setAge(age); + signupDTO.setSex(sex); + signupDTO.setIdentNo(identNo); + signupDTO.setPhone(phone); + signupDTO.setAddress(address); + signupDTO.setPosition(position); + signupDTO.setGrade(grade); + signupDTO.setJobType(jobType); + signupDTO.setMilitary(military); + signupDTO.setBankName(bankName); + signupDTO.setAccount(account); + signupDTO.setCenterId(centerId); + signupDTO.setOrganizationId(organizationId); + + authCommandService.signup(signupDTO); + + // Grant role to the user + GrantDTO grantDTO = new GrantDTO(); + grantDTO.setLoginId(loginId); + grantDTO.setRole(role); + authCommandService.grantAuthority(grantDTO); + + log.info("{} 유저를 {} 역할로 생성합니다.", loginId, role); + } else { + log.info("{} 유저 정보가 이미 존재합니다.", loginId); + } } } From b6b7a5e7a91e490b8fb9638ef55e7f5023e5a353 Mon Sep 17 00:00:00 2001 From: woosuk1 Date: Thu, 21 Nov 2024 23:49:57 +0900 Subject: [PATCH 281/563] =?UTF-8?q?feat:=20=EC=98=81=EC=97=85=20=ED=86=B5?= =?UTF-8?q?=EA=B3=84=20read=20=EA=B5=AC=ED=98=84=EC=A4=91(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/CenterController.java | 45 +++- ...erService.java => CenterQueryService.java} | 3 +- ...eImpl.java => CenterQueryServiceImpl.java} | 18 +- .../query/controller/MemberController.java | 44 +++- .../member/query/dto/MemberCenterListDTO.java | 16 ++ .../member/query/repository/MemberMapper.java | 2 +- .../query/service/MemberQueryService.java | 2 +- .../query/service/MemberQueryServiceImpl.java | 2 +- .../query/controller/ProductController.java | 18 +- .../controller/SalesHistoryController.java | 92 +++++++- .../query/dto/SalesHistoryRankedDataDTO.java | 3 + .../query/repository/SalesHistoryMapper.java | 28 ++- .../service/SalesHistoryQueryService.java | 8 + .../service/SalesHistoryQueryServiceImpl.java | 70 +++++- src/main/resources/application-dev.yml | 2 + src/main/resources/application-prod.yml | 2 +- .../member/query/repository/MemberMapper.xml | 6 +- .../query/repository/SalesHistoryMapper.xml | 205 +++++++++++++++++- 18 files changed, 510 insertions(+), 56 deletions(-) rename src/main/java/stanl_2/final_backend/domain/center/query/service/{CenterService.java => CenterQueryService.java} (93%) rename src/main/java/stanl_2/final_backend/domain/center/query/service/{CenterServiceImpl.java => CenterQueryServiceImpl.java} (72%) create mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberCenterListDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java b/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java index 856459f5..a7873f0c 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java @@ -15,19 +15,20 @@ import stanl_2.final_backend.domain.center.query.dto.CenterSearchRequestDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectIdDTO; -import stanl_2.final_backend.domain.center.query.service.CenterService; +import stanl_2.final_backend.domain.center.query.service.CenterQueryService; +import java.util.List; import java.util.Map; @RestController("queryCenterController") @RequestMapping("/api/v1/center") public class CenterController { - private final CenterService centerService; + private final CenterQueryService centerQueryService; @Autowired - public CenterController(CenterService centerService) { - this.centerService = centerService; + public CenterController(CenterQueryService centerQueryService) { + this.centerQueryService = centerQueryService; } @Operation(summary = "영업매장 조회") @@ -40,7 +41,7 @@ public CenterController(CenterService centerService) { @GetMapping("") public ResponseEntity getCenterAll(@PageableDefault(size = 20) Pageable pageable){ - Page responseCenters = centerService.selectAll(pageable); + Page responseCenters = centerQueryService.selectAll(pageable); return ResponseEntity.ok(CenterResponseMessage.builder() .httpStatus(200) @@ -56,10 +57,10 @@ public ResponseEntity getCenterAll(@PageableDefault(size @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("{id}") - public ResponseEntity getCenterById(@PathVariable("id") String id){ + @GetMapping("{centerId}") + public ResponseEntity getCenterById(@PathVariable("centerId") String centerId){ - CenterSelectIdDTO centerSelectIdDTO = centerService.selectByCenterId(id); + CenterSelectIdDTO centerSelectIdDTO = centerQueryService.selectByCenterId(centerId); return ResponseEntity.ok(CenterResponseMessage.builder() .httpStatus(200) @@ -84,11 +85,35 @@ public ResponseEntity getCenterBySearch(@RequestParam Map centerSearchRequestDTO.setName(params.get("name")); centerSearchRequestDTO.setAddress(params.get("address")); - Page responseCenters = centerService.selectBySearch(centerSearchRequestDTO, pageable); + Page responseCenters = centerQueryService.selectBySearch(centerSearchRequestDTO, pageable); return ResponseEntity.ok(CenterResponseMessage.builder() .httpStatus(200) - .msg("검색 조회 성공") + .msg("영업매장 검색 성공") + .result(responseCenters) + .build()); + } + + @Operation(summary = "영업매장리스트 검색(통계용)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = CenterResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/searchList") + public ResponseEntity getCenterListBySearch(@RequestParam Map params){ + + CenterSearchRequestDTO centerSearchRequestDTO = new CenterSearchRequestDTO(); + centerSearchRequestDTO.setId(params.get("id")); + centerSearchRequestDTO.setName(params.get("name")); + centerSearchRequestDTO.setAddress(params.get("address")); + + List responseCenters = centerQueryService.selectCenterListBySearch(centerSearchRequestDTO); + + return ResponseEntity.ok(CenterResponseMessage.builder() + .httpStatus(200) + .msg("영업매장리스트 검색(통계용) 성공") .result(responseCenters) .build()); } diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterService.java b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryService.java similarity index 93% rename from src/main/java/stanl_2/final_backend/domain/center/query/service/CenterService.java rename to src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryService.java index f5a230f1..aba736e9 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterService.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryService.java @@ -8,10 +8,9 @@ import stanl_2.final_backend.domain.center.query.dto.CenterSelectIdDTO; import java.util.List; -import java.util.Map; @Service -public interface CenterService { +public interface CenterQueryService { CenterSelectIdDTO selectByCenterId(String id); Page selectAll(Pageable pageable); diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryServiceImpl.java similarity index 72% rename from src/main/java/stanl_2/final_backend/domain/center/query/service/CenterServiceImpl.java rename to src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryServiceImpl.java index d60fd2bc..9daccff3 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryServiceImpl.java @@ -8,27 +8,22 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.A_sample.common.exception.SampleCommonException; -import stanl_2.final_backend.domain.A_sample.common.exception.SampleErrorCode; -import stanl_2.final_backend.domain.center.common.util.RequestList; import stanl_2.final_backend.domain.center.query.dto.CenterSearchRequestDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectIdDTO; import stanl_2.final_backend.domain.center.query.repository.CenterMapper; -import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; import java.util.List; -import java.util.Map; @Slf4j @Service("queryCenterServiceImpl") -public class CenterServiceImpl implements CenterService{ +public class CenterQueryServiceImpl implements CenterQueryService { private final CenterMapper centerMapper; private final RedisTemplate redisTemplate; @Autowired - public CenterServiceImpl(CenterMapper centerMapper, RedisTemplate redisTemplate) { + public CenterQueryServiceImpl(CenterMapper centerMapper, RedisTemplate redisTemplate) { this.centerMapper = centerMapper; this.redisTemplate = redisTemplate; } @@ -61,15 +56,6 @@ public Page selectBySearch(CenterSearchRequestDTO centerSear int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); -// String cacheKey = "myCache::center::offset=" + offset + "::size=" + size; -// -// List centerList = (List) redisTemplate.opsForValue().get(cacheKey); -// if (centerList == null) { -// System.out.println("여기까진 들어가나?"); -// centerList = centerMapper.findCenterBySearch(size, offset, centerSearchRequestDTO); -// redisTemplate.opsForValue().set(cacheKey, centerList); -// } - List centerList = centerMapper.findCenterBySearch(size, offset, centerSearchRequestDTO); int total = centerMapper.findCenterBySearchCount(centerSearchRequestDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java b/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java index 06203c0c..007b8091 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java @@ -7,16 +7,16 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.member.common.response.MemberResponseMessage; +import stanl_2.final_backend.domain.member.query.dto.MemberCenterListDTO; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import java.security.GeneralSecurityException; import java.security.Principal; +import java.util.List; @RestController(value = "queryMemberController") @RequestMapping("/api/v1/member") @@ -48,6 +48,44 @@ public ResponseEntity getMemberInfo(Principal principal) .build()); } + @Operation(summary = "회원 정보 조건(매장) 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("{centerId}") + public ResponseEntity getMemberByCenterId(@PathVariable("centerId") String centerId) throws GeneralSecurityException { + + List memberList = memberQueryService.selectMemberByCenterId(centerId); + + return ResponseEntity.ok(MemberResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(memberList) + .build()); + } + + @Operation(summary = "회원 정보 조건(매장리스트) 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("centerList") + public ResponseEntity getMemberByCenterList(@RequestBody MemberCenterListDTO memberCenterListDTO) throws GeneralSecurityException { + + List memberList = memberQueryService.selectMemberByCenterList(memberCenterListDTO.getCenterList()); + + return ResponseEntity.ok(MemberResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(memberList) + .build()); + } + diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberCenterListDTO.java b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberCenterListDTO.java new file mode 100644 index 00000000..809388f4 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberCenterListDTO.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.member.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class MemberCenterListDTO { + private List centerList; +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java b/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java index 8dfe4349..dd4811fe 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java @@ -15,5 +15,5 @@ public interface MemberMapper { List findMembersByCenterId(@Param("centerId") String centerId); - List findMembersByCenterList(@Param("centerList") List centerList); + List findMembersByCenterList(@Param("centerList") List centerList); } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java index ec1bd996..1bac53a0 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java @@ -14,7 +14,7 @@ public interface MemberQueryService { List selectMemberByCenterId(String centerId); - List selectMemberByCenterList(List centerList); + List selectMemberByCenterList(List centerList); String selectNameById(String memberId) throws GeneralSecurityException; // MemberDetailDTO selectMemberDetail(String name) throws GeneralSecurityException; diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index a2adee7e..47e77393 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -74,7 +74,7 @@ public List selectMemberByCenterId(String centerId){ @Override @Transactional(readOnly = true) - public List selectMemberByCenterList(List centerList) { + public List selectMemberByCenterList(List centerList) { List memberList = memberMapper.findMembersByCenterList(centerList); return memberList; diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java b/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java index 8b72b463..4b954f3b 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java @@ -34,7 +34,9 @@ public ProductController(ProductQueryService productQueryService) { @Operation(summary = "제품 조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = ProductResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = ProductResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) }) @GetMapping("") public ResponseEntity getProductAll(@PageableDefault(size = 20) Pageable pageable){ @@ -43,14 +45,16 @@ public ResponseEntity getProductAll(@PageableDefault(siz return ResponseEntity.ok(ProductResponseMessage.builder() .httpStatus(200) - .msg("조회 성공") + .msg("제품 조회 성공") .result(responseProducts) .build()); } @Operation(summary = "제품 상세조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = ProductResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = ProductResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) }) @GetMapping("{id}") public ResponseEntity getProductById(@PathVariable("id") String id){ @@ -59,14 +63,16 @@ public ResponseEntity getProductById(@PathVariable("id") return ResponseEntity.ok(ProductResponseMessage.builder() .httpStatus(200) - .msg("상세 조회 성공") + .msg("제품 상세조회 성공") .result(productSelectIdDTO) .build()); } @Operation(summary = "제품 검색") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = ProductResponseMessage.class))}) + content = {@Content(schema = @Schema(implementation = ProductResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) }) @GetMapping("/search") public ResponseEntity getProductBySearch(@RequestParam Map params @@ -85,7 +91,7 @@ public ResponseEntity getProductBySearch(@RequestParam M return ResponseEntity.ok(ProductResponseMessage.builder() .httpStatus(200) - .msg("검색 조회 성공") + .msg("제품 검색 성공") .result(responseProducts) .build()); } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java index c83b2032..78d711d6 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java @@ -53,6 +53,11 @@ public SalesHistoryController(SalesHistoryQueryService salesHistoryQueryService) 4. 분류에 따라서 뿌려줌 * */ + + /* 설명. todo + * 1. 각 실적, 수당, 매출액 별로 인자를 통해 동적 쿼리로 처리 가능한지 여부(내림차순) + * 2. 판매내역 날짜별로 + * */ @Operation(summary = "사원 판매내역 조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", @@ -251,7 +256,7 @@ public ResponseEntity getStatistics(@RequestParam M .build()); } - @Operation(summary = "사원 별 통계(실적,수당,매출액) 검색") + @Operation(summary = "사원 별 통계(실적,수당,매출액) 조회기간별 검색") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), @@ -271,7 +276,47 @@ public ResponseEntity getStatisticsBySearch(@Reques .build()); } - @Operation(summary = "매장 별 통계(실적,수당,매출액) 검색") + @Operation(summary = "사원 별 통계(실적,수당,매출액) 월별 검색") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("statistics/search/month") + public ResponseEntity getStatisticsBySearchMonth(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, + @PageableDefault(size = 20) Pageable pageable){ + + Page responseSalesHistory = salesHistoryQueryService.selectStatisticsBySearchMonth(salesHistoryRankedDataDTO,pageable); + + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("사원 별 통계(실적,수당,매출액) 월별 검색 성공") + .result(responseSalesHistory) + .build()); + } + + @Operation(summary = "사원 별 통계(실적,수당,매출액) 연도별 검색") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("statistics/search/year") + public ResponseEntity getStatisticsBySearchYear(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, + @PageableDefault(size = 20) Pageable pageable){ + + Page responseSalesHistory = salesHistoryQueryService.selectStatisticsBySearchYear(salesHistoryRankedDataDTO,pageable); + + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("사원 별 통계(실적,수당,매출액) 연도별 검색 성공") + .result(responseSalesHistory) + .build()); + } + + @Operation(summary = "매장 별 통계(실적,수당,매출액) 조회기간별 검색") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), @@ -282,9 +327,6 @@ public ResponseEntity getStatisticsBySearch(@Reques public ResponseEntity getStatisticsCenterBySearch(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, @PageableDefault(size = 20) Pageable pageable){ - System.out.println(salesHistoryRankedDataDTO.toString()); - System.out.println(salesHistoryRankedDataDTO.getCenterList().toString()); - Page responseSalesHistory = salesHistoryQueryService.selectStatisticsCenterBySearch(salesHistoryRankedDataDTO,pageable); return ResponseEntity.ok(SalesHistoryResponseMessage.builder() @@ -294,5 +336,45 @@ public ResponseEntity getStatisticsCenterBySearch(@ .build()); } +@Operation(summary = "매장 별 통계(실적,수당,매출액) 월별 검색") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("statistics/center/search/month") + public ResponseEntity getStatisticsCenterBySearchMonth(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, + @PageableDefault(size = 20) Pageable pageable){ + + Page responseSalesHistory = salesHistoryQueryService.selectStatisticsCenterBySearchMonth(salesHistoryRankedDataDTO,pageable); + + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("매장 별 통계(실적,수당,매출액) 월별 검색 성공") + .result(responseSalesHistory) + .build()); + } + +@Operation(summary = "매장 별 통계(실적,수당,매출액) 연도별 검색") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("statistics/center/search/year") + public ResponseEntity getStatisticsCenterBySearchYear(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, + @PageableDefault(size = 20) Pageable pageable){ + + Page responseSalesHistory = salesHistoryQueryService.selectStatisticsCenterBySearchYear(salesHistoryRankedDataDTO,pageable); + + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("매장 별 통계(실적,수당,매출액) 연도별 검색 성공") + .result(responseSalesHistory) + .build()); + } + } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryRankedDataDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryRankedDataDTO.java index 000e59b8..ef03f23d 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryRankedDataDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryRankedDataDTO.java @@ -17,10 +17,13 @@ public class SalesHistoryRankedDataDTO { private BigDecimal totalIncentive; private BigDecimal totalPerformance; private BigDecimal totalSales; + private String centerId; private List memberList; private List centerList; private String startDate; private String endDate; private String createdAt; + private String month; + private String year; } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java index e79324ca..d7d6a09c 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java @@ -39,16 +39,40 @@ List findAllRank(@Param("size") int size int findRankCount(); List findStatisticsBySearch(@Param("size") int size - , @Param("offset") int offset, + , @Param("offset") int offset, @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); int findStatisticsBySearchCount(@Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); List findStatisticsCenterBySearch(@Param("size") int size - , @Param("offset") int offset, + , @Param("offset") int offset, @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); int findStatisticsCenterBySearchCount(@Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); + + List findStatisticsBySearchMonth(@Param("size") int size + , @Param("offset") int offset, + @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); + + int findStatisticsBySearchCountMonth(@Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); + + List findStatisticsBySearchYear(@Param("size") int size + , @Param("offset") int offset, + @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); + + int findStatisticsBySearchCountYear(@Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); + + List findStatisticsCenterBySearchMonth(@Param("size") int size + , @Param("offset") int offset, + @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); + + int findStatisticsCenterBySearchCountMonth(@Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); + + List findStatisticsCenterBySearchYear(@Param("size") int size + , @Param("offset") int offset, + @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); + + int findStatisticsCenterBySearchCountYear(@Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java index f982f3db..3e26975f 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java @@ -30,4 +30,12 @@ public interface SalesHistoryQueryService { Page selectStatisticsBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); Page selectStatisticsCenterBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); + + Page selectStatisticsBySearchMonth(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); + + Page selectStatisticsBySearchYear(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); + + Page selectStatisticsCenterBySearchMonth(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); + + Page selectStatisticsCenterBySearchYear(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java index 2e152774..e3c4b77d 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java @@ -172,23 +172,85 @@ public Page selectStatisticsBySearch(SalesHistoryRank } @Override - public Page selectStatisticsCenterBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { + @Transactional(readOnly = true) + public Page selectStatisticsBySearchMonth(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); + + List salesHistoryList = salesHistoryMapper.findStatisticsBySearchMonth(size,offset, salesHistoryRankedDataDTO); + + int total = salesHistoryMapper.findStatisticsBySearchCountMonth(salesHistoryRankedDataDTO); + + if(salesHistoryList.isEmpty() || total == 0){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } + + return new PageImpl<>(salesHistoryList, pageable, total); + } + + @Override + @Transactional(readOnly = true) + public Page selectStatisticsBySearchYear(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); - System.out.println("service check1"); + List salesHistoryList = salesHistoryMapper.findStatisticsBySearchYear(size,offset, salesHistoryRankedDataDTO); + + int total = salesHistoryMapper.findStatisticsBySearchCountYear(salesHistoryRankedDataDTO); + + if(salesHistoryList.isEmpty() || total == 0){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } + + return new PageImpl<>(salesHistoryList, pageable, total); + } + + @Override + @Transactional(readOnly = true) + public Page selectStatisticsCenterBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); List salesHistoryList = salesHistoryMapper.findStatisticsCenterBySearch(size,offset, salesHistoryRankedDataDTO); - System.out.println("service check2"); int total = salesHistoryMapper.findStatisticsCenterBySearchCount(salesHistoryRankedDataDTO); - System.out.println("service check3"); if(salesHistoryList.isEmpty() || total == 0){ throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); } return new PageImpl<>(salesHistoryList, pageable, total); + } + + @Override + public Page selectStatisticsCenterBySearchMonth(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); + List salesHistoryList = salesHistoryMapper.findStatisticsCenterBySearchMonth(size,offset, salesHistoryRankedDataDTO); + + int total = salesHistoryMapper.findStatisticsCenterBySearchCountMonth(salesHistoryRankedDataDTO); + + if(salesHistoryList.isEmpty() || total == 0){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } + + return new PageImpl<>(salesHistoryList, pageable, total); + } + + @Override + public Page selectStatisticsCenterBySearchYear(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); + + List salesHistoryList = salesHistoryMapper.findStatisticsCenterBySearchYear(size,offset, salesHistoryRankedDataDTO); + + int total = salesHistoryMapper.findStatisticsCenterBySearchCountYear(salesHistoryRankedDataDTO); + + if(salesHistoryList.isEmpty() || total == 0){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } + + return new PageImpl<>(salesHistoryList, pageable, total); } } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index aabe59aa..25f34fa9 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -6,3 +6,5 @@ spring: logging: level: org.springframework.security: TRACE + org.mybatis: DEBUG + java.sql: DEBUG \ No newline at end of file diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 083491db..28868e7b 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -5,4 +5,4 @@ spring: logging: level: - org.springframework.security: WARN \ No newline at end of file + org.springframework.security: WARN diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml index 4c710ed3..63e4b5f4 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml @@ -49,15 +49,15 @@ @@ -229,7 +242,102 @@ GROUP BY a.mem_id) AS A - + SELECT + a.mem_id, + SUM(a.sal_hist_ince) AS total_incentive, + SUM(a.sal_hist_no_of_veh) AS total_performance, + SUM(a.sal_hist_tota_sale) AS total_sales, + LEFT(a.created_at, 7) AS MONTH + FROM tb_sales_history a + + + a.mem_id IN + + #{mem_id} + + + + AND a.created_at BETWEEN #{salesHistoryRankedDataDTO.startDate} AND #{salesHistoryRankedDataDTO.endDate} + + AND a.active = TRUE + + GROUP BY a.mem_id, LEFT(a.created_at, 7) + ORDER BY MONTH ASC + LIMIT #{size} OFFSET #{offset} + + + + + + + + + + + + + + + + + + + From acda41e53976d27f73b2da4d63c2bd3fb93611c3 Mon Sep 17 00:00:00 2001 From: woosuk1 Date: Fri, 22 Nov 2024 00:15:04 +0900 Subject: [PATCH 282/563] =?UTF-8?q?feat:=20=EB=8F=99=EC=A0=81=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=EB=A1=9C=20=EC=88=98=EB=8B=B9,=20=EC=8B=A4=EC=A0=81,?= =?UTF-8?q?=20=EB=A7=A4=EC=B6=9C=EC=95=A1=20=EA=B8=B0=EC=A4=80=20=EC=A0=95?= =?UTF-8?q?=EB=A0=AC=20=EA=B5=AC=ED=98=84=20->=20=EB=8F=8C=EB=A0=A4?= =?UTF-8?q?=EB=B4=90=EC=95=BC=ED=95=A8(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SalesHistoryController.java | 10 +- .../query/dto/SalesHistoryRankedDataDTO.java | 1 + .../query/dto/SalesHistorySearchDTO.java | 1 + .../query/dto/SalesHistorySelectDTO.java | 1 + .../query/dto/SalesHistoryStatisticsDTO.java | 1 + .../query/repository/SalesHistoryMapper.java | 3 +- .../service/SalesHistoryQueryService.java | 5 +- .../service/SalesHistoryQueryServiceImpl.java | 5 +- .../query/repository/SalesHistoryMapper.xml | 135 ++++++++++++++++-- 9 files changed, 138 insertions(+), 24 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java index 78d711d6..9440b3d1 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java @@ -55,7 +55,7 @@ public SalesHistoryController(SalesHistoryQueryService salesHistoryQueryService) /* 설명. todo - * 1. 각 실적, 수당, 매출액 별로 인자를 통해 동적 쿼리로 처리 가능한지 여부(내림차순) + * 1. (사원별, 매장별 순위) 각 실적, 수당, 매출액 별로 인자를 통해 동적 쿼리로 처리 가능한지 여부(내림차순) * 2. 판매내역 날짜별로 * */ @Operation(summary = "사원 판매내역 조회") @@ -239,15 +239,11 @@ public ResponseEntity getStatisticsSearchYearByEmpl content = @Content(mediaType = "application/json")) }) @GetMapping("statistics") - public ResponseEntity getStatistics(@RequestParam Map params, + public ResponseEntity getStatistics(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, @PageableDefault(size = 20) Pageable pageable){ -// SalesHistoryRankedDataDTO salesHistoryRankedDataDTO = new SalesHistoryRankedDataDTO(); -// -// salesHistoryRankedDataDTO.setStartDate(params.get("startDate")); -// salesHistoryRankedDataDTO.setEndDate(params.get("endDate")); - Page responseSalesHistory = salesHistoryQueryService.selectStatistics(pageable); + Page responseSalesHistory = salesHistoryQueryService.selectStatistics(salesHistoryRankedDataDTO, pageable); return ResponseEntity.ok(SalesHistoryResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryRankedDataDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryRankedDataDTO.java index ef03f23d..0cab5c35 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryRankedDataDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryRankedDataDTO.java @@ -26,4 +26,5 @@ public class SalesHistoryRankedDataDTO { private String createdAt; private String month; private String year; + private String orderBy; } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java index 38511d84..27596988 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java @@ -13,4 +13,5 @@ public class SalesHistorySearchDTO { private String searcherName; private String startDate; private String endDate; + private String orderBy; } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectDTO.java index e0ee7cd7..c842065d 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySelectDTO.java @@ -26,5 +26,6 @@ public class SalesHistorySelectDTO { private String memberId; private String centerId; private String searcherName; + private String orderBy; } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsDTO.java index 6debab66..3fad0218 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsDTO.java @@ -10,4 +10,5 @@ public class SalesHistoryStatisticsDTO { private int incentive; private int performance; private int totalSales; + private String orderBy; } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java index d7d6a09c..870dfecd 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java @@ -33,7 +33,8 @@ List findAllSalesHistory(@Param("size") int size SalesHistoryStatisticsDTO findStatisticsSearchYearByEmployee(@Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); - List findAllRank(@Param("size") int size + List findAllRank(@Param("salesHistoryRankedDataDTO")SalesHistoryRankedDataDTO salesHistoryRankedDataDTO + , @Param("size") int size , @Param("offset") int offset); int findRankCount(); diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java index 3e26975f..5e9f4e9e 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java @@ -7,9 +7,6 @@ import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectDTO; import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistoryStatisticsDTO; -import java.security.GeneralSecurityException; -import java.util.List; - public interface SalesHistoryQueryService { Page selectAllSalesHistoryByEmployee(SalesHistorySelectDTO salesHistorySelectDTO, Pageable pageable); @@ -25,7 +22,7 @@ public interface SalesHistoryQueryService { SalesHistoryStatisticsDTO selectStatisticsSearchYearByEmployee(SalesHistorySearchDTO salesHistorySearchDTO); - Page selectStatistics(Pageable pageable); + Page selectStatistics(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); Page selectStatisticsBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java index e3c4b77d..efe77079 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java @@ -1,7 +1,6 @@ package stanl_2.final_backend.domain.sales_history.query.service; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; @@ -139,11 +138,11 @@ public SalesHistoryStatisticsDTO selectStatisticsSearchYearByEmployee(SalesHisto @Override @Transactional(readOnly = true) - public Page selectStatistics(Pageable pageable) { + public Page selectStatistics(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); - List salesHistoryList = salesHistoryMapper.findAllRank(size,offset); + List salesHistoryList = salesHistoryMapper.findAllRank(salesHistoryRankedDataDTO, size,offset); int total = salesHistoryMapper.findRankCount(); diff --git a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml index 456a4803..f187598b 100644 --- a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml @@ -148,7 +148,20 @@ GROUP BY LEFT(a.created_at, 7) - ORDER BY MONTH ASC + + + ORDER BY INCENTIVE DESC + + + ORDER BY PERFORMANCE DESC + + + ORDER BY TOTAL_SALES DESC + + + ORDER BY MONTH ASC + + @@ -179,10 +205,21 @@ created_at FROM tb_sales_history WHERE active = TRUE - - - GROUP BY mem_id + + + ORDER BY total_incentive DESC + + + ORDER BY total_performance DESC + + + ORDER BY total_sales DESC + + + ORDER BY created_at desc + + LIMIT #{size} OFFSET #{offset} @@ -217,6 +254,20 @@ AND a.active = TRUE GROUP BY a.mem_id + + + ORDER BY total_incentive DESC + + + ORDER BY total_performance DESC + + + ORDER BY total_sales DESC + + + ORDER BY a.created_at desc + + LIMIT #{size} OFFSET #{offset} @@ -263,7 +314,20 @@ AND a.active = TRUE GROUP BY a.mem_id, LEFT(a.created_at, 7) - ORDER BY MONTH ASC + + + ORDER BY total_incentive DESC + + + ORDER BY total_performance DESC + + + ORDER BY total_sales DESC + + + ORDER BY MONTH ASC + + LIMIT #{size} OFFSET #{offset} @@ -310,7 +374,20 @@ AND a.active = TRUE GROUP BY a.mem_id, LEFT(a.created_at, 4) - ORDER BY YEAR ASC + + + ORDER BY total_incentive DESC + + + ORDER BY total_performance DESC + + + ORDER BY total_sales DESC + + + ORDER BY YEAR ASC + + LIMIT #{size} OFFSET #{offset} @@ -357,6 +434,20 @@ AND a.active = TRUE GROUP BY a.cent_id + + + ORDER BY total_incentive DESC + + + ORDER BY total_performance DESC + + + ORDER BY total_sales DESC + + + ORDER BY a.created_at DESC + + LIMIT #{size} OFFSET #{offset} @@ -403,7 +494,20 @@ AND a.active = TRUE GROUP BY a.cent_id, LEFT(a.created_at, 7) - ORDER BY MONTH ASC + + + ORDER BY total_incentive DESC + + + ORDER BY total_performance DESC + + + ORDER BY total_sales DESC + + + ORDER BY MONTH ASC + + LIMIT #{size} OFFSET #{offset} @@ -450,7 +554,20 @@ AND a.active = TRUE GROUP BY a.cent_id, LEFT(a.created_at, 4) - ORDER BY YEAR ASC + + + ORDER BY total_incentive DESC + + + ORDER BY total_performance DESC + + + ORDER BY total_sales DESC + + + ORDER BY YEAR ASC + + LIMIT #{size} OFFSET #{offset} From 1e944fa9ac7c172e820ad1bd855a48b0eb28b95d Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Fri, 22 Nov 2024 04:24:43 +0900 Subject: [PATCH 283/563] =?UTF-8?q?feat:=20s3=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B3=84=EC=95=BD=EC=84=9C=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EB=82=B4=EC=97=AD=20=EA=B5=AC=ED=98=84=20(#121)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ContractCommandServiceImpl.java | 74 ++++++++----------- .../application/dto/CustomerModifyDTO.java | 1 - .../service/CustomerQueryServiceImpl.java | 6 -- 3 files changed, 30 insertions(+), 51 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index 064bdb68..0484f0a0 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -1,6 +1,7 @@ package stanl_2.final_backend.domain.contract.command.domain.service; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringEscapeUtils; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -18,7 +19,6 @@ import stanl_2.final_backend.domain.customer.command.application.service.CustomerCommandService; import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; import stanl_2.final_backend.domain.customer.query.service.CustomerQueryService; -import stanl_2.final_backend.domain.member.query.dto.MemberDTO; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import stanl_2.final_backend.domain.product.command.application.command.service.ProductCommandService; @@ -84,101 +84,87 @@ private String handleCustomerInfo(ContractRegistDTO contractRegistRequestDTO, St customerRegistDTO.setEmail(contractRegistRequestDTO.getCustomerEmail()); customerRegistDTO.setMemberId(memberId); + // 고객 등록 customerCommandService.registerCustomerInfo(customerRegistDTO); - // 등록 후 고객 정보 재조회 + // 등록 후 재조회 customerDTO = customerQueryService.selectCustomerInfoByPhone(contractRegistRequestDTO.getCustomerPhone()); } + + // 고객 ID 반환 return customerDTO.getCustomerId(); } @Override @Transactional public void registerContract(ContractRegistDTO contractRegistRequestDTO) throws GeneralSecurityException { - - // 영업사원 번호 String memberId = authQueryService.selectMemberIdByLoginId(contractRegistRequestDTO.getMemberId()); + String productId = productQueryService.selectByProductSerialNumber(contractRegistRequestDTO.getSerialNum()).getId(); + String customerId = handleCustomerInfo(contractRegistRequestDTO, memberId); + String centerId = memberQueryService.selectMemberInfo(contractRegistRequestDTO.getMemberId()).getCenterId(); - // 일련번호로 제품테이블의 총식별번호 찾아서 제품 가져오기 - ProductSelectIdDTO productSelectIdDTO = productQueryService.selectByProductSerialNumber(contractRegistRequestDTO.getSerialNum()); - String productId = productSelectIdDTO.getId(); - - String customerId = null; // 고객 ID 변수 선언 - String centerId = null; // 매장 ID 변수 선언 - - // 고객전화번호로 고객테이블 찾아서 고객이 있으면 넘어가고, 고객이 없으면 고객테이블에 고객 정보 넣기 - customerId = handleCustomerInfo(contractRegistRequestDTO, memberId); - - // 회원의 영업 매장번호 - MemberDTO memberDTO = memberQueryService.selectMemberInfo(contractRegistRequestDTO.getMemberId()); - centerId = memberDTO.getCenterId(); - - Contract contract = modelMapper.map(contractRegistRequestDTO, Contract.class); - + // 계약 생성 + Contract contract = new Contract(); contract.setMemberId(memberId); - contract.setCenterId(centerId); // 회원의 매장번호 넣기 - contract.setProductId(productId); // 제품 번호 넣기 + contract.setCenterId(centerId); + contract.setProductId(productId); contract.setCustomerId(customerId); contract.setStatus("WAIT"); + // 고객 정보 암호화 후 설정 contract.setCustomerPhone(aesUtils.encrypt(contractRegistRequestDTO.getCustomerPhone())); contract.setCustomerEmail(aesUtils.encrypt(contractRegistRequestDTO.getCustomerEmail())); contract.setCustomerAddress(aesUtils.encrypt(contractRegistRequestDTO.getCustomerAddress())); contract.setCustomerIdentifiNo(aesUtils.encrypt(contractRegistRequestDTO.getCustomerIdentifiNo())); + // 계약 저장 contractRepository.save(contract); } @Override @Transactional public void modifyContract(ContractModifyDTO contractModifyRequestDTO) throws GeneralSecurityException { - String memberId = authQueryService.selectMemberIdByLoginId(contractModifyRequestDTO.getMemberId()); + // 계약 조회 + Contract contract = (Contract)contractRepository.findByContractIdAndMemberId(contractModifyRequestDTO.getContractId(), memberId) + .orElseThrow(() -> new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND)); - // 가져온 고객 정보에 수정된 값 넣기 + // 고객 정보 수정 CustomerModifyDTO customerModifyDTO = new CustomerModifyDTO(); + customerModifyDTO.setCustomerId(contractModifyRequestDTO.getCustomerId()); customerModifyDTO.setName(contractModifyRequestDTO.getCustomerName()); customerModifyDTO.setAge(contractModifyRequestDTO.getCustomerAge()); customerModifyDTO.setSex(contractModifyRequestDTO.getCustomerSex()); customerModifyDTO.setPhone(contractModifyRequestDTO.getCustomerPhone()); customerModifyDTO.setEmail(contractModifyRequestDTO.getCustomerEmail()); - customerModifyDTO.setCustomerId(contractModifyRequestDTO.getCustomerId()); customerModifyDTO.setMemberId(memberId); + // 고객 정보 업데이트 customerCommandService.modifyCustomerInfo(customerModifyDTO); - Contract contract = (Contract) contractRepository.findByContractIdAndMemberId(contractModifyRequestDTO.getContractId(), memberId) - .orElseThrow(() -> new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND)); - - // 계약서 업데이트 - Contract updateContract = modelMapper.map(contractModifyRequestDTO, Contract.class); - updateContract.setCreatedAt(contract.getCreatedAt()); - updateContract.setUpdatedAt(contract.getUpdatedAt()); - updateContract.setActive(contract.isActive()); - updateContract.setCenterId(contract.getCenterId()); - updateContract.setCreatedUrl(contract.getCreatedUrl()); - updateContract.setCustomerSex(contract.getCustomerSex()); - updateContract.setStatus("WAIT"); - + // 고객 정보가 수정된 경우 계약서의 고객 정보도 업데이트 contract.setCustomerPhone(aesUtils.encrypt(contractModifyRequestDTO.getCustomerPhone())); contract.setCustomerEmail(aesUtils.encrypt(contractModifyRequestDTO.getCustomerEmail())); contract.setCustomerAddress(aesUtils.encrypt(contractModifyRequestDTO.getCustomerAddress())); contract.setCustomerIdentifiNo(aesUtils.encrypt(contractModifyRequestDTO.getCustomerIdentifiNo())); - contractRepository.save(updateContract); + // 계약 상태 업데이트 + contract.setStatus("WAIT"); + + // 수정된 계약 정보 저장 + contractRepository.save(contract); - String updatedS3Url = s3FileService.uploadHtml(contractModifyRequestDTO.getCreatedUrl(), contractModifyRequestDTO.getTitle()); + // 수정 이력 저장 + String unescapedHtml = StringEscapeUtils.unescapeJson(contractModifyRequestDTO.getCreatedUrl()); + String updatedS3Url = s3FileService.uploadHtml(unescapedHtml, contractModifyRequestDTO.getTitle()); UpdateHistoryRegistDTO updateHistoryRegistDTO = new UpdateHistoryRegistDTO(); updateHistoryRegistDTO.setContent(updatedS3Url); updateHistoryRegistDTO.setMemberId(memberId); updateHistoryRegistDTO.setContractId(contractModifyRequestDTO.getContractId()); - UpdateHistory updateHistory = modelMapper.map(updateHistoryRegistDTO, UpdateHistory.class); - - // 수정 내역 테이블에 저장 - updateHistoryRepository.save(updateHistory); + updateHistoryRepository.save(modelMapper.map(updateHistoryRegistDTO, UpdateHistory.class)); } @Override diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/application/dto/CustomerModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/customer/command/application/dto/CustomerModifyDTO.java index 7e6bface..865289e0 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/application/dto/CustomerModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/application/dto/CustomerModifyDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Setter @Getter -@ToString public class CustomerModifyDTO { private String memberId; private String customerId; diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java index bae37c62..ec368a24 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java @@ -7,8 +7,6 @@ import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.customer.common.exception.CustomerCommonException; -import stanl_2.final_backend.domain.customer.common.exception.CustomerErrorCode; import stanl_2.final_backend.domain.customer.query.dto.CustomerDTO; import stanl_2.final_backend.domain.customer.query.dto.CustomerSearchDTO; import stanl_2.final_backend.domain.customer.query.repository.CustomerMapper; @@ -97,14 +95,10 @@ public Page findCustomerByCondition(Pageable pageable, CustomerSear @Transactional(readOnly = true) public CustomerDTO selectCustomerInfoByPhone(String customerPhone) throws GeneralSecurityException { - System.out.println("핸드폰 번호: " + customerPhone); - String encryptedPhone = aesUtils.encrypt(customerPhone); CustomerDTO customerInfoDTO = customerMapper.selectCustomerInfoByPhone(encryptedPhone); - System.out.println("쿼리쪽 고객정보: " + customerInfoDTO); - if (customerInfoDTO == null) { return customerInfoDTO; } From 2a11ecad4553ca5343d3b9fc0a616e4a5cfbdb23 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Fri, 22 Nov 2024 04:51:49 +0900 Subject: [PATCH 284/563] =?UTF-8?q?refactor:=20s3=20=EC=83=98=ED=94=8C=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80=20(#121)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/controller/SampleController.java | 6 ++++-- .../application/service/SampleCommandService.java | 3 ++- .../domain/service/SampleCommandServiceImpl.java | 12 ++++++++++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java index d8594418..03fb2239 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java @@ -10,6 +10,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleRegistDTO; import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleModifyDTO; import stanl_2.final_backend.domain.A_sample.command.application.service.SampleCommandService; @@ -45,7 +46,8 @@ public SampleController(SampleCommandService sampleCommandService) { @PostMapping("") public ResponseEntity postTest(@RequestBody SampleRegistDTO sampleRegistRequestDTO, Principal principal, - Authentication authentication) { + Authentication authentication, + @RequestPart("file") MultipartFile imageUrl) { log.info("현재 접속한 회원의 권한"); log.info("{}", authentication.getAuthorities()); @@ -54,7 +56,7 @@ public ResponseEntity postTest(@RequestBody SampleRegistD log.info(principal.getName()); log.info(authentication.getName()); - sampleCommandService.registerSample(sampleRegistRequestDTO); + sampleCommandService.registerSample(sampleRegistRequestDTO, imageUrl); return ResponseEntity.ok(SampleResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/service/SampleCommandService.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/service/SampleCommandService.java index 1a702f58..38f08122 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/service/SampleCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/service/SampleCommandService.java @@ -1,10 +1,11 @@ package stanl_2.final_backend.domain.A_sample.command.application.service; +import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleRegistDTO; import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleModifyDTO; public interface SampleCommandService { - void registerSample(SampleRegistDTO sampleRegistRequestDTO); + void registerSample(SampleRegistDTO sampleRegistRequestDTO, MultipartFile file); SampleModifyDTO modifySample(String id, SampleModifyDTO sampleModifyDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java index 4a68f8df..e34729c9 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java @@ -4,6 +4,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleRegistDTO; import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleModifyDTO; import stanl_2.final_backend.domain.A_sample.command.application.service.SampleCommandService; @@ -11,6 +12,7 @@ import stanl_2.final_backend.domain.A_sample.command.domain.repository.SampleRepository; import stanl_2.final_backend.domain.A_sample.common.exception.SampleCommonException; import stanl_2.final_backend.domain.A_sample.common.exception.SampleErrorCode; +import stanl_2.final_backend.domain.s3.S3FileService; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -21,11 +23,13 @@ public class SampleCommandServiceImpl implements SampleCommandService { private final SampleRepository sampleRepository; private final ModelMapper modelMapper; + private final S3FileService s3FileService; @Autowired - public SampleCommandServiceImpl(SampleRepository sampleRepository, ModelMapper modelMapper) { + public SampleCommandServiceImpl(SampleRepository sampleRepository, ModelMapper modelMapper, S3FileService s3FileService) { this.sampleRepository = sampleRepository; this.modelMapper = modelMapper; + this.s3FileService = s3FileService; } private String getCurrentTimestamp() { @@ -35,10 +39,14 @@ private String getCurrentTimestamp() { @Override @Transactional - public void registerSample(SampleRegistDTO sampleRegistRequestDTO) { + public void registerSample(SampleRegistDTO sampleRegistRequestDTO, MultipartFile file) { Sample newSample = modelMapper.map(sampleRegistRequestDTO, Sample.class); + // s3 사용 + String imageUrl = s3FileService.uploadOneFile(file); +// newSample.setImageUrl(imageUrl); + sampleRepository.save(newSample); } From d437f182cc6b520547450599b40a77f2a93525a8 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Fri, 22 Nov 2024 09:27:29 +0900 Subject: [PATCH 285/563] =?UTF-8?q?fix:=20pr=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#121)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customer/command/domain/aggregate/entity/Customer.java | 1 - .../{OrderResponseMessage.java => S3ResponseMessage.java} | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) rename src/main/java/stanl_2/final_backend/domain/s3/common/response/{OrderResponseMessage.java => S3ResponseMessage.java} (85%) diff --git a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/aggregate/entity/Customer.java b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/aggregate/entity/Customer.java index d8a18bd7..4ef5b4f7 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/command/domain/aggregate/entity/Customer.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/command/domain/aggregate/entity/Customer.java @@ -14,7 +14,6 @@ @Getter @Setter @Entity -@ToString @Table(name = "TB_CUSTOMER_INFO") public class Customer { @Id diff --git a/src/main/java/stanl_2/final_backend/domain/s3/common/response/OrderResponseMessage.java b/src/main/java/stanl_2/final_backend/domain/s3/common/response/S3ResponseMessage.java similarity index 85% rename from src/main/java/stanl_2/final_backend/domain/s3/common/response/OrderResponseMessage.java rename to src/main/java/stanl_2/final_backend/domain/s3/common/response/S3ResponseMessage.java index 53e2eb79..a32d184b 100644 --- a/src/main/java/stanl_2/final_backend/domain/s3/common/response/OrderResponseMessage.java +++ b/src/main/java/stanl_2/final_backend/domain/s3/common/response/S3ResponseMessage.java @@ -7,7 +7,7 @@ @Builder @Getter @Setter -public class OrderResponseMessage { +public class S3ResponseMessage { private int httpStatus; private String msg; private Object result; From 3c735ab634378ec5591fe1c135fcd934ae133b93 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Fri, 22 Nov 2024 11:07:38 +0900 Subject: [PATCH 286/563] =?UTF-8?q?refactor:=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=83=98=ED=94=8C=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#131)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SampleController.java | 32 ++++++++++++++----- .../service/SampleCommandService.java | 4 ++- .../service/SampleCommandServiceImpl.java | 11 ++++++- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java index 03fb2239..7ddef65c 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java @@ -8,7 +8,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; -import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleRegistDTO; @@ -45,18 +44,13 @@ public SampleController(SampleCommandService sampleCommandService) { }) @PostMapping("") public ResponseEntity postTest(@RequestBody SampleRegistDTO sampleRegistRequestDTO, - Principal principal, - Authentication authentication, - @RequestPart("file") MultipartFile imageUrl) { + Principal principal) { - log.info("현재 접속한 회원의 권한"); - log.info("{}", authentication.getAuthorities()); log.info("현재 접속한 회원정보(MEM_LOGIN_ID)"); log.info(principal.getName()); - log.info(authentication.getName()); - sampleCommandService.registerSample(sampleRegistRequestDTO, imageUrl); + sampleCommandService.registerSample(sampleRegistRequestDTO); return ResponseEntity.ok(SampleResponseMessage.builder() .httpStatus(200) @@ -65,6 +59,28 @@ public ResponseEntity postTest(@RequestBody SampleRegistD .build()); } + @Operation(summary = "샘플 파일 요청 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) + }) + @PostMapping("") + public ResponseEntity postTestFile(@RequestPart("dto") SampleRegistDTO sampleRegistRequestDTO, + Principal principal, + @RequestPart("file") MultipartFile imageUrl) { + + + log.info("현재 접속한 회원정보(MEM_LOGIN_ID)"); + log.info(principal.getName()); + sampleCommandService.registerSampleFile(sampleRegistRequestDTO, imageUrl); + + return ResponseEntity.ok(SampleResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + } + /** * [PUT] http://localhost:7777/api/v1/sample?mem_id=SAM_000001 * Request diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/service/SampleCommandService.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/service/SampleCommandService.java index 38f08122..7a2976d1 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/service/SampleCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/service/SampleCommandService.java @@ -5,9 +5,11 @@ import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleModifyDTO; public interface SampleCommandService { - void registerSample(SampleRegistDTO sampleRegistRequestDTO, MultipartFile file); + void registerSample(SampleRegistDTO sampleRegistRequestDTO); SampleModifyDTO modifySample(String id, SampleModifyDTO sampleModifyDTO); void deleteSample(String id); + + void registerSampleFile(SampleRegistDTO sampleRegistRequestDTO, MultipartFile imageUrl); } diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java index e34729c9..e4f955d8 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java @@ -39,10 +39,18 @@ private String getCurrentTimestamp() { @Override @Transactional - public void registerSample(SampleRegistDTO sampleRegistRequestDTO, MultipartFile file) { + public void registerSample(SampleRegistDTO sampleRegistRequestDTO) { Sample newSample = modelMapper.map(sampleRegistRequestDTO, Sample.class); + sampleRepository.save(newSample); + } + + @Override + public void registerSampleFile(SampleRegistDTO sampleRegistRequestDTO, MultipartFile file) { + Sample newSample = modelMapper.map(sampleRegistRequestDTO, Sample.class); + + // s3 사용 String imageUrl = s3FileService.uploadOneFile(file); // newSample.setImageUrl(imageUrl); @@ -81,4 +89,5 @@ public void deleteSample(String id) { sampleRepository.save(sample); } + } From a65065b86fbb410bbc60875823ee788ebf47a027 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 22 Nov 2024 11:25:13 +0900 Subject: [PATCH 287/563] =?UTF-8?q?feat:=20Redis=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C=20(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/repository/NoticeMapper.java | 2 +- .../query/service/NoticeServiceImpl.java | 12 +++---- .../application/dto/ProblemModifyDTO.java | 1 - .../query/repository/ProblemMapper.java | 4 +-- .../query/service/ProblemServiceImpl.java | 31 ++++++++++++++----- .../notices/query/repository/NoticeMapper.xml | 4 +-- .../query/repository/ProblemMapper.xml | 2 +- 7 files changed, 36 insertions(+), 20 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java b/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java index 3c963b0e..bb0dda9a 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java @@ -20,7 +20,7 @@ List findNotices( @Param("searchDTO") SearchDTO searchDTO ); - Integer findNoticesCount(@Param("searchDTO") SearchDTO searchDTO); + int findNoticesCount(@Param("searchDTO") SearchDTO searchDTO); NoticeDTO findNotice(@Param("noticeId") String noticeId); } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java index eaa335f4..2e5a1184 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java @@ -32,20 +32,20 @@ public NoticeServiceImpl(NoticeMapper noticeMapper, RedisTemplate redisTemplate, public Page findNotices(Pageable pageable, SearchDTO searchDTO) { int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); - String cacheKey = "myCache::notices::offset=" + offset + "::size=" + size; + String cacheKey = "NoticeCache::notices::offset=" + offset + "::size=" + size; List notices = (List) redisTemplate.opsForValue().get(cacheKey); if (notices == null) { System.out.println("데이터베이스에서 데이터 조회 중..."); - notices = noticeMapper.findAllNotices(offset, size); - String key = "NoticePage"+offset; - Object value = notices; - long ttlInSeconds = 30*60; - redisService.setKeyWithTTL(key, value, ttlInSeconds); + notices = noticeMapper.findNotices(offset, size, searchDTO); + if (notices != null && !notices.isEmpty()) { // 데이터가 있을 때만 캐싱 + redisService.setKeyWithTTL(cacheKey, notices, 30 * 60); // 캐싱 시 동일 키 사용 + } } else { System.out.println("캐시에서 데이터 조회 중..."); } int totalElements = noticeMapper.findNoticeCount(); // 총 개수 조회 + System.out.println(totalElements); return new PageImpl<>(notices, pageable, totalElements); } diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java index f647082f..d5247969 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java @@ -12,7 +12,6 @@ public class ProblemModifyDTO { private String title; - private String content; private String memberId; diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.java b/src/main/java/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.java index bfff97b1..152e3a26 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.java @@ -12,9 +12,9 @@ public interface ProblemMapper { List findProblems( @Param("offset") Integer offset, @Param("size") Integer size, - @Param("problemDTO") ProblemSearchDTO problemSearchDTO + @Param("problemSearchDTO") ProblemSearchDTO problemSearchDTO ); - Integer findProblemsCount(@Param("problemSearchDTO") ProblemSearchDTO problemSearchDTO); + int findProblemsCount(@Param("problemSearchDTO") ProblemSearchDTO problemSearchDTO); ProblemDTO findProblem(@Param("problemId") String problemId); } diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java index 139502e6..70e1131a 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java @@ -4,30 +4,47 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.problem.query.dto.ProblemDTO; import stanl_2.final_backend.domain.problem.query.dto.ProblemSearchDTO; import stanl_2.final_backend.domain.problem.query.repository.ProblemMapper; +import stanl_2.final_backend.global.redis.RedisService; import java.util.List; @Service("queryProblemServiceImpl") public class ProblemServiceImpl implements ProblemService { private final ProblemMapper problemMapper; + private final RedisTemplate redisTemplate; + private final RedisService redisService; @Autowired - public ProblemServiceImpl(ProblemMapper problemMapper) { + public ProblemServiceImpl(ProblemMapper problemMapper, RedisTemplate redisTemplate, RedisService redisService) { this.problemMapper = problemMapper; + this.redisTemplate = redisTemplate; + this.redisService = redisService; } + @Transactional @Override public Page findProblems(Pageable pageable, ProblemSearchDTO problemSearchDTO) { - Integer offset = Math.toIntExact(pageable.getOffset()); - Integer size = pageable.getPageSize(); - List problems = problemMapper.findProblems(offset,size,problemSearchDTO); - Integer count = problemMapper.findProblemsCount(problemSearchDTO); - Integer totalCount = (count != null) ? problemMapper.findProblemsCount(problemSearchDTO) : 0; + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); + String cacheKey = "ProblemCache::offset=" + offset + "::size=" + size + problemSearchDTO.getTitle(); + // Redis에서 데이터 조회 + List problems = (List) redisTemplate.opsForValue().get(cacheKey); - return new PageImpl<>(problems, pageable, totalCount); + if (problems == null) { + System.out.println("데이터베이스에서 문제 데이터 조회 중..."); + problems = problemMapper.findProblems(offset, size, problemSearchDTO); + if (problems != null && !problems.isEmpty()) + redisService.setKeyWithTTL(cacheKey, problems, 30 * 60); + } else { + System.out.println("캐시에서 문제 데이터 조회 중..."); + } + Integer totalElements = problemMapper.findProblemsCount(problemSearchDTO); + return new PageImpl<>(problems, pageable, totalElements); } @Override diff --git a/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml b/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml index 557bb422..1926500f 100644 --- a/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml @@ -85,7 +85,7 @@ LIMIT #{size} OFFSET #{offset} - SELECT COUNT(*) AS cnt FROM tb_notice a @@ -101,7 +101,7 @@ AND a.mem_id = #{memberId} - AND a.not_ttl = #{title} + AND a.not_ttl LIKE CONCAT('%', #{title}, '%') AND a.created_at BETWEEN #{startDate} AND #{endDate} diff --git a/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml b/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml index 74a4ecdb..b90d07ae 100644 --- a/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml @@ -64,7 +64,7 @@ LIMIT #{size} OFFSET #{offset} - SELECT COUNT(*) AS CNT FROM tb_problem a From d42223724b09bf7274915a362a22645bdfdd6991 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Fri, 22 Nov 2024 11:28:27 +0900 Subject: [PATCH 288/563] =?UTF-8?q?fix:=20=EC=83=98=ED=94=8C=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95(#131)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/application/controller/SampleController.java | 2 +- .../A_sample/command/application/dto/SampleRegistDTO.java | 1 + .../A_sample/command/domain/aggregate/entity/Sample.java | 3 +++ .../command/domain/service/SampleCommandServiceImpl.java | 5 ++--- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java index 7ddef65c..602b5973 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/controller/SampleController.java @@ -64,7 +64,7 @@ public ResponseEntity postTest(@RequestBody SampleRegistD @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) }) - @PostMapping("") + @PostMapping("/file") public ResponseEntity postTestFile(@RequestPart("dto") SampleRegistDTO sampleRegistRequestDTO, Principal principal, @RequestPart("file") MultipartFile imageUrl) { diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/SampleRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/SampleRegistDTO.java index 593fe3ed..042f5d53 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/SampleRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/application/dto/SampleRegistDTO.java @@ -10,4 +10,5 @@ public class SampleRegistDTO { private String id; private String name; private Integer num; + private String imageUrl; } diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/aggregate/entity/Sample.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/aggregate/entity/Sample.java index 3aadf8a5..dd6b58e9 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/aggregate/entity/Sample.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/aggregate/entity/Sample.java @@ -48,6 +48,9 @@ public class Sample { @Column(name = "ACTIVE") private Boolean active = true; + @Column(name = "IMAGE_URL") + private String imageUrl; + /* 설명. updatedAt 자동화 */ // Insert 되기 전에 실행 diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java index e4f955d8..b41aea8b 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java @@ -47,13 +47,12 @@ public void registerSample(SampleRegistDTO sampleRegistRequestDTO) { } @Override - public void registerSampleFile(SampleRegistDTO sampleRegistRequestDTO, MultipartFile file) { + public void registerSampleFile(SampleRegistDTO sampleRegistRequestDTO, MultipartFile imageUrl) { Sample newSample = modelMapper.map(sampleRegistRequestDTO, Sample.class); // s3 사용 - String imageUrl = s3FileService.uploadOneFile(file); -// newSample.setImageUrl(imageUrl); + newSample.setImageUrl(s3FileService.uploadOneFile(imageUrl)); sampleRepository.save(newSample); } From e0f5e98df1b98ab7e9eba3ddbaa1dbb964a3193f Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 22 Nov 2024 11:41:49 +0900 Subject: [PATCH 289/563] =?UTF-8?q?feat:=20Redis=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C=20(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/repository/PromotionMapper.java | 2 +- .../query/service/PromotionServiceImpl.java | 26 ++++++++++++++----- .../query/repository/PromotionMapper.xml | 2 +- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.java index 34c8bad1..d2afa674 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.java @@ -14,7 +14,7 @@ List findPromotions( @Param("size") int size, @Param("promotionDTO") PromotionSearchDTO promotionSearchDTO ); - Integer findPromotionsCount(@Param("promotionSearchDTO") PromotionSearchDTO promotionSearchDTO); + int findPromotionsCount(@Param("promotionSearchDTO") PromotionSearchDTO promotionSearchDTO); PromotionDTO findPromotion(@Param("promotionId") String promotionId); } diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java index a78a506c..31693bdb 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java @@ -4,20 +4,27 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.problem.query.dto.ProblemDTO; import stanl_2.final_backend.domain.promotion.query.dto.PromotionDTO; import stanl_2.final_backend.domain.promotion.query.dto.PromotionSearchDTO; import stanl_2.final_backend.domain.promotion.query.repository.PromotionMapper; +import stanl_2.final_backend.global.redis.RedisService; import java.util.List; @Service("queryPromoteServiceImpl") public class PromotionServiceImpl implements PromotionService{ private final PromotionMapper promotionMapper; + private final RedisTemplate redisTemplate; + private final RedisService redisService; @Autowired - public PromotionServiceImpl(PromotionMapper promotionMapper) { + public PromotionServiceImpl(PromotionMapper promotionMapper, RedisTemplate redisTemplate, RedisService redisService) { this.promotionMapper = promotionMapper; + this.redisTemplate = redisTemplate; + this.redisService = redisService; } @Transactional @@ -25,11 +32,18 @@ public PromotionServiceImpl(PromotionMapper promotionMapper) { public Page findPromotions(Pageable pageable, PromotionSearchDTO promotionSearchDTO) { int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); - List promotions = promotionMapper.findPromotions(offset,size,promotionSearchDTO); - Integer count = promotionMapper.findPromotionsCount(promotionSearchDTO); - int totalCount = (count != null) ? promotionMapper.findPromotionsCount(promotionSearchDTO) : 0; - - return new PageImpl<>(promotions, pageable, totalCount); + String cacheKey = "PromotionCache::offset=" + offset + "::size=" + size +"::title="+ promotionSearchDTO.getTitle()+"::memberId="+ promotionSearchDTO.getMemberId()+"::startDate="+ promotionSearchDTO.getStartDate()+"::endDate="+ promotionSearchDTO.getEndDate(); + List promotions = (List) redisTemplate.opsForValue().get(cacheKey); + if (promotions == null) { + System.out.println("데이터베이스에서 프로모션 데이터 조회 중..."); + promotions = promotionMapper.findPromotions(offset, size, promotionSearchDTO); + if (promotions != null && !promotions.isEmpty()) + redisService.setKeyWithTTL(cacheKey, promotions, 30 * 60); + } else { + System.out.println("캐시에서 프로모션 데이터 조회 중..."); + } + Integer totalElements = promotionMapper.findPromotionsCount(promotionSearchDTO); + return new PageImpl<>(promotions, pageable, totalElements); } @Override diff --git a/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml b/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml index 6d5bd5cb..a0818e5e 100644 --- a/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml @@ -53,7 +53,7 @@ LIMIT #{size} OFFSET #{offset} - SELECT COUNT(*) AS cnt FROM tb_promotion a From f426adf08610e33be317515e46e0878fd0e9e47f Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 22 Nov 2024 11:50:05 +0900 Subject: [PATCH 290/563] =?UTF-8?q?feat:=20Redis=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C=20(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notices/query/service/NoticeServiceImpl.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java index 2e5a1184..eaa335f4 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java @@ -32,20 +32,20 @@ public NoticeServiceImpl(NoticeMapper noticeMapper, RedisTemplate redisTemplate, public Page findNotices(Pageable pageable, SearchDTO searchDTO) { int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); - String cacheKey = "NoticeCache::notices::offset=" + offset + "::size=" + size; + String cacheKey = "myCache::notices::offset=" + offset + "::size=" + size; List notices = (List) redisTemplate.opsForValue().get(cacheKey); if (notices == null) { System.out.println("데이터베이스에서 데이터 조회 중..."); - notices = noticeMapper.findNotices(offset, size, searchDTO); - if (notices != null && !notices.isEmpty()) { // 데이터가 있을 때만 캐싱 - redisService.setKeyWithTTL(cacheKey, notices, 30 * 60); // 캐싱 시 동일 키 사용 - } + notices = noticeMapper.findAllNotices(offset, size); + String key = "NoticePage"+offset; + Object value = notices; + long ttlInSeconds = 30*60; + redisService.setKeyWithTTL(key, value, ttlInSeconds); } else { System.out.println("캐시에서 데이터 조회 중..."); } int totalElements = noticeMapper.findNoticeCount(); // 총 개수 조회 - System.out.println(totalElements); return new PageImpl<>(notices, pageable, totalElements); } From 268d54e6fd92b46260e6dd578a6a4ccc1ca4a149 Mon Sep 17 00:00:00 2001 From: giuseog Date: Fri, 22 Nov 2024 11:50:23 +0900 Subject: [PATCH 291/563] =?UTF-8?q?feat:=20=EC=98=81=EC=97=85=20=ED=86=B5?= =?UTF-8?q?=EA=B3=84=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84(#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SalesHistoryController.java | 97 +++++++++++-- .../query/dto/SalesHistoryRankedDataDTO.java | 1 + .../query/dto/SalesHistorySearchDTO.java | 5 +- .../dto/SalesHistoryStatisticsAverageDTO.java | 18 +++ .../query/repository/SalesHistoryMapper.java | 19 ++- .../service/SalesHistoryQueryService.java | 11 +- .../service/SalesHistoryQueryServiceImpl.java | 53 ++++++- .../query/repository/SalesHistoryMapper.xml | 135 +++++++++++++++++- 8 files changed, 315 insertions(+), 24 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsAverageDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java index 9440b3d1..e33bb182 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java @@ -13,10 +13,7 @@ import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.sales_history.common.response.SalesHistoryResponseMessage; -import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistoryRankedDataDTO; -import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySearchDTO; -import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectDTO; -import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistoryStatisticsDTO; +import stanl_2.final_backend.domain.sales_history.query.dto.*; import stanl_2.final_backend.domain.sales_history.query.service.SalesHistoryQueryService; import java.security.Principal; @@ -56,9 +53,9 @@ public SalesHistoryController(SalesHistoryQueryService salesHistoryQueryService) /* 설명. todo * 1. (사원별, 매장별 순위) 각 실적, 수당, 매출액 별로 인자를 통해 동적 쿼리로 처리 가능한지 여부(내림차순) - * 2. 판매내역 날짜별로 + * 2. 판매내역 날짜별로(조회기간만), 매장별, 사원별 * */ - @Operation(summary = "사원 판매내역 조회") + @Operation(summary = "판매내역 조회(사원, 관리자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), @@ -81,7 +78,7 @@ public ResponseEntity getAllSalesHistoryByEmployee( .build()); } - @Operation(summary = "판매내역 조회") + @Operation(summary = "판매내역 조회(관리자, 담당자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), @@ -100,14 +97,14 @@ public ResponseEntity getAllSalesHistory(@PageableD .build()); } - @Operation(summary = "판매내역 상세 조회") + @Operation(summary = "판매내역 상세 조회(전체)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("employee/{salesHistoryId}") + @GetMapping("{salesHistoryId}") public ResponseEntity getSalesHistoryDetail(@PathVariable("salesHistoryId") String salesHistoryId){ SalesHistorySelectDTO salesHistorySelectDTO = new SalesHistorySelectDTO(); @@ -123,6 +120,48 @@ public ResponseEntity getSalesHistoryDetail(@PathVa .build()); } + @Operation(summary = "판매내역 조회기간별 검색(사원)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/employee/search") + public ResponseEntity getSalesHistorySearchByEmployee(@RequestBody SalesHistorySearchDTO salesHistorySearchDTO + ,Principal principal, + @PageableDefault(size = 20) Pageable pageable){ + + salesHistorySearchDTO.setSearcherName(principal.getName()); + + Page responseSalesHistory = salesHistoryQueryService.selectSalesHistorySearchByEmployee(salesHistorySearchDTO, pageable); + + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("판매내역 조회기간별 검색(사원) 성공") + .result(responseSalesHistory) + .build()); + } + + @Operation(summary = "판매내역 조회기간별 검색") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/search") + public ResponseEntity getSalesHistoryBySearch(@RequestBody SalesHistorySearchDTO salesHistorySearchDTO, + @PageableDefault(size = 20) Pageable pageable){ + + Page responseSalesHistory = salesHistoryQueryService.selectSalesHistoryBySearch(salesHistorySearchDTO, pageable); + + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("판매내역 조회기간별 검색 성공") + .result(responseSalesHistory) + .build()); + } @Operation(summary = "사원 통계(실적,수당,매출액) 조회") @@ -272,6 +311,46 @@ public ResponseEntity getStatisticsBySearch(@Reques .build()); } + @Operation(summary = "사원 별 통계(실적,수당,매출액) 조회기간별 평균 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("statistics/average/employee") + public ResponseEntity getStatisticsEmployeeAverageBySearch(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, + @PageableDefault(size = 20) Pageable pageable){ + + SalesHistoryStatisticsAverageDTO responseSalesHistory = salesHistoryQueryService.selectStatisticsAverageBySearch(salesHistoryRankedDataDTO,pageable); + + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("사원 별 통계(실적,수당,매출액) 조회기간별 평균 조회 성공") + .result(responseSalesHistory) + .build()); + } + + @Operation(summary = "매장 별 통계(실적,수당,매출액) 조회기간별 평균 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("statistics/average/center") + public ResponseEntity getStatisticsCenterAverageBySearch(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, + @PageableDefault(size = 20) Pageable pageable){ + + SalesHistoryStatisticsAverageDTO responseSalesHistory = salesHistoryQueryService.selectStatisticsAverageBySearch(salesHistoryRankedDataDTO,pageable); + + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("매장 별 통계(실적,수당,매출액) 조회기간별 평균 조회 성공") + .result(responseSalesHistory) + .build()); + } + @Operation(summary = "사원 별 통계(실적,수당,매출액) 월별 검색") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryRankedDataDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryRankedDataDTO.java index 0cab5c35..9ef54403 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryRankedDataDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryRankedDataDTO.java @@ -27,4 +27,5 @@ public class SalesHistoryRankedDataDTO { private String month; private String year; private String orderBy; + private String groupBy; } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java index 27596988..1760cd5b 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java @@ -5,6 +5,8 @@ import lombok.NoArgsConstructor; import lombok.Setter; +import java.util.List; + @AllArgsConstructor @NoArgsConstructor @Getter @@ -13,5 +15,6 @@ public class SalesHistorySearchDTO { private String searcherName; private String startDate; private String endDate; - private String orderBy; + private List memberList; + private List centerList; } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsAverageDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsAverageDTO.java new file mode 100644 index 00000000..8ad31b3c --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsAverageDTO.java @@ -0,0 +1,18 @@ +package stanl_2.final_backend.domain.sales_history.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.math.BigDecimal; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class SalesHistoryStatisticsAverageDTO { + private Double averageTotalSales; + private Double averageTotalIncentive; + private Double averageTotalPerformance; +} diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java index 870dfecd..68a0da2e 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java @@ -2,10 +2,7 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; -import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistoryRankedDataDTO; -import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySearchDTO; -import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectDTO; -import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistoryStatisticsDTO; +import stanl_2.final_backend.domain.sales_history.query.dto.*; import java.util.List; @@ -74,6 +71,20 @@ List findStatisticsCenterBySearchYear(@Param("size") @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); int findStatisticsCenterBySearchCountYear(@Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); + + List findSalesHistorySearchByEmployee(@Param("size") int size + , @Param("offset") int offset, + @Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); + + int findSalesHistorySearchCountByEmployee(@Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); + + List findSalesHistoryBySearch(@Param("size") int size + , @Param("offset") int offset, + @Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); + + int findSalesHistoryCountBySearch(@Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); + + SalesHistoryStatisticsAverageDTO findStatisticsAverageBySearch(@Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java index 5e9f4e9e..e11ef099 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java @@ -2,10 +2,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistoryRankedDataDTO; -import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySearchDTO; -import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectDTO; -import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistoryStatisticsDTO; +import stanl_2.final_backend.domain.sales_history.query.dto.*; public interface SalesHistoryQueryService { Page selectAllSalesHistoryByEmployee(SalesHistorySelectDTO salesHistorySelectDTO, Pageable pageable); @@ -35,4 +32,10 @@ public interface SalesHistoryQueryService { Page selectStatisticsCenterBySearchMonth(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); Page selectStatisticsCenterBySearchYear(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); + + Page selectSalesHistorySearchByEmployee(SalesHistorySearchDTO salesHistorySearchDTO, Pageable pageable); + + Page selectSalesHistoryBySearch(SalesHistorySearchDTO salesHistorySearchDTO, Pageable pageable); + + SalesHistoryStatisticsAverageDTO selectStatisticsAverageBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java index efe77079..a371567a 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java @@ -10,10 +10,7 @@ import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import stanl_2.final_backend.domain.sales_history.common.exception.SalesHistoryCommonException; import stanl_2.final_backend.domain.sales_history.common.exception.SalesHistoryErrorCode; -import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistoryRankedDataDTO; -import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySearchDTO; -import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectDTO; -import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistoryStatisticsDTO; +import stanl_2.final_backend.domain.sales_history.query.dto.*; import stanl_2.final_backend.domain.sales_history.query.repository.SalesHistoryMapper; import stanl_2.final_backend.global.utils.AESUtils; import org.springframework.data.redis.core.RedisTemplate; @@ -57,6 +54,7 @@ public Page selectAllSalesHistoryByEmployee(SalesHistoryS return new PageImpl<>(salesHistoryList, pageable, total); } + @Override @Transactional(readOnly = true) public SalesHistorySelectDTO selectSalesHistoryDetail(SalesHistorySelectDTO salesHistorySelectDTO) { @@ -66,6 +64,44 @@ public SalesHistorySelectDTO selectSalesHistoryDetail(SalesHistorySelectDTO sale return salesHistoryDetailDTO; } + @Override + @Transactional(readOnly = true) + public Page selectSalesHistorySearchByEmployee(SalesHistorySearchDTO salesHistorySearchDTO, Pageable pageable) { + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); + +// String searcherId = authQueryService.selectMemberIdByLoginId(salesHistorySearchDTO.getSearcherName()); + salesHistorySearchDTO.setSearcherName(authQueryService.selectMemberIdByLoginId(salesHistorySearchDTO.getSearcherName())); + + List salesHistoryList = salesHistoryMapper.findSalesHistorySearchByEmployee(size,offset, salesHistorySearchDTO); + + int total = salesHistoryMapper.findSalesHistorySearchCountByEmployee(salesHistorySearchDTO); + + if(salesHistoryList.isEmpty() || total == 0){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } + return new PageImpl<>(salesHistoryList, pageable, total); + } + + @Override + public Page selectSalesHistoryBySearch(SalesHistorySearchDTO salesHistorySearchDTO, Pageable pageable) { + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); + + System.out.println("service check1"); + + List salesHistoryList = salesHistoryMapper.findSalesHistoryBySearch(size,offset, salesHistorySearchDTO); + System.out.println("service check2"); + + int total = salesHistoryMapper.findSalesHistoryCountBySearch(salesHistorySearchDTO); + System.out.println("service check3"); + + if(salesHistoryList.isEmpty() || total == 0){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } + return new PageImpl<>(salesHistoryList, pageable, total); + } + @Override @Transactional(readOnly = true) public Page selectAllSalesHistory(Pageable pageable) { @@ -153,6 +189,15 @@ public Page selectStatistics(SalesHistoryRankedDataDT return new PageImpl<>(salesHistoryList, pageable, total); } + @Override + @Transactional(readOnly = true) + public SalesHistoryStatisticsAverageDTO selectStatisticsAverageBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { + + SalesHistoryStatisticsAverageDTO salesHistoryStatisticsAverageDTO = salesHistoryMapper.findStatisticsAverageBySearch(salesHistoryRankedDataDTO); + + return salesHistoryStatisticsAverageDTO; + } + @Override @Transactional(readOnly = true) public Page selectStatisticsBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { diff --git a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml index f187598b..6e30821c 100644 --- a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml @@ -44,6 +44,13 @@ + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + \ No newline at end of file From 908ea75fc126abcd2f7f19cf17c6eda30b2ed081 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 22 Nov 2024 16:02:29 +0900 Subject: [PATCH 295/563] =?UTF-8?q?feat:=20excel=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EC=84=B1=EA=B3=B5=20(#137)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/ProblemController.java | 11 +++++++ .../query/dto/ProblemExcelDownload.java | 30 +++++++++++++++++++ .../query/repository/ProblemMapper.java | 4 +++ .../problem/query/service/ProblemService.java | 3 ++ .../query/service/ProblemServiceImpl.java | 17 ++++++++++- .../query/controller/PromotionController.java | 11 +++++++ .../query/dto/PromotionExcelDownload.java | 25 ++++++++++++++++ .../query/repository/PromotionMapper.java | 4 +++ .../query/service/PromotionService.java | 3 ++ .../query/service/PromotionServiceImpl.java | 17 ++++++++++- .../query/repository/ProblemMapper.xml | 24 +++++++++++++++ .../query/repository/PromotionMapper.xml | 21 ++++++++++++- 12 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemExcelDownload.java create mode 100644 src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionExcelDownload.java diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/controller/ProblemController.java b/src/main/java/stanl_2/final_backend/domain/problem/query/controller/ProblemController.java index 050e4dc3..d128ad2b 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/query/controller/ProblemController.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/controller/ProblemController.java @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -61,4 +62,14 @@ public ResponseEntity getProblem(@PathVariable String problemId){ ProblemDTO problemDTO = problemService.findProblem(problemId); return ResponseEntity.ok(problemDTO); } + @Operation(summary = "문제사항 엑셀 다운 테스트") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "문제사항 엑셀 다운 테스트 성공", + content = {@Content(schema = @Schema(implementation = ProblemResponseMessage.class))}), + }) + @GetMapping("/excel") + public void exportNotice(HttpServletResponse response){ + + problemService.exportProblemsToExcel(response); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemExcelDownload.java b/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemExcelDownload.java new file mode 100644 index 00000000..25a3571b --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemExcelDownload.java @@ -0,0 +1,30 @@ +package stanl_2.final_backend.domain.problem.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import stanl_2.final_backend.global.excel.ExcelColumnName; + +@Getter +@AllArgsConstructor +public class ProblemExcelDownload { + @ExcelColumnName(name = "문제제목") + private String title; + + @ExcelColumnName(name = "문제내용") + private String content; + + @ExcelColumnName(name = "생성일자") + private String createdAt; + + @ExcelColumnName(name = "수정일자") + private String updatedAt; + + @ExcelColumnName(name = "작성자") + private String memberId; + + @ExcelColumnName(name = "고객이름") + private String customerId; + + @ExcelColumnName(name = "제품명") + private String productId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.java b/src/main/java/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.java index 152e3a26..8fb9766e 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.java @@ -2,7 +2,9 @@ import org.apache.ibatis.annotations.Mapper; import org.springframework.data.repository.query.Param; +import stanl_2.final_backend.domain.notices.query.dto.NoticeExcelDownload; import stanl_2.final_backend.domain.problem.query.dto.ProblemDTO; +import stanl_2.final_backend.domain.problem.query.dto.ProblemExcelDownload; import stanl_2.final_backend.domain.problem.query.dto.ProblemSearchDTO; import java.util.List; @@ -17,4 +19,6 @@ List findProblems( int findProblemsCount(@Param("problemSearchDTO") ProblemSearchDTO problemSearchDTO); ProblemDTO findProblem(@Param("problemId") String problemId); + + List findProblemsForExcel(); } diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemService.java b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemService.java index 4db72ae3..87f96f06 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemService.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemService.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.problem.query.service; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import stanl_2.final_backend.domain.problem.query.dto.ProblemDTO; @@ -8,4 +9,6 @@ public interface ProblemService { Page findProblems(Pageable pageable, ProblemSearchDTO problemSearchDTO); ProblemDTO findProblem(String problemId); + void exportProblemsToExcel(HttpServletResponse response); + } diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java index 70e1131a..568ec402 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.problem.query.service; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; @@ -7,9 +8,12 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.notices.query.dto.NoticeExcelDownload; import stanl_2.final_backend.domain.problem.query.dto.ProblemDTO; +import stanl_2.final_backend.domain.problem.query.dto.ProblemExcelDownload; import stanl_2.final_backend.domain.problem.query.dto.ProblemSearchDTO; import stanl_2.final_backend.domain.problem.query.repository.ProblemMapper; +import stanl_2.final_backend.global.excel.ExcelUtilsV1; import stanl_2.final_backend.global.redis.RedisService; import java.util.List; @@ -19,11 +23,14 @@ public class ProblemServiceImpl implements ProblemService { private final ProblemMapper problemMapper; private final RedisTemplate redisTemplate; private final RedisService redisService; + + private final ExcelUtilsV1 excelUtilsV1; @Autowired - public ProblemServiceImpl(ProblemMapper problemMapper, RedisTemplate redisTemplate, RedisService redisService) { + public ProblemServiceImpl(ProblemMapper problemMapper, RedisTemplate redisTemplate, RedisService redisService, ExcelUtilsV1 excelUtilsV1) { this.problemMapper = problemMapper; this.redisTemplate = redisTemplate; this.redisService = redisService; + this.excelUtilsV1 =excelUtilsV1; } @Transactional @@ -47,9 +54,17 @@ public Page findProblems(Pageable pageable, ProblemSearchDTO problem return new PageImpl<>(problems, pageable, totalElements); } + @Transactional @Override public ProblemDTO findProblem(String problemId) { ProblemDTO problemDTO = problemMapper.findProblem(problemId); return problemDTO; } + @Transactional + @Override + public void exportProblemsToExcel(HttpServletResponse response) { + List problemList = problemMapper.findProblemsForExcel(); + + excelUtilsV1.download(ProblemExcelDownload.class, problemList, "problemExcel", response); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/controller/PromotionController.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/controller/PromotionController.java index 7e1039a6..935d95e1 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/query/controller/PromotionController.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/controller/PromotionController.java @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -58,6 +59,16 @@ public ResponseEntity getPromotion(@PathVariable String promotionI PromotionDTO promotionDTO = promotionService.findPromotion(promotionId); return ResponseEntity.ok(promotionDTO); } + @Operation(summary = "프로모션 엑셀 다운") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = PromotionResponseMessage.class))}) + }) + @GetMapping("/excel") + public void exportNotice(HttpServletResponse response){ + + promotionService.exportPromotionToExcel(response); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionExcelDownload.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionExcelDownload.java new file mode 100644 index 00000000..04658b52 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionExcelDownload.java @@ -0,0 +1,25 @@ +package stanl_2.final_backend.domain.promotion.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import stanl_2.final_backend.global.excel.ExcelColumnName; + +@Getter +@AllArgsConstructor +public class PromotionExcelDownload { + @ExcelColumnName(name = "프로모션 제목") + private String title; + + @ExcelColumnName(name = "프로모션 내용") + private String content; + + @ExcelColumnName(name = "생성일자") + private String createdAt; + + @ExcelColumnName(name = "수정일자") + private String updatedAt; + + @ExcelColumnName(name = "작성자") + private String memberId; + +} diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.java index d2afa674..aab59668 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.java @@ -2,7 +2,9 @@ import org.apache.ibatis.annotations.Mapper; import org.springframework.data.repository.query.Param; +import stanl_2.final_backend.domain.problem.query.dto.ProblemExcelDownload; import stanl_2.final_backend.domain.promotion.query.dto.PromotionDTO; +import stanl_2.final_backend.domain.promotion.query.dto.PromotionExcelDownload; import stanl_2.final_backend.domain.promotion.query.dto.PromotionSearchDTO; import java.util.List; @@ -17,4 +19,6 @@ List findPromotions( int findPromotionsCount(@Param("promotionSearchDTO") PromotionSearchDTO promotionSearchDTO); PromotionDTO findPromotion(@Param("promotionId") String promotionId); + + List findPromotionsForExcel(); } diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionService.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionService.java index 654fd787..dfe8d41f 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionService.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionService.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.promotion.query.service; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import stanl_2.final_backend.domain.promotion.query.dto.PromotionDTO; @@ -9,4 +10,6 @@ public interface PromotionService { Page findPromotions(Pageable pageable, PromotionSearchDTO PromotionSearchDTO); PromotionDTO findPromotion(String promotionId); + + void exportPromotionToExcel(HttpServletResponse response); } diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java index 31693bdb..a391b7cd 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.promotion.query.service; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; @@ -8,9 +9,12 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.problem.query.dto.ProblemDTO; +import stanl_2.final_backend.domain.problem.query.dto.ProblemExcelDownload; import stanl_2.final_backend.domain.promotion.query.dto.PromotionDTO; +import stanl_2.final_backend.domain.promotion.query.dto.PromotionExcelDownload; import stanl_2.final_backend.domain.promotion.query.dto.PromotionSearchDTO; import stanl_2.final_backend.domain.promotion.query.repository.PromotionMapper; +import stanl_2.final_backend.global.excel.ExcelUtilsV1; import stanl_2.final_backend.global.redis.RedisService; import java.util.List; @@ -20,11 +24,13 @@ public class PromotionServiceImpl implements PromotionService{ private final PromotionMapper promotionMapper; private final RedisTemplate redisTemplate; private final RedisService redisService; + private final ExcelUtilsV1 excelUtilsV1; @Autowired - public PromotionServiceImpl(PromotionMapper promotionMapper, RedisTemplate redisTemplate, RedisService redisService) { + public PromotionServiceImpl(PromotionMapper promotionMapper, RedisTemplate redisTemplate, RedisService redisService, ExcelUtilsV1 excelUtilsV1) { this.promotionMapper = promotionMapper; this.redisTemplate = redisTemplate; this.redisService = redisService; + this.excelUtilsV1 =excelUtilsV1; } @Transactional @@ -46,9 +52,18 @@ public Page findPromotions(Pageable pageable, PromotionSearchDTO p return new PageImpl<>(promotions, pageable, totalElements); } + @Transactional @Override public PromotionDTO findPromotion(String promotionId) { PromotionDTO promotionDTO = promotionMapper.findPromotion(promotionId); return promotionDTO; } + + @Transactional + @Override + public void exportPromotionToExcel(HttpServletResponse response) { + List promotionList = promotionMapper.findPromotionsForExcel(); + + excelUtilsV1.download(PromotionExcelDownload.class, promotionList, "promotionExcel", response); + } } diff --git a/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml b/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml index b90d07ae..3601f8e0 100644 --- a/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml @@ -104,4 +104,28 @@ WHERE prob_id = #{problemId}; + + + + + + + + + + + + + diff --git a/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml b/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml index a0818e5e..e84e83a3 100644 --- a/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml @@ -25,7 +25,6 @@ - + + + + + + + + + + \ No newline at end of file From be959eff43be27d206515356a483bd9e2d59dbfc Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 22 Nov 2024 16:14:12 +0900 Subject: [PATCH 296/563] =?UTF-8?q?fix:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/notices/query/service/NoticeServiceImpl.java | 4 ++-- .../domain/problem/query/service/ProblemServiceImpl.java | 1 + .../domain/notices/query/repository/NoticeMapper.xml | 1 + .../domain/promotion/query/repository/PromotionMapper.xml | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java index 7e0638a1..0529b62c 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java @@ -52,13 +52,13 @@ public Page findNotices(Pageable pageable, SearchDTO searchDTO) { int totalElements = noticeMapper.findNoticeCount(); // 총 개수 조회 return new PageImpl<>(notices, pageable, totalElements); } - + @Transactional @Override public NoticeDTO findNotice(String noticeId) { NoticeDTO notice = noticeMapper.findNotice(noticeId); return notice; } - + @Transactional @Override public void exportNoticesToExcel(HttpServletResponse response) { List noticeList = noticeMapper.findNoticesForExcel(); diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java index 568ec402..e7aeadf3 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java @@ -25,6 +25,7 @@ public class ProblemServiceImpl implements ProblemService { private final RedisService redisService; private final ExcelUtilsV1 excelUtilsV1; + @Autowired public ProblemServiceImpl(ProblemMapper problemMapper, RedisTemplate redisTemplate, RedisService redisService, ExcelUtilsV1 excelUtilsV1) { this.problemMapper = problemMapper; diff --git a/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml b/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml index 4cc23047..4ac6f562 100644 --- a/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml @@ -124,6 +124,7 @@ FROM tb_notice a WHERE not_id = #{noticeId}; + diff --git a/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml b/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml index e84e83a3..9efb2c44 100644 --- a/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml @@ -69,6 +69,7 @@ + + + + \ No newline at end of file From 74e658647e8df7dca22d40cb11676cce114e8ef5 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Sat, 23 Nov 2024 15:49:14 +0900 Subject: [PATCH 301/563] =?UTF-8?q?fix:=20PR=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81(#141)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log/query/controller/LogController.java | 23 ------------------- .../query/service/LogQueryServiceImpl.java | 2 -- 2 files changed, 25 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java b/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java index 6f74c262..2184edf5 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java @@ -19,8 +19,6 @@ import stanl_2.final_backend.domain.log.query.dto.LogSearchDTO; import stanl_2.final_backend.domain.log.query.service.LogQueryService; -import java.util.List; - @RestController(value = "queryLogController") @RequestMapping("/api/v1/log") public class LogController { @@ -54,30 +52,9 @@ public ResponseEntity getLogs( LogSearchDTO searchLogDTO = new LogSearchDTO(ipAddress, requestTime, status, method, uri); Page logDTOPage = logQueryService.selectLogs(pageable, searchLogDTO); - -// List log = logQueryService.selectLog(); - return ResponseEntity.ok(LogResponseMessage.builder() .httpStatus(200) .result(logDTOPage) .build()); } - - @Operation(summary = "로그 검색 조회(시스템 관리자)") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = LogResponseMessage.class))}), - @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", - content = @Content(mediaType = "application/json")) - }) - @GetMapping("list") - public ResponseEntity getLogSearch(){ - - - - return ResponseEntity.ok(LogResponseMessage.builder() - .httpStatus(200) - .result(null) - .build()); - } } diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java index 7ae707ce..0804a7cf 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java @@ -32,8 +32,6 @@ public Page selectLogs(Pageable pageable, LogSearchDTO searchLogDTO) { List logs = logMapper.findLogs(offset, size, searchLogDTO); - log.info("####"); - log.info("{}", logs); int totalElements = logMapper.findLogsCnt(); return new PageImpl<>(logs, pageable, totalElements); } From 46b66c8c2572a9262967fccd7eb0bbcb6aad0f75 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Sun, 24 Nov 2024 00:48:01 +0900 Subject: [PATCH 302/563] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EB=82=B4=20=EB=A7=A4=EC=9E=A5=20=EC=A0=84=EC=B2=B4=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C,=20=EC=83=81=EC=84=B8=EC=A1=B0=ED=9A=8C,=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=EC=A1=B0=ED=9A=8C=20(#143)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ContractController.java | 6 +- .../query/controller/ContractController.java | 96 ++++++- .../query/repository/ContractMapper.java | 16 ++ .../query/service/ContractQueryService.java | 9 + .../service/ContractQueryServiceImpl.java | 70 ++++- .../query/repository/ContractMapper.xml | 264 ++++++++++++++++-- 6 files changed, 427 insertions(+), 34 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java index c98ea6fc..bb05bf4d 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java @@ -29,7 +29,7 @@ public ContractController(ContractCommandService contractCommandService) { this.contractCommandService = contractCommandService; } - @Operation(summary = "계약서 등록(영업사원)") + @Operation(summary = "계약서 등록(영업사원, 관리자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "계약서 등록 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) @@ -48,7 +48,7 @@ public ResponseEntity postContract(@RequestBody Contrac .build()); } - @Operation(summary = "계약서 수정(영업사원)") + @Operation(summary = "계약서 수정(영업사원, 관리자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "계약서 수정 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) @@ -69,7 +69,7 @@ public ResponseEntity putContract(@PathVariable String .build()); } - @Operation(summary = "계약서 삭제(영업사원)") + @Operation(summary = "계약서 삭제(영업사원, 관리자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "계약서 삭제 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java index 1b93154a..e7a995c1 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java @@ -17,6 +17,7 @@ import stanl_2.final_backend.domain.contract.query.dto.ContractSeletIdDTO; import stanl_2.final_backend.domain.contract.query.service.ContractQueryService; +import java.security.GeneralSecurityException; import java.security.Principal; @Slf4j @@ -117,8 +118,95 @@ public ResponseEntity getContractBySearchEmployee(Princ .build()); } - // 영업담당자, 관리자 조회 - @Operation(summary = "계약서 전체 조회(영업관리자, 담당자)") + // 영업 관리자 조회 + @Operation(summary = "계약서 전체 조회(영업관리자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "계약서 전체 조회 성공", + content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) + }) + @GetMapping("center") + public ResponseEntity getAllContractAdmin(@PageableDefault(size = 10) Pageable pageable, + Principal principal) throws GeneralSecurityException { + + ContractSelectAllDTO contractSelectAllDTO = new ContractSelectAllDTO(); + contractSelectAllDTO.setMemberId(principal.getName()); + + Page responseContracts = contractQueryService.selectAllContractAdmin(contractSelectAllDTO, pageable); + + return ResponseEntity.ok(ContractResponseMessage.builder() + .httpStatus(200) + .msg("계약서 전체 조회 성공") + .result(responseContracts) + .build()); + } + + @Operation(summary = "계약서 상세 조회(영업관리자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "계약서 상세 조회 성공", + content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) + }) + @GetMapping("center/{contractId}") + public ResponseEntity getDetailContractAdmin(@PathVariable String contractId, + Principal principal) throws GeneralSecurityException { + + ContractSeletIdDTO contractSeletIdDTO = new ContractSeletIdDTO(); + contractSeletIdDTO.setContractId(contractId); + contractSeletIdDTO.setMemberId(principal.getName()); + + ContractSeletIdDTO responseContract = contractQueryService.selectDetailContractAdmin(contractSeletIdDTO); + + return ResponseEntity.ok(ContractResponseMessage.builder() + .httpStatus(200) + .msg("계약서 상세조회 성공") + .result(responseContract) + .build()); + } + + @Operation(summary = "계약서 검색 조회(영업관리자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "계약서 검색 조회 성공", + content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) + }) + @GetMapping("center/search") + public ResponseEntity getContractBySearchAdmin(Principal principal, + @RequestParam(required = false) String searchMemberId, + @RequestParam(required = false) String centerId, + @RequestParam(required = false) String title, + @RequestParam(required = false) String startAt, + @RequestParam(required = false) String endAt, + @RequestParam(required = false) String customerName, + @RequestParam(required = false) String customerClassifcation, + @RequestParam(required = false) String productId, + @RequestParam(required = false) String status, + @RequestParam(required = false) String companyName, + @RequestParam(required = false) String customerPurchaseCondition, + @PageableDefault(size = 10) Pageable pageable) throws GeneralSecurityException { + + ContractSearchDTO contractSearchDTO = new ContractSearchDTO(); + contractSearchDTO.setMemberId(principal.getName()); + contractSearchDTO.setSearchMemberId(searchMemberId); + contractSearchDTO.setCenterId(centerId); + contractSearchDTO.setTitle(title); + contractSearchDTO.setStartAt(startAt); + contractSearchDTO.setEndAt(endAt); + contractSearchDTO.setCustomerName(customerName); + contractSearchDTO.setCustomerClassifcation(customerClassifcation); + contractSearchDTO.setProductId(productId); + contractSearchDTO.setStatus(status); + contractSearchDTO.setCompanyName(companyName); + contractSearchDTO.setCustomerPurchaseCondition(customerPurchaseCondition); + + Page responseContracts = contractQueryService.selectBySearchAdmin(contractSearchDTO, pageable); + + return ResponseEntity.ok(ContractResponseMessage.builder() + .httpStatus(200) + .msg("계약서 검색 조회 성공") + .result(responseContracts) + .build()); + } + + // 영업담당자 조회 + @Operation(summary = "계약서 전체 조회(영업담당자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "계약서 전체 조회 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) @@ -137,7 +225,7 @@ public ResponseEntity getAllContract(@PageableDefault(s .build()); } - @Operation(summary = "계약서 상세 조회(영업사원)") + @Operation(summary = "계약서 상세 조회(영업담당자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "계약서 상세 조회 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) @@ -157,7 +245,7 @@ public ResponseEntity getDetailContract(@PathVariable S .build()); } - @Operation(summary = "계약서 검색 조회(영업사원)") + @Operation(summary = "계약서 검색 조회(영업담당자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "계약서 검색 조회 성공", content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java index 13a0345d..8ff8be7e 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java @@ -37,4 +37,20 @@ List findContractBySearch(@Param("offset") int offset, @Param("contractSearchDTO") ContractSearchDTO contractSearchDTO); int findContractBySearchCount(ContractSearchDTO contractSearchDTO); + + List findContractAllByCenterId(@Param("offset") int offset, + @Param("pageSize") int pageSize, + @Param("centerId") String centerId); + + Integer findContractCountByCenterId(@Param("centerId") String centerId); + + ContractSeletIdDTO findContractByIdAndCenterId(@Param("contractId")String contractId, + @Param("centerId") String centerId); + + List findContractBySearchAndCenterId(@Param("offset") int offset, + @Param("pageSize") int pageSize, + @Param("contractSearchDTO") ContractSearchDTO contractSearchDTO, + @Param("centerId") String centerId); + + Integer findContractBySearchAndCenterCount(@Param("contractSearchDTO") ContractSearchDTO contractSearchDTO, @Param("centerId") String centerId); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java index 0d1e7ba3..b70f57d5 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java @@ -6,6 +6,8 @@ import stanl_2.final_backend.domain.contract.query.dto.ContractSelectAllDTO; import stanl_2.final_backend.domain.contract.query.dto.ContractSeletIdDTO; +import java.security.GeneralSecurityException; + public interface ContractQueryService { Page selectAllContract(ContractSelectAllDTO contractSelectAllDTO, Pageable pageable); @@ -21,4 +23,11 @@ public interface ContractQueryService { Page selectBySearchEmployee(ContractSearchDTO contractSearchDTO, Pageable pageable); + Page selectAllContractAdmin(ContractSelectAllDTO contractSelectAllDTO, Pageable pageable) throws GeneralSecurityException; + + ContractSeletIdDTO selectDetailContractAdmin(ContractSeletIdDTO contractSeletIdDTO) throws GeneralSecurityException; + + Page selectBySearchAdmin(ContractSearchDTO contractSearchDTO, Pageable pageable) throws GeneralSecurityException; + + } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java index 65749787..fd61d147 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java @@ -17,7 +17,9 @@ import stanl_2.final_backend.domain.contract.query.dto.ContractSeletIdDTO; import stanl_2.final_backend.domain.contract.query.repository.ContractMapper; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; +import java.security.GeneralSecurityException; import java.util.List; @Slf4j @@ -26,12 +28,14 @@ public class ContractQueryServiceImpl implements ContractQueryService { private final ContractMapper contractMapper; private final AuthQueryService authQueryService; + private final MemberQueryService memberQueryService; private final RedisTemplate redisTemplate; @Autowired - public ContractQueryServiceImpl(ContractMapper contractMapper, AuthQueryService authQueryService, @Qualifier("redisTemplate") RedisTemplate redisTemplate) { + public ContractQueryServiceImpl(ContractMapper contractMapper, AuthQueryService authQueryService, MemberQueryService memberQueryService, @Qualifier("redisTemplate") RedisTemplate redisTemplate) { this.contractMapper = contractMapper; this.authQueryService = authQueryService; + this.memberQueryService = memberQueryService; this.redisTemplate = redisTemplate; } @@ -102,7 +106,69 @@ public Page selectBySearchEmployee(ContractSearchDTO contract return new PageImpl<>(contracts, pageable, totalContract); } - // 영업담당자, 관리자 조회 + // 영업 관리자 조회 + @Override + @Transactional(readOnly = true) + public Page selectAllContractAdmin(ContractSelectAllDTO contractSelectAllDTO, Pageable pageable) throws GeneralSecurityException { + String centerId = memberQueryService.selectMemberInfo(contractSelectAllDTO.getMemberId()).getCenterId(); + + int offset = Math.toIntExact(pageable.getOffset()); + int pageSize = pageable.getPageSize(); + + String caschKey = "myCache::contracts::offset=" + offset + "::pageSize=" + pageSize; + + List contracts = (List) redisTemplate.opsForValue().get(caschKey); + + if (contracts == null) { + contracts = contractMapper.findContractAllByCenterId(offset, pageSize, centerId); + + if (contracts == null) { + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); + } + + redisTemplate.opsForValue().set(caschKey, contracts); + } + + Integer count = contractMapper.findContractCountByCenterId(centerId); + int totalContract = (count != null) ? count : 0; + + return new PageImpl<>(contracts, pageable, totalContract); + } + + @Override + @Transactional(readOnly = true) + public ContractSeletIdDTO selectDetailContractAdmin(ContractSeletIdDTO contractSeletIdDTO) throws GeneralSecurityException { + String centerId = memberQueryService.selectMemberInfo(contractSeletIdDTO.getMemberId()).getCenterId(); + + ContractSeletIdDTO responseContract = contractMapper.findContractByIdAndCenterId(contractSeletIdDTO.getContractId(), centerId); + + // 이스케이프된 HTML 제거 + String unescapedHtml = StringEscapeUtils.unescapeJson(responseContract.getCreatedUrl()); + responseContract.setCreatedUrl(unescapedHtml); + + return responseContract; + } + + @Override + @Transactional(readOnly = true) + public Page selectBySearchAdmin(ContractSearchDTO contractSearchDTO, Pageable pageable) throws GeneralSecurityException { + String centerId = memberQueryService.selectMemberInfo(contractSearchDTO.getMemberId()).getCenterId(); + + int offset = Math.toIntExact(pageable.getOffset()); + int pageSize = pageable.getPageSize(); + List contracts = contractMapper.findContractBySearchAndCenterId(offset, pageSize, contractSearchDTO, centerId); + + if (contracts == null) { + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); + } + + Integer count = contractMapper.findContractBySearchAndCenterCount(contractSearchDTO, centerId); + int totalContract = (count != null) ? count : 0; + + return new PageImpl<>(contracts, pageable, totalContract); + } + + // 영업담당자 조회 @Override @Transactional(readOnly = true) public Page selectAllContract(ContractSelectAllDTO contractSelectAllDTO, Pageable pageable) { diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index 6d861024..c8eab5db 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -11,7 +11,7 @@ - + @@ -23,8 +23,8 @@ - - + + @@ -68,6 +68,8 @@ + + @@ -117,7 +119,7 @@ FROM tb_contract a WHERE a.conr_id = #{ contractId } AND a.mem_id = #{ memberId } - AND a.active = TRUE; + AND a.active = TRUE + + + + + + + + + + + \ No newline at end of file From bc3a624efdc7cc00cd3a07e1502b5cafef4741be Mon Sep 17 00:00:00 2001 From: giuseog Date: Sun, 24 Nov 2024 02:01:11 +0900 Subject: [PATCH 303/563] =?UTF-8?q?feat:=20=EC=97=91=EC=85=80=20=EB=8B=A4?= =?UTF-8?q?=EC=9A=B4=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EC=A4=91(#145)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CenterController.java | 6 +- .../dto/request/CenterDeleteRequestDTO.java | 16 -- .../dto/request/CenterModifyRequestDTO.java | 3 +- .../dto/request/CenterRegistRequestDTO.java | 1 - .../service/CenterCommandService.java | 2 +- .../domain/aggregate/entity/Center.java | 6 +- .../service/CenterCommandServiceImpl.java | 17 +- .../center/common/util/RequestList.java | 14 -- .../query/controller/CenterController.java | 15 ++ .../center/query/dto/CenterExcelDownload.java | 26 +++ .../query/dto/CenterSearchRequestDTO.java | 6 +- .../center/query/dto/CenterSelectAllDTO.java | 8 +- .../center/query/dto/CenterSelectIdDTO.java | 7 +- .../center/query/repository/CenterMapper.java | 9 +- .../query/service/CenterQueryService.java | 6 +- .../query/service/CenterQueryServiceImpl.java | 21 ++- .../domain/aggregate/entity/Contract.java | 6 +- .../service/ContractCommandServiceImpl.java | 4 +- .../query/dto/ContractSeletIdDTO.java | 4 +- .../query/service/CustomerQueryService.java | 2 + .../service/CustomerQueryServiceImpl.java | 11 ++ .../controller/EvaluationController.java | 26 +-- .../application/dto/EvaluationModifyDTO.java | 2 +- .../application/dto/EvaluationRegistDTO.java | 2 - .../service/EvaluationCommandService.java | 2 +- .../domain/aggregate/entity/Evaluation.java | 5 +- .../service/EvaluationCommandServiceImpl.java | 4 +- .../common/exception/EvaluationErrorCode.java | 2 +- .../controller/EvaluationController.java | 24 +-- .../evaluation/query/dto/EvaluationDTO.java | 1 - .../query/dto/EvaluationExcelDownload.java | 29 ++++ .../query/repository/EvaluationMapper.java | 5 +- .../query/service/EvaluationQueryService.java | 3 + .../service/EvaluationQueryServiceImpl.java | 107 ++++++++---- .../service/ProductCommandService.java | 4 +- .../service/ProductCommandServiceImpl.java | 7 +- .../query/controller/ProductController.java | 15 ++ .../query/dto/ProductExcelDownload.java | 24 +++ .../query/repository/ProductMapper.java | 3 + .../query/service/ProductQueryService.java | 4 +- .../service/ProductQueryServiceImpl.java | 36 +++- .../dto/SalesHistoryRegistDTO.java | 2 +- .../domain/aggregate/entity/SalesHistory.java | 2 +- .../SalesHistoryCommandServiceImpl.java | 25 ++- .../exception/SalesHistoryErrorCode.java | 8 +- .../controller/SalesHistoryController.java | 41 ++--- .../query/dto/SalesHistoryExcelDownload.java | 33 ++++ .../query/repository/SalesHistoryMapper.java | 2 + .../service/SalesHistoryQueryService.java | 3 + .../service/SalesHistoryQueryServiceImpl.java | 158 +++++++++++++++++- .../center/query/repository/CenterMapper.xml | 34 +++- .../query/repository/EvaluationMapper.xml | 22 +++ .../query/repository/ProductMapper.xml | 21 +++ .../query/repository/SalesHistoryMapper.xml | 30 ++++ 54 files changed, 660 insertions(+), 216 deletions(-) delete mode 100644 src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterDeleteRequestDTO.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/center/common/util/RequestList.java create mode 100644 src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterExcelDownload.java create mode 100644 src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationExcelDownload.java create mode 100644 src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductExcelDownload.java create mode 100644 src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryExcelDownload.java diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java index b9ef59f4..0ef81383 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java @@ -31,11 +31,9 @@ public CenterController(CenterCommandService centerCommandService) { }) @PostMapping("") public ResponseEntity postTest(@RequestBody CenterRegistRequestDTO centerRegistRequestDTO){ - /* 설명. memberId 토큰으로 받는 것 고려 */ centerCommandService.registCenter(centerRegistRequestDTO); - return ResponseEntity.ok(CenterResponseMessage.builder() .httpStatus(200) .msg("등록 성공") @@ -52,7 +50,9 @@ public ResponseEntity postTest(@RequestBody CenterRegistR public ResponseEntity putTest(@PathVariable("id") String id, @RequestBody CenterModifyRequestDTO centerModifyRequestDTO){ - centerCommandService.modifyCenter(id, centerModifyRequestDTO); + centerModifyRequestDTO.setCenterId(id); + + centerCommandService.modifyCenter(centerModifyRequestDTO); return ResponseEntity.ok(CenterResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterDeleteRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterDeleteRequestDTO.java deleted file mode 100644 index 4b7ec007..00000000 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterDeleteRequestDTO.java +++ /dev/null @@ -1,16 +0,0 @@ -package stanl_2.final_backend.domain.center.command.application.dto.request; - -import lombok.*; - -@AllArgsConstructor -@NoArgsConstructor -@Setter -@Getter -@ToString -public class CenterDeleteRequestDTO { - private String name; - private String address; - private String phone; - private Integer memberCount; - private String operatingAt; -} diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyRequestDTO.java index 494587e7..b0e374cd 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyRequestDTO.java @@ -6,9 +6,8 @@ @NoArgsConstructor @Setter @Getter -@ToString public class CenterModifyRequestDTO { - private String id; + private String centerId; private String name; private String address; private String phone; diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterRegistRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterRegistRequestDTO.java index c3a967fd..9cd5cbac 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterRegistRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterRegistRequestDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Setter @Getter -@ToString public class CenterRegistRequestDTO { private String name; private String address; diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandService.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandService.java index d5d137a5..da7c976b 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandService.java @@ -8,6 +8,6 @@ public interface CenterCommandService { void registCenter(CenterRegistRequestDTO centerRegistRequestDTO); - void modifyCenter(String id, CenterModifyRequestDTO centerModifyRequestDTO); + void modifyCenter(CenterModifyRequestDTO centerModifyRequestDTO); void deleteCenter(String id); } diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java b/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java index 0199ba4f..8aa111b4 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java @@ -25,7 +25,7 @@ public class Center { parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "CEN") ) @Column(name ="CENT_ID") - private String id; + private String centerId; @Column(name = "CENT_NAME", nullable = false) private String name; @@ -54,16 +54,12 @@ public class Center { @Column(name = "ACTIVE", nullable = false) private Boolean active = true; - - /* 설명. updatedAt 자동화 */ - // Insert 되기 전에 실행 @PrePersist private void prePersist() { this.createdAt = getCurrentTime(); this.updatedAt = this.createdAt; } - // Update 되기 전에 실행 @PreUpdate private void preUpdate() { this.updatedAt = getCurrentTime(); diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java index c8c87681..405df59f 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java @@ -1,23 +1,21 @@ package stanl_2.final_backend.domain.center.command.domain.service; -import lombok.extern.slf4j.Slf4j; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.A_sample.common.exception.SampleCommonException; -import stanl_2.final_backend.domain.A_sample.common.exception.SampleErrorCode; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterModifyRequestDTO; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistRequestDTO; import stanl_2.final_backend.domain.center.command.application.service.CenterCommandService; import stanl_2.final_backend.domain.center.command.domain.aggregate.entity.Center; import stanl_2.final_backend.domain.center.command.domain.repository.CenterRepository; +import stanl_2.final_backend.domain.center.common.exception.CenterCommonException; +import stanl_2.final_backend.domain.center.common.exception.CenterErrorCode; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; -@Slf4j @Service("commandCenterServiceImpl") public class CenterCommandServiceImpl implements CenterCommandService { @@ -42,13 +40,13 @@ public void registCenter(CenterRegistRequestDTO centerRegistRequestDTO) { @Override @Transactional - public void modifyCenter(String id, CenterModifyRequestDTO centerModifyRequestDTO) { - Center center = centerRepository.findById(id) - .orElseThrow(() -> new SampleCommonException(SampleErrorCode.CENTER_NOT_FOUND)); + public void modifyCenter(CenterModifyRequestDTO centerModifyRequestDTO) { + Center center = centerRepository.findById(centerModifyRequestDTO.getCenterId()) + .orElseThrow(() -> new CenterCommonException(CenterErrorCode.CENTER_NOT_FOUND)); Center updateCenter = modelMapper.map(centerModifyRequestDTO, Center.class); - updateCenter.setId(center.getId()); + updateCenter.setCenterId(center.getCenterId()); updateCenter.setCreatedAt(center.getCreatedAt()); updateCenter.setUpdatedAt(getCurrentTime()); updateCenter.setActive(center.getActive()); @@ -59,9 +57,8 @@ public void modifyCenter(String id, CenterModifyRequestDTO centerModifyRequestDT @Override @Transactional public void deleteCenter(String id) { - Center center = centerRepository.findById(id) - .orElseThrow(() -> new SampleCommonException(SampleErrorCode.CENTER_NOT_FOUND)); + .orElseThrow(() -> new CenterCommonException(CenterErrorCode.CENTER_NOT_FOUND)); center.setActive(false); center.setDeletedAt(getCurrentTime()); diff --git a/src/main/java/stanl_2/final_backend/domain/center/common/util/RequestList.java b/src/main/java/stanl_2/final_backend/domain/center/common/util/RequestList.java deleted file mode 100644 index c625aa6b..00000000 --- a/src/main/java/stanl_2/final_backend/domain/center/common/util/RequestList.java +++ /dev/null @@ -1,14 +0,0 @@ -package stanl_2.final_backend.domain.center.common.util; - -import lombok.*; -import org.springframework.data.domain.Pageable; - -@Builder -@Getter -@Setter -@AllArgsConstructor -@NoArgsConstructor -public class RequestList { - private T data; - private Pageable pageable; -} diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java b/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java index a7873f0c..9d68463c 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java @@ -5,12 +5,14 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.center.common.response.CenterResponseMessage; import stanl_2.final_backend.domain.center.query.dto.CenterSearchRequestDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; @@ -118,4 +120,17 @@ public ResponseEntity getCenterListBySearch(@RequestParam .build()); } + @Operation(summary = "매장 엑셀 다운") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "매장 엑셀 다운 테스트 성공", + content = {@Content(schema = @Schema(implementation = CenterResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/excel") + public void exportCenter(HttpServletResponse response){ + + centerQueryService.exportCenterToExcel(response); + } + } diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterExcelDownload.java b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterExcelDownload.java new file mode 100644 index 00000000..d436ea3d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterExcelDownload.java @@ -0,0 +1,26 @@ +package stanl_2.final_backend.domain.center.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import stanl_2.final_backend.global.excel.ExcelColumnName; + +@Getter +@AllArgsConstructor +public class CenterExcelDownload { + + @ExcelColumnName(name = "매장번호") + private String centerId; + + @ExcelColumnName(name = "지점 이름") + private String name; + + @ExcelColumnName(name = "주소") + private String address; + + @ExcelColumnName(name = "사원 수") + private Integer memberCount; + + @ExcelColumnName(name = "운영시간") + private String operatingAt; + +} diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSearchRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSearchRequestDTO.java index f5aab488..4dc96cef 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSearchRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSearchRequestDTO.java @@ -1,12 +1,14 @@ package stanl_2.final_backend.domain.center.query.dto; -import lombok.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; @AllArgsConstructor @NoArgsConstructor @Getter @Setter -@ToString public class CenterSearchRequestDTO { private String id; private String name; diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectAllDTO.java index bb127578..dc64074e 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectAllDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectAllDTO.java @@ -1,12 +1,14 @@ package stanl_2.final_backend.domain.center.query.dto; -import lombok.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; @AllArgsConstructor @NoArgsConstructor @Getter @Setter -@ToString public class CenterSelectAllDTO { private String id; private String name; @@ -16,6 +18,4 @@ public class CenterSelectAllDTO { private String operatingAt; private String createdAt; private String updatedAt; - private String deletedAt; - private Boolean active; } diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectIdDTO.java b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectIdDTO.java index 46767464..9a96216a 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectIdDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectIdDTO.java @@ -1,12 +1,15 @@ package stanl_2.final_backend.domain.center.query.dto; -import lombok.*; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; @AllArgsConstructor @NoArgsConstructor @Getter @Setter -@ToString public class CenterSelectIdDTO { private String id; diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java b/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java index 6a0867cf..ca70bedc 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java @@ -2,15 +2,12 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; -import org.springframework.data.domain.Page; -import stanl_2.final_backend.domain.center.common.util.RequestList; +import stanl_2.final_backend.domain.center.query.dto.CenterExcelDownload; import stanl_2.final_backend.domain.center.query.dto.CenterSearchRequestDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectIdDTO; -import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySearchDTO; import java.util.List; -import java.util.Map; @Mapper public interface CenterMapper { @@ -28,4 +25,8 @@ List findCenterBySearch(@Param("size") int size , @Param("centerSearchRequestDTO") CenterSearchRequestDTO centerSearchRequestDTO); List findCenterListBySearch(@Param("centerSearchRequestDTO") CenterSearchRequestDTO centerSearchRequestDTO); + + String findNameById(@Param("id") String id); + + List findCentersForExcel(); } diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryService.java b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryService.java index aba736e9..95ecffd6 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryService.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.center.query.service; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -9,7 +10,6 @@ import java.util.List; -@Service public interface CenterQueryService { CenterSelectIdDTO selectByCenterId(String id); @@ -18,4 +18,8 @@ public interface CenterQueryService { Page selectBySearch(CenterSearchRequestDTO centerSearchRequestDTO, Pageable pageable); List selectCenterListBySearch(CenterSearchRequestDTO centerSearchRequestDTO); + + String selectNameById(String id); + + void exportCenterToExcel(HttpServletResponse response); } diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryServiceImpl.java index 9daccff3..0e064cac 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryServiceImpl.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.center.query.service; +import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; @@ -8,10 +9,13 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.A_sample.query.dto.SampleExcelDownload; +import stanl_2.final_backend.domain.center.query.dto.CenterExcelDownload; import stanl_2.final_backend.domain.center.query.dto.CenterSearchRequestDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectIdDTO; import stanl_2.final_backend.domain.center.query.repository.CenterMapper; +import stanl_2.final_backend.global.excel.ExcelUtilsV1; import java.util.List; @@ -21,11 +25,13 @@ public class CenterQueryServiceImpl implements CenterQueryService { private final CenterMapper centerMapper; private final RedisTemplate redisTemplate; + private final ExcelUtilsV1 excelUtilsV1; @Autowired - public CenterQueryServiceImpl(CenterMapper centerMapper, RedisTemplate redisTemplate) { + public CenterQueryServiceImpl(CenterMapper centerMapper, RedisTemplate redisTemplate, ExcelUtilsV1 excelUtilsV1) { this.centerMapper = centerMapper; this.redisTemplate = redisTemplate; + this.excelUtilsV1 = excelUtilsV1; } @Override @@ -71,5 +77,18 @@ public List selectCenterListBySearch(CenterSearchRequestDTO return centerList; } + @Override + @Transactional + public String selectNameById(String id) { + + String centerName = centerMapper.findNameById(id); + return centerName; + } + @Override + public void exportCenterToExcel(HttpServletResponse response) { + List centerList = centerMapper.findCentersForExcel(); + + excelUtilsV1.download(CenterExcelDownload.class, centerList, "centerExcel", response); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java index 59785006..47b96f79 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java @@ -94,12 +94,10 @@ public class Contract { private String status = "WAIT"; @Column(name = "CONR_NO_OF_VEH", nullable = false) - @ColumnDefault("1") - private String numberOfVehicles; + private Integer numberOfVehicles = 1; @Column(name = "CONR_TOTA_SALE", nullable = false) - @ColumnDefault("0") - private String totalSales; + private Integer totalSales = 0; @Lob @Column(name = "CREATED_URL", nullable = false, columnDefinition = "TEXT") diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index 9d683c43..b4b1fd16 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -207,13 +207,13 @@ public void modifyContractStatus(ContractStatusModifyDTO contractStatusModifyDTO // 제품 재고 수 줄이기 ProductSelectIdDTO productSelectIdDTO = productQueryService.selectByProductSerialNumber(contract.getSerialNum()); String productId = productSelectIdDTO.getId(); - productCommandService.modifyProductStock(productId, 1); + productCommandService.modifyProductStock(productId, contract.getNumberOfVehicles()); } else if (contractStatusModifyDTO.getStatus().equals("CANCLED")) { salesHistoryCommandService.deleteSalesHistory(contract.getContractId()); ProductSelectIdDTO productSelectIdDTO = productQueryService.selectByProductSerialNumber(contract.getSerialNum()); String productId = productSelectIdDTO.getId(); - productCommandService.deleteProductStock(productId, 1); + productCommandService.deleteProductStock(productId, contract.getNumberOfVehicles()); } } } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java index 71f2f1d6..9e4e8b8f 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java @@ -29,8 +29,8 @@ public class ContractSeletIdDTO { private String deliveryDate; private String deliveryLocation; private String status; - private String NumberOfVehicles; - private String totalSales; + private Integer NumberOfVehicles; + private Integer totalSales; private String carName; private String createdUrl; private String updatedUrl; diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java index ddc4e1e2..6ed35de8 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java @@ -15,4 +15,6 @@ public interface CustomerQueryService { Page findCustomerByCondition(Pageable pageable, CustomerSearchDTO customerSearchDTO) throws GeneralSecurityException; CustomerDTO selectCustomerInfoByPhone(String customerPhone) throws GeneralSecurityException; + + String selectCustomerNameById(String customerId) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java index ec368a24..99b4ec72 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java @@ -109,4 +109,15 @@ public CustomerDTO selectCustomerInfoByPhone(String customerPhone) throws Genera return customerInfoDTO; } + + @Override + @Transactional(readOnly = true) + public String selectCustomerNameById(String customerId) throws GeneralSecurityException { + + CustomerDTO customerInfoDTO = customerMapper.selectCustomerInfoById(customerId); + + String customerName = aesUtils.decrypt(customerInfoDTO.getName()); + + return customerName; + } } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/controller/EvaluationController.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/controller/EvaluationController.java index e8ad3713..e5f7cd0a 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/controller/EvaluationController.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/controller/EvaluationController.java @@ -25,7 +25,7 @@ public EvaluationController(EvaluationCommandService evaluationCommandService) { this.evaluationCommandService = evaluationCommandService; } - @Operation(summary = "평가서 요청 테스트") + @Operation(summary = "평가서 등록") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = EvaluationResponseMessage.class))}) @@ -37,19 +37,12 @@ public ResponseEntity postEvaluation(@RequestBody Eva return ResponseEntity.ok(EvaluationResponseMessage.builder() .httpStatus(200) - .msg("성공") + .msg("평가서 등록 성공") .result(null) .build()); } - /** - * [PUT] http://localhost:7777/api/v1/sample?mem_id=SAM_000001 - * Request - * { - * "name": "abcc" - * } - * */ - @Operation(summary = "평가서 수정 테스트") + @Operation(summary = "평가서 수정") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))}) @@ -58,19 +51,18 @@ public ResponseEntity postEvaluation(@RequestBody Eva public ResponseEntity putEvaluation(@PathVariable String id, @RequestBody EvaluationModifyDTO evaluationModifyRequestDTO) { - evaluationCommandService.modifyEvaluation(id,evaluationModifyRequestDTO); + evaluationModifyRequestDTO.setEvaluationId(id); + + evaluationCommandService.modifyEvaluation(evaluationModifyRequestDTO); return ResponseEntity.ok(EvaluationResponseMessage.builder() .httpStatus(200) - .msg("성공") + .msg("평가서 수정 성공") .result(null) .build()); } - /** - * [DELETE] http://localhost:7777/api/v1/sample?mem_id=SAM_000001 - * */ - @Operation(summary = "평가서 삭제 테스트") + @Operation(summary = "평가서 삭제") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = EvaluationResponseMessage.class))}) @@ -82,7 +74,7 @@ public ResponseEntity deleteEvaluation(@PathVariable return ResponseEntity.ok(EvaluationResponseMessage.builder() .httpStatus(200) - .msg("성공") + .msg("평가서 삭제 성공") .result(null) .build()); } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationModifyDTO.java index b2e30198..0178ddba 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationModifyDTO.java @@ -6,8 +6,8 @@ @NoArgsConstructor @Setter @Getter -@ToString public class EvaluationModifyDTO { + private String EvaluationId; private String title; private String content; private String centerId; diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationRegistDTO.java index fcc29121..aba39bfb 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationRegistDTO.java @@ -6,12 +6,10 @@ @NoArgsConstructor @Setter @Getter -@ToString public class EvaluationRegistDTO { private String title; private String content; private String memberId; private String writerId; - /* 설명. 레포지토리에서 불러오게 할지, 서비스 층에 만들지 고민 중 (centerId)*/ private String centerId; } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/service/EvaluationCommandService.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/service/EvaluationCommandService.java index 5fd0a3cd..85ac4546 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/service/EvaluationCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/service/EvaluationCommandService.java @@ -7,7 +7,7 @@ public interface EvaluationCommandService { void registerEvaluation(EvaluationRegistDTO evaluationRegistRequestDTO); - void modifyEvaluation(String id, EvaluationModifyDTO evaluationModifyRequestDTO); + void modifyEvaluation(EvaluationModifyDTO evaluationModifyRequestDTO); void deleteEvaluation(String id); } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/aggregate/entity/Evaluation.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/aggregate/entity/Evaluation.java index a582a4b9..df3a709a 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/aggregate/entity/Evaluation.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/aggregate/entity/Evaluation.java @@ -23,7 +23,7 @@ public class Evaluation { @GeneratedValue(generator = "PrefixGeneratorConfig") @GenericGenerator(name = "PrefixGeneratorConfig", type = PrefixGeneratorConfig.class, - parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "EVAL") + parameters = @org.hibernate.annotations.Parameter(name = "prefix", value = "EVA") ) @Column(name = "EVAL_ID") private String evaluationId; @@ -55,15 +55,12 @@ public class Evaluation { @Column(name = "WRI_ID", nullable = false) private String writerId; - /* 설명. updatedAt 자동화 */ - // Insert 되기 전에 실행 @PrePersist private void prePersist() { this.createdAt = getCurrentTime(); this.updatedAt = this.createdAt; } - // Update 되기 전에 실행 @PreUpdate private void preUpdate() { this.updatedAt = getCurrentTime(); diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/service/EvaluationCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/service/EvaluationCommandServiceImpl.java index 6708a479..aecb4565 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/service/EvaluationCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/service/EvaluationCommandServiceImpl.java @@ -44,8 +44,8 @@ public void registerEvaluation(EvaluationRegistDTO evaluationRegistRequestDTO) { @Override @Transactional - public void modifyEvaluation(String id, EvaluationModifyDTO evaluationModifyRequestDTO) { - Evaluation evaluation = evaluationRepository.findById(id) + public void modifyEvaluation(EvaluationModifyDTO evaluationModifyRequestDTO) { + Evaluation evaluation = evaluationRepository.findById(evaluationModifyRequestDTO.getEvaluationId()) .orElseThrow(() -> new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND)); Evaluation updateEvaluation = modelMapper.map(evaluationModifyRequestDTO, Evaluation.class); diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/common/exception/EvaluationErrorCode.java b/src/main/java/stanl_2/final_backend/domain/evaluation/common/exception/EvaluationErrorCode.java index 88630557..971dfd3a 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/common/exception/EvaluationErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/common/exception/EvaluationErrorCode.java @@ -38,8 +38,8 @@ public enum EvaluationErrorCode { * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. */ - SAMPLE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "sample 데이터를 찾지 못했습니다"), EVALUATION_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "evaluation 데이터를 찾지 못했습니다."), + MEMBER_CENTER_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "member, center 데이터를 찾지 못했습니다."), /** * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. */ diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java index 615632b8..4afecb92 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -34,10 +35,6 @@ public EvaluationController(EvaluationQueryService evaluationQueryService) { this.evaluationQueryService = evaluationQueryService; } - - /** - * [GET] http://localhost:7777/api/v1/sample/SAM_000000001 - * */ @Operation(summary = "평가서 관리자 전체 조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", @@ -61,9 +58,6 @@ public ResponseEntity getAllEvaluationsByManager(Prin .build()); } - /** - * [GET] http://localhost:7777/api/v1/sample/SAM_000000001 - * */ @Operation(summary = "평가서 담당자 전체 조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", @@ -87,9 +81,6 @@ public ResponseEntity getAllEvaluationsByRepresentati .build()); } - /** - * [GET] http://localhost:7777/api/v1/sample/detail/SAM_000000001 - * */ @Operation(summary = "평가서 상세 조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", @@ -172,4 +163,17 @@ public ResponseEntity getEvaluationBySearchByRepresen .build()); } + @Operation(summary = "평가서 엑셀 다운") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "평가서 엑셀 다운 성공", + content = {@Content(schema = @Schema(implementation = ProductResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/excel") + public void exportEvaluation(HttpServletResponse response){ + + evaluationQueryService.exportEvaluationToExcel(response); + } + } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java index 1d8bea8f..257135df 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java @@ -18,5 +18,4 @@ public class EvaluationDTO { private String centerId; private String memberId; private String writerId; - private Collection roles; } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationExcelDownload.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationExcelDownload.java new file mode 100644 index 00000000..bf33f131 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationExcelDownload.java @@ -0,0 +1,29 @@ +package stanl_2.final_backend.domain.evaluation.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import stanl_2.final_backend.global.excel.ExcelColumnName; + +@AllArgsConstructor +@Getter +@Setter +public class EvaluationExcelDownload { + @ExcelColumnName(name = "평가서번호") + private String evalId; + + @ExcelColumnName(name = "제목") + private String title; + + @ExcelColumnName(name = "매장이름") + private String centerId; + + @ExcelColumnName(name = "사원이름") + private String memberId; + + @ExcelColumnName(name = "평가작성자") + private String writerId; + + @ExcelColumnName(name = "작성일자") + private String createdAt; +} diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.java index fa255f58..0b108832 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.java @@ -3,6 +3,7 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationDTO; +import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationExcelDownload; import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationSearchDTO; import java.util.List; @@ -15,7 +16,7 @@ List findEvaluationByCenterId(@Param("size") int size ,@Param("offset") int offset ,@Param("centerId") String centerId); - EvaluationDTO findEvaluationById(String id); + EvaluationDTO findEvaluationById(@Param("id") String id); List findAllEvaluations(@Param("size") int size ,@Param("offset") int offset); @@ -35,4 +36,6 @@ List findEvaluationByCenterIdAndSearch(@Param("size") int size int findEvaluationBySearchCount(@Param("evaluationSearchDTO") EvaluationSearchDTO evaluationSearchDTO); int findEvaluationByCenterIdAndSearchCount(@Param("evaluationSearchDTO") EvaluationSearchDTO evaluationSearchDTO); + + List findEvaluationForExcel(); } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryService.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryService.java index ebbf3977..bd08f413 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryService.java @@ -1,6 +1,7 @@ package stanl_2.final_backend.domain.evaluation.query.service; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationDTO; @@ -18,4 +19,6 @@ public interface EvaluationQueryService { Page selectEvaluationBySearchByManager(Pageable pageable, EvaluationSearchDTO evaluationSearchDTO) throws GeneralSecurityException; Page selectEvaluationBySearchByRepresentative(Pageable pageable, EvaluationSearchDTO evaluationSearchDTO) throws GeneralSecurityException; + + void exportEvaluationToExcel(HttpServletResponse response); } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java index 0d01b9bd..92189a6d 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java @@ -1,27 +1,30 @@ package stanl_2.final_backend.domain.evaluation.query.service; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.A_sample.query.dto.SampleExcelDownload; +import stanl_2.final_backend.domain.center.query.service.CenterQueryService; import stanl_2.final_backend.domain.evaluation.common.exception.EvaluationCommonException; import stanl_2.final_backend.domain.evaluation.common.exception.EvaluationErrorCode; -import stanl_2.final_backend.domain.evaluation.common.util.EvaluationRequestList; import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationDTO; +import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationExcelDownload; import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationSearchDTO; import stanl_2.final_backend.domain.evaluation.query.repository.EvaluationMapper; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.member.query.service.MemberQueryService; -import stanl_2.final_backend.global.exception.GlobalCommonException; -import stanl_2.final_backend.global.exception.GlobalErrorCode; -import stanl_2.final_backend.global.utils.AESUtils; +import stanl_2.final_backend.domain.product.common.exception.ProductCommonException; +import stanl_2.final_backend.domain.product.common.exception.ProductErrorCode; +import stanl_2.final_backend.global.excel.ExcelUtilsV1; import java.security.GeneralSecurityException; import java.util.List; -import java.util.Map; import java.util.Optional; @Service @@ -30,12 +33,16 @@ public class EvaluationQueryServiceImpl implements EvaluationQueryService { private final EvaluationMapper evaluationMapper; private final MemberQueryService memberQueryService; private final AuthQueryService authQueryService; + private final CenterQueryService centerQueryService; + private final ExcelUtilsV1 excelUtilsV1; @Autowired - public EvaluationQueryServiceImpl(EvaluationMapper evaluationMapper, MemberQueryService memberQueryService, AuthQueryService authQueryService) { + public EvaluationQueryServiceImpl(EvaluationMapper evaluationMapper, MemberQueryService memberQueryService, AuthQueryService authQueryService, CenterQueryService centerQueryService, ExcelUtilsV1 excelUtilsV1) { this.evaluationMapper = evaluationMapper; this.memberQueryService = memberQueryService; this.authQueryService = authQueryService; + this.centerQueryService = centerQueryService; + this.excelUtilsV1 = excelUtilsV1; } @Override @@ -54,21 +61,21 @@ public Page selectAllEvaluationsByManager(EvaluationDTO evaluatio if(evaluationList.isEmpty() || total == 0) { throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); } - - /* 설명. ID를 LOGINID로 바꿔주는 메소드 찾아서... */ -// evaluationList.forEach(evaluation -> { -// try { -// evaluation.setMemberId(evaluation.getMemberName())); -// } catch (GeneralSecurityException e) { -// throw new RuntimeException(e); -// } -// }); + evaluationList.forEach(evaluation -> { + try { + evaluation.setMemberId(memberQueryService.selectNameById(evaluation.getMemberId())); + evaluation.setWriterId(memberQueryService.selectNameById(evaluation.getWriterId())); + evaluation.setCenterId(centerQueryService.selectNameById(evaluation.getCenterId())); + } catch (Exception e) { + throw new EvaluationCommonException(EvaluationErrorCode.MEMBER_CENTER_NOT_FOUND); + } + }); return new PageImpl<>(evaluationList, pageable, total); } @Override @Transactional(readOnly = true) - public Page selectAllEvaluationsByRepresentative(EvaluationDTO evaluationDTO, Pageable pageable) throws GeneralSecurityException { + public Page selectAllEvaluationsByRepresentative(EvaluationDTO evaluationDTO, Pageable pageable) { int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); @@ -80,14 +87,16 @@ public Page selectAllEvaluationsByRepresentative(EvaluationDTO ev throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); } - /* 설명. ID를 LOGINID로 바꿔주는 메소드 찾아서... */ -// evaluationList.forEach(evaluation -> { -// try { -// evaluation.setMemberId(evaluation.getMemberName())); -// } catch (GeneralSecurityException e) { -// throw new RuntimeException(e); -// } -// }); + evaluationList.forEach(evaluation -> { + try { + evaluation.setMemberId(memberQueryService.selectNameById(evaluation.getMemberId())); + evaluation.setWriterId(memberQueryService.selectNameById(evaluation.getWriterId())); + evaluation.setCenterId(centerQueryService.selectNameById(evaluation.getCenterId())); + } catch (Exception e) { + throw new EvaluationCommonException(EvaluationErrorCode.MEMBER_CENTER_NOT_FOUND); + } + }); + return new PageImpl<>(evaluationList, pageable, total); } @@ -128,19 +137,20 @@ public Page selectEvaluationBySearchByManager(Pageable pageable, if(evaluationList.isEmpty() || total == 0){ throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); } - /* 설명. ID를 LOGINID로 바꿔주는 메소드 찾아서... */ -// evaluationList.forEach(evaluation -> { -// try { -// evaluation.setMemberId(evaluation.getMemberName())); -// } catch (GeneralSecurityException e) { -// throw new RuntimeException(e); -// } -// }); + evaluationList.forEach(evaluation -> { + try { + evaluation.setMemberId(memberQueryService.selectNameById(evaluation.getMemberId())); + evaluation.setWriterId(memberQueryService.selectNameById(evaluation.getWriterId())); + evaluation.setCenterId(centerQueryService.selectNameById(evaluation.getCenterId())); + } catch (Exception e) { + throw new EvaluationCommonException(EvaluationErrorCode.MEMBER_CENTER_NOT_FOUND); + } + }); return new PageImpl<>(evaluationList, pageable, total); } @Override @Transactional(readOnly = true) - public Page selectEvaluationBySearchByRepresentative(Pageable pageable, EvaluationSearchDTO evaluationSearchDTO) throws GeneralSecurityException { + public Page selectEvaluationBySearchByRepresentative(Pageable pageable, EvaluationSearchDTO evaluationSearchDTO){ int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); @@ -161,7 +171,38 @@ public Page selectEvaluationBySearchByRepresentative(Pageable pag throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); } + evaluationList.forEach(evaluation -> { + try { + evaluation.setMemberId(memberQueryService.selectNameById(evaluation.getMemberId())); + evaluation.setWriterId(memberQueryService.selectNameById(evaluation.getWriterId())); + evaluation.setCenterId(centerQueryService.selectNameById(evaluation.getCenterId())); + } catch (Exception e) { + throw new EvaluationCommonException(EvaluationErrorCode.MEMBER_CENTER_NOT_FOUND); + } + }); + return new PageImpl<>(evaluationList, pageable, total); + } + + @Override + public void exportEvaluationToExcel(HttpServletResponse response) { + List evaluationList = evaluationMapper.findEvaluationForExcel(); + + if(evaluationList == null) { + throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); + } + + evaluationList.forEach(evaluation -> { + try { + evaluation.setMemberId(memberQueryService.selectNameById(evaluation.getMemberId())); + evaluation.setWriterId(memberQueryService.selectNameById(evaluation.getWriterId())); + evaluation.setCenterId(centerQueryService.selectNameById(evaluation.getCenterId())); + } catch (Exception e) { + throw new EvaluationCommonException(EvaluationErrorCode.MEMBER_CENTER_NOT_FOUND); + } + }); + + excelUtilsV1.download(EvaluationExcelDownload.class, evaluationList, "EvaluationExcel", response); } } diff --git a/src/main/java/stanl_2/final_backend/domain/product/command/application/command/service/ProductCommandService.java b/src/main/java/stanl_2/final_backend/domain/product/command/application/command/service/ProductCommandService.java index 49622f36..d420c1bf 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/command/application/command/service/ProductCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/product/command/application/command/service/ProductCommandService.java @@ -1,7 +1,7 @@ package stanl_2.final_backend.domain.product.command.application.command.service; public interface ProductCommandService { - void modifyProductStock(String productId, int numberOfVehicles); + void modifyProductStock(String productId, Integer numberOfVehicles); - void deleteProductStock(String productId, int numberOfVehicles); + void deleteProductStock(String productId, Integer numberOfVehicles); } diff --git a/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/service/ProductCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/service/ProductCommandServiceImpl.java index 6853b9fa..ce0b1536 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/service/ProductCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/service/ProductCommandServiceImpl.java @@ -1,6 +1,7 @@ package stanl_2.final_backend.domain.product.command.application.domain.service; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.product.command.application.command.service.ProductCommandService; import stanl_2.final_backend.domain.product.command.application.domain.aggregate.entity.Product; import stanl_2.final_backend.domain.product.command.application.domain.repository.ProductRepository; @@ -17,7 +18,8 @@ public ProductCommandServiceImpl(ProductRepository productRepository) { } @Override - public void modifyProductStock(String productId, int numberOfVehicles) { + @Transactional + public void modifyProductStock(String productId, Integer numberOfVehicles) { Product product = productRepository.findById(productId) .orElseThrow(() -> new ProductCommonException(ProductErrorCode.PRODUCT_NOT_FOUND)); @@ -27,7 +29,8 @@ public void modifyProductStock(String productId, int numberOfVehicles) { } @Override - public void deleteProductStock(String productId, int numberOfVehicles) { + @Transactional + public void deleteProductStock(String productId, Integer numberOfVehicles) { Product product = productRepository.findById(productId) .orElseThrow(() -> new ProductCommonException(ProductErrorCode.PRODUCT_NOT_FOUND)); diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java b/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java index 4b954f3b..e346ec75 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java @@ -5,12 +5,14 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.center.common.response.CenterResponseMessage; import stanl_2.final_backend.domain.product.common.response.ProductResponseMessage; import stanl_2.final_backend.domain.product.query.dto.ProductSearchRequestDTO; @@ -95,4 +97,17 @@ public ResponseEntity getProductBySearch(@RequestParam M .result(responseProducts) .build()); } + + @Operation(summary = "제품 엑셀 다운") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "제품 엑셀 다운 성공", + content = {@Content(schema = @Schema(implementation = ProductResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/excel") + public void exportProduct(HttpServletResponse response){ + + productQueryService.exportProductsToExcel(response); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductExcelDownload.java b/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductExcelDownload.java new file mode 100644 index 00000000..cf60069f --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductExcelDownload.java @@ -0,0 +1,24 @@ +package stanl_2.final_backend.domain.product.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import stanl_2.final_backend.global.excel.ExcelColumnName; + +@Getter +@AllArgsConstructor +public class ProductExcelDownload { + @ExcelColumnName(name = "제품번호") + private String productId; + + @ExcelColumnName(name = "제품 이름") + private String name; + + @ExcelColumnName(name = "일련번호") + private String serialNumber; + + @ExcelColumnName(name = "차량가액") + private Integer cost; + + @ExcelColumnName(name = "재고 수") + private String stock; +} diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java b/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java index 8e0ac637..de3c4950 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java @@ -1,6 +1,7 @@ package stanl_2.final_backend.domain.product.query.repository; import org.apache.ibatis.annotations.Mapper; +import stanl_2.final_backend.domain.A_sample.query.dto.SampleExcelDownload; import stanl_2.final_backend.domain.product.common.util.RequestList; import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; @@ -20,4 +21,6 @@ public interface ProductMapper { int findProductBySearchCount(Map paramMap); ProductSelectIdDTO findProductBySerialNumber(String serialNumber); + + List findProductsForExcel(); } diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryService.java b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryService.java index 0d2faf49..42ea98a6 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryService.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.product.query.service; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -16,6 +17,7 @@ public interface ProductQueryService { Page> selectProductBySearch(Map paramMap); - @Transactional ProductSelectIdDTO selectByProductSerialNumber(String id); + + void exportProductsToExcel(HttpServletResponse response); } diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java index 9145422e..7aa7cab2 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java @@ -1,14 +1,19 @@ package stanl_2.final_backend.domain.product.query.service; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.A_sample.query.dto.SampleExcelDownload; +import stanl_2.final_backend.domain.product.common.exception.ProductCommonException; +import stanl_2.final_backend.domain.product.common.exception.ProductErrorCode; import stanl_2.final_backend.domain.product.common.util.RequestList; import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; import stanl_2.final_backend.domain.product.query.repository.ProductMapper; +import stanl_2.final_backend.global.excel.ExcelUtilsV1; import java.util.List; import java.util.Map; @@ -17,10 +22,12 @@ public class ProductQueryServiceImpl implements ProductQueryService { private final ProductMapper productMapper; + private final ExcelUtilsV1 excelUtilsV1; @Autowired - public ProductQueryServiceImpl(ProductMapper productMapper) { + public ProductQueryServiceImpl(ProductMapper productMapper, ExcelUtilsV1 excelUtilsV1) { this.productMapper = productMapper; + this.excelUtilsV1 = excelUtilsV1; } @Override @@ -34,6 +41,10 @@ public Page> selectAll(Pageable pageable) { int total = productMapper.findProductCount(); + if(productList == null || total == 0) { + throw new ProductCommonException(ProductErrorCode.PRODUCT_NOT_FOUND); + } + return new PageImpl<>(productList, pageable, total); } @@ -43,6 +54,10 @@ public Page> selectAll(Pageable pageable) { public ProductSelectIdDTO selectByProductId(String id) { ProductSelectIdDTO productSelectIdDTO = productMapper.findProductById(id); + if(productSelectIdDTO == null) { + throw new ProductCommonException(ProductErrorCode.PRODUCT_NOT_FOUND); + } + return productSelectIdDTO; } @@ -56,6 +71,10 @@ public Page> selectProductBySearch(Map param int total = productMapper.findProductBySearchCount(paramMap); + if(productList == null || total == 0) { + throw new ProductCommonException(ProductErrorCode.PRODUCT_NOT_FOUND); + } + return new PageImpl<>(productList, pageable, total); } @@ -63,10 +82,23 @@ public Page> selectProductBySearch(Map param @Transactional public ProductSelectIdDTO selectByProductSerialNumber(String id) { - /* 설명. 상세 조회 시, Mapper에서 product와 productOption join해서 보여줄 것 */ ProductSelectIdDTO productSelectIdDTO = productMapper.findProductBySerialNumber(id); + if(productSelectIdDTO == null) { + throw new ProductCommonException(ProductErrorCode.PRODUCT_NOT_FOUND); + } + return productSelectIdDTO; } + @Override + public void exportProductsToExcel(HttpServletResponse response) { + List productList = productMapper.findProductsForExcel(); + + if(productList == null) { + throw new ProductCommonException(ProductErrorCode.PRODUCT_NOT_FOUND); + } + + excelUtilsV1.download(SampleExcelDownload.class, productList, "productExcel", response); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/command/application/dto/SalesHistoryRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/command/application/dto/SalesHistoryRegistDTO.java index 5f51d4d5..bbd3032f 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/command/application/dto/SalesHistoryRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/command/application/dto/SalesHistoryRegistDTO.java @@ -12,7 +12,7 @@ public class SalesHistoryRegistDTO { private Integer numberOfVehicles; private Integer totalSales; - private Integer incentive; + private Double incentive; private String contractId; private String customerInfoId; private String productId; diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/aggregate/entity/SalesHistory.java b/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/aggregate/entity/SalesHistory.java index 740dc6d9..d19f5ec8 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/aggregate/entity/SalesHistory.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/aggregate/entity/SalesHistory.java @@ -35,7 +35,7 @@ public class SalesHistory { private Integer totalSales; @Column(name = "SAL_HIST_INCE", nullable = false) - private Integer incentive; + private Double incentive; @Column(name = "CREATED_AT", nullable = false) private String createdAt; diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/service/SalesHistoryCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/service/SalesHistoryCommandServiceImpl.java index 08b93977..1c126fac 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/service/SalesHistoryCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/service/SalesHistoryCommandServiceImpl.java @@ -22,13 +22,11 @@ public class SalesHistoryCommandServiceImpl implements SalesHistoryCommandService { private final SalesHistoryRepository salesHistoryRepository; - private final ModelMapper modelMapper; private final ContractQueryService contractQueryService; @Autowired - public SalesHistoryCommandServiceImpl(SalesHistoryRepository salesHistoryRepository, ModelMapper modelMapper, ContractQueryService contractQueryService) { + public SalesHistoryCommandServiceImpl(SalesHistoryRepository salesHistoryRepository,ContractQueryService contractQueryService) { this.salesHistoryRepository = salesHistoryRepository; - this.modelMapper = modelMapper; this.contractQueryService = contractQueryService; } @@ -40,9 +38,7 @@ private String getCurrentTime() { @Override @Transactional public void registerSalesHistory(String contractId) { - /* 설명. 넘어오는 값은 계약서 승인 후 - 계약서 id */ ContractSeletIdDTO salesHistoryDTO = new ContractSeletIdDTO(); - /* 설명. 값 주입 dto */ SalesHistoryRegistDTO salesHistoryRegistDTO = new SalesHistoryRegistDTO(); salesHistoryDTO.setContractId(contractId); @@ -54,9 +50,8 @@ public void registerSalesHistory(String contractId) { } salesHistoryRegistDTO.setContractId(contractId); - /* 설명. contract 자료형 변환 후 주석 풀 예정 */ -// salesHistoryRegistDTO.setTotalSales(responseContract.getTotalSales()); -// salesHistoryRegistDTO.setNumberOfVehicles(responseContract.getNumberOfVehicles()); + salesHistoryRegistDTO.setTotalSales(responseContract.getTotalSales()); + salesHistoryRegistDTO.setNumberOfVehicles(responseContract.getNumberOfVehicles()); salesHistoryRegistDTO.setCustomerInfoId(responseContract.getCustomerId()); salesHistoryRegistDTO.setProductId(responseContract.getProductId()); salesHistoryRegistDTO.setCenterId(responseContract.getCenterId()); @@ -64,13 +59,13 @@ public void registerSalesHistory(String contractId) { String customerPurchaseCondition = responseContract.getCustomerPurchaseCondition(); -// if(customerPurchaseCondition.equals("CASH")){ -// salesHistoryRegistDTO.setIncentive(responseContract.getTotalSales() * 0.035); -// }else if(customerPurchaseCondition.equals("INSTALLMENT")){ -// salesHistoryRegistDTO.setIncentive(responseContract.getTotalSales() * 0.04); -// }else if(customerPurchaseCondition.equals("LEASE")){ -// salesHistoryRegistDTO.setIncentive(responseContract.getTotalSales() * 0.045); -// } + if(customerPurchaseCondition.equals("CASH")){ + salesHistoryRegistDTO.setIncentive(responseContract.getTotalSales() * 0.035); + }else if(customerPurchaseCondition.equals("INSTALLMENT")){ + salesHistoryRegistDTO.setIncentive(responseContract.getTotalSales() * 0.04); + }else if(customerPurchaseCondition.equals("LEASE")){ + salesHistoryRegistDTO.setIncentive(responseContract.getTotalSales() * 0.045); + } } @Override diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/common/exception/SalesHistoryErrorCode.java b/src/main/java/stanl_2/final_backend/domain/sales_history/common/exception/SalesHistoryErrorCode.java index e177ec74..384cdbc5 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/common/exception/SalesHistoryErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/common/exception/SalesHistoryErrorCode.java @@ -38,9 +38,11 @@ public enum SalesHistoryErrorCode { * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다. * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. */ - SAMPLE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "sample 데이터를 찾지 못했습니다"), - SALES_HISTORY_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "판매내역 데이터를 찾지 못했습니다"), - CONTRACT_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "계약서 데이터를 찾지 못했습니다"), + MEMBER_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "사원 데이터를 찾지 못했습니다"), + CUSTOMER_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "고객 데이터를 찾지 못했습니다"), + CENTER_NOT_FOUND(404003, HttpStatus.NOT_FOUND, "센터 데이터를 찾지 못했습니다"), + SALES_HISTORY_NOT_FOUND(404004, HttpStatus.NOT_FOUND, "판매내역 데이터를 찾지 못했습니다"), + CONTRACT_NOT_FOUND(404005, HttpStatus.NOT_FOUND, "계약서 데이터를 찾지 못했습니다"), /** * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. */ diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java index e33bb182..13f5d7b5 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java @@ -5,19 +5,19 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; -import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.product.common.response.ProductResponseMessage; import stanl_2.final_backend.domain.sales_history.common.response.SalesHistoryResponseMessage; import stanl_2.final_backend.domain.sales_history.query.dto.*; import stanl_2.final_backend.domain.sales_history.query.service.SalesHistoryQueryService; import java.security.Principal; -import java.util.List; import java.util.Map; @RestController(value = "querySalesHistoryController") @@ -30,31 +30,6 @@ public SalesHistoryController(SalesHistoryQueryService salesHistoryQueryService) this.salesHistoryQueryService = salesHistoryQueryService; } - /* 설명. - * - 사원 -> 일주일별, 월별, 연별(조회기간별) - - -> 사원 랭킹(윈도우 함수 이용) - -* default: 월별 판매내역 List -* - 1. (지역: 센터 검색 조회(NULL 혹은 '지역') - - 2. (매장명: NULL 혹은 1) - -1- NULL일 시 1번 결과를 다시 받는다. -> 그 행의 지역 검색결과(centerList)을 바탕으로 mem_id 조회 - -2- 1일 시 selectCenterById를 통해서 반환 값을 member에 where =cent_id)인 값 반환 - - 3. (이름:null 혹은 1) - - null 일 시-2-의 리스트를 다시 받는다. -> select윈도우 함수 이용해서 멤버 결과 뿌려주기 - - 1 일시 하나의 멤버 - - 4. 분류에 따라서 뿌려줌 - * */ - - - /* 설명. todo - * 1. (사원별, 매장별 순위) 각 실적, 수당, 매출액 별로 인자를 통해 동적 쿼리로 처리 가능한지 여부(내림차순) - * 2. 판매내역 날짜별로(조회기간만), 매장별, 사원별 - * */ @Operation(summary = "판매내역 조회(사원, 관리자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", @@ -451,5 +426,17 @@ public ResponseEntity getStatisticsCenterBySearchYe .build()); } + @Operation(summary = "판매내역 엑셀 다운") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "판매내역 엑셀 다운 성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/excel") + public void exporSalesHistory(HttpServletResponse response){ + + salesHistoryQueryService.exportSalesHistoryToExcel(response); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryExcelDownload.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryExcelDownload.java new file mode 100644 index 00000000..66b7e361 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryExcelDownload.java @@ -0,0 +1,33 @@ +package stanl_2.final_backend.domain.sales_history.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import stanl_2.final_backend.global.excel.ExcelColumnName; + +@Getter +@AllArgsConstructor +@Setter +public class SalesHistoryExcelDownload { + + @ExcelColumnName(name = "판매내역번호") + private String salesHistoryId; + @ExcelColumnName(name = "차량대수") + private Integer salesHistoryNumberOfVehicles; + @ExcelColumnName(name = "매출액") + private Integer salesHistoryTotalSales; + @ExcelColumnName(name = "수당") + private Integer salesHistoryIncentive; + @ExcelColumnName(name = "작성일자") + private String createdAt; + @ExcelColumnName(name = "계약서번호") + private String contractId; + @ExcelColumnName(name = "고객번호") + private String customerId; + @ExcelColumnName(name = "제품번호") + private String productId; + @ExcelColumnName(name = "담당자번호") + private String memberId; + @ExcelColumnName(name = "매장번호") + private String centerId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java index 68a0da2e..846587ce 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java @@ -85,6 +85,8 @@ List findSalesHistoryBySearch(@Param("size") int size int findSalesHistoryCountBySearch(@Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); SalesHistoryStatisticsAverageDTO findStatisticsAverageBySearch(@Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); + + List findSalesHistoryForExcel(); } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java index e11ef099..e2618532 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.sales_history.query.service; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import stanl_2.final_backend.domain.sales_history.query.dto.*; @@ -38,4 +39,6 @@ public interface SalesHistoryQueryService { Page selectSalesHistoryBySearch(SalesHistorySearchDTO salesHistorySearchDTO, Pageable pageable); SalesHistoryStatisticsAverageDTO selectStatisticsAverageBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); + + void exportSalesHistoryToExcel(HttpServletResponse response); } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java index a371567a..df34a613 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java @@ -1,19 +1,29 @@ package stanl_2.final_backend.domain.sales_history.query.service; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.aop.scope.ScopedProxyUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.A_sample.query.dto.SampleExcelDownload; +import stanl_2.final_backend.domain.center.query.service.CenterQueryService; +import stanl_2.final_backend.domain.customer.query.service.CustomerQueryService; +import stanl_2.final_backend.domain.evaluation.common.exception.EvaluationCommonException; +import stanl_2.final_backend.domain.evaluation.common.exception.EvaluationErrorCode; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.member.query.service.MemberQueryService; +import stanl_2.final_backend.domain.product.common.exception.ProductCommonException; +import stanl_2.final_backend.domain.product.common.exception.ProductErrorCode; import stanl_2.final_backend.domain.sales_history.common.exception.SalesHistoryCommonException; import stanl_2.final_backend.domain.sales_history.common.exception.SalesHistoryErrorCode; import stanl_2.final_backend.domain.sales_history.query.dto.*; import stanl_2.final_backend.domain.sales_history.query.repository.SalesHistoryMapper; -import stanl_2.final_backend.global.utils.AESUtils; import org.springframework.data.redis.core.RedisTemplate; +import stanl_2.final_backend.global.excel.ExcelUtilsV1; import java.time.Duration; import java.util.List; @@ -22,18 +32,22 @@ public class SalesHistoryQueryServiceImpl implements SalesHistoryQueryService { private final SalesHistoryMapper salesHistoryMapper; - private final AESUtils aesUtils; private final MemberQueryService memberQueryService; private final AuthQueryService authQueryService; private final RedisTemplate redisTemplate; + private final CenterQueryService centerQueryService; + private final CustomerQueryService customerQueryService; + private final ExcelUtilsV1 excelUtilsV1; @Autowired - public SalesHistoryQueryServiceImpl(SalesHistoryMapper salesHistoryMapper, AESUtils aesUtils, MemberQueryService memberQueryService, AuthQueryService authQueryService, RedisTemplate redisTemplate) { + public SalesHistoryQueryServiceImpl(SalesHistoryMapper salesHistoryMapper, MemberQueryService memberQueryService, AuthQueryService authQueryService, RedisTemplate redisTemplate, CenterQueryService centerQueryService, CustomerQueryService customerQueryService, ExcelUtilsV1 excelUtilsV1) { this.salesHistoryMapper = salesHistoryMapper; - this.aesUtils = aesUtils; this.memberQueryService = memberQueryService; this.authQueryService = authQueryService; this.redisTemplate = redisTemplate; + this.centerQueryService = centerQueryService; + this.customerQueryService = customerQueryService; + this.excelUtilsV1 = excelUtilsV1; } @Override @@ -51,6 +65,25 @@ public Page selectAllSalesHistoryByEmployee(SalesHistoryS if(salesHistoryList.isEmpty() || total == 0){ throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); } + + salesHistoryList.forEach(salesHistory -> { + try { + salesHistory.setMemberId(memberQueryService.selectNameById(salesHistory.getMemberId())); + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.MEMBER_NOT_FOUND); + } + try { + salesHistory.setCustomerId(customerQueryService.selectCustomerNameById(salesHistory.getCustomerId())); + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.CUSTOMER_NOT_FOUND); + } + try { + salesHistory.setCenterId(centerQueryService.selectNameById(salesHistory.getCenterId())); + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.CENTER_NOT_FOUND); + } + }); + return new PageImpl<>(salesHistoryList, pageable, total); } @@ -61,6 +94,10 @@ public SalesHistorySelectDTO selectSalesHistoryDetail(SalesHistorySelectDTO sale SalesHistorySelectDTO salesHistoryDetailDTO = salesHistoryMapper.findSalesHistoryDetail(salesHistorySelectDTO); + if(salesHistoryDetailDTO == null){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } + return salesHistoryDetailDTO; } @@ -70,7 +107,6 @@ public Page selectSalesHistorySearchByEmployee(SalesHisto int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); -// String searcherId = authQueryService.selectMemberIdByLoginId(salesHistorySearchDTO.getSearcherName()); salesHistorySearchDTO.setSearcherName(authQueryService.selectMemberIdByLoginId(salesHistorySearchDTO.getSearcherName())); List salesHistoryList = salesHistoryMapper.findSalesHistorySearchByEmployee(size,offset, salesHistorySearchDTO); @@ -80,6 +116,25 @@ public Page selectSalesHistorySearchByEmployee(SalesHisto if(salesHistoryList.isEmpty() || total == 0){ throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); } + + salesHistoryList.forEach(salesHistory -> { + try { + salesHistory.setMemberId(memberQueryService.selectNameById(salesHistory.getMemberId())); + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.MEMBER_NOT_FOUND); + } + try { + salesHistory.setCustomerId(customerQueryService.selectCustomerNameById(salesHistory.getCustomerId())); + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.CUSTOMER_NOT_FOUND); + } + try { + salesHistory.setCenterId(centerQueryService.selectNameById(salesHistory.getCenterId())); + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.CENTER_NOT_FOUND); + } + }); + return new PageImpl<>(salesHistoryList, pageable, total); } @@ -88,17 +143,32 @@ public Page selectSalesHistoryBySearch(SalesHistorySearch int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); - System.out.println("service check1"); - List salesHistoryList = salesHistoryMapper.findSalesHistoryBySearch(size,offset, salesHistorySearchDTO); - System.out.println("service check2"); int total = salesHistoryMapper.findSalesHistoryCountBySearch(salesHistorySearchDTO); - System.out.println("service check3"); if(salesHistoryList.isEmpty() || total == 0){ throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); } + + salesHistoryList.forEach(salesHistory -> { + try { + salesHistory.setMemberId(memberQueryService.selectNameById(salesHistory.getMemberId())); + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.MEMBER_NOT_FOUND); + } + try { + salesHistory.setCustomerId(customerQueryService.selectCustomerNameById(salesHistory.getCustomerId())); + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.CUSTOMER_NOT_FOUND); + } + try { + salesHistory.setCenterId(centerQueryService.selectNameById(salesHistory.getCenterId())); + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.CENTER_NOT_FOUND); + } + }); + return new PageImpl<>(salesHistoryList, pageable, total); } @@ -116,6 +186,24 @@ public Page selectAllSalesHistory(Pageable pageable) { throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); } + salesHistoryList.forEach(salesHistory -> { + try { + salesHistory.setMemberId(memberQueryService.selectNameById(salesHistory.getMemberId())); + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.MEMBER_NOT_FOUND); + } + try { + salesHistory.setCustomerId(customerQueryService.selectCustomerNameById(salesHistory.getCustomerId())); + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.CUSTOMER_NOT_FOUND); + } + try { + salesHistory.setCenterId(centerQueryService.selectNameById(salesHistory.getCenterId())); + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.CENTER_NOT_FOUND); + } + }); + return new PageImpl<>(salesHistoryList, pageable, total); } @@ -133,6 +221,10 @@ public SalesHistoryStatisticsDTO selectStatisticsByEmployee(SalesHistorySelectDT // 캐시에 데이터가 없을 경우 DB에서 조회 salesHistoryStatisticsDTO = salesHistoryMapper.findStatisticsByEmployee(salesHistorySelectDTO); + if(salesHistoryStatisticsDTO == null){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } + // Redis에 데이터 저장 /만료 시간 설정 if (salesHistoryStatisticsDTO != null) { redisTemplate.opsForValue().set(cacheKey, salesHistoryStatisticsDTO, Duration.ofMinutes(10)); // 캐시 10분 유지 @@ -159,6 +251,9 @@ public SalesHistoryStatisticsDTO selectStatisticsSearchMonthByEmployee(SalesHist SalesHistoryStatisticsDTO salesHistoryStatisticsDTO = salesHistoryMapper.findStatisticsSearchMonthByEmployee(salesHistorySearchDTO); + if(salesHistoryStatisticsDTO == null){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } return salesHistoryStatisticsDTO; } @@ -169,6 +264,9 @@ public SalesHistoryStatisticsDTO selectStatisticsSearchYearByEmployee(SalesHisto SalesHistoryStatisticsDTO salesHistoryStatisticsDTO = salesHistoryMapper.findStatisticsSearchYearByEmployee(salesHistorySearchDTO); + if(salesHistoryStatisticsDTO == null){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } return salesHistoryStatisticsDTO; } @@ -186,6 +284,14 @@ public Page selectStatistics(SalesHistoryRankedDataDT throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); } + salesHistoryList.forEach(salesHistory -> { + try { + salesHistory.setMemberId(memberQueryService.selectNameById(salesHistory.getMemberId())); + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.MEMBER_NOT_FOUND); + } + }); + return new PageImpl<>(salesHistoryList, pageable, total); } @@ -195,6 +301,10 @@ public SalesHistoryStatisticsAverageDTO selectStatisticsAverageBySearch(SalesHis SalesHistoryStatisticsAverageDTO salesHistoryStatisticsAverageDTO = salesHistoryMapper.findStatisticsAverageBySearch(salesHistoryRankedDataDTO); + if(salesHistoryStatisticsAverageDTO == null){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } + return salesHistoryStatisticsAverageDTO; } @@ -297,4 +407,34 @@ public Page selectStatisticsCenterBySearchYear(SalesH return new PageImpl<>(salesHistoryList, pageable, total); } + + @Override + public void exportSalesHistoryToExcel(HttpServletResponse response) { + List salesHistoryList = salesHistoryMapper.findSalesHistoryForExcel(); + + if(salesHistoryList == null) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } + + salesHistoryList.forEach(salesHistory -> { + try { + salesHistory.setMemberId(memberQueryService.selectNameById(salesHistory.getMemberId())); + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.MEMBER_NOT_FOUND); + } + try { + salesHistory.setCustomerId(customerQueryService.selectCustomerNameById(salesHistory.getCustomerId())); + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.CUSTOMER_NOT_FOUND); + } + try { + salesHistory.setCenterId(centerQueryService.selectNameById(salesHistory.getCenterId())); + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.CENTER_NOT_FOUND); + } + }); + + excelUtilsV1.download(SalesHistoryExcelDownload.class, salesHistoryList, "SalesHistoryExcel", response); + + } } diff --git a/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml b/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml index e2771de9..c0640912 100644 --- a/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml @@ -30,6 +30,14 @@ + + + + + + + + + + + + + diff --git a/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml b/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml index f252913f..92b7c548 100644 --- a/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml @@ -15,6 +15,28 @@ + + + + + + + + + + + SELECT a.prod_id, @@ -151,4 +159,17 @@ + + + diff --git a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml index 405214c2..f78a144c 100644 --- a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml @@ -17,6 +17,19 @@ + + + + + + + + + + + + + @@ -69,6 +82,23 @@ LIMIT #{size} OFFSET #{offset} + + From 035e5cca3e15ea1f40f330262317320a3c46502b Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Sun, 24 Nov 2024 21:42:52 +0900 Subject: [PATCH 307/563] =?UTF-8?q?fix:=20Notice=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20(#26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/NoticeController.java | 2 +- .../domain/notices/query/dto/NoticeDTO.java | 15 ------ .../domain/notices/query/dto/SearchDTO.java | 5 ++ .../s3/controller/S3FileUploadController.java | 54 ------------------- .../domain/s3/entity/TestFile.java | 15 ------ .../notices/query/repository/NoticeMapper.xml | 6 ++- 6 files changed, 11 insertions(+), 86 deletions(-) delete mode 100644 src/main/java/stanl_2/final_backend/domain/s3/controller/S3FileUploadController.java delete mode 100644 src/main/java/stanl_2/final_backend/domain/s3/entity/TestFile.java diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java index 37ebb454..c856ba5c 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java @@ -46,7 +46,7 @@ public ResponseEntity> getNotices( ) { Pageable pageable = PageRequest.of(page, size); - SearchDTO searchDTO = new SearchDTO(title, tag, classification, memberId, startDate, endDate); + SearchDTO searchDTO = new SearchDTO(title,tag,memberId,classification,startDate,endDate); Page noticeDTOPage = noticeService.findNotices(pageable,searchDTO); return ResponseEntity.ok(noticeDTOPage); diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java index 17b84579..70f2b3fd 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java @@ -32,19 +32,4 @@ public class NoticeDTO { private String memberId; - - - public NoticeDTO(Notice notice) { - this.noticeId = notice.getNoticeId(); - this.title = notice.getTitle(); - this.tag = notice.getTag(); - this.classification = notice.getClassification(); - this.content = notice.getContent(); - this.createdAt = notice.getCreatedAt(); - this.updatedAt = notice.getUpdatedAt(); - this.deletedAt = notice.getDeletedAt(); - this.active = notice.getActive(); - this.memberId = notice.getMemberId(); - } - } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/dto/SearchDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/query/dto/SearchDTO.java index ab6334ed..d6384cc5 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/dto/SearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/dto/SearchDTO.java @@ -11,6 +11,11 @@ public class SearchDTO { private String noticeId; private String title; private String tag; + private String content; + private String active; + private String createdAt; + private String updatedAt; + private String deletedAt; private String memberId; private String classification; private String startDate; diff --git a/src/main/java/stanl_2/final_backend/domain/s3/controller/S3FileUploadController.java b/src/main/java/stanl_2/final_backend/domain/s3/controller/S3FileUploadController.java deleted file mode 100644 index 4cebb496..00000000 --- a/src/main/java/stanl_2/final_backend/domain/s3/controller/S3FileUploadController.java +++ /dev/null @@ -1,54 +0,0 @@ -package stanl_2.final_backend.domain.s3.controller; - -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.model.ObjectMetadata; -import io.swagger.v3.oas.annotations.Operation; -import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -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 org.springframework.web.multipart.MultipartFile; - -import java.io.IOException; - -@RestController -@RequestMapping("/upload") -@RequiredArgsConstructor -public class S3FileUploadController { - - private final AmazonS3 amazonS3Client; - - @Value("${cloud.aws.s3.bucket}") - private String bucket; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - private String name; - @PostMapping - @Operation(summary = "파일 업로드 회원가입 이전에 파일 업로드 해야됨 //민석") - public ResponseEntity uploadFile(@RequestParam("file") MultipartFile file) { - try { - String fileName = file.getOriginalFilename(); - String fileUrl = "https://" + bucket + ".s3." + amazonS3Client.getRegionName() + ".amazonaws.com/" + fileName; - ObjectMetadata metadata = new ObjectMetadata(); - metadata.setContentType(file.getContentType()); - metadata.setContentLength(file.getSize()); - amazonS3Client.putObject(bucket, fileName, file.getInputStream(), metadata); - setName(fileUrl); - return ResponseEntity.ok(fileUrl); - } catch (IOException e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); - } - } -} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/s3/entity/TestFile.java b/src/main/java/stanl_2/final_backend/domain/s3/entity/TestFile.java deleted file mode 100644 index 9def4760..00000000 --- a/src/main/java/stanl_2/final_backend/domain/s3/entity/TestFile.java +++ /dev/null @@ -1,15 +0,0 @@ -package stanl_2.final_backend.domain.s3.entity; - -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; - -@Entity -public class TestFile { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - private String name; - private String FileUrl; -} diff --git a/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml b/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml index 4ac6f562..c221b122 100644 --- a/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml @@ -22,6 +22,11 @@ + + + + + @@ -59,7 +64,6 @@ a.not_cont, a.created_at, a.updated_at, - a.deleted_at, a.active, a.mem_id FROM tb_notice a From b855071ceaccb6dfbf91eb8e85a8bf3f0c07ff1d Mon Sep 17 00:00:00 2001 From: giuseog Date: Mon, 25 Nov 2024 02:01:26 +0900 Subject: [PATCH 308/563] =?UTF-8?q?fix:=20product=20dto=EA=B0=92=20?= =?UTF-8?q?=EC=88=98=EC=A0=95(#145)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/domain/service/ContractCommandServiceImpl.java | 6 +++--- .../domain/product/query/dto/ProductSelectAllDTO.java | 2 +- .../domain/product/query/dto/ProductSelectIdDTO.java | 2 +- .../domain/product/query/repository/ProductMapper.java | 3 ++- .../product/query/service/ProductQueryServiceImpl.java | 5 +++-- .../domain/product/query/repository/ProductMapper.xml | 4 ++-- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index b4b1fd16..088dc55a 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -99,7 +99,7 @@ private String handleCustomerInfo(ContractRegistDTO contractRegistRequestDTO, St @Transactional public void registerContract(ContractRegistDTO contractRegistRequestDTO) throws GeneralSecurityException { String memberId = authQueryService.selectMemberIdByLoginId(contractRegistRequestDTO.getMemberId()); - String productId = productQueryService.selectByProductSerialNumber(contractRegistRequestDTO.getSerialNum()).getId(); + String productId = productQueryService.selectByProductSerialNumber(contractRegistRequestDTO.getSerialNum()).getProductId(); String customerId = handleCustomerInfo(contractRegistRequestDTO, memberId); String centerId = memberQueryService.selectMemberInfo(contractRegistRequestDTO.getMemberId()).getCenterId(); @@ -206,13 +206,13 @@ public void modifyContractStatus(ContractStatusModifyDTO contractStatusModifyDTO // 제품 재고 수 줄이기 ProductSelectIdDTO productSelectIdDTO = productQueryService.selectByProductSerialNumber(contract.getSerialNum()); - String productId = productSelectIdDTO.getId(); + String productId = productSelectIdDTO.getProductId(); productCommandService.modifyProductStock(productId, contract.getNumberOfVehicles()); } else if (contractStatusModifyDTO.getStatus().equals("CANCLED")) { salesHistoryCommandService.deleteSalesHistory(contract.getContractId()); ProductSelectIdDTO productSelectIdDTO = productQueryService.selectByProductSerialNumber(contract.getSerialNum()); - String productId = productSelectIdDTO.getId(); + String productId = productSelectIdDTO.getProductId(); productCommandService.deleteProductStock(productId, contract.getNumberOfVehicles()); } } diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectAllDTO.java index 60e5a488..c832e58b 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectAllDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectAllDTO.java @@ -8,7 +8,7 @@ @Setter @ToString public class ProductSelectAllDTO { - private String id; + private String productId; private String name; private String serialNumber; private String cost; diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectIdDTO.java b/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectIdDTO.java index b81b6ce7..e24962b3 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectIdDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectIdDTO.java @@ -8,7 +8,7 @@ @Setter @ToString public class ProductSelectIdDTO { - private String id; + private String productId; private String name; private String serialNumber; private String cost; diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java b/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java index de3c4950..0b96a807 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java @@ -3,6 +3,7 @@ import org.apache.ibatis.annotations.Mapper; import stanl_2.final_backend.domain.A_sample.query.dto.SampleExcelDownload; import stanl_2.final_backend.domain.product.common.util.RequestList; +import stanl_2.final_backend.domain.product.query.dto.ProductExcelDownload; import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; import java.util.List; @@ -22,5 +23,5 @@ public interface ProductMapper { ProductSelectIdDTO findProductBySerialNumber(String serialNumber); - List findProductsForExcel(); + List findProductsForExcel(); } diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java index 7de5513a..4cf0c6b3 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java @@ -11,6 +11,7 @@ import stanl_2.final_backend.domain.product.common.exception.ProductCommonException; import stanl_2.final_backend.domain.product.common.exception.ProductErrorCode; import stanl_2.final_backend.domain.product.common.util.RequestList; +import stanl_2.final_backend.domain.product.query.dto.ProductExcelDownload; import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; import stanl_2.final_backend.domain.product.query.repository.ProductMapper; import stanl_2.final_backend.global.excel.ExcelUtilsV1; @@ -94,12 +95,12 @@ public ProductSelectIdDTO selectByProductSerialNumber(String id) { @Override @Transactional public void exportProductsToExcel(HttpServletResponse response) { - List productList = productMapper.findProductsForExcel(); + List productList = productMapper.findProductsForExcel(); if(productList == null) { throw new ProductCommonException(ProductErrorCode.PRODUCT_NOT_FOUND); } - excelUtilsV1.download(SampleExcelDownload.class, productList, "productExcel", response); + excelUtilsV1.download(ProductExcelDownload.class, productList, "productExcel", response); } } diff --git a/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml b/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml index 055d1aa1..3a133cc2 100644 --- a/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml @@ -5,7 +5,7 @@ - + @@ -13,7 +13,7 @@ - + From 2395ccf62b768d447b6a147cc35d1043473f23f8 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Mon, 25 Nov 2024 09:36:25 +0900 Subject: [PATCH 309/563] =?UTF-8?q?refactor:=20=EA=B3=84=EC=95=BD=EC=84=9C?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ContractCommandServiceImpl.java | 7 ++- .../common/exception/ContractErrorCode.java | 1 + .../query/dto/ContractSelectAllDTO.java | 2 + .../query/repository/UpdateHistoryMapper.java | 8 +++ .../service/ContractQueryServiceImpl.java | 59 +++++++++++++------ .../service/UpdateHistoryQueryService.java | 6 ++ .../UpdateHistoryQueryServiceImpl.java | 27 +++++++++ .../query/repository/ContractMapper.xml | 41 +++---------- .../query/repository/UpdateHistoryMapper.xml | 14 +++++ 9 files changed, 114 insertions(+), 51 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/query/repository/UpdateHistoryMapper.java create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/query/service/UpdateHistoryQueryService.java create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/query/service/UpdateHistoryQueryServiceImpl.java create mode 100644 src/main/resources/stanl_2/final_backend/domain/contract/query/repository/UpdateHistoryMapper.xml diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index 51037bec..2736ca0b 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -104,7 +104,7 @@ public void registerContract(ContractRegistDTO contractRegistRequestDTO) throws String centerId = memberQueryService.selectMemberInfo(contractRegistRequestDTO.getMemberId()).getCenterId(); // 계약 생성 - Contract contract = new Contract(); + Contract contract = modelMapper.map(contractRegistRequestDTO, Contract.class); contract.setMemberId(memberId); contract.setCenterId(centerId); contract.setProductId(productId); @@ -117,6 +117,11 @@ public void registerContract(ContractRegistDTO contractRegistRequestDTO) throws contract.setCustomerAddress(aesUtils.encrypt(contractRegistRequestDTO.getCustomerAddress())); contract.setCustomerIdentifiNo(aesUtils.encrypt(contractRegistRequestDTO.getCustomerIdentifiNo())); + String unescapedHtml = StringEscapeUtils.unescapeJson(contractRegistRequestDTO.getCreatedUrl()); + String updatedS3Url = s3FileService.uploadHtml(unescapedHtml, contractRegistRequestDTO.getTitle()); + + contract.setCreatedUrl(updatedS3Url); + // 계약 저장 contractRepository.save(contract); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ContractErrorCode.java b/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ContractErrorCode.java index bab81813..e4f6c47f 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ContractErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ContractErrorCode.java @@ -39,6 +39,7 @@ public enum ContractErrorCode { * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. */ CONTRACT_NOT_FOUND(40401, HttpStatus.NOT_FOUND, "계약서를 찾을 수 없습니다."), + UPDATE_HISTORY_NOT_FOUND(40402, HttpStatus.NOT_FOUND, "수정내역에서 계약서를 찾을 수 없습니다."), /** diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java index d29af5be..ae4f1f61 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java @@ -21,4 +21,6 @@ public class ContractSelectAllDTO { private String customerId; private String productId; private String carName; + private String customerClassifcation; + private String customerPurchaseCondition; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/repository/UpdateHistoryMapper.java b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/UpdateHistoryMapper.java new file mode 100644 index 00000000..736296ef --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/UpdateHistoryMapper.java @@ -0,0 +1,8 @@ +package stanl_2.final_backend.domain.contract.query.repository; + +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface UpdateHistoryMapper { + String selectUpdateHistoryByContractId(String contractId); +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java index fd61d147..4df64eb5 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java @@ -29,13 +29,15 @@ public class ContractQueryServiceImpl implements ContractQueryService { private final ContractMapper contractMapper; private final AuthQueryService authQueryService; private final MemberQueryService memberQueryService; + private final UpdateHistoryQueryService updateHistoryQueryService; private final RedisTemplate redisTemplate; @Autowired - public ContractQueryServiceImpl(ContractMapper contractMapper, AuthQueryService authQueryService, MemberQueryService memberQueryService, @Qualifier("redisTemplate") RedisTemplate redisTemplate) { + public ContractQueryServiceImpl(ContractMapper contractMapper, AuthQueryService authQueryService, MemberQueryService memberQueryService, UpdateHistoryQueryService updateHistoryQueryService, @Qualifier("redisTemplate") RedisTemplate redisTemplate) { this.contractMapper = contractMapper; this.authQueryService = authQueryService; this.memberQueryService = memberQueryService; + this.updateHistoryQueryService = updateHistoryQueryService; this.redisTemplate = redisTemplate; } @@ -79,9 +81,17 @@ public ContractSeletIdDTO selectDetailContractEmployee(ContractSeletIdDTO contra ContractSeletIdDTO responseContract = contractMapper.findContractByIdAndMemId(contractSeletIdDTO.getContractId(), memberId); - // 이스케이프된 HTML 제거 - String unescapedHtml = StringEscapeUtils.unescapeJson(responseContract.getCreatedUrl()); - responseContract.setCreatedUrl(unescapedHtml); + if (responseContract == null) { + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); + } + + String content = updateHistoryQueryService.selectUpdateHistoryByContractId(responseContract.getContractId()); + + if (content == null) { + String unescapedHtml = StringEscapeUtils.unescapeJson(responseContract.getCreatedUrl()); + responseContract.setCreatedUrl(unescapedHtml); + } + responseContract.setCreatedUrl(content); return responseContract; } @@ -176,18 +186,24 @@ public Page selectAllContract(ContractSelectAllDTO contrac int offset = Math.toIntExact(pageable.getOffset()); int pageSize = pageable.getPageSize(); - String caschKey = "myCache::contracts::offset=" + offset + "::pageSize=" + pageSize; - - List contracts = (List) redisTemplate.opsForValue().get(caschKey); +// String caschKey = "myCache::contracts::offset=" + offset + "::pageSize=" + pageSize; +// +// List contracts = (List) redisTemplate.opsForValue().get(caschKey); +// +// if (contracts == null) { +// contracts = contractMapper.findContractAll(offset, pageSize); +// +// if (contracts == null) { +// throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); +// } +// +// redisTemplate.opsForValue().set(caschKey, contracts); +// } + + List contracts = contractMapper.findContractAll(offset, pageSize); if (contracts == null) { - contracts = contractMapper.findContractAll(offset, pageSize); - - if (contracts == null) { - throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); - } - - redisTemplate.opsForValue().set(caschKey, contracts); + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); } Integer count = contractMapper.findContractCount(); @@ -203,9 +219,18 @@ public ContractSeletIdDTO selectDetailContract(ContractSeletIdDTO contractSeletI ContractSeletIdDTO responseContract = contractMapper.findContractById(contractSeletIdDTO.getContractId()); - // 이스케이프된 HTML 제거 - String unescapedHtml = StringEscapeUtils.unescapeJson(responseContract.getCreatedUrl()); - responseContract.setCreatedUrl(unescapedHtml); + if (responseContract == null) { + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); + } + + String content = updateHistoryQueryService.selectUpdateHistoryByContractId(responseContract.getContractId()); + + if (content == null) { + String unescapedHtml = StringEscapeUtils.unescapeJson(responseContract.getCreatedUrl()); + responseContract.setCreatedUrl(unescapedHtml); + return responseContract; + } + responseContract.setCreatedUrl(content); return responseContract; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/UpdateHistoryQueryService.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/UpdateHistoryQueryService.java new file mode 100644 index 00000000..00da36e1 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/UpdateHistoryQueryService.java @@ -0,0 +1,6 @@ +package stanl_2.final_backend.domain.contract.query.service; + +public interface UpdateHistoryQueryService { + + String selectUpdateHistoryByContractId(String contractId); +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/UpdateHistoryQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/UpdateHistoryQueryServiceImpl.java new file mode 100644 index 00000000..e8cfb32d --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/UpdateHistoryQueryServiceImpl.java @@ -0,0 +1,27 @@ +package stanl_2.final_backend.domain.contract.query.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.contract.query.repository.UpdateHistoryMapper; + +@Service +public class UpdateHistoryQueryServiceImpl implements UpdateHistoryQueryService { + + private final UpdateHistoryMapper updateHistoryMapper; + + @Autowired + public UpdateHistoryQueryServiceImpl(UpdateHistoryMapper updateHistoryMapper) { + this.updateHistoryMapper = updateHistoryMapper; + } + + + @Override + @Transactional(readOnly = true) + public String selectUpdateHistoryByContractId(String contractId) { + + String content = updateHistoryMapper.selectUpdateHistoryByContractId(contractId); + + return content; + } +} diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index c8eab5db..0492666d 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -170,7 +170,9 @@ a.conr_car_name, a.conr_stat, a.created_url, - a.active + a.active, + a.conr_cust_cla, + a.conr_cust_pur_cond FROM tb_contract a WHERE a.mem_id = #{ memberId } AND a.active = TRUE @@ -380,7 +382,7 @@ - SELECT a.conr_id, a.conr_cust_name, @@ -393,39 +395,12 @@ a.created_at, a.updated_at, a.deleted_at, - a.active + a.active, + a.conr_cust_cla, + a.conr_cust_pur_cond FROM tb_contract a - - a.active = TRUE + WHERE a.active = TRUE AND a.cent_id = #{centerId} - - AND a.cent_id = #{contractSearchDTO.centerId} - - - AND a.conr_name LIKE CONCAT('%', #{contractSearchDTO.title}, '%') - - - AND a.conr_cust_name LIKE CONCAT('%', #{contractSearchDTO.customerName}, '%') - - - AND a.conr_cust_cla LIKE CONCAT('%', #{contractSearchDTO.customerClassifcation}, '%') - - - AND a.conr_stat = #{contractSearchDTO.status} - - - AND a.conr_comp_name LIKE CONCAT('%', #{contractSearchDTO.companyName}, '%') - - - AND a.conr_cust_pur_cond LIKE CONCAT('%', #{contractSearchDTO.customerPurchaseCondition}, '%') - - - AND a.created_at BETWEEN #{contractSearchDTO.startAt} AND #{contractSearchDTO.endAt} - - - AND b.prod_name LIKE CONCAT('%', #{contractSearchDTO.productId}, '%') - - ORDER BY a.created_at DESC LIMIT #{pageSize} OFFSET #{offset} diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/UpdateHistoryMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/UpdateHistoryMapper.xml new file mode 100644 index 00000000..2caa85f3 --- /dev/null +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/UpdateHistoryMapper.xml @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file From 2238afc61295d28d61f74381afdbc81467e778f0 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Mon, 25 Nov 2024 09:37:11 +0900 Subject: [PATCH 310/563] =?UTF-8?q?refactor:=20=EA=B3=84=EC=95=BD=EC=84=9C?= =?UTF-8?q?=20=EC=A0=84=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20DTO=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95=20(#150)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/contract/query/dto/ContractSelectAllDTO.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java index ae4f1f61..762bc9d5 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java @@ -13,9 +13,6 @@ public class ContractSelectAllDTO { private String customerName; private String companyName; private String status; - private String createdUrl; - private String updatedUrl; - private boolean active; private String memberId; private String centerId; private String customerId; From 6ee404cf028fb2964cf998f20afa82156035fc1b Mon Sep 17 00:00:00 2001 From: giuseog Date: Mon, 25 Nov 2024 10:35:47 +0900 Subject: [PATCH 311/563] =?UTF-8?q?feat:=20s3=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=EC=A4=91(#152)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/controller/CenterController.java | 14 +++++++------- ...rModifyRequestDTO.java => CenterModifyDTO.java} | 2 +- ...rRegistRequestDTO.java => CenterRegistDTO.java} | 3 ++- .../application/service/CenterCommandService.java | 8 ++++---- .../command/domain/aggregate/entity/Center.java | 3 +++ .../domain/service/CenterCommandServiceImpl.java | 14 +++++++------- .../query/controller/ProductController.java | 2 ++ 7 files changed, 26 insertions(+), 20 deletions(-) rename src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/{CenterModifyRequestDTO.java => CenterModifyDTO.java} (89%) rename src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/{CenterRegistRequestDTO.java => CenterRegistDTO.java} (83%) diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java index 0ef81383..58837e45 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java @@ -8,8 +8,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.center.command.application.dto.request.CenterModifyRequestDTO; -import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistRequestDTO; +import stanl_2.final_backend.domain.center.command.application.dto.request.CenterModifyDTO; +import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistDTO; import stanl_2.final_backend.domain.center.command.application.service.CenterCommandService; import stanl_2.final_backend.domain.center.common.response.CenterResponseMessage; @@ -30,9 +30,9 @@ public CenterController(CenterCommandService centerCommandService) { content = {@Content(schema = @Schema(implementation = CenterResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postTest(@RequestBody CenterRegistRequestDTO centerRegistRequestDTO){ + public ResponseEntity postTest(@RequestBody CenterRegistDTO centerRegistDTO){ - centerCommandService.registCenter(centerRegistRequestDTO); + centerCommandService.registCenter(centerRegistDTO); return ResponseEntity.ok(CenterResponseMessage.builder() .httpStatus(200) @@ -48,11 +48,11 @@ public ResponseEntity postTest(@RequestBody CenterRegistR }) @PutMapping("{id}") public ResponseEntity putTest(@PathVariable("id") String id, - @RequestBody CenterModifyRequestDTO centerModifyRequestDTO){ + @RequestBody CenterModifyDTO centerModifyDTO){ - centerModifyRequestDTO.setCenterId(id); + centerModifyDTO.setCenterId(id); - centerCommandService.modifyCenter(centerModifyRequestDTO); + centerCommandService.modifyCenter(centerModifyDTO); return ResponseEntity.ok(CenterResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyDTO.java similarity index 89% rename from src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyRequestDTO.java rename to src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyDTO.java index b0e374cd..50a5548e 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyDTO.java @@ -6,7 +6,7 @@ @NoArgsConstructor @Setter @Getter -public class CenterModifyRequestDTO { +public class CenterModifyDTO { private String centerId; private String name; private String address; diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterRegistRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterRegistDTO.java similarity index 83% rename from src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterRegistRequestDTO.java rename to src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterRegistDTO.java index 9cd5cbac..9680b48a 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterRegistRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterRegistDTO.java @@ -6,10 +6,11 @@ @NoArgsConstructor @Setter @Getter -public class CenterRegistRequestDTO { +public class CenterRegistDTO { private String name; private String address; private String phone; private Integer memberCount; private String operatingAt; + private String imageUrl; } diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandService.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandService.java index da7c976b..1b9e68bc 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandService.java @@ -1,13 +1,13 @@ package stanl_2.final_backend.domain.center.command.application.service; import org.springframework.stereotype.Service; -import stanl_2.final_backend.domain.center.command.application.dto.request.CenterModifyRequestDTO; -import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistRequestDTO; +import stanl_2.final_backend.domain.center.command.application.dto.request.CenterModifyDTO; +import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistDTO; @Service public interface CenterCommandService { - void registCenter(CenterRegistRequestDTO centerRegistRequestDTO); - void modifyCenter(CenterModifyRequestDTO centerModifyRequestDTO); + void registCenter(CenterRegistDTO centerRegistDTO); + void modifyCenter(CenterModifyDTO centerModifyDTO); void deleteCenter(String id); } diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java b/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java index 8aa111b4..f272abec 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/domain/aggregate/entity/Center.java @@ -51,6 +51,9 @@ public class Center { @Column(name = "DELETED_AT") private String deletedAt; + @Column(name = "IMAGE_URL", nullable = false) + private String imageUrl; + @Column(name = "ACTIVE", nullable = false) private Boolean active = true; diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java index 405df59f..b34a2093 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java @@ -4,8 +4,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.center.command.application.dto.request.CenterModifyRequestDTO; -import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistRequestDTO; +import stanl_2.final_backend.domain.center.command.application.dto.request.CenterModifyDTO; +import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistDTO; import stanl_2.final_backend.domain.center.command.application.service.CenterCommandService; import stanl_2.final_backend.domain.center.command.domain.aggregate.entity.Center; import stanl_2.final_backend.domain.center.command.domain.repository.CenterRepository; @@ -31,20 +31,20 @@ public CenterCommandServiceImpl(CenterRepository centerRepository, ModelMapper m @Override @Transactional - public void registCenter(CenterRegistRequestDTO centerRegistRequestDTO) { + public void registCenter(CenterRegistDTO centerRegistDTO) { - Center newCenter = modelMapper.map(centerRegistRequestDTO, Center.class); + Center newCenter = modelMapper.map(centerRegistDTO, Center.class); centerRepository.save(newCenter); } @Override @Transactional - public void modifyCenter(CenterModifyRequestDTO centerModifyRequestDTO) { - Center center = centerRepository.findById(centerModifyRequestDTO.getCenterId()) + public void modifyCenter(CenterModifyDTO centerModifyDTO) { + Center center = centerRepository.findById(centerModifyDTO.getCenterId()) .orElseThrow(() -> new CenterCommonException(CenterErrorCode.CENTER_NOT_FOUND)); - Center updateCenter = modelMapper.map(centerModifyRequestDTO, Center.class); + Center updateCenter = modelMapper.map(centerModifyDTO, Center.class); updateCenter.setCenterId(center.getCenterId()); updateCenter.setCreatedAt(center.getCreatedAt()); diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java b/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java index e346ec75..80c6c456 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java @@ -61,6 +61,8 @@ public ResponseEntity getProductAll(@PageableDefault(siz @GetMapping("{id}") public ResponseEntity getProductById(@PathVariable("id") String id){ + System.out.println("호출"); + ProductSelectIdDTO productSelectIdDTO = productQueryService.selectByProductId(id); return ResponseEntity.ok(ProductResponseMessage.builder() From 9ef12fb48a4f95d75c28bfd8089802d6509d394d Mon Sep 17 00:00:00 2001 From: giuseog Date: Mon, 25 Nov 2024 12:15:38 +0900 Subject: [PATCH 312/563] =?UTF-8?q?feat:=20s3=20=EB=A9=94=EC=86=8C?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80(#152)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CenterController.java | 18 ++++++++++-------- .../dto/request/CenterModifyDTO.java | 1 + .../service/CenterCommandService.java | 5 +++-- .../service/CenterCommandServiceImpl.java | 16 +++++++++++++--- .../center/query/dto/CenterSelectIdDTO.java | 1 + .../controller/EvaluationController.java | 11 +++++++---- .../application/dto/EvaluationModifyDTO.java | 1 + .../application/dto/EvaluationRegistDTO.java | 1 + .../service/EvaluationCommandService.java | 5 +++-- .../domain/aggregate/entity/Evaluation.java | 4 ++++ .../service/EvaluationCommandServiceImpl.java | 16 +++++++++++++--- .../evaluation/query/dto/EvaluationDTO.java | 1 + .../query/dto/EvaluationSearchDTO.java | 1 + src/main/resources/application.yml | 6 +++++- .../center/query/repository/CenterMapper.xml | 3 +++ .../query/repository/EvaluationMapper.xml | 4 +++- 16 files changed, 70 insertions(+), 24 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java index 58837e45..8bcb4dce 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/controller/CenterController.java @@ -8,6 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterModifyDTO; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistDTO; import stanl_2.final_backend.domain.center.command.application.service.CenterCommandService; @@ -30,9 +31,9 @@ public CenterController(CenterCommandService centerCommandService) { content = {@Content(schema = @Schema(implementation = CenterResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postTest(@RequestBody CenterRegistDTO centerRegistDTO){ - - centerCommandService.registCenter(centerRegistDTO); + public ResponseEntity postTest(@RequestPart("dto") CenterRegistDTO centerRegistDTO, + @RequestPart("file") MultipartFile imageUrl){ + centerCommandService.registCenter(centerRegistDTO, imageUrl); return ResponseEntity.ok(CenterResponseMessage.builder() .httpStatus(200) @@ -46,13 +47,14 @@ public ResponseEntity postTest(@RequestBody CenterRegistD @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = CenterResponseMessage.class))}) }) - @PutMapping("{id}") - public ResponseEntity putTest(@PathVariable("id") String id, - @RequestBody CenterModifyDTO centerModifyDTO){ + @PutMapping("{centerId}") + public ResponseEntity putTest(@PathVariable("centerId") String centerId, + @RequestPart("dto") CenterModifyDTO centerModifyDTO, + @RequestPart("file") MultipartFile imageUrl){ - centerModifyDTO.setCenterId(id); + centerModifyDTO.setCenterId(centerId); - centerCommandService.modifyCenter(centerModifyDTO); + centerCommandService.modifyCenter(centerModifyDTO, imageUrl); return ResponseEntity.ok(CenterResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyDTO.java index 50a5548e..3515e4ed 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyDTO.java @@ -13,4 +13,5 @@ public class CenterModifyDTO { private String phone; private Integer memberCount; private String operatingAt; + private String imageUrl; } diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandService.java b/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandService.java index 1b9e68bc..98269eae 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/application/service/CenterCommandService.java @@ -1,13 +1,14 @@ package stanl_2.final_backend.domain.center.command.application.service; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterModifyDTO; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistDTO; @Service public interface CenterCommandService { - void registCenter(CenterRegistDTO centerRegistDTO); - void modifyCenter(CenterModifyDTO centerModifyDTO); + void registCenter(CenterRegistDTO centerRegistDTO, MultipartFile imageUrl); + void modifyCenter(CenterModifyDTO centerModifyDTO, MultipartFile imageUrl); void deleteCenter(String id); } diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java index b34a2093..dc057c11 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java @@ -4,6 +4,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterModifyDTO; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistDTO; import stanl_2.final_backend.domain.center.command.application.service.CenterCommandService; @@ -11,6 +12,7 @@ import stanl_2.final_backend.domain.center.command.domain.repository.CenterRepository; import stanl_2.final_backend.domain.center.common.exception.CenterCommonException; import stanl_2.final_backend.domain.center.common.exception.CenterErrorCode; +import stanl_2.final_backend.domain.s3.service.S3FileService; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -21,31 +23,39 @@ public class CenterCommandServiceImpl implements CenterCommandService { private final CenterRepository centerRepository; private final ModelMapper modelMapper; + private final S3FileService s3FileService; @Autowired - public CenterCommandServiceImpl(CenterRepository centerRepository, ModelMapper modelMapper) { + public CenterCommandServiceImpl(CenterRepository centerRepository, ModelMapper modelMapper, S3FileService s3FileService) { this.centerRepository = centerRepository; this.modelMapper = modelMapper; + this.s3FileService = s3FileService; } @Override @Transactional - public void registCenter(CenterRegistDTO centerRegistDTO) { + public void registCenter(CenterRegistDTO centerRegistDTO, MultipartFile imageUrl) { Center newCenter = modelMapper.map(centerRegistDTO, Center.class); + newCenter.setImageUrl(s3FileService.uploadOneFile(imageUrl)); + centerRepository.save(newCenter); } @Override @Transactional - public void modifyCenter(CenterModifyDTO centerModifyDTO) { + public void modifyCenter(CenterModifyDTO centerModifyDTO, MultipartFile imageUrl) { Center center = centerRepository.findById(centerModifyDTO.getCenterId()) .orElseThrow(() -> new CenterCommonException(CenterErrorCode.CENTER_NOT_FOUND)); + s3FileService.deleteFile(center.getImageUrl()); + Center updateCenter = modelMapper.map(centerModifyDTO, Center.class); + updateCenter.setImageUrl(s3FileService.uploadOneFile(imageUrl)); + updateCenter.setCenterId(center.getCenterId()); updateCenter.setCreatedAt(center.getCreatedAt()); updateCenter.setUpdatedAt(getCurrentTime()); diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectIdDTO.java b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectIdDTO.java index 9a96216a..757099c3 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectIdDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectIdDTO.java @@ -22,4 +22,5 @@ public class CenterSelectIdDTO { private String updatedAt; private String deletedAt; private Boolean active; + private String imageUrl; } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/controller/EvaluationController.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/controller/EvaluationController.java index e5f7cd0a..826de7cb 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/controller/EvaluationController.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/controller/EvaluationController.java @@ -8,6 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.evaluation.command.application.dto.EvaluationModifyDTO; import stanl_2.final_backend.domain.evaluation.command.application.dto.EvaluationRegistDTO; @@ -31,9 +32,10 @@ public EvaluationController(EvaluationCommandService evaluationCommandService) { content = {@Content(schema = @Schema(implementation = EvaluationResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postEvaluation(@RequestBody EvaluationRegistDTO evaluationRegistRequestDTO) { + public ResponseEntity postEvaluation(@RequestPart("dto") EvaluationRegistDTO evaluationRegistRequestDTO, + @RequestPart("file") MultipartFile fileUrl) { - evaluationCommandService.registerEvaluation(evaluationRegistRequestDTO); + evaluationCommandService.registerEvaluation(evaluationRegistRequestDTO, fileUrl); return ResponseEntity.ok(EvaluationResponseMessage.builder() .httpStatus(200) @@ -49,11 +51,12 @@ public ResponseEntity postEvaluation(@RequestBody Eva }) @PutMapping("{id}") public ResponseEntity putEvaluation(@PathVariable String id, - @RequestBody EvaluationModifyDTO evaluationModifyRequestDTO) { + @RequestPart("dto") EvaluationModifyDTO evaluationModifyRequestDTO, + @RequestPart("file") MultipartFile fileUrl) { evaluationModifyRequestDTO.setEvaluationId(id); - evaluationCommandService.modifyEvaluation(evaluationModifyRequestDTO); + evaluationCommandService.modifyEvaluation(evaluationModifyRequestDTO, fileUrl); return ResponseEntity.ok(EvaluationResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationModifyDTO.java index 0178ddba..055ec1f8 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationModifyDTO.java @@ -13,4 +13,5 @@ public class EvaluationModifyDTO { private String centerId; private String memberId; private String writerId; + private String fileUrl; } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationRegistDTO.java index aba39bfb..707b4a2b 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/dto/EvaluationRegistDTO.java @@ -12,4 +12,5 @@ public class EvaluationRegistDTO { private String memberId; private String writerId; private String centerId; + private String fileUrl; } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/service/EvaluationCommandService.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/service/EvaluationCommandService.java index 85ac4546..bcbec2f7 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/service/EvaluationCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/application/service/EvaluationCommandService.java @@ -1,13 +1,14 @@ package stanl_2.final_backend.domain.evaluation.command.application.service; +import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.A_sample.command.application.dto.SampleModifyDTO; import stanl_2.final_backend.domain.evaluation.command.application.dto.EvaluationModifyDTO; import stanl_2.final_backend.domain.evaluation.command.application.dto.EvaluationRegistDTO; public interface EvaluationCommandService { - void registerEvaluation(EvaluationRegistDTO evaluationRegistRequestDTO); + void registerEvaluation(EvaluationRegistDTO evaluationRegistRequestDTO, MultipartFile fileUrl); - void modifyEvaluation(EvaluationModifyDTO evaluationModifyRequestDTO); + void modifyEvaluation(EvaluationModifyDTO evaluationModifyRequestDTO, MultipartFile fileUrl); void deleteEvaluation(String id); } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/aggregate/entity/Evaluation.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/aggregate/entity/Evaluation.java index df3a709a..eebf59b7 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/aggregate/entity/Evaluation.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/aggregate/entity/Evaluation.java @@ -55,6 +55,10 @@ public class Evaluation { @Column(name = "WRI_ID", nullable = false) private String writerId; + @Column(name = "FILE_URL", nullable = false) + private String fileUrl; + + @PrePersist private void prePersist() { this.createdAt = getCurrentTime(); diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/service/EvaluationCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/service/EvaluationCommandServiceImpl.java index aecb4565..6571899a 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/service/EvaluationCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/service/EvaluationCommandServiceImpl.java @@ -4,6 +4,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.evaluation.command.application.dto.EvaluationModifyDTO; import stanl_2.final_backend.domain.evaluation.command.application.dto.EvaluationRegistDTO; import stanl_2.final_backend.domain.evaluation.command.application.service.EvaluationCommandService; @@ -11,6 +12,7 @@ import stanl_2.final_backend.domain.evaluation.command.domain.repository.EvaluationRepository; import stanl_2.final_backend.domain.evaluation.common.exception.EvaluationCommonException; import stanl_2.final_backend.domain.evaluation.common.exception.EvaluationErrorCode; +import stanl_2.final_backend.domain.s3.service.S3FileService; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -21,11 +23,13 @@ public class EvaluationCommandServiceImpl implements EvaluationCommandService { private final EvaluationRepository evaluationRepository; private final ModelMapper modelMapper; + private final S3FileService s3FileService; @Autowired - public EvaluationCommandServiceImpl(EvaluationRepository evaluationRepository, ModelMapper modelMapper) { + public EvaluationCommandServiceImpl(EvaluationRepository evaluationRepository, ModelMapper modelMapper, S3FileService s3FileService) { this.evaluationRepository = evaluationRepository; this.modelMapper = modelMapper; + this.s3FileService = s3FileService; } private String getCurrentTime() { @@ -35,21 +39,27 @@ private String getCurrentTime() { @Override @Transactional - public void registerEvaluation(EvaluationRegistDTO evaluationRegistRequestDTO) { + public void registerEvaluation(EvaluationRegistDTO evaluationRegistRequestDTO, MultipartFile fileUrl) { Evaluation evaluation = modelMapper.map(evaluationRegistRequestDTO, Evaluation.class); + evaluation.setFileUrl(s3FileService.uploadOneFile(fileUrl)); + evaluationRepository.save(evaluation); } @Override @Transactional - public void modifyEvaluation(EvaluationModifyDTO evaluationModifyRequestDTO) { + public void modifyEvaluation(EvaluationModifyDTO evaluationModifyRequestDTO, MultipartFile fileUrl) { Evaluation evaluation = evaluationRepository.findById(evaluationModifyRequestDTO.getEvaluationId()) .orElseThrow(() -> new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND)); + s3FileService.deleteFile(evaluation.getFileUrl()); + Evaluation updateEvaluation = modelMapper.map(evaluationModifyRequestDTO, Evaluation.class); + updateEvaluation.setFileUrl(s3FileService.uploadOneFile(fileUrl)); + updateEvaluation.setEvaluationId(evaluation.getEvaluationId()); updateEvaluation.setCreatedAt(evaluation.getCreatedAt()); updateEvaluation.setUpdatedAt(getCurrentTime()); diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java index 257135df..41d4820b 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java @@ -18,4 +18,5 @@ public class EvaluationDTO { private String centerId; private String memberId; private String writerId; + private String fileUrl; } diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationSearchDTO.java index 42c18f7b..eb2de610 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationSearchDTO.java @@ -21,4 +21,5 @@ public class EvaluationSearchDTO { private String startDate; private String endDate; private String searcherName; + private String fileUrl; } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index bae540df..45b4c61f 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -19,7 +19,11 @@ spring: enabled: false restart: enabled: false - + servlet: + multipart: + enabled: true + max-file-size: 10MB + max-request-size: 10MB jwt: secret-key: ${JWT_SECRET_KEY} header: ${JWT_HEADER} diff --git a/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml b/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml index c0640912..78c0795f 100644 --- a/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml @@ -15,6 +15,7 @@ + @@ -28,6 +29,7 @@ + @@ -50,6 +52,7 @@ , a.updated_at , a.deleted_at , a.active + , a.image_url FROM tb_center a WHERE a.cent_id = #{id} AND a.active = TRUE diff --git a/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml b/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml index 92b7c548..2e65a7c0 100644 --- a/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml @@ -13,6 +13,7 @@ + @@ -74,7 +75,8 @@ a.mem_id, a.wri_id, a.created_at, - a.updated_at + a.updated_at, + a.file_url FROM tb_evaluation a WHERE a.eval_id = #{id} AND a.active = TRUE From 23dc9a349fa451b8ca3ac69511b90b8338c0f8a9 Mon Sep 17 00:00:00 2001 From: giuseog Date: Mon, 25 Nov 2024 12:27:26 +0900 Subject: [PATCH 313/563] =?UTF-8?q?feat:=20product=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80(#152)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/application/domain/aggregate/entity/Product.java | 3 ++- .../domain/product/query/dto/ProductSelectIdDTO.java | 1 + .../domain/product/query/repository/ProductMapper.xml | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/aggregate/entity/Product.java b/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/aggregate/entity/Product.java index 3ad6ed93..ad677e3e 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/aggregate/entity/Product.java +++ b/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/aggregate/entity/Product.java @@ -48,5 +48,6 @@ public class Product { @Column(name ="ACTIVE", nullable = false) private Boolean active = true; - + @Column(name ="IMAGE_URL", nullable = false) + private String imageUrl; } diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectIdDTO.java b/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectIdDTO.java index e24962b3..1d7067a5 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectIdDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectIdDTO.java @@ -13,6 +13,7 @@ public class ProductSelectIdDTO { private String serialNumber; private String cost; private String stock; + private String imageUrl; /* 설명. product option 받아올 필드 */ private Character country; diff --git a/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml b/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml index 3a133cc2..830dfabb 100644 --- a/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml @@ -18,6 +18,7 @@ + From 72b88aed9a069062e3a371197113dd40d3f55ba3 Mon Sep 17 00:00:00 2001 From: giuseog Date: Mon, 25 Nov 2024 12:30:01 +0900 Subject: [PATCH 314/563] =?UTF-8?q?fix:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81(#152)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/product/query/controller/ProductController.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java b/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java index 80c6c456..e346ec75 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java @@ -61,8 +61,6 @@ public ResponseEntity getProductAll(@PageableDefault(siz @GetMapping("{id}") public ResponseEntity getProductById(@PathVariable("id") String id){ - System.out.println("호출"); - ProductSelectIdDTO productSelectIdDTO = productQueryService.selectByProductId(id); return ResponseEntity.ok(ProductResponseMessage.builder() From 566b794ebf47d710bab9ce998a18b902e7ae92b8 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 25 Nov 2024 15:42:59 +0900 Subject: [PATCH 315/563] =?UTF-8?q?feat:=20=EB=A9=A4=EB=B2=84=20=EB=B6=80?= =?UTF-8?q?=EC=84=9C=EB=B3=84=20=EB=A9=A4=EB=B2=84=20=EC=A1=B0=ED=9A=8C(#1?= =?UTF-8?q?54)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/MemberController.java | 20 +++++++++++++++++++ .../domain/member/query/dto/MemberDTO.java | 1 + .../member/query/repository/MemberMapper.java | 2 ++ .../query/service/MemberQueryService.java | 4 ++-- .../query/service/MemberQueryServiceImpl.java | 10 +++++++++- .../dto/OrganizationRegisterDTO.java | 2 +- .../domain/aggregate/entity/Organization.java | 4 ++-- .../query/dto/OrganizationDTO.java | 2 +- .../member/query/repository/MemberMapper.xml | 13 ++++++++++++ .../query/repository/OrganizationMapper.xml | 6 +++--- 10 files changed, 54 insertions(+), 10 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java b/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java index daf6a50f..a3084ee4 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -17,6 +18,7 @@ import java.security.Principal; import java.util.List; +@Slf4j @RestController(value = "queryMemberController") @RequestMapping("/api/v1/member") public class MemberController { @@ -86,6 +88,24 @@ public ResponseEntity getMemberByCenterList(@RequestBody } + @Operation(summary = "회원 정보 조건(부서) 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = MemberResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/organization/{organizationId}") + public ResponseEntity getMemberByOrganizationId(@PathVariable("organizationId") String organizationId) throws GeneralSecurityException { + + List memberList = memberQueryService.selectMemberByOrganizationId(organizationId); + + return ResponseEntity.ok(MemberResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(memberList) + .build()); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java index 6c01021f..6093df3a 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java @@ -10,6 +10,7 @@ @Setter @Getter public class MemberDTO { + private String memberId; private String loginId; private String name; private String email; diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java b/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java index dd4811fe..46c032c5 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java @@ -16,4 +16,6 @@ public interface MemberMapper { List findMembersByCenterId(@Param("centerId") String centerId); List findMembersByCenterList(@Param("centerList") List centerList); + + List findMembersByOrganizationId(@Param("organizationId") String organizationId); } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java index 1bac53a0..612c1f58 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java @@ -1,7 +1,5 @@ package stanl_2.final_backend.domain.member.query.service; -import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; import java.security.GeneralSecurityException; @@ -17,5 +15,7 @@ public interface MemberQueryService { List selectMemberByCenterList(List centerList); String selectNameById(String memberId) throws GeneralSecurityException; + List selectMemberByOrganizationId(String organizationId); + // MemberDetailDTO selectMemberDetail(String name) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index 47e77393..d6473d68 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -68,7 +68,6 @@ public List selectMemberByRole(String role){ public List selectMemberByCenterId(String centerId){ List memberList = memberMapper.findMembersByCenterId(centerId); - return memberList; } @@ -89,4 +88,13 @@ public String selectNameById(String memberId) throws GeneralSecurityException { return name; } + + @Override + @Transactional(readOnly = true) + public List selectMemberByOrganizationId(String organizationId) { + + List memberList = memberMapper.findMembersByOrganizationId(organizationId); + + return memberList; + } } diff --git a/src/main/java/stanl_2/final_backend/domain/organization/command/application/dto/OrganizationRegisterDTO.java b/src/main/java/stanl_2/final_backend/domain/organization/command/application/dto/OrganizationRegisterDTO.java index d404b79f..a76e0982 100644 --- a/src/main/java/stanl_2/final_backend/domain/organization/command/application/dto/OrganizationRegisterDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/organization/command/application/dto/OrganizationRegisterDTO.java @@ -11,5 +11,5 @@ @Getter public class OrganizationRegisterDTO { private String name; - private String depth; + private String parent; } diff --git a/src/main/java/stanl_2/final_backend/domain/organization/command/domain/aggregate/entity/Organization.java b/src/main/java/stanl_2/final_backend/domain/organization/command/domain/aggregate/entity/Organization.java index ff1bcf6f..07db4118 100644 --- a/src/main/java/stanl_2/final_backend/domain/organization/command/domain/aggregate/entity/Organization.java +++ b/src/main/java/stanl_2/final_backend/domain/organization/command/domain/aggregate/entity/Organization.java @@ -27,6 +27,6 @@ public class Organization { @Column(name = "ORG_CHA_NAME") private String name; - @Column(name = "ORG_CHA_DEPTH") - private Integer depth; + @Column(name = "ORG_CHA_PARENT") + private String parent; } diff --git a/src/main/java/stanl_2/final_backend/domain/organization/query/dto/OrganizationDTO.java b/src/main/java/stanl_2/final_backend/domain/organization/query/dto/OrganizationDTO.java index 48853d47..3c3c9f5e 100644 --- a/src/main/java/stanl_2/final_backend/domain/organization/query/dto/OrganizationDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/organization/query/dto/OrganizationDTO.java @@ -12,5 +12,5 @@ public class OrganizationDTO { private String organizationId; private String name; - private Integer depth; + private String parent; } diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml index 63e4b5f4..acd1b49d 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml @@ -4,6 +4,7 @@ "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> + @@ -75,4 +76,16 @@ WHERE a.mem_id = #{ memberId } + + + \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/organization/query/repository/OrganizationMapper.xml b/src/main/resources/stanl_2/final_backend/domain/organization/query/repository/OrganizationMapper.xml index c2d755d1..bf6b95ba 100644 --- a/src/main/resources/stanl_2/final_backend/domain/organization/query/repository/OrganizationMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/organization/query/repository/OrganizationMapper.xml @@ -4,14 +4,14 @@ "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> - + - + From 5344b784058ab60f0571ed4f2e46875dae6ac2fb Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 25 Nov 2024 19:58:58 +0900 Subject: [PATCH 316/563] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=A1=B0=ED=9A=8C(with=20=EC=82=AC=EB=B2=88)(#154)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/MemberController.java | 19 +++++++++++++++++++ .../domain/member/query/dto/MemberDTO.java | 1 + .../query/service/MemberQueryService.java | 4 ++-- .../query/service/MemberQueryServiceImpl.java | 10 +++++++++- .../member/query/repository/MemberMapper.xml | 3 ++- 5 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java b/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java index a3084ee4..990d98ae 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java @@ -49,6 +49,25 @@ public ResponseEntity getMemberInfo(Principal principal) .build()); } + @Operation(summary = "회원 정보 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = MemberResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("info/{memberId}") + public ResponseEntity getMemberInfoBymemberId(@PathVariable("memberId") String memberId) throws GeneralSecurityException { + + MemberDTO memberInfo = memberQueryService.selectMemberInfo(memberId); + + return ResponseEntity.ok(MemberResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(memberInfo) + .build()); + } + @Operation(summary = "회원 정보 조건(매장) 조회") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java index 6093df3a..9e00eecd 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java @@ -28,4 +28,5 @@ public class MemberDTO { private String bankName; private String account; private String centerId; + private String createdAt; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java index 612c1f58..f1776560 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java @@ -11,11 +11,11 @@ public interface MemberQueryService { List selectMemberByRole(String role); List selectMemberByCenterId(String centerId); - List selectMemberByCenterList(List centerList); + String selectNameById(String memberId) throws GeneralSecurityException; - List selectMemberByOrganizationId(String organizationId); + List selectMemberByOrganizationId(String organizationId) throws GeneralSecurityException; // MemberDetailDTO selectMemberDetail(String name) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index d6473d68..8e27ba16 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -91,10 +91,18 @@ public String selectNameById(String memberId) throws GeneralSecurityException { @Override @Transactional(readOnly = true) - public List selectMemberByOrganizationId(String organizationId) { + public List selectMemberByOrganizationId(String organizationId) throws GeneralSecurityException { List memberList = memberMapper.findMembersByOrganizationId(organizationId); + for(int i=0;i + SELECT - a.mem_id, a.mem_login_id, + a.mem_name, a.mem_pos, a.created_at, a.mem_job_type From 5387fc8590f219eda41450e0ccf4767a55607d4c Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 25 Nov 2024 20:59:51 +0900 Subject: [PATCH 317/563] =?UTF-8?q?fix:=20=EC=A3=BC=EC=84=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95(#154)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/query/controller/MemberController.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java b/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java index 990d98ae..9db7a52c 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java @@ -49,17 +49,17 @@ public ResponseEntity getMemberInfo(Principal principal) .build()); } - @Operation(summary = "회원 정보 조회") + @Operation(summary = "회원 정보 조회(with 사번)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = MemberResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("info/{memberId}") - public ResponseEntity getMemberInfoBymemberId(@PathVariable("memberId") String memberId) throws GeneralSecurityException { + @GetMapping("info/{loginId}") + public ResponseEntity getMemberInfoBymemberId(@PathVariable("loginId") String loginId) throws GeneralSecurityException { - MemberDTO memberInfo = memberQueryService.selectMemberInfo(memberId); + MemberDTO memberInfo = memberQueryService.selectMemberInfo(loginId); return ResponseEntity.ok(MemberResponseMessage.builder() .httpStatus(200) From 050ac2f2e33005a8d91eab9885a9a3a842eaa337 Mon Sep 17 00:00:00 2001 From: giuseog Date: Mon, 25 Nov 2024 21:06:48 +0900 Subject: [PATCH 318/563] =?UTF-8?q?feat:=20email=20=EB=B0=98=ED=99=98=20?= =?UTF-8?q?=EA=B0=92=20=EC=B6=94=EA=B0=80(#152)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../center/query/dto/CenterSelectAllDTO.java | 2 +- .../center/query/dto/CenterSelectIdDTO.java | 2 +- .../domain/member/query/dto/MemberDTO.java | 1 + .../query/service/MemberQueryServiceImpl.java | 20 +++++++++++++++++++ .../center/query/repository/CenterMapper.xml | 4 ++-- .../member/query/repository/MemberMapper.xml | 3 +++ 6 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectAllDTO.java index dc64074e..a786ff46 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectAllDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectAllDTO.java @@ -10,7 +10,7 @@ @Getter @Setter public class CenterSelectAllDTO { - private String id; + private String centerId; private String name; private String address; private String phone; diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectIdDTO.java b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectIdDTO.java index 757099c3..452b529f 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectIdDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSelectIdDTO.java @@ -12,7 +12,7 @@ @Setter public class CenterSelectIdDTO { - private String id; + private String centerId; private String name; private String address; private String phone; diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java index 6c01021f..f8641977 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java @@ -10,6 +10,7 @@ @Setter @Getter public class MemberDTO { + private String id; private String loginId; private String name; private String email; diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index 47e77393..12dd53f8 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -10,6 +10,8 @@ import stanl_2.final_backend.domain.member.query.dto.MemberDTO; import stanl_2.final_backend.domain.member.query.repository.MemberMapper; import stanl_2.final_backend.domain.member.query.repository.MemberRoleMapper; +import stanl_2.final_backend.domain.sales_history.common.exception.SalesHistoryCommonException; +import stanl_2.final_backend.domain.sales_history.common.exception.SalesHistoryErrorCode; import stanl_2.final_backend.global.utils.AESUtils; import java.security.GeneralSecurityException; @@ -69,6 +71,16 @@ public List selectMemberByCenterId(String centerId){ List memberList = memberMapper.findMembersByCenterId(centerId); + memberList.forEach(dto -> { + try { + dto.setName(selectNameById(dto.getId())); + + dto.setEmail(aesUtils.decrypt(dto.getEmail())); + } catch (Exception e) { + throw new MemberCommonException(MemberErrorCode.MEMBER_NOT_FOUND); + } + }); + return memberList; } @@ -77,6 +89,14 @@ public List selectMemberByCenterId(String centerId){ public List selectMemberByCenterList(List centerList) { List memberList = memberMapper.findMembersByCenterList(centerList); + memberList.forEach(dto -> { + try { + dto.setName(selectNameById(dto.getId())); + } catch (Exception e) { + throw new MemberCommonException(MemberErrorCode.MEMBER_NOT_FOUND); + } + }); + return memberList; } diff --git a/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml b/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml index 78c0795f..424b2206 100644 --- a/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml @@ -5,7 +5,7 @@ - + @@ -19,7 +19,7 @@ - + diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml index 63e4b5f4..220e4e13 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml @@ -5,6 +5,7 @@ + @@ -48,6 +49,8 @@ SELECT a.cust_id, @@ -69,7 +79,7 @@ WHERE a.active != 'false'; - SELECT a.cust_id, a.cust_name, @@ -128,4 +138,32 @@ FROM tb_customer_info a WHERE a.cust_pho = #{ customerPhone } + + + + + + + + \ No newline at end of file From 7316e2a6175c1f0c45064b9c5de00677a23c80ab Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Tue, 26 Nov 2024 22:24:27 +0900 Subject: [PATCH 323/563] =?UTF-8?q?refactor:=20=EA=B3=84=EC=95=BD=EC=84=9C?= =?UTF-8?q?=20=ED=8E=98=EC=9D=B4=EC=A7=95=20=EB=B0=8F=20=EC=A0=95=EB=A0=AC?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20(#161)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/dto/ContractModifyDTO.java | 1 - .../service/ContractCommandServiceImpl.java | 6 +- .../common/exception/ContractErrorCode.java | 1 + .../query/controller/ContractController.java | 33 ++- .../query/repository/ContractMapper.java | 16 +- .../service/ContractQueryServiceImpl.java | 48 +++-- .../query/repository/ContractMapper.xml | 195 +++++++++++++++++- 7 files changed, 265 insertions(+), 35 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java index 82c9aba4..06acab5b 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java @@ -9,7 +9,6 @@ @NoArgsConstructor @Setter @Getter -@ToString public class ContractModifyDTO { private String contractId; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index 843a94cd..53b6903c 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -141,7 +141,7 @@ private String updateCustomerInfo(ContractModifyDTO contractModifyDTO, String me customerDTO = modelMapper.map(customerCommandService.registerCustomerInfo(customerRegistDTO), CustomerDTO.class); if (customerDTO == null) { - throw new RuntimeException("새로 등록된 고객 정보를 찾을 수 없습니다."); + throw new ContractCommonException(ContractErrorCode.CUSTOMER_NOT_FOUND); } return customerDTO.getCustomerId(); @@ -182,7 +182,7 @@ public void registerContract(ContractRegistDTO contractRegistRequestDTO) throws @Transactional public void modifyContract(ContractModifyDTO contractModifyRequestDTO) throws GeneralSecurityException { String memberId = authQueryService.selectMemberIdByLoginId(contractModifyRequestDTO.getMemberId()); - String productId = String.valueOf(productQueryService.selectByProductSerialNumber(contractModifyRequestDTO.getSerialNum())); + String productId = productQueryService.selectByProductSerialNumber(contractModifyRequestDTO.getSerialNum()).getProductId(); String customerId = updateCustomerInfo(contractModifyRequestDTO, memberId); String centerId = memberQueryService.selectMemberInfo(contractModifyRequestDTO.getMemberId()).getCenterId(); @@ -205,8 +205,6 @@ public void modifyContract(ContractModifyDTO contractModifyRequestDTO) throws Ge updateContract.setCustomerAddress(aesUtils.encrypt(contractModifyRequestDTO.getCustomerAddress())); updateContract.setCustomerIdentifiNo(aesUtils.encrypt(contractModifyRequestDTO.getCustomerIdentifiNo())); - log.info("Update contract: " + updateContract); - // 수정된 계약 정보 저장 contractRepository.save(updateContract); diff --git a/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ContractErrorCode.java b/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ContractErrorCode.java index e4f6c47f..cd190d81 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ContractErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/common/exception/ContractErrorCode.java @@ -40,6 +40,7 @@ public enum ContractErrorCode { */ CONTRACT_NOT_FOUND(40401, HttpStatus.NOT_FOUND, "계약서를 찾을 수 없습니다."), UPDATE_HISTORY_NOT_FOUND(40402, HttpStatus.NOT_FOUND, "수정내역에서 계약서를 찾을 수 없습니다."), + CUSTOMER_NOT_FOUND(40403, HttpStatus.NOT_FOUND, "고객 정보를 찾을 수 없습니다."), /** diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java index e7a995c1..d0abfe87 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java @@ -7,7 +7,9 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -39,11 +41,20 @@ public ContractController(ContractQueryService contractQueryService) { }) @GetMapping("employee") public ResponseEntity getAllContractEmployee(@PageableDefault(size = 10) Pageable pageable, - Principal principal) { + Principal principal, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder) { ContractSelectAllDTO contractSelectAllDTO = new ContractSelectAllDTO(); contractSelectAllDTO.setMemberId(principal.getName()); + // 정렬 추가 + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } + + Page responseContracts = contractQueryService.selectAllContractEmployee(contractSelectAllDTO, pageable); return ResponseEntity.ok(ContractResponseMessage.builder() @@ -126,11 +137,19 @@ public ResponseEntity getContractBySearchEmployee(Princ }) @GetMapping("center") public ResponseEntity getAllContractAdmin(@PageableDefault(size = 10) Pageable pageable, - Principal principal) throws GeneralSecurityException { + Principal principal, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder) throws GeneralSecurityException { ContractSelectAllDTO contractSelectAllDTO = new ContractSelectAllDTO(); contractSelectAllDTO.setMemberId(principal.getName()); + // 정렬 추가 + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } + Page responseContracts = contractQueryService.selectAllContractAdmin(contractSelectAllDTO, pageable); return ResponseEntity.ok(ContractResponseMessage.builder() @@ -212,10 +231,18 @@ public ResponseEntity getContractBySearchAdmin(Principa content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) @GetMapping("") - public ResponseEntity getAllContract(@PageableDefault(size = 10) Pageable pageable) { + public ResponseEntity getAllContract(@PageableDefault(size = 10) Pageable pageable, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder) { ContractSelectAllDTO contractSelectAllDTO = new ContractSelectAllDTO(); + // 정렬 추가 + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } + Page responseContracts = contractQueryService.selectAllContract(contractSelectAllDTO, pageable); return ResponseEntity.ok(ContractResponseMessage.builder() diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java index 8ff8be7e..d12acab0 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java @@ -21,12 +21,16 @@ List findContractBySearchAndMemberId(@Param("offset") int off List findContractAllByMemId(@Param("offset") int offset, @Param("pageSize") int pageSize, - @Param("memberId") String memberId); + @Param("memberId") String memberId, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); int findContractCountByMemId(String memId); List findContractAll(@Param("offset") int offset, - @Param("pageSize") int pageSize); + @Param("pageSize") int pageSize, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); int findContractCount(); @@ -39,13 +43,15 @@ List findContractBySearch(@Param("offset") int offset, int findContractBySearchCount(ContractSearchDTO contractSearchDTO); List findContractAllByCenterId(@Param("offset") int offset, - @Param("pageSize") int pageSize, - @Param("centerId") String centerId); + @Param("pageSize") int pageSize, + @Param("centerId") String centerId, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); Integer findContractCountByCenterId(@Param("centerId") String centerId); ContractSeletIdDTO findContractByIdAndCenterId(@Param("contractId")String contractId, - @Param("centerId") String centerId); + @Param("centerId") String centerId); List findContractBySearchAndCenterId(@Param("offset") int offset, @Param("pageSize") int pageSize, diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java index 4df64eb5..b3f424d6 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java @@ -7,6 +7,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -52,12 +53,21 @@ public Page selectAllContractEmployee(ContractSelectAllDTO int offset = Math.toIntExact(pageable.getOffset()); int pageSize = pageable.getPageSize(); + // 정렬 정보 가져오기 + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } + String caschKey = "myCache::contracts::offset=" + offset + "::pageSize=" + pageSize; List contracts = (List) redisTemplate.opsForValue().get(caschKey); if (contracts == null) { - contracts = contractMapper.findContractAllByMemId(offset, pageSize, memberId); + contracts = contractMapper.findContractAllByMemId(offset, pageSize, memberId, sortField, sortOrder); if (contracts == null) { throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); @@ -125,12 +135,21 @@ public Page selectAllContractAdmin(ContractSelectAllDTO co int offset = Math.toIntExact(pageable.getOffset()); int pageSize = pageable.getPageSize(); + // 정렬 정보 가져오기 + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } + String caschKey = "myCache::contracts::offset=" + offset + "::pageSize=" + pageSize; List contracts = (List) redisTemplate.opsForValue().get(caschKey); if (contracts == null) { - contracts = contractMapper.findContractAllByCenterId(offset, pageSize, centerId); + contracts = contractMapper.findContractAllByCenterId(offset, pageSize, centerId, sortField, sortOrder); if (contracts == null) { throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); @@ -186,21 +205,16 @@ public Page selectAllContract(ContractSelectAllDTO contrac int offset = Math.toIntExact(pageable.getOffset()); int pageSize = pageable.getPageSize(); -// String caschKey = "myCache::contracts::offset=" + offset + "::pageSize=" + pageSize; -// -// List contracts = (List) redisTemplate.opsForValue().get(caschKey); -// -// if (contracts == null) { -// contracts = contractMapper.findContractAll(offset, pageSize); -// -// if (contracts == null) { -// throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); -// } -// -// redisTemplate.opsForValue().set(caschKey, contracts); -// } - - List contracts = contractMapper.findContractAll(offset, pageSize); + // 정렬 정보 가져오기 + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } + + List contracts = contractMapper.findContractAll(offset, pageSize, sortField, sortOrder); if (contracts == null) { throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index 0492666d..2adc67cf 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -193,7 +193,44 @@ a.prod_id FROM tb_contract a WHERE a.active = TRUE - ORDER BY a.created_at DESC + + + + + + ORDER BY a.conr_cust_name ${sortOrder} + + + ORDER BY a.conr_ttl ${sortOrder} + + + ORDER BY a.conr_car_name ${sortOrder} + + + ORDER BY a.conr_stat ${sortOrder} + + + ORDER BY a.conr_comp_name ${sortOrder} + + + ORDER BY a.conr_cust_cla ${sortOrder} + + + ORDER BY a.conr_cust_pur_cond ${sortOrder} + + + ORDER BY a.conr_id ${sortOrder} + + + ORDER BY a.created_at DESC + + + + + + ORDER BY a.created_at DESC + + LIMIT #{pageSize} OFFSET #{offset} @@ -243,7 +280,44 @@ AND b.prod_name LIKE CONCAT('%', #{contractSearchDTO.productId}, '%') - ORDER BY a.created_at DESC + + + + + + ORDER BY a.conr_cust_name ${sortOrder} + + + ORDER BY a.conr_ttl ${sortOrder} + + + ORDER BY a.conr_car_name ${sortOrder} + + + ORDER BY a.conr_stat ${sortOrder} + + + ORDER BY a.conr_comp_name ${sortOrder} + + + ORDER BY a.conr_cust_cla ${sortOrder} + + + ORDER BY a.conr_cust_pur_cond ${sortOrder} + + + ORDER BY a.conr_id ${sortOrder} + + + ORDER BY a.created_at DESC + + + + + + ORDER BY a.created_at DESC + + LIMIT #{pageSize} OFFSET #{offset} @@ -292,7 +366,44 @@ AND b.prod_name LIKE CONCAT('%', #{contractSearchDTO.productId}, '%') - ORDER BY a.created_at DESC + + + + + + ORDER BY a.conr_cust_name ${sortOrder} + + + ORDER BY a.conr_ttl ${sortOrder} + + + ORDER BY a.conr_car_name ${sortOrder} + + + ORDER BY a.conr_stat ${sortOrder} + + + ORDER BY a.conr_comp_name ${sortOrder} + + + ORDER BY a.conr_cust_cla ${sortOrder} + + + ORDER BY a.conr_cust_pur_cond ${sortOrder} + + + ORDER BY a.conr_id ${sortOrder} + + + ORDER BY a.created_at DESC + + + + + + ORDER BY a.created_at DESC + + LIMIT #{pageSize} OFFSET #{offset} @@ -401,7 +512,44 @@ FROM tb_contract a WHERE a.active = TRUE AND a.cent_id = #{centerId} - ORDER BY a.created_at DESC + + + + + + ORDER BY a.conr_cust_name ${sortOrder} + + + ORDER BY a.conr_ttl ${sortOrder} + + + ORDER BY a.conr_car_name ${sortOrder} + + + ORDER BY a.conr_stat ${sortOrder} + + + ORDER BY a.conr_comp_name ${sortOrder} + + + ORDER BY a.conr_cust_cla ${sortOrder} + + + ORDER BY a.conr_cust_pur_cond ${sortOrder} + + + ORDER BY a.conr_id ${sortOrder} + + + ORDER BY a.created_at DESC + + + + + + ORDER BY a.created_at DESC + + LIMIT #{pageSize} OFFSET #{offset} @@ -527,7 +675,44 @@ AND b.prod_name LIKE CONCAT('%', #{contractSearchDTO.productId}, '%') - ORDER BY a.created_at DESC + + + + + + ORDER BY a.conr_cust_name ${sortOrder} + + + ORDER BY a.conr_ttl ${sortOrder} + + + ORDER BY a.conr_car_name ${sortOrder} + + + ORDER BY a.conr_stat ${sortOrder} + + + ORDER BY a.conr_comp_name ${sortOrder} + + + ORDER BY a.conr_cust_cla ${sortOrder} + + + ORDER BY a.conr_cust_pur_cond ${sortOrder} + + + ORDER BY a.conr_id ${sortOrder} + + + ORDER BY a.created_at DESC + + + + + + ORDER BY a.created_at DESC + + LIMIT #{pageSize} OFFSET #{offset} From 99f0b20aedf427b83e7c8e6a44587645890adb24 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Wed, 27 Nov 2024 09:55:26 +0900 Subject: [PATCH 324/563] =?UTF-8?q?refactor:=20=EA=B3=84=EC=95=BD=EC=84=9C?= =?UTF-8?q?=20=EC=A0=95=EB=A0=AC=20=EB=B0=8F=20=EC=97=91=EC=85=80=20(#161)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/ContractController.java | 12 +++++++ .../contract/query/dto/ContractExcelDTO.java | 36 +++++++++++++++++++ .../query/repository/ContractMapper.java | 3 ++ .../query/service/ContractQueryService.java | 2 ++ .../service/ContractQueryServiceImpl.java | 19 +++++++++- .../query/repository/ContractMapper.xml | 24 +++++++++++++ 6 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractExcelDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java index d0abfe87..7c0b5bf6 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -312,4 +313,15 @@ public ResponseEntity getContractBySearch(@RequestParam .result(responseContracts) .build()); } + + @Operation(summary = "엑셀 다운로드") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "계약서 검색 조회 성공", + content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) + }) + @GetMapping("excel") + public void exportContract(HttpServletResponse response) { + + contractQueryService.exportContractToExcel(response); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractExcelDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractExcelDTO.java new file mode 100644 index 00000000..325afd57 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractExcelDTO.java @@ -0,0 +1,36 @@ +package stanl_2.final_backend.domain.contract.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import stanl_2.final_backend.global.excel.ExcelColumnName; + +@Getter +@AllArgsConstructor +public class ContractExcelDTO { + + @ExcelColumnName(name = "계약서 번호") + private String contractId; + + @ExcelColumnName(name = "계약서명") + private String title; + + @ExcelColumnName(name = "고객명") + private String customerName; + + @ExcelColumnName(name = "고객 상호") + private String companyName; + + @ExcelColumnName(name = "승인 상태") + private String status; + + @ExcelColumnName(name = "제품명") + private String carName; + + @ExcelColumnName(name = "고객 구분") + private String customerClassifcation; + + @ExcelColumnName(name = "고객 조건") + private String customerPurchaseCondition; +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java index d12acab0..87b28f7f 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java @@ -2,6 +2,7 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; +import stanl_2.final_backend.domain.contract.query.dto.ContractExcelDTO; import stanl_2.final_backend.domain.contract.query.dto.ContractSearchDTO; import stanl_2.final_backend.domain.contract.query.dto.ContractSelectAllDTO; import stanl_2.final_backend.domain.contract.query.dto.ContractSeletIdDTO; @@ -59,4 +60,6 @@ List findContractBySearchAndCenterId(@Param("offset") int off @Param("centerId") String centerId); Integer findContractBySearchAndCenterCount(@Param("contractSearchDTO") ContractSearchDTO contractSearchDTO, @Param("centerId") String centerId); + + List findContractForExcel(); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java index b70f57d5..a03c13e2 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryService.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.contract.query.service; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import stanl_2.final_backend.domain.contract.query.dto.ContractSearchDTO; @@ -30,4 +31,5 @@ public interface ContractQueryService { Page selectBySearchAdmin(ContractSearchDTO contractSearchDTO, Pageable pageable) throws GeneralSecurityException; + void exportContractToExcel(HttpServletResponse response); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java index b3f424d6..4cb86c15 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.contract.query.service; +import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringEscapeUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -13,12 +14,14 @@ import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.contract.common.exception.ContractCommonException; import stanl_2.final_backend.domain.contract.common.exception.ContractErrorCode; +import stanl_2.final_backend.domain.contract.query.dto.ContractExcelDTO; import stanl_2.final_backend.domain.contract.query.dto.ContractSearchDTO; import stanl_2.final_backend.domain.contract.query.dto.ContractSelectAllDTO; import stanl_2.final_backend.domain.contract.query.dto.ContractSeletIdDTO; import stanl_2.final_backend.domain.contract.query.repository.ContractMapper; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.member.query.service.MemberQueryService; +import stanl_2.final_backend.global.excel.ExcelUtilsV1; import java.security.GeneralSecurityException; import java.util.List; @@ -32,14 +35,16 @@ public class ContractQueryServiceImpl implements ContractQueryService { private final MemberQueryService memberQueryService; private final UpdateHistoryQueryService updateHistoryQueryService; private final RedisTemplate redisTemplate; + private final ExcelUtilsV1 excelUtilsV1; @Autowired - public ContractQueryServiceImpl(ContractMapper contractMapper, AuthQueryService authQueryService, MemberQueryService memberQueryService, UpdateHistoryQueryService updateHistoryQueryService, @Qualifier("redisTemplate") RedisTemplate redisTemplate) { + public ContractQueryServiceImpl(ContractMapper contractMapper, AuthQueryService authQueryService, MemberQueryService memberQueryService, UpdateHistoryQueryService updateHistoryQueryService, @Qualifier("redisTemplate") RedisTemplate redisTemplate, ExcelUtilsV1 excelUtilsV1) { this.contractMapper = contractMapper; this.authQueryService = authQueryService; this.memberQueryService = memberQueryService; this.updateHistoryQueryService = updateHistoryQueryService; this.redisTemplate = redisTemplate; + this.excelUtilsV1 = excelUtilsV1; } // 영업사원 조회 @@ -266,4 +271,16 @@ public Page selectBySearch(ContractSearchDTO contractSearchDT return new PageImpl<>(contracts, pageable, totalContract); } + + @Override + public void exportContractToExcel(HttpServletResponse response) { + List contractExcels = contractMapper.findContractForExcel(); + + if (contractExcels == null) { + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); + } + + excelUtilsV1.download(ContractExcelDTO.class, contractExcels, "contractExcel", response); + } + } \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index 2adc67cf..7785dae1 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -82,6 +82,17 @@ + + + + + + + + + + + + + \ No newline at end of file From 3317a3eae0a32780b2fddf5b7dcc790bb709fe8a Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 11:08:21 +0900 Subject: [PATCH 325/563] =?UTF-8?q?operation:=206=EC=B0=A8=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 96b174ec..fa3045e1 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -77,6 +77,7 @@ jobs: # Elastic Beanstalk 배포 - name: Deploy to Elastic Beanstalk run: | + eb deploy --timeout 60 eb init -p "Docker" ${{ env.APPLICATION_NAME }} --region ${{ env.AWS_REGION }} eb use ${{ env.ENVIRONMENT_NAME }} eb deploy --staged \ No newline at end of file From 84691666aefd6bd4fe128a9693759d2e5f2c517b Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 11:12:15 +0900 Subject: [PATCH 326/563] =?UTF-8?q?operation:=206=EC=B0=A8=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index fa3045e1..543409e1 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -77,7 +77,6 @@ jobs: # Elastic Beanstalk 배포 - name: Deploy to Elastic Beanstalk run: | - eb deploy --timeout 60 eb init -p "Docker" ${{ env.APPLICATION_NAME }} --region ${{ env.AWS_REGION }} eb use ${{ env.ENVIRONMENT_NAME }} - eb deploy --staged \ No newline at end of file + eb deploy --staged --timeout 60 \ No newline at end of file From 748d1c080e816a7ec9a29a3ae413d5e5a692a658 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 11:38:11 +0900 Subject: [PATCH 327/563] =?UTF-8?q?operation:=206=EC=B0=A8=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 543409e1..1d61f487 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -79,4 +79,4 @@ jobs: run: | eb init -p "Docker" ${{ env.APPLICATION_NAME }} --region ${{ env.AWS_REGION }} eb use ${{ env.ENVIRONMENT_NAME }} - eb deploy --staged --timeout 60 \ No newline at end of file + eb deploy --staged --timeout 30 \ No newline at end of file From afd0119d235d69cc7c4f5c6686fd18568255b435 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 11:50:22 +0900 Subject: [PATCH 328/563] =?UTF-8?q?operation:=206=EC=B0=A8=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 1d61f487..a92599d2 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -79,4 +79,4 @@ jobs: run: | eb init -p "Docker" ${{ env.APPLICATION_NAME }} --region ${{ env.AWS_REGION }} eb use ${{ env.ENVIRONMENT_NAME }} - eb deploy --staged --timeout 30 \ No newline at end of file + eb deploy --staged --timeout 50 \ No newline at end of file From 82ad4ff89f121bfa0f70f1c7ea0c1e15399025c5 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 12:04:59 +0900 Subject: [PATCH 329/563] =?UTF-8?q?operation:=206=EC=B0=A8=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yaml | 2 +- docker-compose.yml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index a92599d2..96b174ec 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -79,4 +79,4 @@ jobs: run: | eb init -p "Docker" ${{ env.APPLICATION_NAME }} --region ${{ env.AWS_REGION }} eb use ${{ env.ENVIRONMENT_NAME }} - eb deploy --staged --timeout 50 \ No newline at end of file + eb deploy --staged \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index c5b62445..ee4baf39 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,6 +2,8 @@ version: '3.8' services: backend: + env_file: + - .env build: context: . dockerfile: Dockerfile From 1bf2e297589877e061f35e71de2f3d58cc78ed0d Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 12:27:57 +0900 Subject: [PATCH 330/563] =?UTF-8?q?operation:=206=EC=B0=A8=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index ee4baf39..c5eb987b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,6 +19,11 @@ services: depends_on: redis: condition: service_healthy + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:8080/actuator/health" ] + interval: 10s + timeout: 5s + retries: 5 networks: - app-network restart: always @@ -47,7 +52,8 @@ services: dockerfile: Dockerfile container_name: nginx depends_on: - - backend + backend: + condition: service_healthy ports: - "80:80" networks: From 92e1e9953057063561183529f99201c85de0c34b Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 12:39:56 +0900 Subject: [PATCH 331/563] =?UTF-8?q?operation:=206=EC=B0=A8=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index c5eb987b..74ce53ee 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,11 +19,6 @@ services: depends_on: redis: condition: service_healthy - healthcheck: - test: [ "CMD", "curl", "-f", "http://localhost:8080/actuator/health" ] - interval: 10s - timeout: 5s - retries: 5 networks: - app-network restart: always From dafbe2d1e7c0dedd3ccf0327051f189b4c3b7f4e Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 12:42:26 +0900 Subject: [PATCH 332/563] =?UTF-8?q?operation:=206=EC=B0=A8=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nginx/default.conf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nginx/default.conf b/nginx/default.conf index 50fa47fc..23ccc12e 100644 --- a/nginx/default.conf +++ b/nginx/default.conf @@ -1,7 +1,8 @@ upstream backend { - server backend:8080; # 백엔드 컨테이너의 포트를 8080으로 수정 + server 172.20.0.3:8080; } + server { listen 80; From 147f22812b47005bd80123c762d7ca4e3c73a8eb Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 13:38:13 +0900 Subject: [PATCH 333/563] =?UTF-8?q?operation:=206=EC=B0=A8=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 74ce53ee..ef450eca 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,6 +23,11 @@ services: - app-network restart: always entrypoint: [ "sh", "-c", "sleep 10 && java -jar app.jar" ] + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:8080/health" ] + interval: 30s + timeout: 10s + retries: 3 redis: image: redis:7.0-alpine From 7c04d93c72691ebe2ad2bd520451f2363e617e9d Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 13:51:01 +0900 Subject: [PATCH 334/563] =?UTF-8?q?operation:=206=EC=B0=A8=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index ab443bd2..e5d11cca 100644 --- a/build.gradle +++ b/build.gradle @@ -30,6 +30,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3' + implementation 'org.springframework.boot:spring-boot-starter-actuator' testImplementation 'org.springframework.security:spring-security-test' testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:3.0.3' compileOnly 'org.projectlombok:lombok' From 0f49046f09df28d5ef653675937e83e6f5f6033e Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 14:11:16 +0900 Subject: [PATCH 335/563] =?UTF-8?q?operation:=206=EC=B0=A8=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index ef450eca..db24ba9b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -24,7 +24,7 @@ services: restart: always entrypoint: [ "sh", "-c", "sleep 10 && java -jar app.jar" ] healthcheck: - test: [ "CMD", "curl", "-f", "http://localhost:8080/health" ] + test: [ "CMD", "curl", "-f", "http://localhost:8080/swagger-ui/index.html" ] interval: 30s timeout: 10s retries: 3 From b9f9f4817fc29edff38b79e56d212e3f206ffecd Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 14:20:18 +0900 Subject: [PATCH 336/563] =?UTF-8?q?operation:=206=EC=B0=A8=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index db24ba9b..74ce53ee 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,11 +23,6 @@ services: - app-network restart: always entrypoint: [ "sh", "-c", "sleep 10 && java -jar app.jar" ] - healthcheck: - test: [ "CMD", "curl", "-f", "http://localhost:8080/swagger-ui/index.html" ] - interval: 30s - timeout: 10s - retries: 3 redis: image: redis:7.0-alpine From 184b911dcf696ef4ecfbc55d4546a7f2f7191016 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 14:46:51 +0900 Subject: [PATCH 337/563] =?UTF-8?q?operation:=206=EC=B0=A8=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yaml | 2 +- docker-compose.yml | 3 +-- nginx/Dockerfile | 2 +- nginx/default.conf | 3 +-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 96b174ec..a92599d2 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -79,4 +79,4 @@ jobs: run: | eb init -p "Docker" ${{ env.APPLICATION_NAME }} --region ${{ env.AWS_REGION }} eb use ${{ env.ENVIRONMENT_NAME }} - eb deploy --staged \ No newline at end of file + eb deploy --staged --timeout 50 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 74ce53ee..ee4baf39 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,8 +47,7 @@ services: dockerfile: Dockerfile container_name: nginx depends_on: - backend: - condition: service_healthy + - backend ports: - "80:80" networks: diff --git a/nginx/Dockerfile b/nginx/Dockerfile index 8b8e0c7b..e132ed6b 100644 --- a/nginx/Dockerfile +++ b/nginx/Dockerfile @@ -2,4 +2,4 @@ FROM nginx:alpine # Nginx 설정 파일 복사 COPY default.conf /etc/nginx/conf.d/default.conf -RUN ls -la /etc/nginx/conf.d/ # 설정 파일 복사 확인 +RUN ls -la /etc/nginx/conf.d/ # 설정 파일 복사 확인 \ No newline at end of file diff --git a/nginx/default.conf b/nginx/default.conf index 23ccc12e..50fa47fc 100644 --- a/nginx/default.conf +++ b/nginx/default.conf @@ -1,8 +1,7 @@ upstream backend { - server 172.20.0.3:8080; + server backend:8080; # 백엔드 컨테이너의 포트를 8080으로 수정 } - server { listen 80; From 3dc018de7fa3d62a3277ad649b5a251782fa8ded Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 14:59:42 +0900 Subject: [PATCH 338/563] =?UTF-8?q?operation:=206=EC=B0=A8=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 507c4ac7..8d8f164f 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -49,11 +49,11 @@ logging: cloud: aws: credentials: - access-key: ${S3_ACCESSKEY} - secret-key: ${S3_SECRETKEY} + access-key: AKIAQE3ROMWSWEBT7F7O + secret-key: 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 s3: - bucket: ${S3_BUCKETNAME} + bucket: motivebk region: - static: ${S3_REGION} + static: ap-northeast-2 stack: auto: false From 85e56a5d58361ce2792f2f9803c5e41eb6104e06 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 17:07:40 +0900 Subject: [PATCH 339/563] =?UTF-8?q?feat:=20=ED=8F=AC=ED=8A=B8=20=EB=B3=80?= =?UTF-8?q?=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/config/ProdSecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index 3ad2d625..b7b75fc6 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -76,7 +76,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); - config.setAllowedOrigins(Collections.singletonList("http://localhost:5173")); + config.setAllowedOrigins(Collections.singletonList("http://localhost:4173")); config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); config.setAllowCredentials(true); config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "X-XSRF-TOKEN")); From 4616236c0b7660a7a21410a543141d108b22469c Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 17:08:30 +0900 Subject: [PATCH 340/563] =?UTF-8?q?feat:=20=EB=B3=B5=EA=B5=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/config/ProdSecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index b7b75fc6..3ad2d625 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -76,7 +76,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); - config.setAllowedOrigins(Collections.singletonList("http://localhost:4173")); + config.setAllowedOrigins(Collections.singletonList("http://localhost:5173")); config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); config.setAllowCredentials(true); config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "X-XSRF-TOKEN")); From 9b30515c694f70f475deebeb8e93f4a8d38db65c Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 17:27:52 +0900 Subject: [PATCH 341/563] =?UTF-8?q?feat:=20s3=20=EC=BB=AC=EB=9F=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80(#163)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notices/command/application/dto/NoticeModifyDTO.java | 1 + .../notices/command/application/dto/NoticeRegistDTO.java | 2 ++ .../domain/notices/command/domain/aggragate/entity/Notice.java | 3 +++ 3 files changed, 6 insertions(+) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java index 8c860781..b1c518cf 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java @@ -17,4 +17,5 @@ public class NoticeModifyDTO { private String classification; private String content; private String memberLoginId; + private String fileUrl; } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java index 1c50cc0e..819aeec4 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java @@ -21,4 +21,6 @@ public class NoticeRegistDTO { private String memberLoginId; private String memberId; + + private String fileUrl; } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java index 9c9b9966..92943ae3 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/aggragate/entity/Notice.java @@ -55,6 +55,9 @@ public class Notice { @Column(name = "MEM_ID", nullable = false) private String memberId; + @Column(name = "FILE_URL") + private String fileUrl; + @PrePersist private void prePersist() { this.createdAt = getCurrentTime(); From 2f5969f67886d40a29ba8cc24e8211ee2198b2c8 Mon Sep 17 00:00:00 2001 From: giuseog Date: Wed, 27 Nov 2024 18:08:15 +0900 Subject: [PATCH 342/563] =?UTF-8?q?fix:=20centerId=20dto=20=EC=88=98?= =?UTF-8?q?=EC=A0=95,=20=ED=8E=98=EC=9D=B4=EC=A7=95=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20=EC=A4=91(#164)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/CenterController.java | 24 ++++++-- .../query/dto/CenterSearchRequestDTO.java | 2 +- .../center/query/repository/CenterMapper.java | 8 ++- .../query/service/CenterQueryServiceImpl.java | 23 ++++++- .../query/controller/ProductController.java | 28 ++++++--- .../query/repository/ProductMapper.java | 17 +++++- .../query/service/ProductQueryService.java | 6 +- .../service/ProductQueryServiceImpl.java | 37 +++++++++--- .../center/query/repository/CenterMapper.xml | 60 ++++++++++++++++++- 9 files changed, 172 insertions(+), 33 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java b/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java index 9d68463c..306bdab6 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java @@ -8,7 +8,9 @@ import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -41,7 +43,14 @@ public CenterController(CenterQueryService centerQueryService) { content = @Content(mediaType = "application/json")) }) @GetMapping("") - public ResponseEntity getCenterAll(@PageableDefault(size = 20) Pageable pageable){ + public ResponseEntity getCenterAll(@PageableDefault(size = 20) Pageable pageable, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder){ + + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } Page responseCenters = centerQueryService.selectAll(pageable); @@ -80,13 +89,20 @@ public ResponseEntity getCenterById(@PathVariable("center }) @GetMapping("/search") public ResponseEntity getCenterBySearch(@RequestParam Map params - ,@PageableDefault(size = 20) Pageable pageable){ + ,@PageableDefault(size = 20) Pageable pageable, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder){ CenterSearchRequestDTO centerSearchRequestDTO = new CenterSearchRequestDTO(); - centerSearchRequestDTO.setId(params.get("id")); + centerSearchRequestDTO.setCenterId(params.get("centerId")); centerSearchRequestDTO.setName(params.get("name")); centerSearchRequestDTO.setAddress(params.get("address")); + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } + Page responseCenters = centerQueryService.selectBySearch(centerSearchRequestDTO, pageable); return ResponseEntity.ok(CenterResponseMessage.builder() @@ -107,7 +123,7 @@ public ResponseEntity getCenterBySearch(@RequestParam Map public ResponseEntity getCenterListBySearch(@RequestParam Map params){ CenterSearchRequestDTO centerSearchRequestDTO = new CenterSearchRequestDTO(); - centerSearchRequestDTO.setId(params.get("id")); + centerSearchRequestDTO.setCenterId(params.get("centerId")); centerSearchRequestDTO.setName(params.get("name")); centerSearchRequestDTO.setAddress(params.get("address")); diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSearchRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSearchRequestDTO.java index 4dc96cef..1b5d05ce 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSearchRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSearchRequestDTO.java @@ -10,7 +10,7 @@ @Getter @Setter public class CenterSearchRequestDTO { - private String id; + private String centerId; private String name; private String address; } diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java b/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java index ca70bedc..3b225c77 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/repository/CenterMapper.java @@ -14,7 +14,9 @@ public interface CenterMapper { CenterSelectIdDTO findCenterById(String id); List findCenterAll(@Param("size") int size - , @Param("offset") int offset); + , @Param("offset") int offset, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); Integer findCenterCount(); @@ -22,7 +24,9 @@ List findCenterAll(@Param("size") int size List findCenterBySearch(@Param("size") int size , @Param("offset") int offset - , @Param("centerSearchRequestDTO") CenterSearchRequestDTO centerSearchRequestDTO); + , @Param("centerSearchRequestDTO") CenterSearchRequestDTO centerSearchRequestDTO, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); List findCenterListBySearch(@Param("centerSearchRequestDTO") CenterSearchRequestDTO centerSearchRequestDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryServiceImpl.java index 695c703a..7ff06fb7 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryServiceImpl.java @@ -16,10 +16,10 @@ import stanl_2.final_backend.domain.center.query.dto.CenterSelectIdDTO; import stanl_2.final_backend.domain.center.query.repository.CenterMapper; import stanl_2.final_backend.global.excel.ExcelUtilsV1; +import org.springframework.data.domain.Sort; import java.util.List; -@Slf4j @Service("queryCenterServiceImpl") public class CenterQueryServiceImpl implements CenterQueryService { @@ -49,7 +49,16 @@ public Page selectAll(Pageable pageable) { int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); - List centerList = centerMapper.findCenterAll(size, offset); + + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } + + List centerList = centerMapper.findCenterAll(size, offset, sortField, sortOrder); int total = centerMapper.findCenterCount(); @@ -62,7 +71,15 @@ public Page selectBySearch(CenterSearchRequestDTO centerSear int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); - List centerList = centerMapper.findCenterBySearch(size, offset, centerSearchRequestDTO); + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } + + List centerList = centerMapper.findCenterBySearch(size, offset, centerSearchRequestDTO, sortField, sortOrder); int total = centerMapper.findCenterBySearchCount(centerSearchRequestDTO); return new PageImpl<>(centerList, pageable, total); diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java b/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java index e346ec75..9e192c76 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java @@ -8,7 +8,9 @@ import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -16,6 +18,7 @@ import stanl_2.final_backend.domain.center.common.response.CenterResponseMessage; import stanl_2.final_backend.domain.product.common.response.ProductResponseMessage; import stanl_2.final_backend.domain.product.query.dto.ProductSearchRequestDTO; +import stanl_2.final_backend.domain.product.query.dto.ProductSelectAllDTO; import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; import stanl_2.final_backend.domain.product.query.service.ProductQueryService; @@ -41,9 +44,16 @@ public ProductController(ProductQueryService productQueryService) { content = @Content(mediaType = "application/json")) }) @GetMapping("") - public ResponseEntity getProductAll(@PageableDefault(size = 20) Pageable pageable){ + public ResponseEntity getProductAll(@PageableDefault(size = 20) Pageable pageable, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder){ - Page> responseProducts = productQueryService.selectAll(pageable); + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } + + Page responseProducts = productQueryService.selectAll(pageable); return ResponseEntity.ok(ProductResponseMessage.builder() .httpStatus(200) @@ -78,18 +88,22 @@ public ResponseEntity getProductById(@PathVariable("id") }) @GetMapping("/search") public ResponseEntity getProductBySearch(@RequestParam Map params - ,@PageableDefault(size = 20) Pageable pageable){ + ,@PageableDefault(size = 20) Pageable pageable){ ProductSearchRequestDTO productSearchRequestDTO = new ProductSearchRequestDTO(); productSearchRequestDTO.setId(params.get("id")); productSearchRequestDTO.setName(params.get("name")); productSearchRequestDTO.setSerialNumber(params.get("serialNumber")); - Map paramMap = new HashMap<>(); - paramMap.put("productSearchRequestDTO", productSearchRequestDTO); - paramMap.put("pageable", pageable); + String sortField = params.get("sortField"); + String sortOrder = params.get("sortOrder"); + + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } - Page> responseProducts = productQueryService.selectProductBySearch(paramMap); + Page responseProducts = productQueryService.selectProductBySearch(productSearchRequestDTO, pageable); return ResponseEntity.ok(ProductResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java b/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java index 0b96a807..b23bbbca 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java @@ -1,9 +1,13 @@ package stanl_2.final_backend.domain.product.query.repository; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; import stanl_2.final_backend.domain.A_sample.query.dto.SampleExcelDownload; +import stanl_2.final_backend.domain.center.query.dto.CenterSearchRequestDTO; import stanl_2.final_backend.domain.product.common.util.RequestList; import stanl_2.final_backend.domain.product.query.dto.ProductExcelDownload; +import stanl_2.final_backend.domain.product.query.dto.ProductSearchRequestDTO; +import stanl_2.final_backend.domain.product.query.dto.ProductSelectAllDTO; import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; import java.util.List; @@ -11,15 +15,22 @@ @Mapper public interface ProductMapper { - List> findProductAll(RequestList requestList); + List findProductAll(@Param("size") int size + , @Param("offset") int offset, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); int findProductCount(); ProductSelectIdDTO findProductById(String id); - List> findProductBySearch(Map paramMap); + List> findProductBySearch(@Param("size") int size + , @Param("offset") int offset + , @Param("productSearchRequestDTO") ProductSearchRequestDTO productSearchRequestDTO, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); - int findProductBySearchCount(Map paramMap); + int findProductBySearchCount(@Param("productSearchRequestDTO") ProductSearchRequestDTO productSearchRequestDTO); ProductSelectIdDTO findProductBySerialNumber(String serialNumber); diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryService.java b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryService.java index 42ea98a6..da335991 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryService.java @@ -5,17 +5,19 @@ import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.product.query.dto.ProductSearchRequestDTO; +import stanl_2.final_backend.domain.product.query.dto.ProductSelectAllDTO; import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; import java.util.Map; @Service public interface ProductQueryService { - Page> selectAll(Pageable pageable); + Page selectAll(Pageable pageable); ProductSelectIdDTO selectByProductId(String id); - Page> selectProductBySearch(Map paramMap); + Page selectProductBySearch(ProductSearchRequestDTO productSearchRequestDTO, Pageable pageable); ProductSelectIdDTO selectByProductSerialNumber(String id); diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java index 4cf0c6b3..3d9a0fc7 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java @@ -5,6 +5,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.A_sample.query.dto.SampleExcelDownload; @@ -12,6 +13,8 @@ import stanl_2.final_backend.domain.product.common.exception.ProductErrorCode; import stanl_2.final_backend.domain.product.common.util.RequestList; import stanl_2.final_backend.domain.product.query.dto.ProductExcelDownload; +import stanl_2.final_backend.domain.product.query.dto.ProductSearchRequestDTO; +import stanl_2.final_backend.domain.product.query.dto.ProductSelectAllDTO; import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; import stanl_2.final_backend.domain.product.query.repository.ProductMapper; import stanl_2.final_backend.global.excel.ExcelUtilsV1; @@ -33,12 +36,19 @@ public ProductQueryServiceImpl(ProductMapper productMapper, ExcelUtilsV1 excelUt @Override @Transactional - public Page> selectAll(Pageable pageable) { - RequestList requestList = RequestList.builder() - .pageable(pageable) - .build(); + public Page selectAll(Pageable pageable) { + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); + + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } - List> productList = productMapper.findProductAll(requestList); + List productList = productMapper.findProductAll(size, offset, sortField, sortOrder); int total = productMapper.findProductCount(); @@ -64,13 +74,22 @@ public ProductSelectIdDTO selectByProductId(String id) { @Override @Transactional - public Page> selectProductBySearch(Map paramMap) { + public Page selectProductBySearch(ProductSearchRequestDTO productSearchRequestDTO, Pageable pageable) { + + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); - Pageable pageable = (Pageable) paramMap.get("pageable"); + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } - List> productList = productMapper.findProductBySearch(paramMap); + List> productList = productMapper.findProductBySearch(size, offset, productSearchRequestDTO, sortField, sortOrder); - int total = productMapper.findProductBySearchCount(paramMap); + int total = productMapper.findProductBySearchCount(productSearchRequestDTO); if(productList == null || total == 0) { throw new ProductCommonException(ProductErrorCode.PRODUCT_NOT_FOUND); diff --git a/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml b/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml index 424b2206..02e05d3b 100644 --- a/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml @@ -69,7 +69,35 @@ , a.updated_at FROM tb_center a WHERE a.active = TRUE - ORDER BY a.created_at DESC + + + + + + ORDER BY a.cent_id ${sortOrder} + + + ORDER BY a.cent_name ${sortOrder} + + + ORDER BY a.cent_adr ${sortOrder} + + + ORDER BY a.cent_mem_cnt ${sortOrder} + + + ORDER BY a.cent_opr_at ${sortOrder} + + + ORDER BY a.created_at DESC + + + + + + ORDER BY a.created_at DESC + + LIMIT #{size} OFFSET #{offset} @@ -96,7 +124,35 @@ AND a.cent_adr LIKE CONCAT('%', #{centerSearchRequestDTO.address}, '%') - ORDER BY a.created_at DESC + + + + + + ORDER BY a.cent_id ${sortOrder} + + + ORDER BY a.cent_name ${sortOrder} + + + ORDER BY a.cent_adr ${sortOrder} + + + ORDER BY a.cent_mem_cnt ${sortOrder} + + + ORDER BY a.cent_opr_at ${sortOrder} + + + ORDER BY a.created_at DESC + + + + + + ORDER BY a.created_at DESC + + LIMIT #{size} OFFSET #{offset} From 4f406a6c48020d3484ac5a45116f1f8de6a2bb77 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 19:56:54 +0900 Subject: [PATCH 343/563] =?UTF-8?q?feat:=20problem=20S3=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20=EC=BB=AC=EB=9F=BC=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#163)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/controller/NoticeController.java | 15 +++++++++++---- .../query/controller/NoticeController.java | 1 + .../domain/notices/query/dto/NoticeDTO.java | 1 + .../notices/query/service/NoticeServiceImpl.java | 2 ++ .../notices/query/repository/NoticeMapper.xml | 4 +++- 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java index b8f78c99..9b576b2d 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java @@ -8,13 +8,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeDeleteDTO; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeModifyDTO; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; import stanl_2.final_backend.domain.notices.command.application.service.NoticeCommandService; import stanl_2.final_backend.domain.notices.common.response.NoticeResponseMessage; -import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleDeleteDTO; +import stanl_2.final_backend.domain.s3.command.domain.service.S3FileServiceImpl; import java.security.Principal; @@ -25,10 +26,13 @@ public class NoticeController { private final NoticeCommandService noticeCommandService; private final AuthQueryService authQueryService; + private final S3FileServiceImpl s3FileService; + @Autowired - public NoticeController(NoticeCommandService noticeCommandService, AuthQueryService authQueryService){ + public NoticeController(NoticeCommandService noticeCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService){ this.noticeCommandService = noticeCommandService; this.authQueryService =authQueryService; + this.s3FileService = s3FileService; } @Operation(summary = "공지사항 작성") @@ -36,10 +40,13 @@ public NoticeController(NoticeCommandService noticeCommandService, AuthQueryServ @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = NoticeResponseMessage.class))}) }) - @PostMapping("") - public ResponseEntity postNotice(@RequestBody NoticeRegistDTO noticeRegistDTO, Principal principal){ + @PostMapping(value = "") + public ResponseEntity postNotice(@RequestPart("notice") NoticeRegistDTO noticeRegistDTO, // JSON 데이터 + @RequestPart("file") MultipartFile file, + Principal principal){ String memberId =authQueryService.selectMemberIdByLoginId(principal.getName()); noticeRegistDTO.setMemberId(memberId); + noticeRegistDTO.setFileUrl(s3FileService.uploadOneFile(file)); noticeCommandService.registerNotice(noticeRegistDTO, principal); return ResponseEntity.ok(NoticeResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java index c856ba5c..20c7d485 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java @@ -60,6 +60,7 @@ public ResponseEntity> getNotices( @GetMapping("{noticeId}") public ResponseEntity getNotice(@PathVariable String noticeId){ NoticeDTO noticeDTO = noticeService.findNotice(noticeId); + System.out.println(noticeDTO); return ResponseEntity.ok(noticeDTO); } @Operation(summary = "공지사항 엑셀 다운 테스트") diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java index 70f2b3fd..ea657c5d 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/dto/NoticeDTO.java @@ -32,4 +32,5 @@ public class NoticeDTO { private String memberId; + private String fileUrl; } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java index 0529b62c..4a700750 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java @@ -56,6 +56,8 @@ public Page findNotices(Pageable pageable, SearchDTO searchDTO) { @Override public NoticeDTO findNotice(String noticeId) { NoticeDTO notice = noticeMapper.findNotice(noticeId); + System.out.println(notice.getContent()); + System.out.println(notice.getFileUrl()); return notice; } @Transactional diff --git a/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml b/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml index c221b122..96cf93c3 100644 --- a/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml @@ -15,6 +15,7 @@ + @@ -124,7 +125,8 @@ a.updated_at, a.deleted_at, a.active, - a.mem_id + a.mem_id, + a.file_url FROM tb_notice a WHERE not_id = #{noticeId}; From dffabc7e13260635443bf8bf813b8482f400c7a6 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 19:58:24 +0900 Subject: [PATCH 344/563] =?UTF-8?q?feat:=20file=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/SampleCommandServiceImpl.java | 2 +- .../service/CenterCommandServiceImpl.java | 2 +- .../service/ContractCommandServiceImpl.java | 2 +- .../service/EvaluationCommandServiceImpl.java | 2 +- .../controller/FileController.java | 46 +++++++++++++++++++ .../s3/command/application/dto/FileDTO.java | 12 +++++ .../application}/service/S3FileService.java | 4 +- .../command/domain/domain/aggregate/File.java | 29 ++++++++++++ .../domain/repository/FileRepository.java | 8 ++++ .../domain}/service/S3FileServiceImpl.java | 39 +++++++++++++++- .../s3/common/exception/S3ErrorCode.java | 2 +- 11 files changed, 140 insertions(+), 8 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/s3/command/application/controller/FileController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/s3/command/application/dto/FileDTO.java rename src/main/java/stanl_2/final_backend/domain/s3/{ => command/application}/service/S3FileService.java (72%) create mode 100644 src/main/java/stanl_2/final_backend/domain/s3/command/domain/domain/aggregate/File.java create mode 100644 src/main/java/stanl_2/final_backend/domain/s3/command/domain/repository/FileRepository.java rename src/main/java/stanl_2/final_backend/domain/s3/{ => command/domain}/service/S3FileServiceImpl.java (71%) diff --git a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java index ab02ad89..5fab444e 100644 --- a/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java @@ -12,7 +12,7 @@ import stanl_2.final_backend.domain.A_sample.command.domain.repository.SampleRepository; import stanl_2.final_backend.domain.A_sample.common.exception.SampleCommonException; import stanl_2.final_backend.domain.A_sample.common.exception.SampleErrorCode; -import stanl_2.final_backend.domain.s3.service.S3FileService; +import stanl_2.final_backend.domain.s3.command.application.service.S3FileService; import java.time.ZoneId; import java.time.ZonedDateTime; diff --git a/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java index dc057c11..09753336 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java @@ -12,7 +12,7 @@ import stanl_2.final_backend.domain.center.command.domain.repository.CenterRepository; import stanl_2.final_backend.domain.center.common.exception.CenterCommonException; import stanl_2.final_backend.domain.center.common.exception.CenterErrorCode; -import stanl_2.final_backend.domain.s3.service.S3FileService; +import stanl_2.final_backend.domain.s3.command.application.service.S3FileService; import java.time.ZoneId; import java.time.ZonedDateTime; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index 53b6903c..ab977674 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -24,7 +24,7 @@ import stanl_2.final_backend.domain.product.command.application.command.service.ProductCommandService; import stanl_2.final_backend.domain.product.query.dto.ProductSelectIdDTO; import stanl_2.final_backend.domain.product.query.service.ProductQueryService; -import stanl_2.final_backend.domain.s3.service.S3FileService; +import stanl_2.final_backend.domain.s3.command.application.service.S3FileService; import stanl_2.final_backend.domain.sales_history.command.application.service.SalesHistoryCommandService; import stanl_2.final_backend.global.utils.AESUtils; diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/service/EvaluationCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/service/EvaluationCommandServiceImpl.java index 6571899a..0bfe55b5 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/service/EvaluationCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/command/domain/service/EvaluationCommandServiceImpl.java @@ -12,7 +12,7 @@ import stanl_2.final_backend.domain.evaluation.command.domain.repository.EvaluationRepository; import stanl_2.final_backend.domain.evaluation.common.exception.EvaluationCommonException; import stanl_2.final_backend.domain.evaluation.common.exception.EvaluationErrorCode; -import stanl_2.final_backend.domain.s3.service.S3FileService; +import stanl_2.final_backend.domain.s3.command.application.service.S3FileService; import java.time.ZoneId; import java.time.ZonedDateTime; diff --git a/src/main/java/stanl_2/final_backend/domain/s3/command/application/controller/FileController.java b/src/main/java/stanl_2/final_backend/domain/s3/command/application/controller/FileController.java new file mode 100644 index 00000000..62407461 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/s3/command/application/controller/FileController.java @@ -0,0 +1,46 @@ +package stanl_2.final_backend.domain.s3.command.application.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import stanl_2.final_backend.domain.s3.command.application.dto.FileDTO; +import stanl_2.final_backend.domain.s3.command.domain.service.S3FileServiceImpl; +import stanl_2.final_backend.domain.s3.common.response.S3ResponseMessage; + +@RestController("commandFileController") +@RequestMapping("/api/v1/file") +public class FileController { + private final S3FileServiceImpl s3FileService; + + @Autowired + public FileController(S3FileServiceImpl s3FileService) { + this.s3FileService = s3FileService; + } + + @Operation(summary = "파일 업로드") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = S3ResponseMessage.class))}) + }) + @PostMapping(value = "") + public ResponseEntity postFile(FileDTO fileDTO,@RequestPart("file") MultipartFile file) { + fileDTO.setFileUrl(s3FileService.uploadOneFile(file)); + String url=s3FileService.uploadOneFile(file); + s3FileService.registerFile(fileDTO); + return ResponseEntity.ok(S3ResponseMessage.builder() + .httpStatus(200) + .msg(url) + .result(null) + .build()); + + } +} \ No newline at end of file diff --git a/src/main/java/stanl_2/final_backend/domain/s3/command/application/dto/FileDTO.java b/src/main/java/stanl_2/final_backend/domain/s3/command/application/dto/FileDTO.java new file mode 100644 index 00000000..d6e8be99 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/s3/command/application/dto/FileDTO.java @@ -0,0 +1,12 @@ +package stanl_2.final_backend.domain.s3.command.application.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class FileDTO { + private String fileId; + private String fileUrl; +} diff --git a/src/main/java/stanl_2/final_backend/domain/s3/service/S3FileService.java b/src/main/java/stanl_2/final_backend/domain/s3/command/application/service/S3FileService.java similarity index 72% rename from src/main/java/stanl_2/final_backend/domain/s3/service/S3FileService.java rename to src/main/java/stanl_2/final_backend/domain/s3/command/application/service/S3FileService.java index 46608a4b..ada8fffd 100644 --- a/src/main/java/stanl_2/final_backend/domain/s3/service/S3FileService.java +++ b/src/main/java/stanl_2/final_backend/domain/s3/command/application/service/S3FileService.java @@ -1,6 +1,7 @@ -package stanl_2.final_backend.domain.s3.service; +package stanl_2.final_backend.domain.s3.command.application.service; import org.springframework.web.multipart.MultipartFile; +import stanl_2.final_backend.domain.s3.command.application.dto.FileDTO; public interface S3FileService { @@ -21,4 +22,5 @@ public interface S3FileService { // 파일 다운로드 byte[] downloadFile(String fileName); + void registerFile(FileDTO fileDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/s3/command/domain/domain/aggregate/File.java b/src/main/java/stanl_2/final_backend/domain/s3/command/domain/domain/aggregate/File.java new file mode 100644 index 00000000..79a538cc --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/s3/command/domain/domain/aggregate/File.java @@ -0,0 +1,29 @@ +package stanl_2.final_backend.domain.s3.command.domain.domain.aggregate; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.GenericGenerator; +import stanl_2.final_backend.global.config.PrefixGeneratorConfig; + +@Entity +@Table(name="TB_FILE") +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class File { + @Id + @GeneratedValue(generator = "PrefixGeneratorConfig") + @GenericGenerator(name = "PrefixGeneratorConfig", + type = PrefixGeneratorConfig.class, + parameters = @org.hibernate.annotations.Parameter(name="prefix", value = "FIL") + ) + @Column(name = "FIL_ID") + private String fileId; + + @Column(name = "FIL_URL") + private String fileUrl; +} diff --git a/src/main/java/stanl_2/final_backend/domain/s3/command/domain/repository/FileRepository.java b/src/main/java/stanl_2/final_backend/domain/s3/command/domain/repository/FileRepository.java new file mode 100644 index 00000000..9239851b --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/s3/command/domain/repository/FileRepository.java @@ -0,0 +1,8 @@ +package stanl_2.final_backend.domain.s3.command.domain.repository; + + +import org.springframework.data.jpa.repository.JpaRepository; +import stanl_2.final_backend.domain.s3.command.domain.domain.aggregate.File; + +public interface FileRepository extends JpaRepository { +} diff --git a/src/main/java/stanl_2/final_backend/domain/s3/service/S3FileServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/s3/command/domain/service/S3FileServiceImpl.java similarity index 71% rename from src/main/java/stanl_2/final_backend/domain/s3/service/S3FileServiceImpl.java rename to src/main/java/stanl_2/final_backend/domain/s3/command/domain/service/S3FileServiceImpl.java index b87d1e57..24479ebe 100644 --- a/src/main/java/stanl_2/final_backend/domain/s3/service/S3FileServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/s3/command/domain/service/S3FileServiceImpl.java @@ -1,4 +1,4 @@ -package stanl_2.final_backend.domain.s3.service; +package stanl_2.final_backend.domain.s3.command.domain.service; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.DeleteObjectRequest; @@ -6,11 +6,23 @@ import com.amazonaws.services.s3.model.S3Object; import com.amazonaws.services.s3.model.S3ObjectInputStream; import com.amazonaws.util.IOUtils; +import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import stanl_2.final_backend.domain.notices.command.application.dto.NoticeAlarmDTO; +import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; +import stanl_2.final_backend.domain.notices.command.domain.aggragate.entity.Notice; +import stanl_2.final_backend.domain.notices.common.exception.NoticeCommonException; +import stanl_2.final_backend.domain.notices.common.exception.NoticeErrorCode; +import stanl_2.final_backend.domain.s3.command.application.dto.FileDTO; +import stanl_2.final_backend.domain.s3.command.application.service.S3FileService; +import stanl_2.final_backend.domain.s3.command.domain.domain.aggregate.File; +import stanl_2.final_backend.domain.s3.command.domain.repository.FileRepository; import stanl_2.final_backend.domain.s3.common.exception.S3CommonException; import stanl_2.final_backend.domain.s3.common.exception.S3ErrorCode; @@ -18,6 +30,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.security.Principal; import java.util.ArrayList; import java.util.UUID; @@ -26,10 +39,14 @@ public class S3FileServiceImpl implements S3FileService { private final AmazonS3Client amazonS3Client; + private final FileRepository fileRepository; + private final ModelMapper modelMapper; @Autowired - public S3FileServiceImpl(AmazonS3Client amazonS3Client) { + public S3FileServiceImpl(AmazonS3Client amazonS3Client, FileRepository fileRepository, ModelMapper modelMapper) { this.amazonS3Client = amazonS3Client; + this.fileRepository = fileRepository; + this.modelMapper = modelMapper; } @Value("${cloud.aws.s3.bucket}") @@ -125,10 +142,28 @@ private String getFileExtension(String fileName) { fileValidate.add(".JPEG"); fileValidate.add(".PNG"); fileValidate.add(".html"); + fileValidate.add(".xlsx"); + fileValidate.add(".pdf"); String idxFileName = fileName.substring(fileName.lastIndexOf(".")); if(!fileValidate.contains(idxFileName)) { throw new S3CommonException(S3ErrorCode.FILE_NOT_FOUND); } return fileName.substring(fileName.lastIndexOf(".")); } + + + @Transactional + public void registerFile(FileDTO fileDTO) { + try { + File file = modelMapper.map(fileDTO, File.class); + fileRepository.save(file); + + } catch (DataIntegrityViolationException e){ + // DB 무결정 제약 조건 (NOT NULL, UNIQUE) 위반 + throw new S3CommonException(S3ErrorCode.DATA_INTEGRITY_VIOLATION); + } catch (Exception e) { + // 서버 오류 + throw new S3CommonException(S3ErrorCode.INTERNAL_SERVER_ERROR); + } + } } diff --git a/src/main/java/stanl_2/final_backend/domain/s3/common/exception/S3ErrorCode.java b/src/main/java/stanl_2/final_backend/domain/s3/common/exception/S3ErrorCode.java index a92ac193..b2894ad2 100644 --- a/src/main/java/stanl_2/final_backend/domain/s3/common/exception/S3ErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/s3/common/exception/S3ErrorCode.java @@ -39,7 +39,7 @@ public enum S3ErrorCode { * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다. */ FILE_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "파일이 존재하지 않습니다."), - + DATA_INTEGRITY_VIOLATION(40402, HttpStatus.BAD_REQUEST, "데이터 무결 위반하였습니다."), /** * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다. */ From dde4811aa37ba524c05fc1c135f3106dfefa9105 Mon Sep 17 00:00:00 2001 From: giuseog Date: Wed, 27 Nov 2024 20:13:11 +0900 Subject: [PATCH 345/563] =?UTF-8?q?feat:=20center,=20product=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=95=20=EA=B5=AC=ED=98=84(#164)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/controller/ProductController.java | 47 ++++++++++++++ .../command/dto/ProductRegistDTO.java | 19 ++++++ .../service/ProductCommandService.java | 5 ++ .../domain/aggregate/entity/Product.java | 2 +- .../service/ProductCommandServiceImpl.java | 21 +++++- .../query/controller/ProductController.java | 4 +- .../query/dto/ProductSelectAllDTO.java | 1 - .../query/repository/ProductMapper.java | 2 +- .../service/ProductQueryServiceImpl.java | 2 +- .../query/repository/ProductMapper.xml | 65 +++++++++++++++++-- 10 files changed, 157 insertions(+), 11 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/product/command/application/command/controller/ProductController.java create mode 100644 src/main/java/stanl_2/final_backend/domain/product/command/application/command/dto/ProductRegistDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/product/command/application/command/controller/ProductController.java b/src/main/java/stanl_2/final_backend/domain/product/command/application/command/controller/ProductController.java new file mode 100644 index 00000000..f84c2b32 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/product/command/application/command/controller/ProductController.java @@ -0,0 +1,47 @@ +package stanl_2.final_backend.domain.product.command.application.command.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import stanl_2.final_backend.domain.product.command.application.command.dto.ProductRegistDTO; +import stanl_2.final_backend.domain.product.command.application.command.service.ProductCommandService; +import stanl_2.final_backend.domain.product.common.response.ProductResponseMessage; + +@RestController("commandProductController") +@RequestMapping("/api/v1/product") + +public class ProductController { + + private final ProductCommandService productCommandService; + + @Autowired + public ProductController(ProductCommandService productCommandService) { + this.productCommandService = productCommandService; + } + + @Operation(summary = "제품 등록") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = ProductResponseMessage.class))}) + }) + @PostMapping("") + public ResponseEntity postTest(@RequestPart("dto") ProductRegistDTO productRegistDTO, + @RequestPart("file") MultipartFile imageUrl){ + productCommandService.registProduct(productRegistDTO, imageUrl); + + return ResponseEntity.ok(ProductResponseMessage.builder() + .httpStatus(200) + .msg("제품 등록 성공") + .result(null) + .build()); + } +} diff --git a/src/main/java/stanl_2/final_backend/domain/product/command/application/command/dto/ProductRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/product/command/application/command/dto/ProductRegistDTO.java new file mode 100644 index 00000000..bd39a405 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/product/command/application/command/dto/ProductRegistDTO.java @@ -0,0 +1,19 @@ +package stanl_2.final_backend.domain.product.command.application.command.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class ProductRegistDTO { + private String productId; + private String name; + private String serialNumber; + private String cost; + private String stock; + private String imageUrl; +} diff --git a/src/main/java/stanl_2/final_backend/domain/product/command/application/command/service/ProductCommandService.java b/src/main/java/stanl_2/final_backend/domain/product/command/application/command/service/ProductCommandService.java index d420c1bf..8dde319e 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/command/application/command/service/ProductCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/product/command/application/command/service/ProductCommandService.java @@ -1,7 +1,12 @@ package stanl_2.final_backend.domain.product.command.application.command.service; +import org.springframework.web.multipart.MultipartFile; +import stanl_2.final_backend.domain.product.command.application.command.dto.ProductRegistDTO; + public interface ProductCommandService { void modifyProductStock(String productId, Integer numberOfVehicles); void deleteProductStock(String productId, Integer numberOfVehicles); + + void registProduct(ProductRegistDTO productRegistDTO, MultipartFile imageUrl); } diff --git a/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/aggregate/entity/Product.java b/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/aggregate/entity/Product.java index ad677e3e..1ef0e122 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/aggregate/entity/Product.java +++ b/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/aggregate/entity/Product.java @@ -48,6 +48,6 @@ public class Product { @Column(name ="ACTIVE", nullable = false) private Boolean active = true; - @Column(name ="IMAGE_URL", nullable = false) + @Column(name ="IMAGE_URL") private String imageUrl; } diff --git a/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/service/ProductCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/service/ProductCommandServiceImpl.java index ce0b1536..9ca1c571 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/service/ProductCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/product/command/application/domain/service/ProductCommandServiceImpl.java @@ -1,20 +1,29 @@ package stanl_2.final_backend.domain.product.command.application.domain.service; +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; +import stanl_2.final_backend.domain.product.command.application.command.dto.ProductRegistDTO; import stanl_2.final_backend.domain.product.command.application.command.service.ProductCommandService; import stanl_2.final_backend.domain.product.command.application.domain.aggregate.entity.Product; import stanl_2.final_backend.domain.product.command.application.domain.repository.ProductRepository; import stanl_2.final_backend.domain.product.common.exception.ProductCommonException; import stanl_2.final_backend.domain.product.common.exception.ProductErrorCode; +import stanl_2.final_backend.domain.s3.service.S3FileService; @Service public class ProductCommandServiceImpl implements ProductCommandService { private final ProductRepository productRepository; + private final ModelMapper modelMapper; + private final S3FileService s3FileService; - public ProductCommandServiceImpl(ProductRepository productRepository) { + public ProductCommandServiceImpl(ProductRepository productRepository, ModelMapper modelMapper, S3FileService s3FileService) { this.productRepository = productRepository; + this.modelMapper = modelMapper; + this.s3FileService = s3FileService; } @Override @@ -37,4 +46,14 @@ public void deleteProductStock(String productId, Integer numberOfVehicles) { product.setStock(product.getStock() + numberOfVehicles); productRepository.save(product); } + + @Override + @Transactional + public void registProduct(ProductRegistDTO productRegistDTO, MultipartFile imageUrl) { + Product newProduct = modelMapper.map(productRegistDTO, Product.class); + + newProduct.setImageUrl(s3FileService.uploadOneFile(imageUrl)); + + productRepository.save(newProduct); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java b/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java index 9e192c76..8c83aa09 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java @@ -44,7 +44,7 @@ public ProductController(ProductQueryService productQueryService) { content = @Content(mediaType = "application/json")) }) @GetMapping("") - public ResponseEntity getProductAll(@PageableDefault(size = 20) Pageable pageable, + public ResponseEntity getProductAll(@PageableDefault(size = 10) Pageable pageable, @RequestParam(required = false) String sortField, @RequestParam(required = false) String sortOrder){ @@ -88,7 +88,7 @@ public ResponseEntity getProductById(@PathVariable("id") }) @GetMapping("/search") public ResponseEntity getProductBySearch(@RequestParam Map params - ,@PageableDefault(size = 20) Pageable pageable){ + ,@PageableDefault(size = 10) Pageable pageable){ ProductSearchRequestDTO productSearchRequestDTO = new ProductSearchRequestDTO(); productSearchRequestDTO.setId(params.get("id")); diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectAllDTO.java index c832e58b..d905e17c 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectAllDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSelectAllDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Getter @Setter -@ToString public class ProductSelectAllDTO { private String productId; private String name; diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java b/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java index b23bbbca..4a399f63 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java @@ -24,7 +24,7 @@ List findProductAll(@Param("size") int size ProductSelectIdDTO findProductById(String id); - List> findProductBySearch(@Param("size") int size + List findProductBySearch(@Param("size") int size , @Param("offset") int offset , @Param("productSearchRequestDTO") ProductSearchRequestDTO productSearchRequestDTO, @Param("sortField") String sortField, diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java index 3d9a0fc7..bd3225cf 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java @@ -87,7 +87,7 @@ public Page selectProductBySearch(ProductSearchRequestDTO p sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; } - List> productList = productMapper.findProductBySearch(size, offset, productSearchRequestDTO, sortField, sortOrder); + List productList = productMapper.findProductBySearch(size, offset, productSearchRequestDTO, sortField, sortOrder); int total = productMapper.findProductBySearchCount(productSearchRequestDTO); diff --git a/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml b/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml index 830dfabb..4bc1deae 100644 --- a/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml @@ -61,6 +61,7 @@ a.prod_ser_no, a.prod_cost, a.prod_stck, + a.image_url, b.opt_cnty, b.opt_mnfr, b.opt_vhc_type, @@ -111,8 +112,36 @@ a.prod_stck FROM tb_product a WHERE a.active = TRUE - ORDER BY a.created_at DESC - OFFSET #{pageable.offset} ROWS FETCH NEXT #{pageable.pageSize} ROWS ONLY; + + + + + + ORDER BY a.prod_id ${sortOrder} + + + ORDER BY a.prod_name ${sortOrder} + + + ORDER BY a.prod_ser_no ${sortOrder} + + + ORDER BY a.prod_cost ${sortOrder} + + + ORDER BY a.prod_stck ${sortOrder} + + + ORDER BY a.created_at DESC + + + + + + ORDER BY a.created_at DESC + + + LIMIT #{size} OFFSET #{offset} From 5187000171ce9ca8fed8e21207a4bdcef8374b3d Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 20:55:16 +0900 Subject: [PATCH 352/563] =?UTF-8?q?fix:=20Problem=20dto=20=EC=88=98?= =?UTF-8?q?=EC=A0=95(#165)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/problem/command/application/dto/ProblemModifyDTO.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java index d5247969..d114da4c 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java @@ -14,5 +14,6 @@ public class ProblemModifyDTO { private String title; private String content; private String memberId; + private String fileUrl; } From 9e81ae5e99e10f883a3c4e586b1ab967709aeccd Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 20:59:19 +0900 Subject: [PATCH 353/563] =?UTF-8?q?fix:=20Promotion=20dto=20=EC=88=98?= =?UTF-8?q?=EC=A0=95(#165)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PromotionController.java | 16 ++++++++++++---- .../application/dto/PromotionModifyDTO.java | 1 + .../application/dto/PromotionRegistDTO.java | 2 ++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java index 7a487fd4..c82a8f22 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java @@ -8,11 +8,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.problem.command.application.dto.ProblemRegistDTO; import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionModifyDTO; import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionRegistDTO; import stanl_2.final_backend.domain.promotion.command.application.service.PromotionCommandService; import stanl_2.final_backend.domain.promotion.common.response.PromotionResponseMessage; +import stanl_2.final_backend.domain.s3.command.domain.service.S3FileServiceImpl; import java.security.Principal; @@ -21,11 +24,13 @@ public class PromotionController { private final PromotionCommandService promotionCommandService; private final AuthQueryService authQueryService; + private final S3FileServiceImpl s3FileService; @Autowired - public PromotionController(PromotionCommandService promotionCommandService, AuthQueryService authQueryService) { + public PromotionController(PromotionCommandService promotionCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService) { this.promotionCommandService = promotionCommandService; this.authQueryService =authQueryService; + this.s3FileService = s3FileService; } @Operation(summary = "프로모션 작성") @@ -34,10 +39,13 @@ public PromotionController(PromotionCommandService promotionCommandService, Auth content = {@Content(schema = @Schema(implementation = PromotionResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postNotice(@RequestBody PromotionRegistDTO prmotionRegistDTO, Principal principal){ + public ResponseEntity postNotice(@RequestPart("promotion") PromotionRegistDTO promotionRegistDTO, // JSON 데이터 + @RequestPart("file") MultipartFile file, + Principal principal){ String memberId = authQueryService.selectMemberIdByLoginId(principal.getName()); - prmotionRegistDTO.setMemberId(memberId); - promotionCommandService.registerPromotion(prmotionRegistDTO,principal); + promotionRegistDTO.setMemberId(memberId); + promotionRegistDTO.setFileUrl(s3FileService.uploadOneFile(file)); + promotionCommandService.registerPromotion(promotionRegistDTO,principal); return ResponseEntity.ok(PromotionResponseMessage.builder() .httpStatus(200) .msg("성공") diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java index cb68c8c2..4ae4f152 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java @@ -14,5 +14,6 @@ public class PromotionModifyDTO { private String title; private String memberId; private String content; + private String fileUrl; } diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionRegistDTO.java index bd487135..b0870898 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionRegistDTO.java @@ -16,4 +16,6 @@ public class PromotionRegistDTO { private String content; private String memberId; + + private String fileUrl; } From b0447b7fc8127cb298ea1192ebc2043983a137f0 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 21:00:47 +0900 Subject: [PATCH 354/563] =?UTF-8?q?fix:=20Promotion=20xml=20=EC=88=98?= =?UTF-8?q?=EC=A0=95(#165)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/promotion/query/repository/PromotionMapper.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml b/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml index 9efb2c44..465b2700 100644 --- a/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/promotion/query/repository/PromotionMapper.xml @@ -13,6 +13,7 @@ + @@ -79,7 +80,8 @@ a.updated_at, a.deleted_at, a.active, - a.mem_id + a.mem_id, + a.file_url FROM tb_promotion a WHERE prm_id = #{promotionId}; From e0641314c8d9832576ffa0b6940c8df8857931c9 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 21:01:46 +0900 Subject: [PATCH 355/563] =?UTF-8?q?fix:=20Promotion=20dto=20=EC=88=98?= =?UTF-8?q?=EC=A0=95(#165)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../final_backend/domain/promotion/query/dto/PromotionDTO.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionDTO.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionDTO.java index 5e186062..32e85f84 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/dto/PromotionDTO.java @@ -27,4 +27,6 @@ public class PromotionDTO { private String memberId; + private String fileUrl; + } From b2bf8478cfe6f29d08d0ce5cc5701312028e720f Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 21:12:59 +0900 Subject: [PATCH 356/563] =?UTF-8?q?fix:=20Problem=20xml=20=EC=88=98?= =?UTF-8?q?=EC=A0=95(#165)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/problem/query/repository/ProblemMapper.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml b/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml index 681485db..a5a01c9e 100644 --- a/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml @@ -100,7 +100,7 @@ a.active, a.cst_id, a.mem_id, - a.prod_id + a.prod_id, a.file_url FROM tb_problem a WHERE prob_id = #{problemId}; From fa366eea9ce83661d02a52394d5b97d5c2cbc83c Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 27 Nov 2024 21:59:57 +0900 Subject: [PATCH 357/563] =?UTF-8?q?fix:=20redis=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/notices/query/service/NoticeServiceImpl.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java index 0529b62c..b1db0e8b 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java @@ -37,7 +37,10 @@ public NoticeServiceImpl(NoticeMapper noticeMapper, RedisTemplate redisTemplate, public Page findNotices(Pageable pageable, SearchDTO searchDTO) { int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); - String cacheKey = "NoticeCache::notices::offset=" + offset + "::size=" + size; + String cacheKey = "NoticeCache::notices::offset=" + offset + "::size=" + size + + "::title=" + searchDTO.getTitle()+ "::tag=" + searchDTO.getTag() + +"::memberid=" + searchDTO.getMemberId()+ "::classification=" + searchDTO.getClassification() + + "::startDate=" + searchDTO.getStartDate()+ "::endDate=" + searchDTO.getEndDate(); List notices = (List) redisTemplate.opsForValue().get(cacheKey); if (notices == null) { From ab686f869fc81ee265ef926c80bede3ce2736682 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Thu, 28 Nov 2024 02:00:12 +0900 Subject: [PATCH 358/563] =?UTF-8?q?refactor:=20=EB=A1=9C=EA=B7=B8=20?= =?UTF-8?q?=EB=B0=8F=20@ToString=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/application/controller/ContractController.java | 3 ++- .../contract/command/domain/aggregate/entity/Contract.java | 1 + .../command/domain/service/ContractCommandServiceImpl.java | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java index ca585e83..354fa105 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java @@ -58,7 +58,7 @@ public ResponseEntity postContract(@RequestBody Contrac public ResponseEntity putContract(@PathVariable String contractId, @RequestBody ContractModifyDTO contractModifyRequestDTO, Principal principal) throws GeneralSecurityException { - + log.info("수정임: " + contractId); contractModifyRequestDTO.setContractId(contractId); contractModifyRequestDTO.setMemberId(principal.getName()); @@ -101,6 +101,7 @@ public ResponseEntity deleteContract(@PathVariable Stri public ResponseEntity putContractStatus(@PathVariable String contractId, @RequestBody ContractStatusModifyDTO contractStatusModifyDTO, Principal principal) { + log.info("putContractStatus: " + contractStatusModifyDTO.getStatus()); // DTO에 설정 contractStatusModifyDTO.setContractId(contractId); diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java index 47b96f79..240a08d3 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java @@ -16,6 +16,7 @@ @Getter @Setter @Entity +@ToString @Table(name = "TB_CONTRACT") public class Contract { diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index 53b6903c..a6c4da8e 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -245,6 +245,8 @@ public void modifyContractStatus(ContractStatusModifyDTO contractStatusModifyDTO // 계약 조회 및 수정 Contract contract = contractRepository.findByContractId(contractStatusModifyDTO.getContractId()); + log.info("Contract : " + contract); + if (contract == null) { throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); } From 7d1f095530cd9c4ddd2d6766c57bc999da911cae Mon Sep 17 00:00:00 2001 From: giuseog Date: Thu, 28 Nov 2024 09:32:56 +0900 Subject: [PATCH 359/563] =?UTF-8?q?feat:=20=EA=B2=80=EC=83=89=20=EC=9C=84?= =?UTF-8?q?=ED=95=B4=20mapper=20=EC=88=98=EC=A0=95(#170)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/center/query/repository/CenterMapper.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml b/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml index 02e05d3b..d490d7f0 100644 --- a/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/center/query/repository/CenterMapper.xml @@ -114,8 +114,8 @@ FROM tb_center a a.active = TRUE - - AND a.cent_id LIKE #{centerSearchRequestDTO.id} + + AND a.cent_id LIKE CONCAT('%', #{centerSearchRequestDTO.centerId}, '%') AND a.cent_name LIKE CONCAT('%', #{centerSearchRequestDTO.name}, '%') @@ -169,8 +169,8 @@ FROM tb_center a a.active = TRUE - - AND a.cent_id LIKE #{centerSearchRequestDTO.id} + + AND a.cent_id LIKE CONCAT('%', #{centerSearchRequestDTO.centerId}, '%') AND a.cent_name LIKE CONCAT('%', #{centerSearchRequestDTO.name}, '%') @@ -194,8 +194,8 @@ FROM tb_center a a.active = TRUE - - AND a.cent_id LIKE #{centerSearchRequestDTO.id} + + AND a.cent_id LIKE CONCAT('%', #{centerSearchRequestDTO.centerId}, '%') AND a.cent_name LIKE CONCAT('%', #{centerSearchRequestDTO.name}, '%') From 82216d0b1a95b697028ac9f5a91d9cde0f83e688 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 28 Nov 2024 10:39:29 +0900 Subject: [PATCH 360/563] =?UTF-8?q?fix:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EA=B4=80=EB=A0=A8=20=EB=A0=88=EB=94=94=EC=8A=A4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=82=AC=ED=95=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/service/NoticeCommandServiceImpl.java | 11 +++++++++-- .../final_backend/global/redis/RedisService.java | 13 +++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java index 78f44fed..2addb0c1 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java @@ -19,6 +19,7 @@ import stanl_2.final_backend.domain.notices.common.exception.NoticeErrorCode; import stanl_2.final_backend.domain.schedule.common.exception.ScheduleCommonException; import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; +import stanl_2.final_backend.global.redis.RedisService; import java.security.Principal; import java.time.ZoneId; @@ -31,6 +32,7 @@ public class NoticeCommandServiceImpl implements NoticeCommandService { private final NoticeRepository noticeRepository; + private final RedisService redisService; private final AuthQueryService authQueryService; private final ModelMapper modelMapper; @@ -38,11 +40,13 @@ public class NoticeCommandServiceImpl implements NoticeCommandService { @Autowired public NoticeCommandServiceImpl(NoticeRepository noticeRepository, ModelMapper modelMapper, - AuthQueryService authQueryService, AlarmCommandService alarmCommandService) { + AuthQueryService authQueryService, AlarmCommandService alarmCommandService, + RedisService redisService) { this.noticeRepository = noticeRepository; this.modelMapper = modelMapper; this.authQueryService =authQueryService; this.alarmCommandService = alarmCommandService; + this.redisService = redisService; } private String getCurrentTimestamp() { @@ -54,6 +58,7 @@ private String getCurrentTimestamp() { @Transactional public void registerNotice(NoticeRegistDTO noticeRegistDTO, Principal principal) { + redisService.clearNoticeCache(); String memberId= principal.getName(); noticeRegistDTO.setMemberId(memberId); @@ -77,6 +82,8 @@ public void registerNotice(NoticeRegistDTO noticeRegistDTO, @Override @Transactional public NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO,Principal principal) { + + redisService.clearNoticeCache(); String memberId= principal.getName(); Notice notice = noticeRepository.findById(id) @@ -111,7 +118,7 @@ public NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO,P @Override @Transactional public void deleteNotice(NoticeDeleteDTO noticeDeleteDTO, Principal principal) { - + redisService.clearNoticeCache(); String memberId = principal.getName(); Notice notice = noticeRepository.findByNoticeId(noticeDeleteDTO.getNoticeId()) diff --git a/src/main/java/stanl_2/final_backend/global/redis/RedisService.java b/src/main/java/stanl_2/final_backend/global/redis/RedisService.java index b41f0bd1..4f50bce4 100644 --- a/src/main/java/stanl_2/final_backend/global/redis/RedisService.java +++ b/src/main/java/stanl_2/final_backend/global/redis/RedisService.java @@ -1,9 +1,11 @@ package stanl_2.final_backend.global.redis; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.time.Duration; +import java.util.Set; @Service public class RedisService { @@ -28,4 +30,15 @@ public Object getKey(String key) { public boolean setTTL(String key, long ttlInSeconds) { return redisTemplate.expire(key, Duration.ofSeconds(ttlInSeconds)); } + + public void clearNoticeCache() { + Set keys = redisTemplate.keys("NoticeCache*"); + if (keys != null && !keys.isEmpty()) { + redisTemplate.delete(keys); // Delete all matching keys + System.out.println("Deleted NoticeCache keys: " + keys); + } else { + System.out.println("No keys found for pattern 'NoticeCache*'."); + } + } + } \ No newline at end of file From 155e31408e1403881dd024e63b9f8a5e42094784 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 28 Nov 2024 10:42:29 +0900 Subject: [PATCH 361/563] =?UTF-8?q?fix:=20=ED=94=84=EB=A1=9C=EB=AA=A8?= =?UTF-8?q?=EC=85=98=20=EA=B4=80=EB=A0=A8=20=EB=A0=88=EB=94=94=EC=8A=A4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=82=AC=ED=95=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/aggregate/service/PromotionServiceImpl.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java index 29dca26c..8ecb963d 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java @@ -16,6 +16,7 @@ import stanl_2.final_backend.domain.promotion.command.domain.aggregate.repository.PromotionRepository; import stanl_2.final_backend.domain.promotion.common.exception.PromotionCommonException; import stanl_2.final_backend.domain.promotion.common.exception.PromotionErrorCode; +import stanl_2.final_backend.global.redis.RedisService; import java.security.Principal; import java.time.ZoneId; @@ -25,12 +26,14 @@ @Service("commandPromotionService") public class PromotionServiceImpl implements PromotionCommandService { + private final RedisService redisService; private final PromotionRepository promotionRepository; private final ModelMapper modelMapper; @Autowired - public PromotionServiceImpl(PromotionRepository promotionRepository, ModelMapper modelMapper) { + public PromotionServiceImpl(PromotionRepository promotionRepository, ModelMapper modelMapper,RedisService redisService) { + this.redisService = redisService; this.promotionRepository = promotionRepository; this.modelMapper = modelMapper; } @@ -42,6 +45,7 @@ private String getCurrentTimestamp() { @Transactional @Override public void registerPromotion(PromotionRegistDTO promotionRegistDTO, Principal principal) { + redisService.clearNoticeCache(); String memberId =principal.getName(); promotionRegistDTO.setMemberId(memberId); try { @@ -58,6 +62,7 @@ public void registerPromotion(PromotionRegistDTO promotionRegistDTO, Principal p @Transactional @Override public PromotionModifyDTO modifyPromotion(String promotionId, PromotionModifyDTO promotionModifyDTO, Principal principal) { + redisService.clearNoticeCache(); String memberId= principal.getName(); Promotion promotion = promotionRepository.findById(promotionId) .orElseThrow(() -> new PromotionCommonException(PromotionErrorCode.PROMOTION_NOT_FOUND)); @@ -88,6 +93,7 @@ public PromotionModifyDTO modifyPromotion(String promotionId, PromotionModifyDTO @Transactional @Override public void deletePromotion(String promotionId, Principal principal) { + redisService.clearNoticeCache(); String memberId= principal.getName(); Promotion promotion = promotionRepository.findById(promotionId) .orElseThrow(()-> new PromotionCommonException(PromotionErrorCode.PROMOTION_NOT_FOUND)); From 878994ebc0fe43bd8bf7c98f9f1b44f4994cfeb0 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 28 Nov 2024 10:42:58 +0900 Subject: [PATCH 362/563] =?UTF-8?q?fix:=20=EB=AC=B8=EC=A0=9C=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EA=B4=80=EB=A0=A8=20=EB=A0=88=EB=94=94=EC=8A=A4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=82=AC=ED=95=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/aggregate/service/ProblemServiceImpl.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java index 62399dd8..72a54fcb 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java @@ -18,6 +18,7 @@ import stanl_2.final_backend.domain.promotion.common.exception.PromotionErrorCode; import stanl_2.final_backend.domain.schedule.common.exception.ScheduleCommonException; import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; +import stanl_2.final_backend.global.redis.RedisService; import java.security.Principal; import java.time.ZoneId; @@ -28,13 +29,14 @@ public class ProblemServiceImpl implements ProblemCommandService { private final ProblemRepository problemRepository; - + private final RedisService redisService; private final ModelMapper modelMapper; @Autowired - public ProblemServiceImpl(ProblemRepository problemRepository, ModelMapper modelMapper) { + public ProblemServiceImpl(ProblemRepository problemRepository, ModelMapper modelMapper, RedisService redisService) { this.problemRepository = problemRepository; this.modelMapper = modelMapper; + this.redisService = redisService; } private String getCurrentTimestamp() { ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); @@ -44,6 +46,7 @@ private String getCurrentTimestamp() { @Transactional @Override public void registerProblem(ProblemRegistDTO problemRegistDTO, Principal principal) { + redisService.clearNoticeCache(); String memberId =principal.getName(); problemRegistDTO.setMemberId(memberId); try { @@ -60,6 +63,7 @@ public void registerProblem(ProblemRegistDTO problemRegistDTO, Principal princip @Transactional @Override public ProblemModifyDTO modifyProblem(String problemId, ProblemModifyDTO problemModifyDTO,Principal principal) { + redisService.clearNoticeCache(); String memberId= principal.getName(); Problem problem = problemRepository.findById(problemId) .orElseThrow(() -> new ProblemCommonException(ProblemErrorCode.PROBLEM_NOT_FOUND)); @@ -92,6 +96,7 @@ public ProblemModifyDTO modifyProblem(String problemId, ProblemModifyDTO problem @Transactional @Override public void deleteProblem(String problemId, Principal principal) { + redisService.clearNoticeCache(); String memberId= principal.getName(); Problem problem = problemRepository.findById(problemId) .orElseThrow(()-> new ProblemCommonException(ProblemErrorCode.PROBLEM_NOT_FOUND)); From 142a142580d3f04b0eb1c7e8c33df0915387b275 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 28 Nov 2024 11:05:39 +0900 Subject: [PATCH 363/563] =?UTF-8?q?fix:=20=EB=AC=B8=EC=A0=9C=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EA=B4=80=EB=A0=A8=20=EB=A0=88=EB=94=94=EC=8A=A4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=82=AC=ED=95=AD=20(#174)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/aggregate/service/ProblemServiceImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java index 72a54fcb..837e68a8 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java @@ -46,7 +46,7 @@ private String getCurrentTimestamp() { @Transactional @Override public void registerProblem(ProblemRegistDTO problemRegistDTO, Principal principal) { - redisService.clearNoticeCache(); + redisService.clearProblemCache(); String memberId =principal.getName(); problemRegistDTO.setMemberId(memberId); try { @@ -63,7 +63,7 @@ public void registerProblem(ProblemRegistDTO problemRegistDTO, Principal princip @Transactional @Override public ProblemModifyDTO modifyProblem(String problemId, ProblemModifyDTO problemModifyDTO,Principal principal) { - redisService.clearNoticeCache(); + redisService.clearProblemCache(); String memberId= principal.getName(); Problem problem = problemRepository.findById(problemId) .orElseThrow(() -> new ProblemCommonException(ProblemErrorCode.PROBLEM_NOT_FOUND)); @@ -96,7 +96,7 @@ public ProblemModifyDTO modifyProblem(String problemId, ProblemModifyDTO problem @Transactional @Override public void deleteProblem(String problemId, Principal principal) { - redisService.clearNoticeCache(); + redisService.clearProblemCache(); String memberId= principal.getName(); Problem problem = problemRepository.findById(problemId) .orElseThrow(()-> new ProblemCommonException(ProblemErrorCode.PROBLEM_NOT_FOUND)); From 3e4550a2e485cf2b98e2d1937c5ee2848360702a Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 28 Nov 2024 11:05:50 +0900 Subject: [PATCH 364/563] =?UTF-8?q?fix:=20=ED=94=84=EB=A1=9C=EB=AA=A8?= =?UTF-8?q?=EC=85=98=20=EA=B4=80=EB=A0=A8=20=EB=A0=88=EB=94=94=EC=8A=A4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=82=AC=ED=95=AD=20(#174)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/aggregate/service/PromotionServiceImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java index 8ecb963d..bf46b68e 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java @@ -45,7 +45,7 @@ private String getCurrentTimestamp() { @Transactional @Override public void registerPromotion(PromotionRegistDTO promotionRegistDTO, Principal principal) { - redisService.clearNoticeCache(); + redisService.clearPromotionCache(); String memberId =principal.getName(); promotionRegistDTO.setMemberId(memberId); try { @@ -62,7 +62,7 @@ public void registerPromotion(PromotionRegistDTO promotionRegistDTO, Principal p @Transactional @Override public PromotionModifyDTO modifyPromotion(String promotionId, PromotionModifyDTO promotionModifyDTO, Principal principal) { - redisService.clearNoticeCache(); + redisService.clearPromotionCache(); String memberId= principal.getName(); Promotion promotion = promotionRepository.findById(promotionId) .orElseThrow(() -> new PromotionCommonException(PromotionErrorCode.PROMOTION_NOT_FOUND)); @@ -93,7 +93,7 @@ public PromotionModifyDTO modifyPromotion(String promotionId, PromotionModifyDTO @Transactional @Override public void deletePromotion(String promotionId, Principal principal) { - redisService.clearNoticeCache(); + redisService.clearPromotionCache(); String memberId= principal.getName(); Promotion promotion = promotionRepository.findById(promotionId) .orElseThrow(()-> new PromotionCommonException(PromotionErrorCode.PROMOTION_NOT_FOUND)); From 50e12d14560dc040b30e5ce90cc56b42f32b1be4 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 28 Nov 2024 11:05:55 +0900 Subject: [PATCH 365/563] =?UTF-8?q?fix:=20=ED=94=84=EB=A1=9C=EB=AA=A8?= =?UTF-8?q?=EC=85=98=20=EA=B4=80=EB=A0=A8=20=EB=A0=88=EB=94=94=EC=8A=A4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=82=AC=ED=95=AD=20(#174)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/redis/RedisService.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/java/stanl_2/final_backend/global/redis/RedisService.java b/src/main/java/stanl_2/final_backend/global/redis/RedisService.java index 4f50bce4..bf29e8a6 100644 --- a/src/main/java/stanl_2/final_backend/global/redis/RedisService.java +++ b/src/main/java/stanl_2/final_backend/global/redis/RedisService.java @@ -41,4 +41,22 @@ public void clearNoticeCache() { } } + public void clearPromotionCache() { + Set keys = redisTemplate.keys("PromotionCache*"); + if (keys != null && !keys.isEmpty()) { + redisTemplate.delete(keys); // Delete all matching keys + System.out.println("Deleted PromotionCache keys: " + keys); + } else { + System.out.println("No keys found for pattern 'PromotionCache*'."); + } + }public void clearProblemCache() { + Set keys = redisTemplate.keys("ProblemCache*"); + if (keys != null && !keys.isEmpty()) { + redisTemplate.delete(keys); // Delete all matching keys + System.out.println("Deleted ProblemCache keys: " + keys); + } else { + System.out.println("No keys found for pattern 'ProblemCache*'."); + } + } + } \ No newline at end of file From dd3f367f2121e5b8aead8de664eeac89569b20d4 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 28 Nov 2024 12:18:12 +0900 Subject: [PATCH 366/563] =?UTF-8?q?fix:=20@RequestPart("dto")=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/application/controller/NoticeController.java | 2 +- .../command/application/controller/ProblemController.java | 2 +- .../command/application/controller/PromotionController.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java index 9b576b2d..e5fcaf9d 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java @@ -41,7 +41,7 @@ public NoticeController(NoticeCommandService noticeCommandService, AuthQueryServ content = {@Content(schema = @Schema(implementation = NoticeResponseMessage.class))}) }) @PostMapping(value = "") - public ResponseEntity postNotice(@RequestPart("notice") NoticeRegistDTO noticeRegistDTO, // JSON 데이터 + public ResponseEntity postNotice(@RequestPart("dto") NoticeRegistDTO noticeRegistDTO, // JSON 데이터 @RequestPart("file") MultipartFile file, Principal principal){ String memberId =authQueryService.selectMemberIdByLoginId(principal.getName()); diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java index b9de8876..31988f11 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java @@ -38,7 +38,7 @@ public ProblemController(ProblemCommandService problemCommandService, AuthQueryS content = {@Content(schema = @Schema(implementation = ProblemResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postProblem(@RequestPart("problem") ProblemRegistDTO problemRegistDTO, // JSON 데이터 + public ResponseEntity postProblem(@RequestPart("dto") ProblemRegistDTO problemRegistDTO, // JSON 데이터 @RequestPart("file") MultipartFile file, Principal principal){ String memberId = authQueryService.selectMemberIdByLoginId(principal.getName()); diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java index c82a8f22..5032ee30 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java @@ -39,7 +39,7 @@ public PromotionController(PromotionCommandService promotionCommandService, Auth content = {@Content(schema = @Schema(implementation = PromotionResponseMessage.class))}) }) @PostMapping("") - public ResponseEntity postNotice(@RequestPart("promotion") PromotionRegistDTO promotionRegistDTO, // JSON 데이터 + public ResponseEntity postNotice(@RequestPart("dto") PromotionRegistDTO promotionRegistDTO, // JSON 데이터 @RequestPart("file") MultipartFile file, Principal principal){ String memberId = authQueryService.selectMemberIdByLoginId(principal.getName()); From 4b2dbf4a80506b6008dc1c1a696c2ec7647b7e24 Mon Sep 17 00:00:00 2001 From: giuseog Date: Thu, 28 Nov 2024 12:30:11 +0900 Subject: [PATCH 367/563] =?UTF-8?q?feat:=20=EA=B2=80=EC=83=89=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20product=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95(#?= =?UTF-8?q?177)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../product/query/controller/ProductController.java | 8 ++++---- .../product/query/dto/ProductSearchRequestDTO.java | 2 +- .../domain/product/query/repository/ProductMapper.java | 2 +- .../product/query/service/ProductQueryService.java | 2 +- .../product/query/service/ProductQueryServiceImpl.java | 4 ++-- .../domain/product/query/repository/ProductMapper.xml | 10 +++++----- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java b/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java index 8c83aa09..f0f8db9c 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/controller/ProductController.java @@ -68,10 +68,10 @@ public ResponseEntity getProductAll(@PageableDefault(siz @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("{id}") - public ResponseEntity getProductById(@PathVariable("id") String id){ + @GetMapping("{productId}") + public ResponseEntity getProductById(@PathVariable("productId") String productId){ - ProductSelectIdDTO productSelectIdDTO = productQueryService.selectByProductId(id); + ProductSelectIdDTO productSelectIdDTO = productQueryService.selectByProductId(productId); return ResponseEntity.ok(ProductResponseMessage.builder() .httpStatus(200) @@ -91,7 +91,7 @@ public ResponseEntity getProductBySearch(@RequestParam M ,@PageableDefault(size = 10) Pageable pageable){ ProductSearchRequestDTO productSearchRequestDTO = new ProductSearchRequestDTO(); - productSearchRequestDTO.setId(params.get("id")); + productSearchRequestDTO.setProductId(params.get("productId")); productSearchRequestDTO.setName(params.get("name")); productSearchRequestDTO.setSerialNumber(params.get("serialNumber")); diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSearchRequestDTO.java b/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSearchRequestDTO.java index cd14943c..39cebaef 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSearchRequestDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/dto/ProductSearchRequestDTO.java @@ -8,7 +8,7 @@ @Setter @ToString public class ProductSearchRequestDTO { - private String id; + private String productId; private String name; private String serialNumber; } diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java b/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java index 4a399f63..9a546b8a 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/repository/ProductMapper.java @@ -22,7 +22,7 @@ List findProductAll(@Param("size") int size int findProductCount(); - ProductSelectIdDTO findProductById(String id); + ProductSelectIdDTO findProductById(String productId); List findProductBySearch(@Param("size") int size , @Param("offset") int offset diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryService.java b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryService.java index da335991..9b6dd57d 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryService.java @@ -15,7 +15,7 @@ public interface ProductQueryService { Page selectAll(Pageable pageable); - ProductSelectIdDTO selectByProductId(String id); + ProductSelectIdDTO selectByProductId(String productId); Page selectProductBySearch(ProductSearchRequestDTO productSearchRequestDTO, Pageable pageable); diff --git a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java index bd3225cf..9bc30ffc 100644 --- a/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/product/query/service/ProductQueryServiceImpl.java @@ -62,8 +62,8 @@ public Page selectAll(Pageable pageable) { @Override @Transactional - public ProductSelectIdDTO selectByProductId(String id) { - ProductSelectIdDTO productSelectIdDTO = productMapper.findProductById(id); + public ProductSelectIdDTO selectByProductId(String productId) { + ProductSelectIdDTO productSelectIdDTO = productMapper.findProductById(productId); if(productSelectIdDTO == null) { throw new ProductCommonException(ProductErrorCode.PRODUCT_NOT_FOUND); diff --git a/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml b/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml index 4bc1deae..7ec87675 100644 --- a/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml @@ -89,7 +89,7 @@ b.opt_sond FROM tb_product a JOIN tb_product_option b ON a.prod_id = b.prod_id - WHERE a.prod_id = #{id} AND a.active = TRUE + WHERE a.prod_id = #{productId} AND a.active = TRUE SELECT a.cust_id, @@ -84,7 +96,8 @@ a.cust_id, a.cust_name, a.cust_sex, - a.cust_pho + a.cust_pho, + a.mem_id FROM tb_customer_info a a.active != 'false' @@ -97,6 +110,9 @@ AND a.cust_pho = #{ phone } + + AND a.cust_id= #{ customerId } + ORDER BY a.created_at DESC LIMIT #{ size } OFFSET #{ offset } @@ -164,6 +180,18 @@ WHERE a.cust_id = #{ customerId }; + \ No newline at end of file From 61e7c6719926116e93894474d006958a0c02e322 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 28 Nov 2024 16:45:39 +0900 Subject: [PATCH 370/563] =?UTF-8?q?feat:=20=EC=9D=BD=EA=B8=B0=20=EC=A0=84?= =?UTF-8?q?=EC=9A=A9=20DB=20=EA=B5=AC=EC=84=B1=20=EC=99=84=EB=A3=8C=20(#17?= =?UTF-8?q?5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/service/AuthQueryServiceImpl.java | 1 - .../query/service/MemberQueryServiceImpl.java | 10 +-- .../controller/NoticeController.java | 16 +++- .../service/NoticeCommandServiceImpl.java | 15 ++-- .../query/service/NoticeServiceImpl.java | 8 +- .../final_backend/domain/operation_test.java | 12 --- .../global/config/DataSourceConfig.java | 78 +++++++++++++++++++ .../ReplicationRoutingDataSource.java | 15 ++++ src/main/resources/application.yml | 25 ++++-- 9 files changed, 139 insertions(+), 41 deletions(-) delete mode 100644 src/main/java/stanl_2/final_backend/domain/operation_test.java create mode 100644 src/main/java/stanl_2/final_backend/global/config/DataSourceConfig.java create mode 100644 src/main/java/stanl_2/final_backend/global/config/datasource/ReplicationRoutingDataSource.java diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java index 5991a042..0f939812 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java @@ -20,7 +20,6 @@ public AuthQueryServiceImpl(AuthMapper authMapper) { } @Override - @Transactional(readOnly = true) public String selectMemberIdByLoginId(String loginId){ String id = authMapper.selectIdByMemberName(loginId); diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index 16d4b855..e8ccf861 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -32,7 +32,7 @@ public MemberQueryServiceImpl(MemberMapper memberMapper, AESUtils aesUtils, } @Override - @Transactional(readOnly = true) + @Transactional public MemberDTO selectMemberInfo(String name) throws GeneralSecurityException { MemberDTO memberInfo = memberMapper.findMemberInfoById(name); @@ -55,7 +55,7 @@ public MemberDTO selectMemberInfo(String name) throws GeneralSecurityException { } @Override - @Transactional(readOnly = true) + @Transactional public List selectMemberByRole(String role){ List memberList = memberRoleMapper.findMembersbyRole(role); @@ -64,7 +64,7 @@ public List selectMemberByRole(String role){ } @Override - @Transactional(readOnly = true) + @Transactional public List selectMemberByCenterId(String centerId){ List memberList = memberMapper.findMembersByCenterId(centerId); @@ -85,7 +85,7 @@ public List selectMemberByCenterId(String centerId){ } @Override - @Transactional(readOnly = true) + @Transactional public List selectMemberByCenterList(List centerList) { List memberList = memberMapper.findMembersByCenterList(centerList); @@ -101,7 +101,7 @@ public List selectMemberByCenterList(List centerList) { } @Override - @Transactional(readOnly = true) + @Transactional public String selectNameById(String memberId) throws GeneralSecurityException { String name = memberMapper.findNameById(memberId); diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java index 9b576b2d..d7be9a75 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java @@ -7,6 +7,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; @@ -16,23 +17,28 @@ import stanl_2.final_backend.domain.notices.command.application.service.NoticeCommandService; import stanl_2.final_backend.domain.notices.common.response.NoticeResponseMessage; import stanl_2.final_backend.domain.s3.command.domain.service.S3FileServiceImpl; +import stanl_2.final_backend.global.config.datasource.ReplicationRoutingDataSource; import java.security.Principal; +import static org.springframework.transaction.support.TransactionSynchronizationManager.isCurrentTransactionReadOnly; + @RestController("commandNoticeController") @RequestMapping("/api/v1/notice") public class NoticeController { private final NoticeCommandService noticeCommandService; private final AuthQueryService authQueryService; - + private final ReplicationRoutingDataSource replicationRoutingDataSource; private final S3FileServiceImpl s3FileService; @Autowired - public NoticeController(NoticeCommandService noticeCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService){ + public NoticeController(NoticeCommandService noticeCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService, ReplicationRoutingDataSource replicationRoutingDataSource){ + this.replicationRoutingDataSource = replicationRoutingDataSource; this.noticeCommandService = noticeCommandService; this.authQueryService =authQueryService; this.s3FileService = s3FileService; + } @Operation(summary = "공지사항 작성") @@ -40,20 +46,22 @@ public NoticeController(NoticeCommandService noticeCommandService, AuthQueryServ @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = NoticeResponseMessage.class))}) }) + @PostMapping(value = "") - public ResponseEntity postNotice(@RequestPart("notice") NoticeRegistDTO noticeRegistDTO, // JSON 데이터 + public ResponseEntity postNotice(@RequestPart("notice") NoticeRegistDTO noticeRegistDTO, @RequestPart("file") MultipartFile file, Principal principal){ + replicationRoutingDataSource.determineCurrentLookupKey(); String memberId =authQueryService.selectMemberIdByLoginId(principal.getName()); noticeRegistDTO.setMemberId(memberId); noticeRegistDTO.setFileUrl(s3FileService.uploadOneFile(file)); noticeCommandService.registerNotice(noticeRegistDTO, principal); + System.out.println(isCurrentTransactionReadOnly()); return ResponseEntity.ok(NoticeResponseMessage.builder() .httpStatus(200) .msg("성공") .result(null) .build()); - } @Operation(summary = "공지사항 수정") @ApiResponses(value = { diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java index 2addb0c1..71deb2b9 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java @@ -26,6 +26,7 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import static org.springframework.transaction.support.TransactionSynchronizationManager.isCurrentTransactionReadOnly; @Slf4j @Service("commandNoticeService") public class NoticeCommandServiceImpl implements NoticeCommandService { @@ -55,21 +56,22 @@ private String getCurrentTimestamp() { } @Override - @Transactional + @Transactional(readOnly = false) public void registerNotice(NoticeRegistDTO noticeRegistDTO, Principal principal) { + System.out.println("[Before Cache Clear] Transaction ReadOnly: " + isCurrentTransactionReadOnly()); redisService.clearNoticeCache(); String memberId= principal.getName(); noticeRegistDTO.setMemberId(memberId); - try { Notice notice = modelMapper.map(noticeRegistDTO, Notice.class); + System.out.println("[After Cache Clear] Transaction ReadOnly: " + isCurrentTransactionReadOnly()); Notice newNotice = noticeRepository.save(notice); - + System.out.println("1. [After Cache Clear] Transaction ReadOnly: " + isCurrentTransactionReadOnly()); NoticeAlarmDTO noticeAlarmDTO = modelMapper.map(newNotice, NoticeAlarmDTO.class); - + System.out.println("2. [After Cache Clear] Transaction ReadOnly: " + isCurrentTransactionReadOnly()); alarmCommandService.sendNoticeAlarm(noticeAlarmDTO); - + System.out.println("3. [After Cache Clear] Transaction ReadOnly: " + isCurrentTransactionReadOnly()); } catch (DataIntegrityViolationException e){ // DB 무결정 제약 조건 (NOT NULL, UNIQUE) 위반 throw new NoticeCommonException(NoticeErrorCode.DATA_INTEGRITY_VIOLATION); @@ -78,9 +80,7 @@ public void registerNotice(NoticeRegistDTO noticeRegistDTO, throw new NoticeCommonException(NoticeErrorCode.INTERNAL_SERVER_ERROR); } } - @Override - @Transactional public NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO,Principal principal) { redisService.clearNoticeCache(); @@ -116,7 +116,6 @@ public NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO,P @Override - @Transactional public void deleteNotice(NoticeDeleteDTO noticeDeleteDTO, Principal principal) { redisService.clearNoticeCache(); String memberId = principal.getName(); diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java index b1db0e8b..38588c8d 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java @@ -17,7 +17,7 @@ import java.util.List; - +@Transactional(readOnly = true) @Service("queryNoticeServiceImpl") public class NoticeServiceImpl implements NoticeService{ private final NoticeMapper noticeMapper; @@ -32,7 +32,7 @@ public NoticeServiceImpl(NoticeMapper noticeMapper, RedisTemplate redisTemplate, this.excelUtilsV1 =excelUtilsV1; } - @Transactional + @Override public Page findNotices(Pageable pageable, SearchDTO searchDTO) { int offset = Math.toIntExact(pageable.getOffset()); @@ -55,13 +55,13 @@ public Page findNotices(Pageable pageable, SearchDTO searchDTO) { int totalElements = noticeMapper.findNoticeCount(); // 총 개수 조회 return new PageImpl<>(notices, pageable, totalElements); } - @Transactional + @Transactional(readOnly = true) @Override public NoticeDTO findNotice(String noticeId) { NoticeDTO notice = noticeMapper.findNotice(noticeId); return notice; } - @Transactional + @Transactional(readOnly = true) @Override public void exportNoticesToExcel(HttpServletResponse response) { List noticeList = noticeMapper.findNoticesForExcel(); diff --git a/src/main/java/stanl_2/final_backend/domain/operation_test.java b/src/main/java/stanl_2/final_backend/domain/operation_test.java deleted file mode 100644 index 36bbbcc9..00000000 --- a/src/main/java/stanl_2/final_backend/domain/operation_test.java +++ /dev/null @@ -1,12 +0,0 @@ -package stanl_2.final_backend.domain; - - -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; -@RestController -public class operation_test { - @GetMapping("health") - public String healthCheck() { - return "CI/CD 배포 성공!!"; - } -} diff --git a/src/main/java/stanl_2/final_backend/global/config/DataSourceConfig.java b/src/main/java/stanl_2/final_backend/global/config/DataSourceConfig.java new file mode 100644 index 00000000..7591812f --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/config/DataSourceConfig.java @@ -0,0 +1,78 @@ +package stanl_2.final_backend.global.config; + +import com.zaxxer.hikari.HikariDataSource; +import jakarta.persistence.EntityManagerFactory; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; +import org.springframework.context.annotation.Primary; +import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.transaction.PlatformTransactionManager; +import stanl_2.final_backend.global.config.datasource.ReplicationRoutingDataSource; + +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class DataSourceConfig { + @ConfigurationProperties(prefix = "spring.datasource.writer.hikari") + @Bean(name = "writerDataSource") + public DataSource writerDataSource() { + // DataSourceBuilder를 사용해 HikariDataSource 타입의 데이터 소스를 생성합니다. + return DataSourceBuilder.create().type(HikariDataSource.class).build(); + } + + @ConfigurationProperties(prefix = "spring.datasource.reader.hikari") + @Bean(name = "readerDataSource") + public DataSource readerDataSource() { + // DataSourceBuilder를 사용해 HikariDataSource 타입의 데이터 소스를 생성합니다. + return DataSourceBuilder.create().type(HikariDataSource.class).build(); + } + + @DependsOn({"writerDataSource", "readerDataSource"}) + @Bean + public DataSource routingDataSource( + @Qualifier("writerDataSource") DataSource writer, + @Qualifier("readerDataSource") DataSource reader) { + + ReplicationRoutingDataSource routingDataSource = new ReplicationRoutingDataSource(); + + Map dataSourceMap = new HashMap<>(); + + dataSourceMap.put("writer", writer); // 쓰기 전용 데이터 소스 + dataSourceMap.put("reader", reader); // 읽기 전용 데이터 소스 + + routingDataSource.setTargetDataSources(dataSourceMap); // 대상 데이터 소스 설정 + routingDataSource.setDefaultTargetDataSource(writer); // 기본 데이터 소스 설정 + routingDataSource.afterPropertiesSet(); + + return routingDataSource; + } + + /** + * `routingDataSource`가 생성된 이후에 이 메서드를 실행하도록 지정 + * `LazyConnectionDataSourceProxy`는 실제 연결을 사용하는 시점에 데이터베이스 연결을 지연 생성 + * 성능 최적화를 도모하고 트랜잭션 관리에서 데이터 소스의 사용을 효율적으로 처리 가능 + * + * @param routingDataSource 라우팅 가능한 데이터 소스 + * @return 지연된 연결 처리를 지원하는 데이터 소스 프록시 + */ + @DependsOn("routingDataSource") + @Primary // 이 빈을 기본 `DataSource`로 설정 + @Bean + public DataSource dataSource(DataSource routingDataSource) { + return new LazyConnectionDataSourceProxy(routingDataSource); + } + + @Bean + public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory){ + JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(); + jpaTransactionManager.setEntityManagerFactory(entityManagerFactory); + return jpaTransactionManager; + } +} diff --git a/src/main/java/stanl_2/final_backend/global/config/datasource/ReplicationRoutingDataSource.java b/src/main/java/stanl_2/final_backend/global/config/datasource/ReplicationRoutingDataSource.java new file mode 100644 index 00000000..9428f55f --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/config/datasource/ReplicationRoutingDataSource.java @@ -0,0 +1,15 @@ +package stanl_2.final_backend.global.config.datasource; + +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +import static org.springframework.transaction.support.TransactionSynchronizationManager.isCurrentTransactionReadOnly; + +public class ReplicationRoutingDataSource extends AbstractRoutingDataSource { + + public Object determineCurrentLookupKey() { + String dataSourceName = isCurrentTransactionReadOnly() ? "reader" : "writer"; + System.out.println(">>>>>> current data source : {}"+ dataSourceName); + return dataSourceName; + + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8d8f164f..882af8e3 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -7,13 +7,20 @@ spring: name: finalbackend datasource: - driver-class-name: org.mariadb.jdbc.Driver - # url: jdbc:mariadb://${DATABASE_URL}:${MARIA_DATABASE_PORT}/${MARIA_DATABASE_NAME} - url: jdbc:mariadb://motive.cdw8kw8go27z.ap-northeast-2.rds.amazonaws.com:3306/motive - username: admin - password: motivepassword - # username: ${DB_USERNAME} - # password: ${DB_PASSWORD} + writer: + hikari: + jdbc-url: jdbc:mariadb://motive.cdw8kw8go27z.ap-northeast-2.rds.amazonaws.com:3306/motive + username: admin + password: motivepassword + driver-class-name: org.mariadb.jdbc.Driver + + reader: + hikari: + jdbc-url: jdbc:mariadb://motivereadonly.cdw8kw8go27z.ap-northeast-2.rds.amazonaws.com:3306/motive + username: admin + password: motivepassword + driver-class-name: org.mariadb.jdbc.Driver + profiles: active: ${SPRING_PROFILES_ACTIVE} @@ -57,3 +64,7 @@ cloud: static: ap-northeast-2 stack: auto: false + + # url: jdbc:mariadb://${DATABASE_URL}:${MARIA_DATABASE_PORT}/${MARIA_DATABASE_NAME} + # username: ${DB_USERNAME} + # password: ${DB_PASSWORD} // 추후 반영예정 \ No newline at end of file From 91118b76200ec32e57335adca9f25aaa16de3ccc Mon Sep 17 00:00:00 2001 From: giuseog Date: Thu, 28 Nov 2024 17:27:08 +0900 Subject: [PATCH 371/563] =?UTF-8?q?feat:=20center,=20evaluation=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=95,=20=EC=A0=95=EB=A0=AC=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC(#180)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/CenterController.java | 1 - .../query/service/CenterQueryServiceImpl.java | 2 - .../controller/EvaluationController.java | 50 +++++++++--- .../evaluation/query/dto/EvaluationDTO.java | 2 +- .../query/dto/EvaluationSearchDTO.java | 2 +- .../query/repository/EvaluationMapper.java | 22 +++-- .../service/EvaluationQueryServiceImpl.java | 57 +++++++++++-- .../query/repository/EvaluationMapper.xml | 80 ++++++++++++++++--- 8 files changed, 173 insertions(+), 43 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java b/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java index 306bdab6..05f7cf4b 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/controller/CenterController.java @@ -14,7 +14,6 @@ import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.center.common.response.CenterResponseMessage; import stanl_2.final_backend.domain.center.query.dto.CenterSearchRequestDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; diff --git a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryServiceImpl.java index 7ff06fb7..6b1bdb34 100644 --- a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryServiceImpl.java @@ -1,7 +1,6 @@ package stanl_2.final_backend.domain.center.query.service; import jakarta.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; @@ -9,7 +8,6 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.A_sample.query.dto.SampleExcelDownload; import stanl_2.final_backend.domain.center.query.dto.CenterExcelDownload; import stanl_2.final_backend.domain.center.query.dto.CenterSearchRequestDTO; import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java index 4afecb92..ace4bd7c 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/controller/EvaluationController.java @@ -8,26 +8,26 @@ import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; -import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.evaluation.common.response.EvaluationResponseMessage; import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationDTO; import stanl_2.final_backend.domain.evaluation.query.dto.EvaluationSearchDTO; import stanl_2.final_backend.domain.evaluation.query.service.EvaluationQueryService; import stanl_2.final_backend.domain.product.common.response.ProductResponseMessage; -import stanl_2.final_backend.domain.product.query.dto.ProductSearchRequestDTO; import java.security.GeneralSecurityException; import java.security.Principal; -import java.util.HashMap; import java.util.Map; @RestController(value = "queryEvaluationController") @RequestMapping("/api/v1/evaluation") public class EvaluationController { + private final EvaluationQueryService evaluationQueryService; @Autowired @@ -44,11 +44,18 @@ public EvaluationController(EvaluationQueryService evaluationQueryService) { }) @GetMapping("/manager") public ResponseEntity getAllEvaluationsByManager(Principal principal, - Pageable pageable) throws GeneralSecurityException { + @PageableDefault(size = 20) Pageable pageable, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder) throws GeneralSecurityException { EvaluationDTO evaluationDTO = new EvaluationDTO(); evaluationDTO.setMemberId(principal.getName()); + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } + Page responseEvaluations = evaluationQueryService.selectAllEvaluationsByManager(evaluationDTO, pageable); return ResponseEntity.ok(EvaluationResponseMessage.builder() @@ -67,11 +74,18 @@ public ResponseEntity getAllEvaluationsByManager(Prin }) @GetMapping("/representative") public ResponseEntity getAllEvaluationsByRepresentative(Principal principal, - Pageable pageable) throws GeneralSecurityException { + @PageableDefault(size = 20) Pageable pageable, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder) throws GeneralSecurityException { EvaluationDTO evaluationDTO = new EvaluationDTO(); evaluationDTO.setMemberId(principal.getName()); + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } + Page responseEvaluations = evaluationQueryService.selectAllEvaluationsByRepresentative(evaluationDTO, pageable); return ResponseEntity.ok(EvaluationResponseMessage.builder() @@ -110,11 +124,13 @@ public ResponseEntity getEvaluationDetail(@PathVariab }) @GetMapping("/manager/search") public ResponseEntity getEvaluationBySearchByManager(@RequestParam Map params - ,Principal principal - , @PageableDefault(size = 20) Pageable pageable) throws GeneralSecurityException { + ,Principal principal + ,@PageableDefault(size = 20) Pageable pageable, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder) throws GeneralSecurityException { EvaluationSearchDTO evaluationSearchDTO = new EvaluationSearchDTO(); - evaluationSearchDTO.setEvalId(params.get("evalId")); + evaluationSearchDTO.setEvaluationId(params.get("evaluationId")); evaluationSearchDTO.setTitle(params.get("title")); evaluationSearchDTO.setWriterName(params.get("writerName")); evaluationSearchDTO.setMemberName(params.get("memberName")); @@ -123,6 +139,11 @@ public ResponseEntity getEvaluationBySearchByManager( evaluationSearchDTO.setEndDate(params.get("endDate")); evaluationSearchDTO.setSearcherName(principal.getName()); + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } + Page responseEvaluations = evaluationQueryService.selectEvaluationBySearchByManager(pageable, evaluationSearchDTO); return ResponseEntity.ok(EvaluationResponseMessage.builder() @@ -141,11 +162,13 @@ public ResponseEntity getEvaluationBySearchByManager( }) @GetMapping("/representative/search") public ResponseEntity getEvaluationBySearchByRepresentative(@RequestParam Map params - ,Principal principal - , @PageableDefault(size = 20) Pageable pageable) throws GeneralSecurityException { + ,Principal principal + ,@PageableDefault(size = 20) Pageable pageable, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder) throws GeneralSecurityException { EvaluationSearchDTO evaluationSearchDTO = new EvaluationSearchDTO(); - evaluationSearchDTO.setEvalId(params.get("evalId")); + evaluationSearchDTO.setEvaluationId(params.get("evaluationId")); evaluationSearchDTO.setTitle(params.get("title")); evaluationSearchDTO.setWriterName(params.get("writerName")); evaluationSearchDTO.setMemberName(params.get("memberName")); @@ -154,6 +177,11 @@ public ResponseEntity getEvaluationBySearchByRepresen evaluationSearchDTO.setEndDate(params.get("endDate")); evaluationSearchDTO.setSearcherName(principal.getName()); + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } + Page responseEvaluations = evaluationQueryService.selectEvaluationBySearchByRepresentative(pageable, evaluationSearchDTO); return ResponseEntity.ok(EvaluationResponseMessage.builder() diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java index 41d4820b..4696196b 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationDTO.java @@ -10,7 +10,7 @@ @Getter @Setter public class EvaluationDTO { - private String evalId; + private String evaluationId; private String title; private String content; private String createdAt; diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationSearchDTO.java index eb2de610..27fbb350 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/dto/EvaluationSearchDTO.java @@ -13,7 +13,7 @@ @Getter @Setter public class EvaluationSearchDTO { - private String evalId; + private String evaluationId; private String title; private String writerName; private String memberName; diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.java index 0b108832..f08339f7 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.java @@ -13,25 +13,33 @@ public interface EvaluationMapper { List findEvaluationByCenterId(@Param("size") int size - ,@Param("offset") int offset - ,@Param("centerId") String centerId); + ,@Param("offset") int offset + ,@Param("centerId") String centerId, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); EvaluationDTO findEvaluationById(@Param("id") String id); List findAllEvaluations(@Param("size") int size - ,@Param("offset") int offset); + ,@Param("offset") int offset, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); int findEvaluationCount(); int findEvaluationCountByCenterId(@Param("centerId") String centerId); List findEvaluationBySearch(@Param("size") int size - ,@Param("offset") int offset - ,@Param("evaluationSearchDTO") EvaluationSearchDTO evaluationSearchDTO); + ,@Param("offset") int offset + ,@Param("evaluationSearchDTO") EvaluationSearchDTO evaluationSearchDTO, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); List findEvaluationByCenterIdAndSearch(@Param("size") int size - ,@Param("offset") int offset - ,@Param("evaluationSearchDTO") EvaluationSearchDTO evaluationSearchDTO); + ,@Param("offset") int offset + ,@Param("evaluationSearchDTO") EvaluationSearchDTO evaluationSearchDTO, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); int findEvaluationBySearchCount(@Param("evaluationSearchDTO") EvaluationSearchDTO evaluationSearchDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java index b48f256c..82d85d42 100644 --- a/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/evaluation/query/service/EvaluationQueryServiceImpl.java @@ -2,13 +2,12 @@ import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.A_sample.query.dto.SampleExcelDownload; import stanl_2.final_backend.domain.center.query.service.CenterQueryService; import stanl_2.final_backend.domain.evaluation.common.exception.EvaluationCommonException; import stanl_2.final_backend.domain.evaluation.common.exception.EvaluationErrorCode; @@ -19,8 +18,6 @@ import stanl_2.final_backend.domain.member.query.dto.MemberDTO; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.member.query.service.MemberQueryService; -import stanl_2.final_backend.domain.product.common.exception.ProductCommonException; -import stanl_2.final_backend.domain.product.common.exception.ProductErrorCode; import stanl_2.final_backend.global.excel.ExcelUtilsV1; import java.security.GeneralSecurityException; @@ -54,7 +51,16 @@ public Page selectAllEvaluationsByManager(EvaluationDTO evaluatio MemberDTO memberDTO = memberQueryService.selectMemberInfo(evaluationDTO.getMemberId()); String centerId = memberDTO.getCenterId(); - List evaluationList = evaluationMapper.findEvaluationByCenterId(size,offset, centerId); + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } + + List evaluationList = evaluationMapper.findEvaluationByCenterId(size,offset, centerId, sortField, sortOrder); int total = evaluationMapper.findEvaluationCountByCenterId(centerId); @@ -79,7 +85,17 @@ public Page selectAllEvaluationsByRepresentative(EvaluationDTO ev int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); - List evaluationList = evaluationMapper.findAllEvaluations(size,offset); + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } + + + List evaluationList = evaluationMapper.findAllEvaluations(size,offset, sortField, sortOrder); int total = evaluationMapper.findEvaluationCount(); @@ -109,6 +125,15 @@ public EvaluationDTO selectEvaluationById(String id) { if(evaluationDTO == null){ throw new EvaluationCommonException(EvaluationErrorCode.EVALUATION_NOT_FOUND); } + + try { + evaluationDTO.setMemberId(memberQueryService.selectNameById(evaluationDTO.getMemberId())); + evaluationDTO.setWriterId(memberQueryService.selectNameById(evaluationDTO.getWriterId())); + evaluationDTO.setCenterId(centerQueryService.selectNameById(evaluationDTO.getCenterId())); + } catch (Exception e) { + throw new EvaluationCommonException(EvaluationErrorCode.MEMBER_CENTER_NOT_FOUND); + } + return evaluationDTO; } @@ -118,6 +143,14 @@ public Page selectEvaluationBySearchByManager(Pageable pageable, int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } + String writerName = Optional.ofNullable(evaluationSearchDTO.getWriterName()).orElse(""); if (!writerName.isEmpty()) { evaluationSearchDTO.setWriterName(authQueryService.selectMemberIdByLoginId(writerName)); @@ -130,7 +163,7 @@ public Page selectEvaluationBySearchByManager(Pageable pageable, MemberDTO memberDTO = memberQueryService.selectMemberInfo(evaluationSearchDTO.getSearcherName()); evaluationSearchDTO.setCenterId(memberDTO.getCenterId()); - List evaluationList = evaluationMapper.findEvaluationByCenterIdAndSearch(size,offset, evaluationSearchDTO); + List evaluationList = evaluationMapper.findEvaluationByCenterIdAndSearch(size,offset, evaluationSearchDTO, sortField, sortOrder); int total = evaluationMapper.findEvaluationByCenterIdAndSearchCount(evaluationSearchDTO); @@ -154,6 +187,14 @@ public Page selectEvaluationBySearchByRepresentative(Pageable pag int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } + String writerName = Optional.ofNullable(evaluationSearchDTO.getWriterName()).orElse(""); if (!writerName.isEmpty()) { evaluationSearchDTO.setWriterName(authQueryService.selectMemberIdByLoginId(writerName)); @@ -163,7 +204,7 @@ public Page selectEvaluationBySearchByRepresentative(Pageable pag evaluationSearchDTO.setMemberName(authQueryService.selectMemberIdByLoginId(memberName)); } - List evaluationList = evaluationMapper.findEvaluationBySearch(size,offset, evaluationSearchDTO); + List evaluationList = evaluationMapper.findEvaluationBySearch(size,offset, evaluationSearchDTO, sortField, sortOrder); int total = evaluationMapper.findEvaluationBySearchCount(evaluationSearchDTO); diff --git a/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml b/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml index 2e65a7c0..9b42eb05 100644 --- a/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/evaluation/query/repository/EvaluationMapper.xml @@ -5,7 +5,7 @@ - + @@ -17,7 +17,7 @@ - + @@ -92,8 +92,8 @@ FROM tb_evaluation a a.active = TRUE AND a.cent_id = #{evaluationSearchDTO.centerId} - - AND a.eval_id = #{evaluationSearchDTO.evalId} + + AND a.eval_id = #{evaluationSearchDTO.evaluationId} AND a.wri_id = #{evaluationSearchDTO.writerName} @@ -108,7 +108,35 @@ AND a.created_at BETWEEN #{evaluationSearchDTO.startDate} AND #{evaluationSearchDTO.endDate} - ORDER BY a.created_at DESC + + + + + + ORDER BY a.eval_id ${sortOrder} + + + ORDER BY a.eval_ttl ${sortOrder} + + + ORDER BY a.wri_id ${sortOrder} + + + ORDER BY a.mem_id ${sortOrder} + + + ORDER BY a.cent_id ${sortOrder} + + + ORDER BY a.created_at DESC + + + + + + ORDER BY a.created_at DESC + + LIMIT #{size} OFFSET #{offset} @@ -123,8 +151,8 @@ FROM tb_evaluation a a.active = TRUE - - AND a.eval_id = #{evaluationSearchDTO.evalId} + + AND a.eval_id = #{evaluationSearchDTO.evaluationId} AND a.wri_id = #{evaluationSearchDTO.writerName} @@ -142,7 +170,35 @@ AND a.created_at BETWEEN #{evaluationSearchDTO.startDate} AND #{evaluationSearchDTO.endDate} - ORDER BY a.created_at DESC + + + + + + ORDER BY a.eval_id ${sortOrder} + + + ORDER BY a.eval_ttl ${sortOrder} + + + ORDER BY a.wri_id ${sortOrder} + + + ORDER BY a.mem_id ${sortOrder} + + + ORDER BY a.cent_id ${sortOrder} + + + ORDER BY a.created_at DESC + + + + + + ORDER BY a.created_at DESC + + LIMIT #{size} OFFSET #{offset} @@ -166,8 +222,8 @@ FROM tb_evaluation a a.active = TRUE - - AND a.eval_id = #{evaluationSearchDTO.evalId} + + AND a.eval_id = #{evaluationSearchDTO.evaluationId} AND a.wri_id = #{evaluationSearchDTO.writerName} @@ -193,8 +249,8 @@ FROM tb_evaluation a a.active = TRUE AND a.cent_id = #{evaluationSearchDTO.centerId} - - AND a.eval_id = #{evaluationSearchDTO.evalId} + + AND a.eval_id = #{evaluationSearchDTO.evaluationId} AND a.wri_id = #{evaluationSearchDTO.writerName} From 15e0b3a6ddb4d1684531afce7014c8523914d331 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 28 Nov 2024 19:14:37 +0900 Subject: [PATCH 372/563] =?UTF-8?q?feat:=20=EC=9D=BD=EA=B8=B0=20=EC=A0=84?= =?UTF-8?q?=EC=9A=A9=20DB=20=EA=B5=AC=EC=84=B1=20=EC=99=84=EB=A3=8C=20(#17?= =?UTF-8?q?5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/NoticeController.java | 19 +++++++++-- .../service/NoticeCommandServiceImpl.java | 4 --- .../query/controller/NoticeController.java | 32 +++++++++++++++++-- .../query/service/NoticeServiceImpl.java | 8 ++++- .../global/config/DataSourceConfig.java | 9 ------ .../ReplicationRoutingDataSource.java | 3 +- src/main/resources/application.yml | 2 +- 7 files changed, 56 insertions(+), 21 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java index d7be9a75..9ace0424 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java @@ -19,6 +19,7 @@ import stanl_2.final_backend.domain.s3.command.domain.service.S3FileServiceImpl; import stanl_2.final_backend.global.config.datasource.ReplicationRoutingDataSource; +import javax.sql.DataSource; import java.security.Principal; import static org.springframework.transaction.support.TransactionSynchronizationManager.isCurrentTransactionReadOnly; @@ -51,12 +52,13 @@ public NoticeController(NoticeCommandService noticeCommandService, AuthQueryServ public ResponseEntity postNotice(@RequestPart("notice") NoticeRegistDTO noticeRegistDTO, @RequestPart("file") MultipartFile file, Principal principal){ - replicationRoutingDataSource.determineCurrentLookupKey(); + String memberId =authQueryService.selectMemberIdByLoginId(principal.getName()); noticeRegistDTO.setMemberId(memberId); noticeRegistDTO.setFileUrl(s3FileService.uploadOneFile(file)); noticeCommandService.registerNotice(noticeRegistDTO, principal); - System.out.println(isCurrentTransactionReadOnly()); + String dbUrl = getCurrentDbUrl(); + System.out.println("Current DB URL: " + dbUrl); return ResponseEntity.ok(NoticeResponseMessage.builder() .httpStatus(200) .msg("성공") @@ -108,4 +110,17 @@ public ResponseEntity deleteNotice(Principal principal, .build()); } + private String getCurrentDbUrl() { + try { + // DataSource에서 커넥션을 가져와 URL 확인 + DataSource dataSource = replicationRoutingDataSource; + return dataSource.unwrap(javax.sql.DataSource.class) + .getConnection() + .getMetaData() + .getURL(); + } catch (Exception e) { + e.printStackTrace(); + return "Failed to fetch DB URL"; + } + } } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java index 71deb2b9..74c31e92 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java @@ -65,13 +65,9 @@ public void registerNotice(NoticeRegistDTO noticeRegistDTO, noticeRegistDTO.setMemberId(memberId); try { Notice notice = modelMapper.map(noticeRegistDTO, Notice.class); - System.out.println("[After Cache Clear] Transaction ReadOnly: " + isCurrentTransactionReadOnly()); Notice newNotice = noticeRepository.save(notice); - System.out.println("1. [After Cache Clear] Transaction ReadOnly: " + isCurrentTransactionReadOnly()); NoticeAlarmDTO noticeAlarmDTO = modelMapper.map(newNotice, NoticeAlarmDTO.class); - System.out.println("2. [After Cache Clear] Transaction ReadOnly: " + isCurrentTransactionReadOnly()); alarmCommandService.sendNoticeAlarm(noticeAlarmDTO); - System.out.println("3. [After Cache Clear] Transaction ReadOnly: " + isCurrentTransactionReadOnly()); } catch (DataIntegrityViolationException e){ // DB 무결정 제약 조건 (NOT NULL, UNIQUE) 위반 throw new NoticeCommonException(NoticeErrorCode.DATA_INTEGRITY_VIOLATION); diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java index c856ba5c..b48e4321 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java @@ -11,21 +11,30 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.notices.common.response.NoticeResponseMessage; import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; import stanl_2.final_backend.domain.notices.query.dto.SearchDTO; import stanl_2.final_backend.domain.notices.query.service.NoticeService; +import stanl_2.final_backend.global.config.datasource.ReplicationRoutingDataSource; + +import javax.sql.DataSource; + +import static org.springframework.transaction.support.TransactionSynchronizationManager.isCurrentTransactionReadOnly; @RestController("queryNoticeController") @RequestMapping("/api/v1/notice") public class NoticeController { private final NoticeService noticeService; + private final ReplicationRoutingDataSource replicationRoutingDataSource; + @Autowired - public NoticeController(NoticeService noticeService) { + public NoticeController(NoticeService noticeService, ReplicationRoutingDataSource replicationRoutingDataSource) { this.noticeService = noticeService; + this.replicationRoutingDataSource= replicationRoutingDataSource; } @Operation(summary = "공지사항 조건별 조회") @@ -33,6 +42,7 @@ public NoticeController(NoticeService noticeService) { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = NoticeResponseMessage.class))}) }) + @Transactional(readOnly = true) @GetMapping public ResponseEntity> getNotices( @RequestParam(defaultValue = "0") int page, @@ -47,8 +57,11 @@ public ResponseEntity> getNotices( ) { Pageable pageable = PageRequest.of(page, size); SearchDTO searchDTO = new SearchDTO(title,tag,memberId,classification,startDate,endDate); + System.out.println("1.Transaction ReadOnly: " + isCurrentTransactionReadOnly()); Page noticeDTOPage = noticeService.findNotices(pageable,searchDTO); - + System.out.println("6.Transaction ReadOnly: " + isCurrentTransactionReadOnly()); + String dbUrl = getCurrentDbUrl(); + System.out.println("Current DB URL: " + dbUrl); return ResponseEntity.ok(noticeDTOPage); } @@ -57,6 +70,7 @@ public ResponseEntity> getNotices( @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = NoticeResponseMessage.class))}) }) + @Transactional(readOnly = true) @GetMapping("{noticeId}") public ResponseEntity getNotice(@PathVariable String noticeId){ NoticeDTO noticeDTO = noticeService.findNotice(noticeId); @@ -73,4 +87,18 @@ public void exportNotice(HttpServletResponse response){ noticeService.exportNoticesToExcel(response); } + + private String getCurrentDbUrl() { + try { + // DataSource에서 커넥션을 가져와 URL 확인 + DataSource dataSource = replicationRoutingDataSource; + return dataSource.unwrap(javax.sql.DataSource.class) + .getConnection() + .getMetaData() + .getURL(); + } catch (Exception e) { + e.printStackTrace(); + return "Failed to fetch DB URL"; + } + } } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java index 38588c8d..aaa9084b 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java @@ -17,6 +17,8 @@ import java.util.List; +import static org.springframework.transaction.support.TransactionSynchronizationManager.isCurrentTransactionReadOnly; + @Transactional(readOnly = true) @Service("queryNoticeServiceImpl") public class NoticeServiceImpl implements NoticeService{ @@ -33,16 +35,20 @@ public NoticeServiceImpl(NoticeMapper noticeMapper, RedisTemplate redisTemplate, } + @Transactional(readOnly = true) @Override public Page findNotices(Pageable pageable, SearchDTO searchDTO) { + System.out.println("2.Transaction ReadOnly: " + isCurrentTransactionReadOnly()); int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); + System.out.println("3.Transaction ReadOnly: " + isCurrentTransactionReadOnly()); String cacheKey = "NoticeCache::notices::offset=" + offset + "::size=" + size + "::title=" + searchDTO.getTitle()+ "::tag=" + searchDTO.getTag() +"::memberid=" + searchDTO.getMemberId()+ "::classification=" + searchDTO.getClassification() + "::startDate=" + searchDTO.getStartDate()+ "::endDate=" + searchDTO.getEndDate(); - + System.out.println("4.Transaction ReadOnly: " + isCurrentTransactionReadOnly()); List notices = (List) redisTemplate.opsForValue().get(cacheKey); + System.out.println("5.Transaction ReadOnly: " + isCurrentTransactionReadOnly()); if (notices == null) { System.out.println("데이터베이스에서 데이터 조회 중..."); notices = noticeMapper.findNotices(offset, size, searchDTO); diff --git a/src/main/java/stanl_2/final_backend/global/config/DataSourceConfig.java b/src/main/java/stanl_2/final_backend/global/config/DataSourceConfig.java index 7591812f..aeae0086 100644 --- a/src/main/java/stanl_2/final_backend/global/config/DataSourceConfig.java +++ b/src/main/java/stanl_2/final_backend/global/config/DataSourceConfig.java @@ -23,7 +23,6 @@ public class DataSourceConfig { @ConfigurationProperties(prefix = "spring.datasource.writer.hikari") @Bean(name = "writerDataSource") public DataSource writerDataSource() { - // DataSourceBuilder를 사용해 HikariDataSource 타입의 데이터 소스를 생성합니다. return DataSourceBuilder.create().type(HikariDataSource.class).build(); } @@ -54,14 +53,6 @@ public DataSource routingDataSource( return routingDataSource; } - /** - * `routingDataSource`가 생성된 이후에 이 메서드를 실행하도록 지정 - * `LazyConnectionDataSourceProxy`는 실제 연결을 사용하는 시점에 데이터베이스 연결을 지연 생성 - * 성능 최적화를 도모하고 트랜잭션 관리에서 데이터 소스의 사용을 효율적으로 처리 가능 - * - * @param routingDataSource 라우팅 가능한 데이터 소스 - * @return 지연된 연결 처리를 지원하는 데이터 소스 프록시 - */ @DependsOn("routingDataSource") @Primary // 이 빈을 기본 `DataSource`로 설정 @Bean diff --git a/src/main/java/stanl_2/final_backend/global/config/datasource/ReplicationRoutingDataSource.java b/src/main/java/stanl_2/final_backend/global/config/datasource/ReplicationRoutingDataSource.java index 9428f55f..16edcc83 100644 --- a/src/main/java/stanl_2/final_backend/global/config/datasource/ReplicationRoutingDataSource.java +++ b/src/main/java/stanl_2/final_backend/global/config/datasource/ReplicationRoutingDataSource.java @@ -5,10 +5,9 @@ import static org.springframework.transaction.support.TransactionSynchronizationManager.isCurrentTransactionReadOnly; public class ReplicationRoutingDataSource extends AbstractRoutingDataSource { - + @Override public Object determineCurrentLookupKey() { String dataSourceName = isCurrentTransactionReadOnly() ? "reader" : "writer"; - System.out.println(">>>>>> current data source : {}"+ dataSourceName); return dataSourceName; } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 882af8e3..3be38658 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -16,7 +16,7 @@ spring: reader: hikari: - jdbc-url: jdbc:mariadb://motivereadonly.cdw8kw8go27z.ap-northeast-2.rds.amazonaws.com:3306/motive + jdbc-url: jdbc:mariadb://readonlymotive.cdw8kw8go27z.ap-northeast-2.rds.amazonaws.com:3306/motive username: admin password: motivepassword driver-class-name: org.mariadb.jdbc.Driver From 4e2bb900b8d7430c905fbc147b10ce4939f822c8 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Thu, 28 Nov 2024 19:57:55 +0900 Subject: [PATCH 373/563] =?UTF-8?q?refactor:=20=EA=B2=80=EC=83=89=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/ContractController.java | 21 +++-- .../contract/query/dto/ContractSearchDTO.java | 2 +- .../query/repository/ContractMapper.java | 4 +- .../service/ContractQueryServiceImpl.java | 30 ++++++- src/main/resources/application.yml | 8 ++ .../query/repository/ContractMapper.xml | 87 ++++++++++--------- 6 files changed, 98 insertions(+), 54 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java index 7c0b5bf6..77b7bbac 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java @@ -101,7 +101,7 @@ public ResponseEntity getContractBySearchEmployee(Princ @RequestParam(required = false) String endAt, @RequestParam(required = false) String customerName, @RequestParam(required = false) String customerClassifcation, - @RequestParam(required = false) String productId, + @RequestParam(required = false) String carName, @RequestParam(required = false) String status, @RequestParam(required = false) String companyName, @RequestParam(required = false) String customerPurchaseCondition, @@ -116,7 +116,7 @@ public ResponseEntity getContractBySearchEmployee(Princ contractSearchDTO.setEndAt(endAt); contractSearchDTO.setCustomerName(customerName); contractSearchDTO.setCustomerClassifcation(customerClassifcation); - contractSearchDTO.setProductId(productId); + contractSearchDTO.setCarName(carName); contractSearchDTO.setStatus(status); contractSearchDTO.setCompanyName(companyName); contractSearchDTO.setCustomerPurchaseCondition(customerPurchaseCondition); @@ -196,7 +196,7 @@ public ResponseEntity getContractBySearchAdmin(Principa @RequestParam(required = false) String endAt, @RequestParam(required = false) String customerName, @RequestParam(required = false) String customerClassifcation, - @RequestParam(required = false) String productId, + @RequestParam(required = false) String carName, @RequestParam(required = false) String status, @RequestParam(required = false) String companyName, @RequestParam(required = false) String customerPurchaseCondition, @@ -211,7 +211,7 @@ public ResponseEntity getContractBySearchAdmin(Principa contractSearchDTO.setEndAt(endAt); contractSearchDTO.setCustomerName(customerName); contractSearchDTO.setCustomerClassifcation(customerClassifcation); - contractSearchDTO.setProductId(productId); + contractSearchDTO.setCarName(carName); contractSearchDTO.setStatus(status); contractSearchDTO.setCompanyName(companyName); contractSearchDTO.setCustomerPurchaseCondition(customerPurchaseCondition); @@ -286,12 +286,21 @@ public ResponseEntity getContractBySearch(@RequestParam @RequestParam(required = false) String endAt, @RequestParam(required = false) String customerName, @RequestParam(required = false) String customerClassifcation, - @RequestParam(required = false) String productId, + @RequestParam(required = false) String carName, @RequestParam(required = false) String status, @RequestParam(required = false) String companyName, @RequestParam(required = false) String customerPurchaseCondition, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder, @PageableDefault(size = 10) Pageable pageable) { + log.info("검색조건: " + sortField + "ddd: " + sortOrder); + // 정렬 추가 + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } + ContractSearchDTO contractSearchDTO = new ContractSearchDTO(); contractSearchDTO.setSearchMemberId(searchMemberId); contractSearchDTO.setCenterId(centerId); @@ -300,7 +309,7 @@ public ResponseEntity getContractBySearch(@RequestParam contractSearchDTO.setEndAt(endAt); contractSearchDTO.setCustomerName(customerName); contractSearchDTO.setCustomerClassifcation(customerClassifcation); - contractSearchDTO.setProductId(productId); + contractSearchDTO.setCarName(carName); contractSearchDTO.setStatus(status); contractSearchDTO.setCompanyName(companyName); contractSearchDTO.setCustomerPurchaseCondition(customerPurchaseCondition); diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java index 943083c8..939717b8 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java @@ -6,6 +6,7 @@ @NoArgsConstructor @Getter @Setter +@ToString public class ContractSearchDTO { private String contractId; @@ -39,7 +40,6 @@ public class ContractSearchDTO { private String searchMemberId; private String centerId; private String customerId; - private String productId; private String startAt; private String endAt; private String carName; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java index 87b28f7f..32f25733 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java @@ -39,7 +39,9 @@ List findContractAll(@Param("offset") int offset, List findContractBySearch(@Param("offset") int offset, @Param("pageSize") int pageSize, - @Param("contractSearchDTO") ContractSearchDTO contractSearchDTO); + @Param("contractSearchDTO") ContractSearchDTO contractSearchDTO, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); int findContractBySearchCount(ContractSearchDTO contractSearchDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java index 4cb86c15..d5f90dae 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java @@ -260,16 +260,38 @@ public Page selectBySearch(ContractSearchDTO contractSearchDT int offset = Math.toIntExact(pageable.getOffset()); int pageSize = pageable.getPageSize(); - List contracts = contractMapper.findContractBySearch(offset, pageSize, contractSearchDTO); + + // 정렬 정보 가져오기 + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } + + log.info("service Field: " + sortField); + log.info("service order: " + sortOrder); + + List contracts = contractMapper.findContractBySearch(offset, pageSize, contractSearchDTO, sortField, sortOrder); if (contracts == null) { throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); } - Integer count = contractMapper.findContractBySearchCount(contractSearchDTO); - int totalContract = (count != null) ? count : 0; + log.info("값들어가는지 확인: " + contracts); - return new PageImpl<>(contracts, pageable, totalContract); +// int count = contractMapper.findContractBySearchCount(contractSearchDTO); + int count = 1; +// int totalContract = contractMapper.findContractBySearchCount(contractSearchDTO); + + if (count == 0) { + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); + } + + log.info("count: " + count); + + return new PageImpl<>(contracts, pageable, count); } @Override diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 45b4c61f..14a7a391 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -41,6 +41,10 @@ logging: org: springframework: security: ${SPRING_SECURITY_LOG_LEVEL:TRACE} + apache.ibatis: DEBUG + mybatis: DEBUG + jdbc.sqltiming: DEBUG + jdbc.resultsettable: TRACE cloud: aws: @@ -53,3 +57,7 @@ cloud: static: ${S3_REGION} stack: auto: false + +mybatis: + configuration: + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index 7785dae1..4e0e71fc 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -208,7 +208,7 @@ - + ORDER BY a.conr_cust_name ${sortOrder} @@ -295,7 +295,7 @@ - + ORDER BY a.conr_cust_name ${sortOrder} @@ -332,56 +332,55 @@ LIMIT #{pageSize} OFFSET #{offset} - SELECT a.conr_id, a.conr_cust_name, - a.conr_ttl, a.conr_comp_name, + a.conr_car_name, + a.conr_ttl, a.conr_stat, - a.created_url, a.conr_cust_pur_cond, - a.conr_car_name, - a.created_at, - a.updated_at, - a.deleted_at, - a.active + a.conr_cust_cla FROM tb_contract a a.active = TRUE - + + AND a.mem_id = #{contractSearchDTO.searchMemberId} + + AND a.cent_id = #{contractSearchDTO.centerId} - - AND a.conr_name LIKE CONCAT('%', #{contractSearchDTO.title}, '%') + + AND a.conr_ttl LIKE CONCAT('%', #{contractSearchDTO.title}, '%') - + AND a.conr_cust_name LIKE CONCAT('%', #{contractSearchDTO.customerName}, '%') - + AND a.conr_cust_cla LIKE CONCAT('%', #{contractSearchDTO.customerClassifcation}, '%') - + AND a.conr_stat = #{contractSearchDTO.status} - + AND a.conr_comp_name LIKE CONCAT('%', #{contractSearchDTO.companyName}, '%') - + AND a.conr_cust_pur_cond LIKE CONCAT('%', #{contractSearchDTO.customerPurchaseCondition}, '%') - + AND a.created_at BETWEEN #{contractSearchDTO.startAt} AND #{contractSearchDTO.endAt} - - AND b.prod_name LIKE CONCAT('%', #{contractSearchDTO.productId}, '%') + + AND a.conr_car_name LIKE CONCAT('%', #{contractSearchDTO.carName}, '%') - + ORDER BY a.conr_cust_name ${sortOrder} @@ -469,37 +468,41 @@ - + SELECT + COUNT(*) AS cnt FROM tb_contract a a.active = TRUE - - AND a.cent_id = #{centerId} + + AND a.mem_id = #{contractSearchDTO.searchMemberId} - - AND a.conr_name LIKE CONCAT('%', #{title}, '%') + + AND a.cent_id = #{contractSearchDTO.centerId} - - AND a.conr_cust_name LIKE CONCAT('%', #{customerName}, '%') + + AND a.conr_ttl LIKE CONCAT('%', #{contractSearchDTO.title}, '%') - - AND a.conr_cust_cla LIKE CONCAT('%', #{customerClassifcation}, '%') + + AND a.conr_cust_name LIKE CONCAT('%', #{contractSearchDTO.customerName}, '%') - - AND a.conr_stat = #{status} + + AND a.conr_cust_cla LIKE CONCAT('%', #{contractSearchDTO.customerClassifcation}, '%') - - AND a.conr_comp_name LIKE CONCAT('%', #{companyName}, '%') + + AND a.conr_stat = #{contractSearchDTO.status} - - AND a.conr_cust_pur_cond LIKE CONCAT('%', #{customerPurchaseCondition}, '%') + + AND a.conr_comp_name LIKE CONCAT('%', #{contractSearchDTO.companyName}, '%') - - AND a.created_at BETWEEN #{startAt} AND #{endAt} + + AND a.conr_cust_pur_cond LIKE CONCAT('%', #{contractSearchDTO.customerPurchaseCondition}, '%') - - AND b.prod_name LIKE CONCAT('%', #{productId}, '%') + + AND a.created_at BETWEEN #{contractSearchDTO.startAt} AND #{contractSearchDTO.endAt} + + + AND a.conr_car_name LIKE CONCAT('%', #{contractSearchDTO.carName}, '%') From 513ed6966d35b68b55eb2215c51f9455d8aae80b Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Fri, 29 Nov 2024 17:16:26 +0900 Subject: [PATCH 374/563] =?UTF-8?q?feat:=20=ED=85=8C=EC=9D=B4=EB=B8=94?= =?UTF-8?q?=EC=97=90=20=EB=A1=9C=EA=B7=B8=20=EB=9D=84=EC=9A=B0=EA=B8=B0(#1?= =?UTF-8?q?33)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customer/query/service/CustomerQueryServiceImpl.java | 2 +- .../domain/customer/query/repository/CustomerMapper.xml | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java index 3d272492..2c281c82 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java @@ -88,7 +88,7 @@ public Page findCustomerByCondition(Pageable pageable, Custom params.put("customerId", customerSearchDTO.getCustomerId()); params.put("name", customerSearchDTO.getName()); params.put("sex", customerSearchDTO.getSex()); - params.put("phone", customerSearchDTO.getPhone()); + params.put("phone", aesUtils.encrypt(customerSearchDTO.getPhone())); List customerList = customerMapper.findCustomerByConditions(params); diff --git a/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml b/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml index eb252a2d..30b73fb6 100644 --- a/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml @@ -111,7 +111,7 @@ AND a.cust_pho = #{ phone } - AND a.cust_id= #{ customerId } + AND a.cust_id = #{ customerId } ORDER BY a.created_at DESC @@ -134,6 +134,9 @@ AND a.cust_pho = #{ phone } + + AND a.cust_id= #{ customerId } + From f605a76665942d800047724dfc35987616c2c564 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Fri, 29 Nov 2024 18:09:21 +0900 Subject: [PATCH 375/563] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20=ED=8A=B8?= =?UTF-8?q?=EB=9F=AC=EB=B8=94=EC=8A=88=ED=8C=85=20=ED=9B=84=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=EC=82=AC=ED=95=AD=20=EC=A0=81=EC=9A=A9=20(##182)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/domain/repository/EmitterRepository.java | 2 ++ .../command/domain/repository/EmitterRepositoryImpl.java | 9 ++++++++- .../command/domain/service/AlarmCommandServiceImpl.java | 7 +++++++ src/main/resources/application-prod.yml | 3 ++- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/repository/EmitterRepository.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/repository/EmitterRepository.java index c290c41f..23ccda09 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/repository/EmitterRepository.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/repository/EmitterRepository.java @@ -18,4 +18,6 @@ public interface EmitterRepository { void deleteAllEmitterStartWithMemberId(String memberId); void deleteAllEventCacheStartWithmemberId(String memberId); + + SseEmitter findEmitterByMemberId(String memberId); } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/repository/EmitterRepositoryImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/repository/EmitterRepositoryImpl.java index a10acee5..723e27be 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/repository/EmitterRepositoryImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/repository/EmitterRepositoryImpl.java @@ -70,5 +70,12 @@ public void deleteAllEventCacheStartWithmemberId(String memberId) { ); } - + @Override + public SseEmitter findEmitterByMemberId(String memberId) { + return emitters.entrySet().stream() + .filter(entry -> entry.getKey().startsWith(memberId)) // memberId로 시작하는 키 검색 + .map(Map.Entry::getValue) + .findFirst() + .orElse(null); // 없으면 null 반환 + } } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java index caa13b1a..35b6f4b5 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java @@ -59,6 +59,13 @@ public SseEmitter subscribe(AlarmRegistDTO alarmRegistDTO, HttpServletResponse r String memberId = authQueryService.selectMemberIdByLoginId(alarmRegistDTO.getMemberLoginId()); String emitterId = memberId + "_" + System.currentTimeMillis(); + // 기존 Emitter 확인 및 삭제 + SseEmitter existingEmitter = emitterRepository.findEmitterByMemberId(memberId); + if (existingEmitter != null) { + emitterRepository.deleteAllByEmitterId(memberId); // 기존 Emitter 제거 + existingEmitter.complete(); // 기존 연결 종료 + } + // 클라이언트의 sse 연결 요청에 응답하기 위한 SseEmitter 객체 생성 // 유효시간 지정으로 시간이 지나면 클라이언트에서 자동으로 재연결 요청함 SseEmitter emitter = emitterRepository.save(emitterId, new SseEmitter(DEFAULT_TIMEOUT)); diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 47663e45..ae689fd7 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -12,7 +12,8 @@ spring: jpa: hibernate: ddl-auto: update + open-in-view: false logging: level: - org.springframework.security: WARN + org.springframework.security: WARN \ No newline at end of file From da0b1b69d4930a5e63ca6745fa06b854811b9361 Mon Sep 17 00:00:00 2001 From: giuseog Date: Fri, 29 Nov 2024 18:11:41 +0900 Subject: [PATCH 376/563] =?UTF-8?q?fix:=20=ED=8C=90=EB=A7=A4=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95(#183)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SalesHistoryController.java | 25 ++++----- .../query/dto/SalesHistoryStatisticsDTO.java | 1 + .../query/repository/SalesHistoryMapper.java | 2 +- .../service/SalesHistoryQueryService.java | 4 +- .../service/SalesHistoryQueryServiceImpl.java | 53 +++++++++++++++---- src/main/resources/application-prod.yml | 4 ++ .../query/repository/SalesHistoryMapper.xml | 3 +- 7 files changed, 66 insertions(+), 26 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java index 13f5d7b5..3d2abd29 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java @@ -18,6 +18,7 @@ import stanl_2.final_backend.domain.sales_history.query.service.SalesHistoryQueryService; import java.security.Principal; +import java.util.List; import java.util.Map; @RestController(value = "querySalesHistoryController") @@ -102,7 +103,7 @@ public ResponseEntity getSalesHistoryDetail(@PathVa @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("/employee/search") + @PostMapping("/employee/search") public ResponseEntity getSalesHistorySearchByEmployee(@RequestBody SalesHistorySearchDTO salesHistorySearchDTO ,Principal principal, @PageableDefault(size = 20) Pageable pageable){ @@ -125,7 +126,7 @@ public ResponseEntity getSalesHistorySearchByEmploy @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("/search") + @PostMapping("/search") public ResponseEntity getSalesHistoryBySearch(@RequestBody SalesHistorySearchDTO salesHistorySearchDTO, @PageableDefault(size = 20) Pageable pageable){ @@ -208,7 +209,7 @@ public ResponseEntity getStatisticsSearchMonthByEmp salesHistorySearchDTO.setStartDate(params.get("startDate")); salesHistorySearchDTO.setEndDate(params.get("endDate")); - SalesHistoryStatisticsDTO responseSalesHistory = salesHistoryQueryService.selectStatisticsSearchMonthByEmployee(salesHistorySearchDTO); + List responseSalesHistory = salesHistoryQueryService.selectStatisticsSearchMonthByEmployee(salesHistorySearchDTO); return ResponseEntity.ok(SalesHistoryResponseMessage.builder() .httpStatus(200) @@ -273,7 +274,7 @@ public ResponseEntity getStatistics(@RequestBody Sa @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("statistics/search") + @PostMapping("statistics/search") public ResponseEntity getStatisticsBySearch(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, @PageableDefault(size = 20) Pageable pageable){ @@ -293,7 +294,7 @@ public ResponseEntity getStatisticsBySearch(@Reques @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("statistics/average/employee") + @PostMapping("statistics/average/employee") public ResponseEntity getStatisticsEmployeeAverageBySearch(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, @PageableDefault(size = 20) Pageable pageable){ @@ -313,7 +314,7 @@ public ResponseEntity getStatisticsEmployeeAverageB @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("statistics/average/center") + @PostMapping("statistics/average/center") public ResponseEntity getStatisticsCenterAverageBySearch(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, @PageableDefault(size = 20) Pageable pageable){ @@ -333,7 +334,7 @@ public ResponseEntity getStatisticsCenterAverageByS @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("statistics/search/month") + @PostMapping("statistics/search/month") public ResponseEntity getStatisticsBySearchMonth(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, @PageableDefault(size = 20) Pageable pageable){ @@ -353,7 +354,7 @@ public ResponseEntity getStatisticsBySearchMonth(@R @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("statistics/search/year") + @PostMapping("statistics/search/year") public ResponseEntity getStatisticsBySearchYear(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, @PageableDefault(size = 20) Pageable pageable){ @@ -373,7 +374,7 @@ public ResponseEntity getStatisticsBySearchYear(@Re @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("statistics/center/search") + @PostMapping("statistics/center/search") public ResponseEntity getStatisticsCenterBySearch(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, @PageableDefault(size = 20) Pageable pageable){ @@ -386,14 +387,14 @@ public ResponseEntity getStatisticsCenterBySearch(@ .build()); } -@Operation(summary = "매장 별 통계(실적,수당,매출액) 월별 검색") + @Operation(summary = "매장 별 통계(실적,수당,매출액) 월별 검색") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("statistics/center/search/month") + @PostMapping("statistics/center/search/month") public ResponseEntity getStatisticsCenterBySearchMonth(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, @PageableDefault(size = 20) Pageable pageable){ @@ -413,7 +414,7 @@ public ResponseEntity getStatisticsCenterBySearchMo @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("statistics/center/search/year") + @PostMapping("statistics/center/search/year") public ResponseEntity getStatisticsCenterBySearchYear(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, @PageableDefault(size = 20) Pageable pageable){ diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsDTO.java index 3fad0218..50a244fa 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsDTO.java @@ -11,4 +11,5 @@ public class SalesHistoryStatisticsDTO { private int performance; private int totalSales; private String orderBy; + private String month; } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java index 846587ce..08e1a0a6 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java @@ -26,7 +26,7 @@ List findAllSalesHistory(@Param("size") int size SalesHistoryStatisticsDTO findStatisticsSearchByEmployee(@Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); - SalesHistoryStatisticsDTO findStatisticsSearchMonthByEmployee(@Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); + List findStatisticsSearchMonthByEmployee(@Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); SalesHistoryStatisticsDTO findStatisticsSearchYearByEmployee(@Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java index e2618532..6de5f1a4 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java @@ -5,6 +5,8 @@ import org.springframework.data.domain.Pageable; import stanl_2.final_backend.domain.sales_history.query.dto.*; +import java.util.List; + public interface SalesHistoryQueryService { Page selectAllSalesHistoryByEmployee(SalesHistorySelectDTO salesHistorySelectDTO, Pageable pageable); @@ -16,7 +18,7 @@ public interface SalesHistoryQueryService { SalesHistoryStatisticsDTO selectStatisticsSearchByEmployee(SalesHistorySearchDTO salesHistorySearchDTO); - SalesHistoryStatisticsDTO selectStatisticsSearchMonthByEmployee(SalesHistorySearchDTO salesHistorySearchDTO); + List selectStatisticsSearchMonthByEmployee(SalesHistorySearchDTO salesHistorySearchDTO); SalesHistoryStatisticsDTO selectStatisticsSearchYearByEmployee(SalesHistorySearchDTO salesHistorySearchDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java index 0596e7d3..428b2fe1 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java @@ -1,23 +1,16 @@ package stanl_2.final_backend.domain.sales_history.query.service; import jakarta.servlet.http.HttpServletResponse; -import org.springframework.aop.scope.ScopedProxyUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.A_sample.query.dto.SampleExcelDownload; import stanl_2.final_backend.domain.center.query.service.CenterQueryService; import stanl_2.final_backend.domain.customer.query.service.CustomerQueryService; -import stanl_2.final_backend.domain.evaluation.common.exception.EvaluationCommonException; -import stanl_2.final_backend.domain.evaluation.common.exception.EvaluationErrorCode; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.member.query.service.MemberQueryService; -import stanl_2.final_backend.domain.product.common.exception.ProductCommonException; -import stanl_2.final_backend.domain.product.common.exception.ProductErrorCode; import stanl_2.final_backend.domain.sales_history.common.exception.SalesHistoryCommonException; import stanl_2.final_backend.domain.sales_history.common.exception.SalesHistoryErrorCode; import stanl_2.final_backend.domain.sales_history.query.dto.*; @@ -246,15 +239,23 @@ public SalesHistoryStatisticsDTO selectStatisticsSearchByEmployee(SalesHistorySe @Override @Transactional(readOnly = true) - public SalesHistoryStatisticsDTO selectStatisticsSearchMonthByEmployee(SalesHistorySearchDTO salesHistorySearchDTO) { + public List selectStatisticsSearchMonthByEmployee(SalesHistorySearchDTO salesHistorySearchDTO) { salesHistorySearchDTO.setSearcherName(authQueryService.selectMemberIdByLoginId(salesHistorySearchDTO.getSearcherName())); - SalesHistoryStatisticsDTO salesHistoryStatisticsDTO = salesHistoryMapper.findStatisticsSearchMonthByEmployee(salesHistorySearchDTO); + System.out.println(salesHistorySearchDTO.getSearcherName()); - if(salesHistoryStatisticsDTO == null){ + String parseStartDate = salesHistorySearchDTO.getStartDate(); + String parseEndDate = salesHistorySearchDTO.getEndDate(); + + salesHistorySearchDTO.setStartDate(parseStartDate.substring(0,7)); + salesHistorySearchDTO.setEndDate(parseEndDate.substring(0,7)); + + List salesHistoryStatisticsDTOList = salesHistoryMapper.findStatisticsSearchMonthByEmployee(salesHistorySearchDTO); + + if(salesHistoryStatisticsDTOList == null){ throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); } - return salesHistoryStatisticsDTO; + return salesHistoryStatisticsDTOList; } @Override @@ -262,6 +263,12 @@ public SalesHistoryStatisticsDTO selectStatisticsSearchMonthByEmployee(SalesHist public SalesHistoryStatisticsDTO selectStatisticsSearchYearByEmployee(SalesHistorySearchDTO salesHistorySearchDTO) { salesHistorySearchDTO.setSearcherName(authQueryService.selectMemberIdByLoginId(salesHistorySearchDTO.getSearcherName())); + String parseStartDate = salesHistorySearchDTO.getStartDate(); + String parseEndDate = salesHistorySearchDTO.getEndDate(); + + parseStartDate = parseStartDate.substring(0,4); + parseEndDate = parseStartDate.substring(0,4); + SalesHistoryStatisticsDTO salesHistoryStatisticsDTO = salesHistoryMapper.findStatisticsSearchYearByEmployee(salesHistorySearchDTO); if(salesHistoryStatisticsDTO == null){ @@ -331,6 +338,12 @@ public Page selectStatisticsBySearchMonth(SalesHistor int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); + String parseStartDate = salesHistoryRankedDataDTO.getStartDate(); + String parseEndDate = salesHistoryRankedDataDTO.getEndDate(); + + parseStartDate = parseStartDate.substring(0,7); + parseEndDate = parseStartDate.substring(0,7); + List salesHistoryList = salesHistoryMapper.findStatisticsBySearchMonth(size,offset, salesHistoryRankedDataDTO); int total = salesHistoryMapper.findStatisticsBySearchCountMonth(salesHistoryRankedDataDTO); @@ -348,6 +361,12 @@ public Page selectStatisticsBySearchYear(SalesHistory int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); + String parseStartDate = salesHistoryRankedDataDTO.getStartDate(); + String parseEndDate = salesHistoryRankedDataDTO.getEndDate(); + + parseStartDate = parseStartDate.substring(0,4); + parseEndDate = parseStartDate.substring(0,4); + List salesHistoryList = salesHistoryMapper.findStatisticsBySearchYear(size,offset, salesHistoryRankedDataDTO); int total = salesHistoryMapper.findStatisticsBySearchCountYear(salesHistoryRankedDataDTO); @@ -381,6 +400,12 @@ public Page selectStatisticsCenterBySearchMonth(Sales int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); + String parseStartDate = salesHistoryRankedDataDTO.getStartDate(); + String parseEndDate = salesHistoryRankedDataDTO.getEndDate(); + + parseStartDate = parseStartDate.substring(0,7); + parseEndDate = parseStartDate.substring(0,7); + List salesHistoryList = salesHistoryMapper.findStatisticsCenterBySearchMonth(size,offset, salesHistoryRankedDataDTO); int total = salesHistoryMapper.findStatisticsCenterBySearchCountMonth(salesHistoryRankedDataDTO); @@ -397,6 +422,12 @@ public Page selectStatisticsCenterBySearchYear(SalesH int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); + String parseStartDate = salesHistoryRankedDataDTO.getStartDate(); + String parseEndDate = salesHistoryRankedDataDTO.getEndDate(); + + parseStartDate = parseStartDate.substring(0,4); + parseEndDate = parseStartDate.substring(0,4); + List salesHistoryList = salesHistoryMapper.findStatisticsCenterBySearchYear(size,offset, salesHistoryRankedDataDTO); int total = salesHistoryMapper.findStatisticsCenterBySearchCountYear(salesHistoryRankedDataDTO); diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 47663e45..69c5a992 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -16,3 +16,7 @@ spring: logging: level: org.springframework.security: WARN + +mybatis: + configuration: + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml index f78a144c..11845c26 100644 --- a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml @@ -34,6 +34,7 @@ + @@ -266,7 +267,7 @@ WHERE a.active = TRUE; - SELECT LEFT(a.created_at, 7) AS MONTH, SUM(a.sal_hist_ince) AS INCENTIVE, From 64b66400b683869207a0f888a79e250ee350f1a0 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 29 Nov 2024 18:13:22 +0900 Subject: [PATCH 377/563] =?UTF-8?q?feat:=20problem=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EC=B2=98=EB=A6=AC=EC=83=81=ED=83=9C=20=EC=BB=AC?= =?UTF-8?q?=EB=9F=BC=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=20=EB=B0=94=EA=BE=B8=EA=B8=B0=20API=20=EC=B6=94=EA=B0=80=20(#1?= =?UTF-8?q?84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ProblemController.java | 17 ++++++++++++ .../service/ProblemCommandService.java | 1 + .../domain/aggregate/entity/Problem.java | 5 +++- .../aggregate/service/ProblemServiceImpl.java | 26 +++++++++++++++++++ .../domain/problem/query/dto/ProblemDTO.java | 2 ++ .../query/dto/ProblemExcelDownload.java | 3 +++ .../problem/query/dto/ProblemSearchDTO.java | 1 + .../query/service/ProblemServiceImpl.java | 5 +++- .../domain/aggregate/entity/Promotion.java | 2 +- .../query/controller/PromotionController.java | 2 +- .../security/config/RequestMatcherConfig.java | 1 + .../query/repository/ProblemMapper.xml | 6 +++++ 12 files changed, 67 insertions(+), 4 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java index 31988f11..384e090d 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java @@ -70,6 +70,23 @@ public ResponseEntity modifyProblem(@PathVariable String .build()); } + @Operation(summary = "문제사항 상태 수정") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = ProblemResponseMessage.class))}) + }) + @PutMapping("/status/{problemId}") + public ResponseEntity modifyProblemStatus(@PathVariable String problemId, Principal principal){ + + problemCommandService.modifyStatus(problemId,principal); + + return ResponseEntity.ok(ProblemResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + } + @Operation(summary = "문제사항 삭제") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/service/ProblemCommandService.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/service/ProblemCommandService.java index dd112d3e..ad2ffbf4 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/application/service/ProblemCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/service/ProblemCommandService.java @@ -11,5 +11,6 @@ public interface ProblemCommandService { ProblemModifyDTO modifyProblem(String problemId, ProblemModifyDTO problemModifyDTO, Principal principal); + void modifyStatus(String problemId, Principal principal); void deleteProblem(String problemId, Principal principal); } diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java index b0fbb6b4..3827d556 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java @@ -31,7 +31,7 @@ public class Problem { @Column(name = "PROB_TTL", nullable = false) private String title; - @Column(name = "PROB_CONT", nullable = false) + @Column(name = "PROB_CONT",columnDefinition = "TEXT",nullable = false) private String content; @Column(name = "CREATED_AT", nullable = false, updatable = false, length=19) @@ -55,6 +55,9 @@ public class Problem { @Column(name = "PROD_ID", nullable = false) private String productId; + @Column(name ="PROB_STATUS",nullable = false) + private String status = "PROGRESS"; + @Column(name = "FILE_URL") private String fileUrl; diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java index 837e68a8..03da74eb 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java @@ -73,7 +73,10 @@ public ProblemModifyDTO modifyProblem(String problemId, ProblemModifyDTO problem try { Problem updateProblem = modelMapper.map(problemModifyDTO, Problem.class); updateProblem.setProblemId(problem.getProblemId()); + updateProblem.setProblemId(problem.getTitle()); + updateProblem.setProblemId(problem.getContent()); updateProblem.setMemberId(problem.getMemberId()); + updateProblem.setStatus("DONE"); updateProblem.setCreatedAt(problem.getCreatedAt()); updateProblem.setActive(problem.getActive()); updateProblem.setCustomerId(problem.getCustomerId()); @@ -93,6 +96,29 @@ public ProblemModifyDTO modifyProblem(String problemId, ProblemModifyDTO problem } } + + @Transactional + @Override + public void modifyStatus(String problemId,Principal principal) { + redisService.clearProblemCache(); + String memberId= principal.getName(); + Problem problem = problemRepository.findById(problemId) + .orElseThrow(() -> new ProblemCommonException(ProblemErrorCode.PROBLEM_NOT_FOUND)); + if(!problem.getMemberId().equals(memberId)){ + throw new ProblemCommonException(ProblemErrorCode.AUTHORIZATION_VIOLATION); + } + try { + problem.setStatus("DONE"); + problem.setUpdatedAt(getCurrentTimestamp()); + } catch (DataIntegrityViolationException e) { + // 데이터 무결성 위반 예외 처리 + throw new ProblemCommonException(ProblemErrorCode.DATA_INTEGRITY_VIOLATION); + } catch (Exception e) { + // 서버 오류 + throw new ProblemCommonException(ProblemErrorCode.INTERNAL_SERVER_ERROR); + } + } + @Transactional @Override public void deleteProblem(String problemId, Principal principal) { diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemDTO.java b/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemDTO.java index 34aeaacc..5875e433 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemDTO.java @@ -25,6 +25,8 @@ public class ProblemDTO { private Boolean active; + private String status; + private String customerId; private String memberId; diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemExcelDownload.java b/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemExcelDownload.java index 25a3571b..a7c3bd99 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemExcelDownload.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemExcelDownload.java @@ -27,4 +27,7 @@ public class ProblemExcelDownload { @ExcelColumnName(name = "제품명") private String productId; + + @ExcelColumnName(name = "상태") + private String status; } diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemSearchDTO.java index 99bf78c1..5dde1f81 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/dto/ProblemSearchDTO.java @@ -21,6 +21,7 @@ public class ProblemSearchDTO { private String deletedAt; private String startDate; private String endDate; + private String status; public ProblemSearchDTO(String title, String memberId, String productId, String customerId, String startDate, String endDate) { this.title = title; diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java index e7aeadf3..724d120c 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java @@ -39,7 +39,10 @@ public ProblemServiceImpl(ProblemMapper problemMapper, RedisTemplate findProblems(Pageable pageable, ProblemSearchDTO problemSearchDTO) { int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); - String cacheKey = "ProblemCache::offset=" + offset + "::size=" + size + problemSearchDTO.getTitle(); + String cacheKey = "ProblemCache::offset=" + offset + "::size=" + size +"::title="+problemSearchDTO.getTitle() + + "::memberId="+problemSearchDTO.getMemberId()+"::productId="+problemSearchDTO.getProductId()+ + "::customerId="+problemSearchDTO.getCustomerId()+"::startDate"+problemSearchDTO.getStartDate()+ + "::endDate"+problemSearchDTO.getEndDate(); // Redis에서 데이터 조회 List problems = (List) redisTemplate.opsForValue().get(cacheKey); diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/entity/Promotion.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/entity/Promotion.java index 560c8491..1080a460 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/entity/Promotion.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/entity/Promotion.java @@ -31,7 +31,7 @@ public class Promotion { @Column(name = "PRM_TTL", nullable = false) private String title; - @Column(name = "PRM_CONT", nullable = false) + @Column(name = "PRM_CONT",columnDefinition = "TEXT", nullable = false) private String content; @Column(name = "CREATED_AT", nullable = false, updatable = false, length=19) diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/controller/PromotionController.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/controller/PromotionController.java index 935d95e1..0f872f6f 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/query/controller/PromotionController.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/controller/PromotionController.java @@ -65,7 +65,7 @@ public ResponseEntity getPromotion(@PathVariable String promotionI content = {@Content(schema = @Schema(implementation = PromotionResponseMessage.class))}) }) @GetMapping("/excel") - public void exportNotice(HttpServletResponse response){ + public void exportPromotion(HttpServletResponse response){ promotionService.exportPromotionToExcel(response); } diff --git a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java index 1fd3416f..e53dc305 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java @@ -111,6 +111,7 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception .requestMatchers(HttpMethod.GET, "/api/v1/problem/{problemId}").hasAnyRole("problem-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 문제사항 Id로 조회 (전체) .requestMatchers(HttpMethod.GET, "/api/v1/problem").hasAnyRole("problem-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 문제사항 조건별 조회 (전체) .requestMatchers(HttpMethod.PUT, "/api/v1/problem/{problemId}").hasAnyRole("problem-update", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 문제사항 수정 (전체) + .requestMatchers(HttpMethod.PUT, "/api/v1/problem/status/{problemId}").hasAnyRole("problem-status-update", "GOD") // 문제사항 상태 수정 (전체) .requestMatchers(HttpMethod.POST, "/api/v1/problem").hasAnyRole("problem-create", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 문제사항 작성 (전체) // Product API diff --git a/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml b/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml index a5a01c9e..7ed2da30 100644 --- a/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/problem/query/repository/ProblemMapper.xml @@ -12,6 +12,7 @@ + @@ -25,6 +26,7 @@ + @@ -38,6 +40,7 @@ a.created_at, a.updated_at, a.deleted_at, + a.prob_status, a.cst_id, a.mem_id, a.prod_id, @@ -97,6 +100,7 @@ a.created_at, a.updated_at, a.deleted_at, + a.prob_status, a.active, a.cst_id, a.mem_id, @@ -112,6 +116,7 @@ + @@ -122,6 +127,7 @@ a.prob_cont, a.created_at, a.updated_at, + a.prob_status, a.cst_id, a.mem_id, a.prod_id From be680cbcdb83f2f8aa9c4f8ec0109443c0d79809 Mon Sep 17 00:00:00 2001 From: giuseog Date: Fri, 29 Nov 2024 22:58:01 +0900 Subject: [PATCH 378/563] =?UTF-8?q?feat:=20=EC=A0=84=EC=B2=B4=20=ED=86=B5?= =?UTF-8?q?=EA=B3=84=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=B6=94=EA=B0=80(#1?= =?UTF-8?q?83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SalesHistoryController.java | 62 +++++++++++++ .../query/repository/SalesHistoryMapper.java | 20 +++- .../service/SalesHistoryQueryService.java | 6 ++ .../service/SalesHistoryQueryServiceImpl.java | 63 +++++++++++++ .../query/repository/SalesHistoryMapper.xml | 91 +++++++++++++++++++ 5 files changed, 238 insertions(+), 4 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java index 3d2abd29..561bd361 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java @@ -440,4 +440,66 @@ public void exporSalesHistory(HttpServletResponse response){ salesHistoryQueryService.exportSalesHistoryToExcel(response); } + @Operation(summary = "전체 통계(실적,수당,매출액) 월별 검색(전체)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @PostMapping("statistics/all/month") + public ResponseEntity getAllStatisticsByMonth(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, + @PageableDefault(size = 20) Pageable pageable){ + + + Page responseSalesHistory = salesHistoryQueryService.selectAllStatisticsByMonth(salesHistoryRankedDataDTO, pageable); + + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("전체 통계(실적,수당,매출액) 월별 검색(전체) 성공") + .result(responseSalesHistory) + .build()); + } + + @Operation(summary = "전체 통계(실적,수당,매출액) 연도 별 검색(전체)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @PostMapping("statistics/all/year") + public ResponseEntity getAllStatisticsByYear(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, + @PageableDefault(size = 20) Pageable pageable){ + + + + Page responseSalesHistory = salesHistoryQueryService.selectAllStatisticsByYear(salesHistoryRankedDataDTO, pageable); + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("전체 통계(실적,수당,매출액) 연도 별 검색(전체)") + .result(responseSalesHistory) + .build()); + } + + @Operation(summary = "전체 통계(실적,수당,매출액) 조회기간 별 검색(전체)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @PostMapping("statistics/all") + public ResponseEntity getAllStatisticsBySearch(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, + @PageableDefault(size = 20) Pageable pageable){ + + + + Page responseSalesHistory = salesHistoryQueryService.selectAllStatisticsBySearch(salesHistoryRankedDataDTO, pageable); + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("전체 통계(실적,수당,매출액) 조회기간 별 검색(전체)") + .result(responseSalesHistory) + .build()); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java index 08e1a0a6..ba6d691c 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java @@ -49,25 +49,25 @@ List findStatisticsCenterBySearch(@Param("size") int int findStatisticsCenterBySearchCount(@Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); List findStatisticsBySearchMonth(@Param("size") int size - , @Param("offset") int offset, + , @Param("offset") int offset, @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); int findStatisticsBySearchCountMonth(@Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); List findStatisticsBySearchYear(@Param("size") int size - , @Param("offset") int offset, + , @Param("offset") int offset, @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); int findStatisticsBySearchCountYear(@Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); List findStatisticsCenterBySearchMonth(@Param("size") int size - , @Param("offset") int offset, + , @Param("offset") int offset, @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); int findStatisticsCenterBySearchCountMonth(@Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); List findStatisticsCenterBySearchYear(@Param("size") int size - , @Param("offset") int offset, + , @Param("offset") int offset, @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); int findStatisticsCenterBySearchCountYear(@Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); @@ -87,6 +87,18 @@ List findSalesHistoryBySearch(@Param("size") int size SalesHistoryStatisticsAverageDTO findStatisticsAverageBySearch(@Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); List findSalesHistoryForExcel(); + + List findAllStatisticsByMonth(@Param("size") int size + , @Param("offset") int offset, + @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); + + List findAllStatisticsByYear(@Param("size") int size + , @Param("offset") int offset, + @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); + + List findAllStatisticsBySearch(@Param("size") int size + , @Param("offset") int offset, + @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java index 6de5f1a4..f482d80b 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java @@ -43,4 +43,10 @@ public interface SalesHistoryQueryService { SalesHistoryStatisticsAverageDTO selectStatisticsAverageBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); void exportSalesHistoryToExcel(HttpServletResponse response); + + Page selectAllStatisticsByMonth(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); + + Page selectAllStatisticsByYear(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); + + Page selectAllStatisticsBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java index 428b2fe1..4d82f07e 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java @@ -469,4 +469,67 @@ public void exportSalesHistoryToExcel(HttpServletResponse response) { excelUtilsV1.download(SalesHistoryExcelDownload.class, salesHistoryList, "SalesHistoryExcel", response); } + + @Override + @Transactional + public Page selectAllStatisticsByMonth(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); + + String parseStartDate = salesHistoryRankedDataDTO.getStartDate(); + String parseEndDate = salesHistoryRankedDataDTO.getEndDate(); + + parseStartDate = parseStartDate.substring(0,7); + parseEndDate = parseStartDate.substring(0,7); + + List salesHistoryList = salesHistoryMapper.findAllStatisticsByMonth(size,offset, salesHistoryRankedDataDTO); + + int total = salesHistoryMapper.findStatisticsBySearchCountMonth(salesHistoryRankedDataDTO); + + if(salesHistoryList.isEmpty() || total == 0){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } + + return new PageImpl<>(salesHistoryList, pageable, total); + } + + @Override + @Transactional + public Page selectAllStatisticsByYear(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); + + String parseStartDate = salesHistoryRankedDataDTO.getStartDate(); + String parseEndDate = salesHistoryRankedDataDTO.getEndDate(); + + parseStartDate = parseStartDate.substring(0,4); + parseEndDate = parseStartDate.substring(0,4); + + List salesHistoryList = salesHistoryMapper.findAllStatisticsByYear(size,offset, salesHistoryRankedDataDTO); + + int total = salesHistoryMapper.findStatisticsBySearchCountMonth(salesHistoryRankedDataDTO); + + if(salesHistoryList.isEmpty() || total == 0){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } + + return new PageImpl<>(salesHistoryList, pageable, total); + } + + @Override + @Transactional + public Page selectAllStatisticsBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); + + List salesHistoryList = salesHistoryMapper.findAllStatisticsBySearch(size,offset, salesHistoryRankedDataDTO); + + int total = salesHistoryMapper.findStatisticsBySearchCountMonth(salesHistoryRankedDataDTO); + + if(salesHistoryList.isEmpty() || total == 0){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } + + return new PageImpl<>(salesHistoryList, pageable, total); + } } diff --git a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml index 11845c26..dbcea431 100644 --- a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml @@ -752,5 +752,96 @@ GROUP BY a.mem_id, LEFT(a.created_at, 4)) AS A + + + + + From d243c8546d9fa5b68e476f23d515cfe907cdfb6a Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Sat, 30 Nov 2024 03:11:49 +0900 Subject: [PATCH 379/563] =?UTF-8?q?refactor:=20=EA=B3=84=EC=95=BD=EC=84=9C?= =?UTF-8?q?=20=EA=B2=80=EC=83=89=EC=A1=B0=EA=B1=B4=20=EB=B0=8F=20=ED=8C=90?= =?UTF-8?q?=EB=A7=A4=EB=82=B4=EC=97=AD=20=EC=88=98=EC=A0=95=20(#188)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/controller/ContractController.java | 3 +-- .../domain/service/ContractCommandServiceImpl.java | 2 -- .../contract/query/repository/ContractMapper.java | 2 +- .../query/service/ContractQueryServiceImpl.java | 13 +++---------- .../service/SalesHistoryCommandServiceImpl.java | 12 +++++++++++- .../contract/query/repository/ContractMapper.xml | 7 +++++-- 6 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java index 354fa105..ca585e83 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java @@ -58,7 +58,7 @@ public ResponseEntity postContract(@RequestBody Contrac public ResponseEntity putContract(@PathVariable String contractId, @RequestBody ContractModifyDTO contractModifyRequestDTO, Principal principal) throws GeneralSecurityException { - log.info("수정임: " + contractId); + contractModifyRequestDTO.setContractId(contractId); contractModifyRequestDTO.setMemberId(principal.getName()); @@ -101,7 +101,6 @@ public ResponseEntity deleteContract(@PathVariable Stri public ResponseEntity putContractStatus(@PathVariable String contractId, @RequestBody ContractStatusModifyDTO contractStatusModifyDTO, Principal principal) { - log.info("putContractStatus: " + contractStatusModifyDTO.getStatus()); // DTO에 설정 contractStatusModifyDTO.setContractId(contractId); diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index 2a9b78bc..ab977674 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -245,8 +245,6 @@ public void modifyContractStatus(ContractStatusModifyDTO contractStatusModifyDTO // 계약 조회 및 수정 Contract contract = contractRepository.findByContractId(contractStatusModifyDTO.getContractId()); - log.info("Contract : " + contract); - if (contract == null) { throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java index 32f25733..127a6f70 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java @@ -43,7 +43,7 @@ List findContractBySearch(@Param("offset") int offset, @Param("sortField") String sortField, @Param("sortOrder") String sortOrder); - int findContractBySearchCount(ContractSearchDTO contractSearchDTO); + int findContractBySearchCount(@Param("contractSearchDTO") ContractSearchDTO contractSearchDTO); List findContractAllByCenterId(@Param("offset") int offset, @Param("pageSize") int pageSize, diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java index d5f90dae..ff42af70 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java @@ -270,9 +270,6 @@ public Page selectBySearch(ContractSearchDTO contractSearchDT sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; } - log.info("service Field: " + sortField); - log.info("service order: " + sortOrder); - List contracts = contractMapper.findContractBySearch(offset, pageSize, contractSearchDTO, sortField, sortOrder); if (contracts == null) { @@ -281,17 +278,13 @@ public Page selectBySearch(ContractSearchDTO contractSearchDT log.info("값들어가는지 확인: " + contracts); -// int count = contractMapper.findContractBySearchCount(contractSearchDTO); - int count = 1; -// int totalContract = contractMapper.findContractBySearchCount(contractSearchDTO); + int totalContract = contractMapper.findContractBySearchCount(contractSearchDTO); - if (count == 0) { + if (totalContract == 0) { throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); } - log.info("count: " + count); - - return new PageImpl<>(contracts, pageable, count); + return new PageImpl<>(contracts, pageable, totalContract); } @Override diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/service/SalesHistoryCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/service/SalesHistoryCommandServiceImpl.java index 1c126fac..cc0c28f9 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/service/SalesHistoryCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/service/SalesHistoryCommandServiceImpl.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.sales_history.command.domain.service; +import lombok.extern.slf4j.Slf4j; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -17,17 +18,20 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +@Slf4j @Service @Transactional(readOnly = true) public class SalesHistoryCommandServiceImpl implements SalesHistoryCommandService { private final SalesHistoryRepository salesHistoryRepository; private final ContractQueryService contractQueryService; + private final ModelMapper modelMapper; @Autowired - public SalesHistoryCommandServiceImpl(SalesHistoryRepository salesHistoryRepository,ContractQueryService contractQueryService) { + public SalesHistoryCommandServiceImpl(SalesHistoryRepository salesHistoryRepository, ContractQueryService contractQueryService, ModelMapper modelMapper) { this.salesHistoryRepository = salesHistoryRepository; this.contractQueryService = contractQueryService; + this.modelMapper = modelMapper; } private String getCurrentTime() { @@ -38,6 +42,8 @@ private String getCurrentTime() { @Override @Transactional public void registerSalesHistory(String contractId) { + + log.info("판매실적: " + contractId); ContractSeletIdDTO salesHistoryDTO = new ContractSeletIdDTO(); SalesHistoryRegistDTO salesHistoryRegistDTO = new SalesHistoryRegistDTO(); @@ -66,6 +72,10 @@ public void registerSalesHistory(String contractId) { }else if(customerPurchaseCondition.equals("LEASE")){ salesHistoryRegistDTO.setIncentive(responseContract.getTotalSales() * 0.045); } + + SalesHistory salesHistory = modelMapper.map(salesHistoryRegistDTO, SalesHistory.class); + + salesHistoryRepository.save(salesHistory); } @Override diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index 4e0e71fc..c0f6d761 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -498,8 +498,11 @@ AND a.conr_cust_pur_cond LIKE CONCAT('%', #{contractSearchDTO.customerPurchaseCondition}, '%') - - AND a.created_at BETWEEN #{contractSearchDTO.startAt} AND #{contractSearchDTO.endAt} + + AND a.created_at >= #{contractSearchDTO.startAt} + + + AND a.created_at <= #{contractSearchDTO.endAt} AND a.conr_car_name LIKE CONCAT('%', #{contractSearchDTO.carName}, '%') From 54d4c66c24174e740b5a96b2ca1176b51cbdfbf3 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Sat, 30 Nov 2024 03:17:30 +0900 Subject: [PATCH 380/563] =?UTF-8?q?refactor:=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=A7=80=EC=9A=B0=EA=B8=B0=20(#188)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/application/controller/ContractController.java | 2 +- .../contract/command/domain/aggregate/entity/Contract.java | 1 - .../domain/contract/query/controller/ContractController.java | 1 - .../domain/contract/query/dto/ContractSearchDTO.java | 1 - .../domain/contract/query/service/ContractQueryServiceImpl.java | 2 -- .../command/domain/service/SalesHistoryCommandServiceImpl.java | 1 - 6 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java index ca585e83..5d24b4bd 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java @@ -47,7 +47,7 @@ public ResponseEntity postContract(@RequestBody Contrac .msg("계약서가 성공적으로 등록되었습니다.") .result(null) .build()); - } + } @Operation(summary = "계약서 수정(영업사원, 관리자)") @ApiResponses(value = { diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java index 240a08d3..47b96f79 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java @@ -16,7 +16,6 @@ @Getter @Setter @Entity -@ToString @Table(name = "TB_CONTRACT") public class Contract { diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java index 77b7bbac..bfdd10e9 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java @@ -294,7 +294,6 @@ public ResponseEntity getContractBySearch(@RequestParam @RequestParam(required = false) String sortOrder, @PageableDefault(size = 10) Pageable pageable) { - log.info("검색조건: " + sortField + "ddd: " + sortOrder); // 정렬 추가 if (sortField != null && sortOrder != null) { Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java index 939717b8..0f441a25 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java @@ -6,7 +6,6 @@ @NoArgsConstructor @Getter @Setter -@ToString public class ContractSearchDTO { private String contractId; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java index ff42af70..4b07b0f0 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java @@ -276,8 +276,6 @@ public Page selectBySearch(ContractSearchDTO contractSearchDT throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); } - log.info("값들어가는지 확인: " + contracts); - int totalContract = contractMapper.findContractBySearchCount(contractSearchDTO); if (totalContract == 0) { diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/service/SalesHistoryCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/service/SalesHistoryCommandServiceImpl.java index cc0c28f9..6f99cbe2 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/service/SalesHistoryCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/command/domain/service/SalesHistoryCommandServiceImpl.java @@ -43,7 +43,6 @@ private String getCurrentTime() { @Transactional public void registerSalesHistory(String contractId) { - log.info("판매실적: " + contractId); ContractSeletIdDTO salesHistoryDTO = new ContractSeletIdDTO(); SalesHistoryRegistDTO salesHistoryRegistDTO = new SalesHistoryRegistDTO(); From 8bda63fac2b9a5ef0d0929a8780906fd6df1b660 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Sat, 30 Nov 2024 18:15:01 +0900 Subject: [PATCH 381/563] =?UTF-8?q?fix:=20=EB=A1=9C=EA=B7=B8=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=88=98=EC=A0=95(#160)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/log/query/controller/LogController.java | 3 ++- .../final_backend/domain/log/query/dto/LogSearchDTO.java | 5 ++++- .../final_backend/domain/log/query/repository/LogMapper.xml | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java b/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java index 2184edf5..5f609c3d 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java @@ -41,6 +41,7 @@ public LogController(LogQueryService logQueryService) { public ResponseEntity getLogs( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, + @RequestParam(required = false) String logId, @RequestParam(required = false) String ipAddress, @RequestParam(required = false) String requestTime, @RequestParam(required = false) String status, @@ -49,7 +50,7 @@ public ResponseEntity getLogs( ){ Pageable pageable = PageRequest.of(page, size); - LogSearchDTO searchLogDTO = new LogSearchDTO(ipAddress, requestTime, status, method, uri); + LogSearchDTO searchLogDTO = new LogSearchDTO(logId, ipAddress, requestTime, status, method, uri); Page logDTOPage = logQueryService.selectLogs(pageable, searchLogDTO); return ResponseEntity.ok(LogResponseMessage.builder() diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogSearchDTO.java index 830d2f0a..a35acc81 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogSearchDTO.java @@ -10,6 +10,7 @@ @Getter @Setter public class LogSearchDTO { + private String logId; private String sessionId; private String userAgent; private String ipAddress; @@ -23,11 +24,13 @@ public class LogSearchDTO { private String status; private String errorMessage; - public LogSearchDTO(String ipAddress, + public LogSearchDTO(String logId, + String ipAddress, String requestTime, String status, String method, String uri){ + this.logId = logId; this.ipAddress = ipAddress; this.requestTime = requestTime; this.status = status; diff --git a/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml b/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml index 97f6a679..5355ca77 100644 --- a/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml @@ -43,6 +43,9 @@ a.user_agent FROM tb_log a + + AND a.log_id LIKE CONCAT('%', #{ searchLogDTO.logId }, '%') + AND a.ip_address LIKE CONCAT('%', #{ searchLogDTO.ipAddress }, '%') From dcf569ae44eb887bfde82f3ae6b763dd79d45b70 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Sat, 30 Nov 2024 22:47:44 +0900 Subject: [PATCH 382/563] =?UTF-8?q?feat:=20DB=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=84=A4=EB=AA=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/notices/query/controller/NoticeController.java | 6 ++++++ .../domain/notices/query/service/NoticeServiceImpl.java | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java index b48e4321..16ba5d78 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java @@ -24,6 +24,12 @@ import static org.springframework.transaction.support.TransactionSynchronizationManager.isCurrentTransactionReadOnly; +/* 설명. Service단 설정이 완료되었다면 + String dbUrl = getCurrentDbUrl(); + System.out.println("Current DB URL: " + dbUrl); + 를 통해 현재 접근하고 있는 DB를 조회할 경우와 DB의 값을 등록,수정,삭제할 경우의 + url 주소를 비교해 본다. +*/ @RestController("queryNoticeController") @RequestMapping("/api/v1/notice") public class NoticeController { diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java index aaa9084b..bd17b013 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java @@ -18,6 +18,12 @@ import java.util.List; import static org.springframework.transaction.support.TransactionSynchronizationManager.isCurrentTransactionReadOnly; +/* 설명. 조회의 경우 readOnly= true 를 설정해 readOnlyDB에 접근을 하도록 만든다. + "Transaction ReadOnly: " + isCurrentTransactionReadOnly();를 통해 + 현재의 트랜젝션이 ReadOnly 상태인지 확인할 수 있다. + (readOnly=false)나 default로 설정된 메소드를 호출할 경우 readOnly설정이 풀릴 수도 있음 + 이런 경우, Controller의 api에 (readOnly=true)로 설정해 본다. +*/ @Transactional(readOnly = true) @Service("queryNoticeServiceImpl") From c9946a0f5d065bf8f2c1d123295bcc4fa58685c2 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Sat, 30 Nov 2024 23:07:14 +0900 Subject: [PATCH 383/563] =?UTF-8?q?feat:=20DB=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=84=A4=EB=AA=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/NoticeController.java | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java index 9b576b2d..fca4beb2 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java @@ -16,7 +16,9 @@ import stanl_2.final_backend.domain.notices.command.application.service.NoticeCommandService; import stanl_2.final_backend.domain.notices.common.response.NoticeResponseMessage; import stanl_2.final_backend.domain.s3.command.domain.service.S3FileServiceImpl; +import stanl_2.final_backend.global.config.datasource.ReplicationRoutingDataSource; +import javax.sql.DataSource; import java.security.Principal; @RestController("commandNoticeController") @@ -25,12 +27,13 @@ public class NoticeController { private final NoticeCommandService noticeCommandService; private final AuthQueryService authQueryService; - + private final ReplicationRoutingDataSource replicationRoutingDataSource; private final S3FileServiceImpl s3FileService; @Autowired - public NoticeController(NoticeCommandService noticeCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService){ + public NoticeController(NoticeCommandService noticeCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService,ReplicationRoutingDataSource replicationRoutingDataSource){ this.noticeCommandService = noticeCommandService; + this.replicationRoutingDataSource =replicationRoutingDataSource; this.authQueryService =authQueryService; this.s3FileService = s3FileService; } @@ -41,13 +44,15 @@ public NoticeController(NoticeCommandService noticeCommandService, AuthQueryServ content = {@Content(schema = @Schema(implementation = NoticeResponseMessage.class))}) }) @PostMapping(value = "") - public ResponseEntity postNotice(@RequestPart("notice") NoticeRegistDTO noticeRegistDTO, // JSON 데이터 + public ResponseEntity postNotice(@RequestPart("dto") NoticeRegistDTO noticeRegistDTO, // JSON 데이터 @RequestPart("file") MultipartFile file, Principal principal){ String memberId =authQueryService.selectMemberIdByLoginId(principal.getName()); noticeRegistDTO.setMemberId(memberId); noticeRegistDTO.setFileUrl(s3FileService.uploadOneFile(file)); noticeCommandService.registerNotice(noticeRegistDTO, principal); + String dbUrl = getCurrentDbUrl(); + System.out.println("Current DB URL: " + dbUrl); return ResponseEntity.ok(NoticeResponseMessage.builder() .httpStatus(200) .msg("성공") @@ -100,4 +105,18 @@ public ResponseEntity deleteNotice(Principal principal, .build()); } + private String getCurrentDbUrl() { + try { + // DataSource에서 커넥션을 가져와 URL 확인 + DataSource dataSource = replicationRoutingDataSource; + return dataSource.unwrap(javax.sql.DataSource.class) + .getConnection() + .getMetaData() + .getURL(); + } catch (Exception e) { + e.printStackTrace(); + return "Failed to fetch DB URL"; + } + } + } From 82a68dfb7c43dec8952f716354872bc1e935b6e5 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Sat, 30 Nov 2024 23:31:51 +0900 Subject: [PATCH 384/563] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EC=A1=B0=ED=9A=8C=20=EB=AA=A8=EB=8B=AC=EC=B0=BD=20?= =?UTF-8?q?=EB=94=94=EC=9E=90=EC=9D=B8=20=EC=B4=88=EC=95=88=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20(#140)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/AlarmController.java | 90 ++++++++++++------- .../alarm/query/dto/AlarmSelectDTO.java | 20 +++++ .../alarm/query/repository/AlarmMapper.java | 6 ++ .../query/service/AlarmQueryService.java | 3 + .../query/service/AlarmQueryServiceImpl.java | 19 ++++ .../alarm/scheduler/AlarmScheduler.java | 3 +- .../security/config/RequestMatcherConfig.java | 1 + .../alarm/query/repository/AlarmMapper.xml | 10 +++ 8 files changed, 119 insertions(+), 33 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/controller/AlarmController.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/controller/AlarmController.java index ed367928..eab418c8 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/controller/AlarmController.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/controller/AlarmController.java @@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import stanl_2.final_backend.domain.alarm.common.response.AlarmResponseMessage; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectDTO; import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectReadDTO; import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectTypeDTO; import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectUnreadDTO; @@ -53,53 +54,78 @@ public ResponseEntity selectMemberAlarmType(Principal prin .build()); } - @Operation(summary = "회원 읽은 알림 상세 조회") + @Operation(summary = "회원 알림 상세 조회") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "회원 읽은 알림 상세 조회 완료", + @ApiResponse(responseCode = "200", description = "회원 알림 상세 조회 완료", content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) }) - @GetMapping("/read/{type}") + @GetMapping("/{type}") public ResponseEntity selectReadAlarm(Principal principal, @PathVariable String type, @PageableDefault(size = 8) Pageable pageable){ String memberLoginId = principal.getName(); - AlarmSelectReadDTO alarmSelectReadDTO = new AlarmSelectReadDTO(); - alarmSelectReadDTO.setMemberLoginId(memberLoginId); - alarmSelectReadDTO.setType(type); + AlarmSelectDTO alarmSelectDTO = new AlarmSelectDTO(); + alarmSelectDTO.setMemberLoginId(memberLoginId); + alarmSelectDTO.setType(type); - Page allReadAlarms - = alarmQueryService.selectReadAlarmByType(alarmSelectReadDTO , pageable); + Page allAlarms + = alarmQueryService.selectAlarmByType(alarmSelectDTO , pageable); return ResponseEntity.ok(AlarmResponseMessage.builder() .httpStatus(200) .msg("성공") - .result(allReadAlarms) + .result(allAlarms) .build()); } - @Operation(summary = "회원 읽지 않은 알림 상세 조회") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "회원 읽은 알림 상세 조회 완료", - content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) - }) - @GetMapping("/unread/{type}") - public ResponseEntity selectUnreadAlarm(Principal principal, - @PathVariable String type, - @PageableDefault(size = 8) Pageable pageable){ - String memberLoginId = principal.getName(); - AlarmSelectUnreadDTO alarmSelectUnreadDTO = new AlarmSelectUnreadDTO(); - alarmSelectUnreadDTO.setMemberLoginId(memberLoginId); - alarmSelectUnreadDTO.setType(type); - - Page allUnreadAlarms - = alarmQueryService.selectUnreadAlarmByType(alarmSelectUnreadDTO, pageable); - - return ResponseEntity.ok(AlarmResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(allUnreadAlarms) - .build()); - } +// @Operation(summary = "회원 읽은 알림 상세 조회") +// @ApiResponses(value = { +// @ApiResponse(responseCode = "200", description = "회원 읽은 알림 상세 조회 완료", +// content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) +// }) +// @GetMapping("/read/{type}") +// public ResponseEntity selectReadAlarm(Principal principal, +// @PathVariable String type, +// @PageableDefault(size = 8) Pageable pageable){ +// +// String memberLoginId = principal.getName(); +// AlarmSelectReadDTO alarmSelectReadDTO = new AlarmSelectReadDTO(); +// alarmSelectReadDTO.setMemberLoginId(memberLoginId); +// alarmSelectReadDTO.setType(type); +// +// Page allReadAlarms +// = alarmQueryService.selectReadAlarmByType(alarmSelectReadDTO , pageable); +// +// return ResponseEntity.ok(AlarmResponseMessage.builder() +// .httpStatus(200) +// .msg("성공") +// .result(allReadAlarms) +// .build()); +// } + +// @Operation(summary = "회원 읽지 않은 알림 상세 조회") +// @ApiResponses(value = { +// @ApiResponse(responseCode = "200", description = "회원 읽은 알림 상세 조회 완료", +// content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) +// }) +// @GetMapping("/unread/{type}") +// public ResponseEntity selectUnreadAlarm(Principal principal, +// @PathVariable String type, +// @PageableDefault(size = 8) Pageable pageable){ +// String memberLoginId = principal.getName(); +// AlarmSelectUnreadDTO alarmSelectUnreadDTO = new AlarmSelectUnreadDTO(); +// alarmSelectUnreadDTO.setMemberLoginId(memberLoginId); +// alarmSelectUnreadDTO.setType(type); +// +// Page allUnreadAlarms +// = alarmQueryService.selectUnreadAlarmByType(alarmSelectUnreadDTO, pageable); +// +// return ResponseEntity.ok(AlarmResponseMessage.builder() +// .httpStatus(200) +// .msg("성공") +// .result(allUnreadAlarms) +// .build()); +// } } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java new file mode 100644 index 00000000..5e907885 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java @@ -0,0 +1,20 @@ +package stanl_2.final_backend.domain.alarm.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class AlarmSelectDTO { + + private String message; + private String type; + private String redirectUrl; + private Boolean readStatus; + + private String memberLoginId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.java index 7762738d..7869bffc 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.java @@ -2,6 +2,7 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectDTO; import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectUnreadDTO; import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectReadDTO; import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectTypeDTO; @@ -22,6 +23,11 @@ List findUnReadAlarmsByType(@Param("offset") Integer offse @Param("memberId") String memberId, @Param("type") String type); + List findAllAlarmsByType(@Param("offset") Integer offset, + @Param("pageSize") Integer pageSize, + @Param("memberId") String memberId, + @Param("type") String type); + Integer findReadAlarmsCountByMemberId(String memberId); Integer findUnreadAlarmsCountByMemberId(String memberId); diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryService.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryService.java index 8818ae12..ab46f4ea 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryService.java @@ -2,6 +2,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectDTO; import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectReadDTO; import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectTypeDTO; import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectUnreadDTO; @@ -12,4 +13,6 @@ public interface AlarmQueryService { Page selectReadAlarmByType(AlarmSelectReadDTO alarmSelectReadDTO, Pageable pageable); Page selectUnreadAlarmByType(AlarmSelectUnreadDTO alarmSelectUnreadDTO, Pageable pageable); + + Page selectAlarmByType(AlarmSelectDTO alarmSelectDTO, Pageable pageable); } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryServiceImpl.java index 4fad874f..32c3a413 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryServiceImpl.java @@ -9,6 +9,7 @@ import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.alarm.common.exception.AlarmCommonException; import stanl_2.final_backend.domain.alarm.common.exception.AlarmErrorCode; +import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectDTO; import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectReadDTO; import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectTypeDTO; import stanl_2.final_backend.domain.alarm.query.dto.AlarmSelectUnreadDTO; @@ -76,4 +77,22 @@ public Page selectUnreadAlarmByType(AlarmSelectUnreadDTO a return new PageImpl<>(unReadAlarmList, pageable, totalOrder); } + + @Override + public Page selectAlarmByType(AlarmSelectDTO alarmSelectDTO, Pageable pageable) { + + Integer offset = Math.toIntExact(pageable.getOffset()); + Integer pageSize = pageable.getPageSize(); + + String memberId = authQueryService.selectMemberIdByLoginId(alarmSelectDTO.getMemberLoginId()); + + List readAlarmList + = alarmMapper.findAllAlarmsByType(offset, pageSize, memberId, alarmSelectDTO.getType()); + + Integer count = alarmMapper.findReadAlarmsCountByMemberId(memberId); + int totalOrder = (count != null) ? count : 0; + + return new PageImpl<>(readAlarmList, pageable, totalOrder); + + } } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java index 21d1518d..1fe3b13a 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java @@ -30,7 +30,8 @@ public AlarmScheduler(AlarmCommandService alarmCommandService, ScheduleQueryServ this.scheduleQueryService = scheduleQueryService; } - @Scheduled(cron = "0 0 2 * * *") // 매일 새벽 2시에 실행) +// @Scheduled(cron = "0 0 2 * * *") // 매일 새벽 2시에 실행) + @Scheduled(cron = "0 8 22 * * *") @Transactional public void alarmTodaySchedule(){ diff --git a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java index 1fd3416f..168571a9 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java @@ -20,6 +20,7 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception // Alarm API .requestMatchers(HttpMethod.GET, "/api/v1/alarm").hasAnyRole("alarm-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 회원 알림창 전체 조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/alarm/**").hasAnyRole("alarm-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 회원 태그별 알림창 전체 조회 (전체) .requestMatchers(HttpMethod.GET, "/api/v1/alarm/read/**").hasAnyRole("alarm-read-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 회원 읽은 알림 상세 조회 (전체) .requestMatchers(HttpMethod.GET, "/api/v1/alarm/unread/**").hasAnyRole("alarm-unread-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 회원 읽지 않은 알림 상세 조회 (전체) .requestMatchers(HttpMethod.GET, "/api/v1/alarm/connect").hasAnyRole("alarm-connect-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // sse 연결 (전체) diff --git a/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml b/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml index 1b9bf3ea..1edaf1c8 100644 --- a/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml @@ -46,6 +46,16 @@ AND a.alr_type = #{type} AND a.alr_read_stat = false + - + SELECT - a.cust_id, - a.cust_name, - a.cust_age, - a.cust_sex, - a.cust_pho, - a.cust_emer_pho, - a.cust_ema, - a.mem_id - FROM tb_customer_info a + a.cust_id, + a.cust_name, + a.cust_age, + a.cust_sex, + a.cust_pho, + a.cust_emer_pho, + a.cust_ema, + a.mem_id + FROM tb_customer_info a - - \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml b/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml index 5355ca77..dd6fc093 100644 --- a/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml @@ -17,7 +17,21 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file From db039a5d25fccc2f08659a87304adbc1c478c418 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Sun, 1 Dec 2024 02:40:22 +0900 Subject: [PATCH 386/563] =?UTF-8?q?fix:=20=EB=A1=9C=EA=B7=B8=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EB=82=A0=EC=A7=9C=20=ED=95=B4=EA=B2=B0(#133)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log/query/controller/LogController.java | 5 +-- .../domain/log/query/dto/LogSearchDTO.java | 9 ++++-- .../query/service/LogQueryServiceImpl.java | 9 +++++- .../domain/log/query/repository/LogMapper.xml | 32 +++++++++---------- 4 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java b/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java index 36314d38..96a04180 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java @@ -47,14 +47,15 @@ public ResponseEntity getLogs( @RequestParam(defaultValue = "10") int size, @RequestParam(required = false) String logId, @RequestParam(required = false) String ipAddress, - @RequestParam(required = false) String requestTime, + @RequestParam(required = false) String requestTime_start, + @RequestParam(required = false) String requestTime_end, @RequestParam(required = false) String status, @RequestParam(required = false) String method, @RequestParam(required = false) String uri ){ Pageable pageable = PageRequest.of(page, size); - LogSearchDTO searchLogDTO = new LogSearchDTO(logId, ipAddress, requestTime, status, method, uri); + LogSearchDTO searchLogDTO = new LogSearchDTO(logId, ipAddress, requestTime_start, requestTime_end, status, method, uri); Page logDTOPage = logQueryService.selectLogs(pageable, searchLogDTO); return ResponseEntity.ok(LogResponseMessage.builder() diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogSearchDTO.java index a35acc81..4c6cf4f8 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogSearchDTO.java @@ -19,20 +19,23 @@ public class LogSearchDTO { private String uri; private String method; private String queryString; - private String requestTime; + private String requestTime_start; + private String requestTime_end; private String transactionId; private String status; private String errorMessage; public LogSearchDTO(String logId, String ipAddress, - String requestTime, + String requestTime_start, + String requestTime_end, String status, String method, String uri){ this.logId = logId; this.ipAddress = ipAddress; - this.requestTime = requestTime; + this.requestTime_start = requestTime_start; + this.requestTime_end = requestTime_end; this.status = status; this.method = method; this.uri = uri; diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java index 8ff00cfe..cf9be3d4 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java @@ -56,6 +56,13 @@ public void exportLogToExcel(HttpServletResponse response) { throw new LogCommonException(LogErrorCode.LOG_NOT_FOUND); } - excelUtilsV1.download(LogExcelDTO.class, logExcels, "logExcel", response); + log.info("$$$$$$$$$$$$$$$$$$$$$"); + + for(int i=0;i AND a.ip_address LIKE CONCAT('%', #{ searchLogDTO.ipAddress }, '%') - - AND a.request_time LIKE CONCAT('%', #{ searchLogDTO.requestTime }, '%') + + AND a.request_time BETWEEN #{ searchLogDTO.requestTime_start } AND #{ searchLogDTO.requestTime_end } AND a.status LIKE CONCAT('%', #{ searchLogDTO.status }, '%') @@ -80,21 +80,21 @@ LIMIT #{ size } OFFSET #{ offset } - + \ No newline at end of file From a393ebc639634b2b8254958b9c39e420942392d0 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Sun, 1 Dec 2024 13:25:38 +0900 Subject: [PATCH 387/563] =?UTF-8?q?fix:=20pr=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/log/query/controller/LogController.java | 2 -- .../domain/log/query/service/LogQueryServiceImpl.java | 9 --------- 2 files changed, 11 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java b/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java index 96a04180..4a6c0396 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java @@ -74,8 +74,6 @@ public ResponseEntity getLogs( @GetMapping("excel") public void exportCustomer(HttpServletResponse response){ - log.info("!!!!!"); - logQueryService.exportLogToExcel(response); } } diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java index cf9be3d4..d11c86f8 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java @@ -48,21 +48,12 @@ public Page selectLogs(Pageable pageable, LogSearchDTO searchLogDTO) { @Override public void exportLogToExcel(HttpServletResponse response) { - log.info("222"); List logExcels = logMapper.findLogForExcel(); - log.info("2lkjlasdjfjskdfj"); if(logExcels == null) { throw new LogCommonException(LogErrorCode.LOG_NOT_FOUND); } - log.info("$$$$$$$$$$$$$$$$$$$$$"); - - for(int i=0;i Date: Sun, 1 Dec 2024 15:58:14 +0900 Subject: [PATCH 388/563] =?UTF-8?q?fix:=20=ED=9A=8C=EC=9B=90=20S3=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=EC=A0=81=EC=9A=A9(#192)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AuthController.java | 33 +- .../command/application/dto/SignupDTO.java | 1 + .../service/AuthCommandService.java | 3 +- .../domain/aggregate/entity/Member.java | 3 + .../service/AuthCommandServiceImpl.java | 15 +- .../domain/member/query/dto/MemberDTO.java | 1 + .../query/service/MemberQueryServiceImpl.java | 1 + .../global/dataloader/MemberInitializer.java | 342 +++++++++--------- .../member/query/repository/MemberMapper.xml | 2 + 9 files changed, 217 insertions(+), 184 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java index fbef9b52..8c66c3c5 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java @@ -9,12 +9,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.member.command.application.dto.*; import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; import stanl_2.final_backend.domain.member.common.response.MemberResponseMessage; import java.security.GeneralSecurityException; -import java.security.Principal; @Slf4j @RestController("commandAuthController") @@ -52,21 +52,40 @@ public AuthController(AuthCommandService authCommandService) { * "organizationId": "ORG_000000001" * } */ +// @Operation(summary = "회원가입") +// @ApiResponses(value = { +// @ApiResponse(responseCode = "200", description = "성공", +// content = {@Content(schema = @Schema(implementation = MemberResponseMessage.class))}) +// }) +// @PostMapping("signup") +// public ResponseEntity signup(@RequestBody SignupDTO signupDTO) throws GeneralSecurityException { +// +// authCommandService.signup(signupDTO); +// +// return ResponseEntity.ok(MemberResponseMessage.builder() +// .httpStatus(200) +// .msg("성공") +// .result(null) +// .build()); +// } + + @Operation(summary = "회원가입") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = MemberResponseMessage.class))}) }) @PostMapping("signup") - public ResponseEntity signup(@RequestBody SignupDTO signupDTO) throws GeneralSecurityException { + public ResponseEntity signup(@RequestPart("dto") SignupDTO signupDTO, + @RequestPart("file") MultipartFile imageUrl) throws GeneralSecurityException { - authCommandService.signup(signupDTO); + authCommandService.signup(signupDTO, imageUrl); return ResponseEntity.ok(MemberResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(null) - .build()); + .httpStatus(200) + .msg("성공") + .result(null) + .build()); } /** diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java index 6e27bc55..3ce0ac21 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SignupDTO.java @@ -10,6 +10,7 @@ public class SignupDTO { private String loginId; private String password; private String name; + private String imageUrl; private String email; private Integer age; private String sex; diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java index 3a93cdda..12cef6c4 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java @@ -1,11 +1,12 @@ package stanl_2.final_backend.domain.member.command.application.service; +import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.member.command.application.dto.*; import java.security.GeneralSecurityException; public interface AuthCommandService { - void signup(SignupDTO signupDTO) throws GeneralSecurityException; + void signup(SignupDTO signupDTO, MultipartFile imageUrl) throws GeneralSecurityException; RefreshDTO refreshAccessToken(String refreshToken); diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java index 5ba08b24..b61508f6 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/aggregate/entity/Member.java @@ -40,6 +40,9 @@ public class Member { @Column(name = "MEM_NAME", nullable = false) private String name; + @Column(name = "MEM_IMAGEURL", nullable = false) + private String imageUrl; + @Column(name = "MEM_EMA", nullable = false) private String email; diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java index 2dcfc934..c8c5715c 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java @@ -15,6 +15,7 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.member.command.application.dto.*; import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.Member; @@ -22,6 +23,7 @@ import stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; import stanl_2.final_backend.domain.member.command.domain.repository.MemberRoleRepository; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.s3.command.application.service.S3FileService; import stanl_2.final_backend.global.exception.GlobalCommonException; import stanl_2.final_backend.global.exception.GlobalErrorCode; import stanl_2.final_backend.global.security.service.MemberDetails; @@ -31,9 +33,6 @@ import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.util.Date; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; import java.util.stream.Collectors; @Slf4j @@ -50,6 +49,7 @@ public class AuthCommandServiceImpl implements AuthCommandService { private final AuthenticationManager authenticationManager; private final AuthQueryService authQueryService; private final AESUtils aesUtils; + private final S3FileService s3FileService; @Autowired public AuthCommandServiceImpl(MemberRepository memberRepository, @@ -58,7 +58,8 @@ public AuthCommandServiceImpl(MemberRepository memberRepository, ModelMapper modelMapper, AuthenticationManager authenticationManager, AuthQueryService authQueryService, - AESUtils aesUtils) { + AESUtils aesUtils, + S3FileService s3FileService) { this.memberRepository = memberRepository; this.memberRoleRepository = memberRoleRepository; this.passwordEncoder = passwordEncoder; @@ -66,11 +67,15 @@ public AuthCommandServiceImpl(MemberRepository memberRepository, this.authenticationManager = authenticationManager; this.authQueryService = authQueryService; this.aesUtils = aesUtils; + this.s3FileService = s3FileService; } @Override @Transactional - public void signup(SignupDTO signupDTO) throws GeneralSecurityException { + public void signup(SignupDTO signupDTO, MultipartFile imageUrl) throws GeneralSecurityException { + + // 이미지 업로드 및 암호화 + signupDTO.setImageUrl(aesUtils.encrypt(s3FileService.uploadOneFile(imageUrl))); String hashPwd = passwordEncoder.encode(signupDTO.getPassword()); signupDTO.setPassword(hashPwd); diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java index 9e00eecd..dc5b0da3 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java @@ -13,6 +13,7 @@ public class MemberDTO { private String memberId; private String loginId; private String name; + private String imageUrl; private String email; private Integer age; private String sex; diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index 16d4b855..8d2759c3 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -49,6 +49,7 @@ public MemberDTO selectMemberInfo(String name) throws GeneralSecurityException { memberInfo.setAddress(aesUtils.decrypt(memberInfo.getAddress())); memberInfo.setBankName(aesUtils.decrypt(memberInfo.getBankName())); memberInfo.setAccount(aesUtils.decrypt(memberInfo.getAccount())); + memberInfo.setImageUrl(aesUtils.decrypt(memberInfo.getImageUrl())); memberInfo.setCenterId(memberInfo.getCenterId()); return memberInfo; diff --git a/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java b/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java index f17fedae..3cb5282f 100644 --- a/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java +++ b/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java @@ -1,171 +1,171 @@ -package stanl_2.final_backend.global.dataloader; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.ApplicationArguments; -import org.springframework.boot.ApplicationRunner; -import org.springframework.stereotype.Component; -import stanl_2.final_backend.domain.member.command.application.dto.GrantDTO; -import stanl_2.final_backend.domain.member.command.application.dto.SignupDTO; -import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; -import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.Member; -import stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; - -@Slf4j -@Component -public class MemberInitializer implements ApplicationRunner { - - private final AuthCommandService authCommandService; - private final MemberRepository memberRepository; - - @Autowired - public MemberInitializer(AuthCommandService authCommandService, - MemberRepository memberRepository) { - this.authCommandService = authCommandService; - this.memberRepository = memberRepository; - } - - @Override - public void run(ApplicationArguments args) throws Exception { - - createOrUpdateMember( - "employee", - "employee", - "영업 관리자", - "employee@stanl.com", - 3, - "FEMALE", - "000000-0000003", - "010-0000-0003", - "신대방삼거리", - "영업 관리자", - "고졸", - "백엔드 개발자", - "미필", - "한국은행", - "000-0000-000000-00003", - "CEN_000000001", - "ORG_000000001", - "EMPLOYEE" - ); - - createOrUpdateMember( - "admin", - "admin", - "영업 관리자", - "admin@stanl.com", - 2, - "FEMALE", - "000000-0000002", - "010-0000-0002", - "신대방삼거리", - "영업 관리자", - "고졸", - "백엔드 개발자", - "미필", - "한국은행", - "000-0000-000000-00000", - "CEN_000000001", - "ORG_000000001", - "ADMIN" - ); - - createOrUpdateMember( - "director", - "director", - "영업 담당자", - "director@stanl.com", - 1, - "MALE", - "000000-0000001", - "010-0000-0001", - "STANL2", - "영업담당자", - "고졸", - "영업 담당자", - "미필", - "한국은행", - "000-0000-000000-00000", - "CEN_000000001", - "ORG_000000001", - "DIRECTOR" - ); - - createOrUpdateMember( - "god", - "god", - "시스템관리자", - "god@stanl.com", - 0, - "MALE", - "000000-0000000", - "010-0000-0000", - "신대방삼거리", - "시스템관리자", - "중졸", - "백엔드 개발자", - "미필", - "한국은행", - "000-0000-000000-00000", - "CEN_000000001", - "ORG_000000001", - "GOD" - ); - } - - private void createOrUpdateMember(String loginId, - String password, - String name, - String email, - int age, - String sex, - String identNo, - String phone, - String address, - String position, - String grade, - String jobType, - String military, - String bankName, - String account, - String centerId, - String organizationId, - String role) throws Exception { - - // db에 정보가 있는지 확인 - Member existingMember = memberRepository.findByLoginId(loginId); - if (existingMember == null) { - // Create the user - SignupDTO signupDTO = new SignupDTO(); - signupDTO.setLoginId(loginId); - signupDTO.setPassword(password); - signupDTO.setName(name); - signupDTO.setEmail(email); - signupDTO.setAge(age); - signupDTO.setSex(sex); - signupDTO.setIdentNo(identNo); - signupDTO.setPhone(phone); - signupDTO.setAddress(address); - signupDTO.setPosition(position); - signupDTO.setGrade(grade); - signupDTO.setJobType(jobType); - signupDTO.setMilitary(military); - signupDTO.setBankName(bankName); - signupDTO.setAccount(account); - signupDTO.setCenterId(centerId); - signupDTO.setOrganizationId(organizationId); - - authCommandService.signup(signupDTO); - - // Grant role to the user - GrantDTO grantDTO = new GrantDTO(); - grantDTO.setLoginId(loginId); - grantDTO.setRole(role); - authCommandService.grantAuthority(grantDTO); - - log.info("{} 유저를 {} 역할로 생성합니다.", loginId, role); - } else { - log.info("{} 유저 정보가 이미 존재합니다.", loginId); - } - } -} +//package stanl_2.final_backend.global.dataloader; +// +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.boot.ApplicationArguments; +//import org.springframework.boot.ApplicationRunner; +//import org.springframework.stereotype.Component; +//import stanl_2.final_backend.domain.member.command.application.dto.GrantDTO; +//import stanl_2.final_backend.domain.member.command.application.dto.SignupDTO; +//import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; +//import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.Member; +//import stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; +// +//@Slf4j +//@Component +//public class MemberInitializer implements ApplicationRunner { +// +// private final AuthCommandService authCommandService; +// private final MemberRepository memberRepository; +// +// @Autowired +// public MemberInitializer(AuthCommandService authCommandService, +// MemberRepository memberRepository) { +// this.authCommandService = authCommandService; +// this.memberRepository = memberRepository; +// } +// +// @Override +// public void run(ApplicationArguments args) throws Exception { +// +// createOrUpdateMember( +// "employee", +// "employee", +// "영업 관리자", +// "employee@stanl.com", +// 3, +// "FEMALE", +// "000000-0000003", +// "010-0000-0003", +// "신대방삼거리", +// "영업 관리자", +// "고졸", +// "백엔드 개발자", +// "미필", +// "한국은행", +// "000-0000-000000-00003", +// "CEN_000000001", +// "ORG_000000001", +// "EMPLOYEE" +// ); +// +// createOrUpdateMember( +// "admin", +// "admin", +// "영업 관리자", +// "admin@stanl.com", +// 2, +// "FEMALE", +// "000000-0000002", +// "010-0000-0002", +// "신대방삼거리", +// "영업 관리자", +// "고졸", +// "백엔드 개발자", +// "미필", +// "한국은행", +// "000-0000-000000-00000", +// "CEN_000000001", +// "ORG_000000001", +// "ADMIN" +// ); +// +// createOrUpdateMember( +// "director", +// "director", +// "영업 담당자", +// "director@stanl.com", +// 1, +// "MALE", +// "000000-0000001", +// "010-0000-0001", +// "STANL2", +// "영업담당자", +// "고졸", +// "영업 담당자", +// "미필", +// "한국은행", +// "000-0000-000000-00000", +// "CEN_000000001", +// "ORG_000000001", +// "DIRECTOR" +// ); +// +// createOrUpdateMember( +// "god", +// "god", +// "시스템관리자", +// "god@stanl.com", +// 0, +// "MALE", +// "000000-0000000", +// "010-0000-0000", +// "신대방삼거리", +// "시스템관리자", +// "중졸", +// "백엔드 개발자", +// "미필", +// "한국은행", +// "000-0000-000000-00000", +// "CEN_000000001", +// "ORG_000000001", +// "GOD" +// ); +// } +// +// private void createOrUpdateMember(String loginId, +// String password, +// String name, +// String email, +// int age, +// String sex, +// String identNo, +// String phone, +// String address, +// String position, +// String grade, +// String jobType, +// String military, +// String bankName, +// String account, +// String centerId, +// String organizationId, +// String role) throws Exception { +// +// // db에 정보가 있는지 확인 +// Member existingMember = memberRepository.findByLoginId(loginId); +// if (existingMember == null) { +// // Create the user +// SignupDTO signupDTO = new SignupDTO(); +// signupDTO.setLoginId(loginId); +// signupDTO.setPassword(password); +// signupDTO.setName(name); +// signupDTO.setEmail(email); +// signupDTO.setAge(age); +// signupDTO.setSex(sex); +// signupDTO.setIdentNo(identNo); +// signupDTO.setPhone(phone); +// signupDTO.setAddress(address); +// signupDTO.setPosition(position); +// signupDTO.setGrade(grade); +// signupDTO.setJobType(jobType); +// signupDTO.setMilitary(military); +// signupDTO.setBankName(bankName); +// signupDTO.setAccount(account); +// signupDTO.setCenterId(centerId); +// signupDTO.setOrganizationId(organizationId); +// +// authCommandService.signup(signupDTO); +// +// // Grant role to the user +// GrantDTO grantDTO = new GrantDTO(); +// grantDTO.setLoginId(loginId); +// grantDTO.setRole(role); +// authCommandService.grantAuthority(grantDTO); +// +// log.info("{} 유저를 {} 역할로 생성합니다.", loginId, role); +// } else { +// log.info("{} 유저 정보가 이미 존재합니다.", loginId); +// } +// } +//} diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml index 66b6a095..5c9479d0 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml @@ -7,6 +7,7 @@ + @@ -29,6 +30,7 @@ SELECT a.mem_login_id, a.mem_name, + a.mem_imageurl, a.mem_ema, a.mem_age, a.mem_sex, From 1e37c5815c4c5920d0f415cc19a549705dd47469 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Sun, 1 Dec 2024 16:36:13 +0900 Subject: [PATCH 389/563] =?UTF-8?q?fix:=20DBInitial=20=EC=88=98=EC=A0=95(#?= =?UTF-8?q?192)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AuthController.java | 53 +-- .../global/dataloader/MemberInitializer.java | 412 ++++++++++-------- src/main/resources/application-prod.yml | 2 +- src/main/resources/image/admin.png | Bin 0 -> 668354 bytes src/main/resources/image/director.png | Bin 0 -> 683557 bytes src/main/resources/image/employee.png | Bin 0 -> 687312 bytes src/main/resources/image/god.png | Bin 0 -> 802951 bytes 7 files changed, 260 insertions(+), 207 deletions(-) create mode 100644 src/main/resources/image/admin.png create mode 100644 src/main/resources/image/director.png create mode 100644 src/main/resources/image/employee.png create mode 100644 src/main/resources/image/god.png diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java index 8c66c3c5..03f5b8c0 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java @@ -52,24 +52,6 @@ public AuthController(AuthCommandService authCommandService) { * "organizationId": "ORG_000000001" * } */ -// @Operation(summary = "회원가입") -// @ApiResponses(value = { -// @ApiResponse(responseCode = "200", description = "성공", -// content = {@Content(schema = @Schema(implementation = MemberResponseMessage.class))}) -// }) -// @PostMapping("signup") -// public ResponseEntity signup(@RequestBody SignupDTO signupDTO) throws GeneralSecurityException { -// -// authCommandService.signup(signupDTO); -// -// return ResponseEntity.ok(MemberResponseMessage.builder() -// .httpStatus(200) -// .msg("성공") -// .result(null) -// .build()); -// } - - @Operation(summary = "회원가입") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", @@ -77,15 +59,16 @@ public AuthController(AuthCommandService authCommandService) { }) @PostMapping("signup") public ResponseEntity signup(@RequestPart("dto") SignupDTO signupDTO, - @RequestPart("file") MultipartFile imageUrl) throws GeneralSecurityException { + @RequestPart("file") MultipartFile imageUrl) + throws GeneralSecurityException { authCommandService.signup(signupDTO, imageUrl); return ResponseEntity.ok(MemberResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(null) - .build()); + .httpStatus(200) + .msg("성공") + .result(null) + .build()); } /** @@ -107,10 +90,10 @@ public ResponseEntity grantAuthority(@RequestBody GrantDT authCommandService.grantAuthority(grantDTO); return ResponseEntity.ok(MemberResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(null) - .build()); + .httpStatus(200) + .msg("성공") + .result(null) + .build()); } @@ -133,10 +116,10 @@ public ResponseEntity signin(@RequestBody SigninRequestDT return ResponseEntity.ok( MemberResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(responseDTO) - .build() + .httpStatus(200) + .msg("성공") + .result(responseDTO) + .build() ); } @@ -151,10 +134,10 @@ public ResponseEntity refresh(@RequestBody RefreshDTO ref RefreshDTO newAccessToken = authCommandService.refreshAccessToken(refreshDTO.getRefreshToken()); return ResponseEntity.ok(MemberResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(newAccessToken) - .build()); + .httpStatus(200) + .msg("성공") + .result(newAccessToken) + .build()); } diff --git a/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java b/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java index 3cb5282f..a1900b93 100644 --- a/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java +++ b/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java @@ -1,171 +1,241 @@ -//package stanl_2.final_backend.global.dataloader; -// -//import lombok.extern.slf4j.Slf4j; -//import org.springframework.beans.factory.annotation.Autowired; -//import org.springframework.boot.ApplicationArguments; -//import org.springframework.boot.ApplicationRunner; -//import org.springframework.stereotype.Component; -//import stanl_2.final_backend.domain.member.command.application.dto.GrantDTO; -//import stanl_2.final_backend.domain.member.command.application.dto.SignupDTO; -//import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; -//import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.Member; -//import stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; -// -//@Slf4j -//@Component -//public class MemberInitializer implements ApplicationRunner { -// -// private final AuthCommandService authCommandService; -// private final MemberRepository memberRepository; -// -// @Autowired -// public MemberInitializer(AuthCommandService authCommandService, -// MemberRepository memberRepository) { -// this.authCommandService = authCommandService; -// this.memberRepository = memberRepository; -// } -// -// @Override -// public void run(ApplicationArguments args) throws Exception { -// -// createOrUpdateMember( -// "employee", -// "employee", -// "영업 관리자", -// "employee@stanl.com", -// 3, -// "FEMALE", -// "000000-0000003", -// "010-0000-0003", -// "신대방삼거리", -// "영업 관리자", -// "고졸", -// "백엔드 개발자", -// "미필", -// "한국은행", -// "000-0000-000000-00003", -// "CEN_000000001", -// "ORG_000000001", -// "EMPLOYEE" -// ); -// -// createOrUpdateMember( -// "admin", -// "admin", -// "영업 관리자", -// "admin@stanl.com", -// 2, -// "FEMALE", -// "000000-0000002", -// "010-0000-0002", -// "신대방삼거리", -// "영업 관리자", -// "고졸", -// "백엔드 개발자", -// "미필", -// "한국은행", -// "000-0000-000000-00000", -// "CEN_000000001", -// "ORG_000000001", -// "ADMIN" -// ); -// -// createOrUpdateMember( -// "director", -// "director", -// "영업 담당자", -// "director@stanl.com", -// 1, -// "MALE", -// "000000-0000001", -// "010-0000-0001", -// "STANL2", -// "영업담당자", -// "고졸", -// "영업 담당자", -// "미필", -// "한국은행", -// "000-0000-000000-00000", -// "CEN_000000001", -// "ORG_000000001", -// "DIRECTOR" -// ); -// -// createOrUpdateMember( -// "god", -// "god", -// "시스템관리자", -// "god@stanl.com", -// 0, -// "MALE", -// "000000-0000000", -// "010-0000-0000", -// "신대방삼거리", -// "시스템관리자", -// "중졸", -// "백엔드 개발자", -// "미필", -// "한국은행", -// "000-0000-000000-00000", -// "CEN_000000001", -// "ORG_000000001", -// "GOD" -// ); -// } -// -// private void createOrUpdateMember(String loginId, -// String password, -// String name, -// String email, -// int age, -// String sex, -// String identNo, -// String phone, -// String address, -// String position, -// String grade, -// String jobType, -// String military, -// String bankName, -// String account, -// String centerId, -// String organizationId, -// String role) throws Exception { -// -// // db에 정보가 있는지 확인 -// Member existingMember = memberRepository.findByLoginId(loginId); -// if (existingMember == null) { -// // Create the user -// SignupDTO signupDTO = new SignupDTO(); -// signupDTO.setLoginId(loginId); -// signupDTO.setPassword(password); -// signupDTO.setName(name); -// signupDTO.setEmail(email); -// signupDTO.setAge(age); -// signupDTO.setSex(sex); -// signupDTO.setIdentNo(identNo); -// signupDTO.setPhone(phone); -// signupDTO.setAddress(address); -// signupDTO.setPosition(position); -// signupDTO.setGrade(grade); -// signupDTO.setJobType(jobType); -// signupDTO.setMilitary(military); -// signupDTO.setBankName(bankName); -// signupDTO.setAccount(account); -// signupDTO.setCenterId(centerId); -// signupDTO.setOrganizationId(organizationId); -// -// authCommandService.signup(signupDTO); -// -// // Grant role to the user -// GrantDTO grantDTO = new GrantDTO(); -// grantDTO.setLoginId(loginId); -// grantDTO.setRole(role); -// authCommandService.grantAuthority(grantDTO); -// -// log.info("{} 유저를 {} 역할로 생성합니다.", loginId, role); -// } else { -// log.info("{} 유저 정보가 이미 존재합니다.", loginId); -// } -// } -//} +package stanl_2.final_backend.global.dataloader; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Component; +import org.springframework.util.StreamUtils; +import org.springframework.web.multipart.MultipartFile; +import stanl_2.final_backend.domain.member.command.application.dto.GrantDTO; +import stanl_2.final_backend.domain.member.command.application.dto.SignupDTO; +import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; +import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.Member; +import stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; + +@Slf4j +@Component +public class MemberInitializer implements ApplicationRunner { + + private final AuthCommandService authCommandService; + private final MemberRepository memberRepository; + + @Autowired + public MemberInitializer(AuthCommandService authCommandService, + MemberRepository memberRepository) { + this.authCommandService = authCommandService; + this.memberRepository = memberRepository; + } + + @Override + public void run(ApplicationArguments args) throws Exception { + + createOrUpdateMember( + "employee", + "employee", + "영업 관리자", + "employee@stanl.com", + 3, + "FEMALE", + "000000-0000003", + "010-0000-0003", + "신대방삼거리", + "영업 관리자", + "고졸", + "백엔드 개발자", + "미필", + "한국은행", + "000-0000-000000-00003", + "CEN_000000001", + "ORG_000000001", + "EMPLOYEE", + loadImage("employee.png") + ); + + createOrUpdateMember( + "admin", + "admin", + "영업 관리자", + "admin@stanl.com", + 2, + "FEMALE", + "000000-0000002", + "010-0000-0002", + "신대방삼거리", + "영업 관리자", + "고졸", + "백엔드 개발자", + "미필", + "한국은행", + "000-0000-000000-00000", + "CEN_000000001", + "ORG_000000001", + "ADMIN", + loadImage("admin.png") + ); + + createOrUpdateMember( + "director", + "director", + "영업 담당자", + "director@stanl.com", + 1, + "MALE", + "000000-0000001", + "010-0000-0001", + "STANL2", + "영업담당자", + "고졸", + "영업 담당자", + "미필", + "한국은행", + "000-0000-000000-00000", + "CEN_000000001", + "ORG_000000001", + "DIRECTOR", + loadImage("director.png") + ); + + createOrUpdateMember( + "god", + "god", + "시스템관리자", + "god@stanl.com", + 0, + "MALE", + "000000-0000000", + "010-0000-0000", + "신대방삼거리", + "시스템관리자", + "중졸", + "백엔드 개발자", + "미필", + "한국은행", + "000-0000-000000-00000", + "CEN_000000001", + "ORG_000000001", + "GOD", + loadImage("god.png") + ); + } + + private MultipartFile loadImage(String fileName) throws IOException { + // 리소스 디렉토리에서 파일 로드 + ClassPathResource resource = new ClassPathResource("image/" + fileName); + + // 파일 내용을 byte 배열로 읽기 + byte[] fileContent = StreamUtils.copyToByteArray(resource.getInputStream()); + + // MultipartFile 익명 구현체 생성 + return new MultipartFile() { + @Override + public String getName() { + return fileName; + } + + @Override + public String getOriginalFilename() { + return fileName; + } + + @Override + public String getContentType() { + try { + // 파일의 MIME 타입 결정 + return Files.probeContentType(resource.getFile().toPath()); + } catch (IOException e) { + return "application/octet-stream"; // 기본 MIME 타입 + } + } + + @Override + public boolean isEmpty() { + return fileContent.length == 0; + } + + @Override + public long getSize() { + return fileContent.length; + } + + @Override + public byte[] getBytes() throws IOException { + return fileContent; + } + + @Override + public InputStream getInputStream() throws IOException { + return resource.getInputStream(); + } + + @Override + public void transferTo(File dest) throws IOException, IllegalStateException { + Files.write(dest.toPath(), fileContent); + } + }; + } + + + private void createOrUpdateMember(String loginId, + String password, + String name, + String email, + int age, + String sex, + String identNo, + String phone, + String address, + String position, + String grade, + String jobType, + String military, + String bankName, + String account, + String centerId, + String organizationId, + String role, + MultipartFile imageUrl) throws Exception { + + // db에 정보가 있는지 확인 + Member existingMember = memberRepository.findByLoginId(loginId); + if (existingMember == null) { + // Create the user + SignupDTO signupDTO = new SignupDTO(); + signupDTO.setLoginId(loginId); + signupDTO.setPassword(password); + signupDTO.setName(name); + signupDTO.setEmail(email); + signupDTO.setAge(age); + signupDTO.setSex(sex); + signupDTO.setIdentNo(identNo); + signupDTO.setPhone(phone); + signupDTO.setAddress(address); + signupDTO.setPosition(position); + signupDTO.setGrade(grade); + signupDTO.setJobType(jobType); + signupDTO.setMilitary(military); + signupDTO.setBankName(bankName); + signupDTO.setAccount(account); + signupDTO.setCenterId(centerId); + signupDTO.setOrganizationId(organizationId); + + authCommandService.signup(signupDTO, imageUrl); + + // Grant role to the user + GrantDTO grantDTO = new GrantDTO(); + grantDTO.setLoginId(loginId); + grantDTO.setRole(role); + authCommandService.grantAuthority(grantDTO); + + log.info("{} 유저를 {} 역할로 생성합니다.", loginId, role); + } else { + log.info("{} 유저 정보가 이미 존재합니다.", loginId); + } + } +} diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 7eb65804..72c1ffe0 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -11,7 +11,7 @@ spring: timeout: 5000ms jpa: hibernate: - ddl-auto: update + ddl-auto: create open-in-view: false logging: diff --git a/src/main/resources/image/admin.png b/src/main/resources/image/admin.png new file mode 100644 index 0000000000000000000000000000000000000000..c123cce9379f72dbea0a2a12ca04e7a96b998fd0 GIT binary patch literal 668354 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 EiVGevSOf$q3W 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

)fX>fQEHsMuRw`Mf%#8HBjTtDvoW{pCfV|UJ* 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≦~L_#R$`!+0i69zPBq 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

N($ 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

&C2m-m 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¬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 PJsgaCV}c<3to$a^uZzBgEka!aicp&> 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

t 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�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%0A8# zrO^W{aI#~gK;fI;{O0z#&weh~W&;>9%wX(`4$_%160Z#u@_2CH1$m)|gR$aZ+cQGv z&=W7wgb@ULdr*0f0WHz`18Z-4vSx_?5m2{mqvSa1m3d&X*-`^EnQ$Nwxy@PKxQZ{sZR ziqN}GhMs!*sVs^7_V)DzY4_j%qwV}S*Q>9&HnN(xoF>Cb22ZBki@~S8t>7jV&l%ML z2G(j(@S!&X2DC#XxNCzvcoxseqMKkUZHPralQoOM&YTjC=(tIAHZ99@98xoN=oTA{ zZR`nM(ox~OBUIHXa?@20xo`gm4g);}n;f#CD7%5Ob=uLf%k9F*260~O92wz&r0wlR z1E+3Sm-_JqKAc|zjnu^!;JwP!Pu9pm?ac1F=N$Bu9O8k13oK;OtKrG1_ua^FXoJq1 z?1x@xS@}%arH>s#sEkdWXuF0=l&`F(Ep0f!fCikN_Swh9AF>Rta|1g(3@;u2*)zf8 zKpu0L9^jwfBC~Z4$`?U`=P)KtXUIL81f(4t{oN+c_MgcgCpq&PCFNAICEr24R z8O7b4DL|?CmAvHTrITS`mMStT-A2})2N@^@90CANkkE~00v`qv3hRF@jqCc@?D+R$ zQrec_2^J_30PUY(*~)q80mvvwx0yjNvZhD`-DVi}C^sYZ(4j*W7{XJ;cLZ@xAqZ3V zil|6euXBA27b~+I72!qT5odTUCtcoF&ExbkDk}gn5irDTrdHv zP9iiDof8tuOb93y!=r8GFk25?85bysHph_(OoChRV$A9Uf+x{Ma|WwUH}~MaQhc5r zc@O1Hzw|Q??TtbDuG41|rI72*CEu0=n*W2a_E< zz8&+uob&u1dYtCe0*6Q%>?x7IF&yN@uaQaemB4fNThPjg3PSM2e2NG20;bC^yCkoj zeq+YUGpR8eiYI6ga9+{EvU^>zBJ}R1|ge&qE_e_%V)`QtpgS6c}jQ$fX*C;bHi=3u?#+ zLu|DYBhj9rU@tOJkWRx5elC_Tkw@^h(E7@~kgwNjyaUs6It>^i|NTv;Llch1x3U0j zwufQMa~}{WyyNZJ!S0G(np_|=bAKwUX&*nAY^2qJ1x!xKfsenQ_hRq$ez?!DyU+NN zaWJNEb&joiJg*OyN%;KdKEFMa?9NqJU$?#CnrqSlxG?ZeCQaavg)_V&a{?bh+N7)T zge~%+cg@BG0C>|!@Z*VRN{8I@44np-?W`GW^yopt|j$KyC&tH8x z&iKOEQNs?qj!x*-`3en)XTFz=L*F17xkr0Lc@B1A9PH}-2M;s}thTKepmH!nv&`;q zuRB>^c0M?hkwr)ClRcB%*)euTpg_*>UZ8K)51rI`#83R-v>cWoY8#Efuao3uSW9mz z%W0P`b&Y>yVra}KjZ;Nav<8RF*63a2Ff@e+d^u5V5S`YUXW3RTdd??53qKg}N5;X> zAQ%lL$QCU97R=bX`AGbV4EOQalauMS?@y3$X(qwiZ$ZaMCQ6`C9SrPru5G%O0FTYm zc0e2WLKiR!AmFuRhaIlW1m0$D0+Tw~K`_x5zXg(fbF(?IO*5G>B+k2C;D-Y6cTBtJ zBCwsn*>i<2zm)|aLxf!`c-VKxy}$9QO=kjT1StZUj~x{`ZxO6$S4N?e+LDg*wEYW= zlXV;1V90@3?Qr%v<8XntWz(Bie{TF^oZ^}3v1y+}UB?O(Omw4wB7ce9M5l4nJhh!3q>a)Fv}>K4(NI^ztc4VNRey==37ZQ!w;k2RK58@S;V8#t_%K&2j2r zR0#+Hgag55v<37VhAVE(FR1RInTSM3bY2e|FBW86&;)r1WJH!rlZ63F`ES)DefPhhbY{LEOce z+oIQl25>@BMrZ_a1ej14&f+k19AlcSLxcw`0lUJFDQE@ODnss@k5EO75!L|un;X$= zQ|>*V$(9fO*J~E0X>h*E>&FJBQgA9$V zPQY8fGHUsdJA6Yw$LaM56euj26exW8%U|C9>aYH~Z>LKuk;M$Zgm%Uii&ukpJ$MGh zwFwlO@rcYhDx-m`K>@h)N2c*proo>HZ|#MKNy;r_qz&*aqcQa%+|=C$`d{!SG&v?9 z9wQA_y+_W@974eYxu0HTY();Vt4!yfjKT+g>o@bt?{{>~xp7_Dk7m=9i;R}`p#B9I zT3uo1bgD0bY3a@GZhi}5#^6qmi7pN=@X1Cx4?g%%<|cf4`}oKIu}d_sz2U~Bjjzf( zC#(#}%=To4I%g#M$rc(u)n<>vHTuj(v5VT_V8K#(4NdTx-60Qp(4J@S(8^ZxUSdb5 z*aKU}v!7D-bozxjSwna)W+l;Zf=!x}QLd9w^0r{CLrWcG$*!h&!C|uZwbcoL`h1>&kDvq%2j0=kW&mS%n`I2VChL>i zB`ai$Olp_BI5=Ij>O6#WfcEA0mD!FNEoFWz3qKZk4nJdVLN;(q+b@N$$1_LIUOk8M zx#ZGIJD0%Qd36|NG-Wc-xf>hBUv#Bju7QVct^*vh0OW@bjO`T6#jXm3+(YZ&(+*S4 z8^nc{XC;d;{7A4B*$cHpqhuCiPw>opRNO~XwhR5#pG!Mkk=^2(Ca9d?17xFT|~s$k@DUOcTK(Vn3KYd{gPLYKxUwn>8oA+#>T=0!b)_wYGDiWzGr>-J+~!3y0noV4nF&(#R_)Gs^w~Hi>w`lKL5h^m`56K@ zCxMH|kK%v}5eP2@H#SZ9OkC4WBd-u0(L}rMQ5M2aD4ZMKsIg0wk15I^9)ZQgDtKV% zWS}sNV>T3`4g!ZMXvGLpV#jiLjEgLQGEL1#k(w$DEvmF>lcQnm{B~%YAqoKl3#WCe z4e38Z1TPr}lbHs_2t{Bc7!K{J57Y4m$bC1as!o;?*PL{YW17bt)w+@oV)%clWC;oF^u?&wtd$56~9 z#R*_-EJY?4Z18L^KILC;NGlJJ4-*Uz7V+Rff@<^wX9L;DYXgOp7@9D6Gx^!$;PjzK zet+OYA52E!9er_*K%w~TWUx57kdLoldITPhtN(U#)QS}bUknZhr;Pg2Y~a$i;n0^l z{(S!r?%zKB>CbGR`t%pKTW@{yc3EBxd?0}==fJ@0#nKU(&9O(bhdx=PlIT9eJKA)? zS8Q91GJ}gQ_)NdG%>f9goVS-Pc+P!BoSk_-!8N-e_A`Oo#O>qBpc;N)d-X`QDX2F2 zo6~F{!v2}B5I7Cj8fG*!Au!JaC{kYc1VPa&pXzrm;u{$dV3c-2ZrPmS0jlkmT??M# zu+X%x-i|$z^=)7r8ST3~YA4wIsYh9Mos6=#@FF|5+h=RpV_9RmqpW~dsvG|V8m_a0 z7VlGKgHXV696gP?Q^Sni>yo1kYwa z<{MCC0gE7j;2|twM=QcZ0SK^c#@g?kU@()fvvOqN`shgo(TR3~ww7CTMy0SFd zz^AzuK14|fJUVOB?E1kW*MdKVvH=s>5*&;{N}!h20_vjhXQa&=3pZ@fxw?F%{Kg(?U8&Wzt72_nMN2VYXZVZkM6vluQ zp6KK7Z}>zP{B^EE=z#wYGHt#I{wi}$c#T$5NSEO}U;N@1w+DXmNSA6Jxa86fVX8?c z!KOGEFX)sxvz+neWDemqld~sVCR^CFGTRqAkgu=`@WFfNyI`Wb4i2bG1BV8Kf5TI_vn^I@O^;j$BNO8@j7uyY-a7Ap4 zp+!TV0~6oJ$APQCVgdp4kyv08zIz{?!&5rCXc^E_Zsza&=4OzIWT3Vp)en_MXVhX2>mfozy&|f2aMYF0v)M6_ta~PQTO#44Mm_M zhC1N0ct=@WuK`QD@N1A0fzX!rUE_p@XA~kS&hS;841+SFUi5~KcFoQeNaZG-K36wP)oeO=}21TWF}=R9$+2*Kaf-2PUIN@EW8Q{_uz3`WS;p zLvV7cM7KxfC0T{E8;==SIW_c)!e;{}D`d#BH*Y@q)DzuQ$hEg8N&xx)wL>MuM$ za#9|I&aE+Cl58|k$bI@(q;q!-xka^Ef_x@`)B9mW8UOjL05+w9kb4g8Kx!{gczZGm zR|^y@JsU$e@-VQW!LK@cFSkRM9GKFR7B8(rXS9aP7*O^kFvj6On=PAvkZi(NzWlZ9 zo$&2k-?lft>CM}P$-=E+hXx89Du(3fv$cx};)7>LCP%iD_1r4Ff)H5;^RSLZm)appFSO!}aiNoW05YfeK~^rzjC{cX+OflZ z_f6!>z3IrYcb*)2>ZE~n>`elv*phM3-nJ?rGIY4>#H1c`yEt!f9gi&}kJ^&OaFAoN z?pk#v*WiU_%92eskKQS#4fmW&%d=@R2keQ!g`Key#Uxz<~zrm#k8sD?cV+Dl+-~Vuns@?_;xN2li+4D#P0R5npmF!P|3L zHDyOe!$T(ngX5a3ukG?$!$Q*i$gPuur-rk2Q1QPs2>uRqZ=e!f@Jpw-ufxuBB#&)x zfR*yKfw>Ncao`hV?qb1QC27;aV6$J!QguKGO6aWT1P;omYjoVT*x;0$0C<9Xeywk5 z4n5frGE-l3m*tTGf^o0m4ML1L=87<;5kNESu1Ski&WZFyOs)wO%9Jz?8AK2$aG!I8{5nCgw1^sYn6TXplf=}0doYh zB%hm}0VDd=pzM0NXyqWN?m5tYXZ4B`L3hpbOUMZzn!-n9>z*=baOz&7;ow>49-QC` zMhAGNz0foOYZ_dop|aqk5GiL^qzayE<#7bMa1;KW(CV2zoBb1xWY6<(r+ZyI-<2r| zV8UPWU|$Iea87wV!#a!?m>TWon=&{StYpR^d%~!4G&MRqI&zN6azX+HwAYh{6W(yl z`vPx%)KP%nnH#98`VhB2!Q2-HI(b z#{pCRNG=o%a>Ehwrgz z;LHb|wXJ6IA)u5w6*wq^Z)^fbF5_8Sp6ib4Z)6${CM>FpgD2-~sVsnAxm5_tcFt73 z@eK~@^6bi3Xs=E`*TC6MNxqe9u$MaNjdqmvtb4LBev=e)!?d%8+1PS+P#I+fZvvKf z9;2rMqRGsJCaDi!l`{lnc(QnBnZ3>p8%d_@KzlTkki87bq-Y%?9Za+{>~Kl4QYM@7 z6J$<(XpPR=C$I1!OY9U~XG6f^09&)WDZg|=M|}mq^%Fb#FCB1A5W^nP0I;HyV`x1c zyI0b|XE%KW&lvB%qrp>Xr5<**wjp(tpTWa*xa_+#s~!MGF(8Gx3Y)b_aLT+O--#Ld zZom-ctiS>yvJ5H{(TVQV0T4zWR6qik@juRva&_!D#8Ot8-eeK?AgkxEKE?+uCJbVr z35`dOWa&!UYfn8dusWd|COcy8$?V1`qhN!WPH;*&PMy-*PWhb9Q?N94hM!R2|& zK{QzKjVAd{3*|G~8#3Z}f9v1;FWX&r-<210UEftp-ms_Q>RR9}T%mtJ+&c!Ey`R5u z;{L0<7c0@bI+MT$Y;qQX=qjj(9yTxJ3)}zkfBxxq!*$nfx7>Vl_fJT48D|gf=v=Tf zOvPvl=!$djK;P-D!DAdt1L0)MvRN}W3Dc}W^=GOInK<`gLAe*Puvnc9VJevDeydQ;zA$8ex5nlwe93}hbIevzm zst1jfalJeX9*$)jTxXX#t(Ns{OK@;(Ql7)J*rr)chew$ovddb6L%kd@TjT?G@_BAL zFmM;((?__=;+Q8Olc2LnZn7k>@LHz-Y%&Q)v&7oF9_XYXfgT<_cqp=LhtYI=LuXLZ zTeN_;EaeJvx|Bb((vjt)!FaN1|M<`N69L*9rt&O0^;ohHU2~0Hf*ZR}_8iTugcta) zZ8#lI*4KU+fhIxUY}U18GZhuL849;taxyRKS^)wWDyO&I`!B<{kU#L87efhAjVytp zFn|)Rog1ZaU+@6QDo=o&UBD(#5ZN}}6r~4T4M-Sc_OKNNm}}f!OJFS=A1LPHJ;ZS{^<1kapP6g}*g6J-Yu0gFIJc|2s)JujdE7v%xJ zwj5H1EfYLu2uh&9;3Ysx+r;(Eq~i%S!4pK9LDy|eS2gK54uv=^Uh_s zz>y({D8~{!_vbSP!}-cukuCTWO0qV}0)kOKis=6In5~uxZh1x>4gT79S0a!fJcEJ&4eg{!bd*xk;;q!l5rQ^dMvH zJyst%r3n-mS;0bOATmhK8c?dwJ)fMr2F5u~8Mbw0zCo}nHE0zvDNk@#|07Qg6oOxa zrN|M(O#^ST;Wy*|8L&IF7BzdU#?EIUXS9KUcgIOLBE zYo8%!TgXH}48Nbsu)~kGFMQ#P+kg4L|KIJ7d+yC7(i^(kA(9;%wtBX7Y}aFs-=)loosd6^2dPXJ!^K3*G`~^>uP37=@1+?^kV2E7^K49T+ z7hD3GHL-Td%jn{c|HW^D`l)+*t#m+dx;8QNpo{o`9u8&Q^PA0M%bYhmm*)f=bOCM0 zF42L>Zm)LOF9*Dg_v=iuf9$391aWXBu%IRxco0TA7-j}-kXO*d`L6*(BenpG(42fz#<|^|Rgi4z zCO`wk=+c8-!ZN6x=2&u}@Wet*fHY~C z5x8+~0j&%jVV^*wMj-5VEMjxeB0F>>E`(n&uu@DEKrnzlq(n3;TY~1hjO|Gh`Tl*ELF-n>FO%4Vo1`brHJU>gopN zwCi0GcDAEDUd0Np=c8a=40STgxeODbi*`#t`zR!sJ2O4`(kAfFvf2^M5zaB_+MpE0 z32y3%5mt}-KnJe%*Hm5ZbzC$CBV5S+;FogvNeWzv+$J<(D4FJ3|s2kQJ?k6{6m{rk?RgzvjZ~bX-gWeVlUg(kFvX?x%{jSw zeMaFkpZU!8=YRg^4d}=T-ohOcV;I7_80&R_g`7kIgPZhoXd2w=sMd3V6THhX?8$=X zatQWD)}wovTbd1C1-_+T_n*ihh_&>bPM{EcWvC`)KRs1J16)SW( z`^NT-@BLuA?M-jVUJh^QyGVqD-H9=CO$;LdoZ}dsC8u$0G4$}@pveLQ%_$l4rYB&B zBmNa%G)NgXuAKr11<%^Rz)!~dJLUA~tmf)O=3V+*(7*wLg}#vG+J}7W7=goYLj!uE zvx)#5=qNZG`EREyFba+ZmO6559Q(?CH^8jEPC&QkkOCLGjV^*W_DXq2^)&h=cp$gv zgfHZ(S;&<28~$Wru~OO6Ffh31ce4@XKIK;?0xexfBhRyeoH;q~Fm|2?kNe}~4N0ty zQh1*~uo#wAmrjj(O7lF!f9YseHg_2lFEr#UWUDW^>`*5%l6XTX_T+gncFzRHpP9^< z<&pR}bMG$9zAV|MUZ4<|AglAh<``~g3+>(E083XQYr1-na0qAY}Oa=>KIv7KbaBis*w$yAlLoMT;Se! z=X*YoIAM&FD9a59gp7SibR!K>!?QC=YMf^Vxf-vOUV>l=2u8WiDM1Wj0Yc~)JV;MK zLa{_&)5BE=Q++C!0psUUd}DSmWO+(=XiN|G#VBt4bJlxGf_^Db;Uy1>qTZ zN&a$C*eEyJi{L` zu7ok4W@th~%DiL>{m^yE13II@eD|wLz9&0@zmpb~%OBj(p!-_nIZ8S*fWPxR+?%?M zfn{(g;=>O!HySxV!$20zQIJvii@*4bW*X4^HGu*hOhqL_zVUR)CAx7wrCa4yK|V#6 zWda4Xs;rXNe4GoRO7q=s^%bW@YhdSd`cW>R;jp8(f@$@iP74egDCE3)UU?fQSu3(b z0)8(=4}SZ1e`f*(+v8@^F=wmt(*p&MH0^#GAO&#;r^~=u^j=U0ss;*XuY(bNz@(G# zz3=~E`^QWkz5o6nCN2I_UJ3lh^a8W|EgcPkf=nCvGgrhLQ1t*ukJu?$wh038&(V%d z9D*JwQ|Dx_7O!Fr@NwtQ)I*jW%KGb3nas6hq>1*(ZF=7c($MD8 z?1zAsF<5Cga~IMp_fo<4|e!QUnIJ?Nf!c)`mAO)c5E7yUbgpX==e zW@r@M(8qUuZO;83LX9nxDWNlhn$h#|$ZYv^N_2-bH^Gz1=4_ITxaVpYgTGoO@Rf4q9lMscniX;F9q<28I`gCCh>1BIJ!6ZhnCH8Q1(_5xt zi;b_q5;6?CFpROGh2NZ#hY2^s3^wx^_9vyJPRHradT@VjIv$STn6$pV8)gy_g@Z4l zf@d9Yl*EBan5>OqerOQktuS(+DI?HupcSTTo6u1{ZGw4%mk|>6ly+&0;Az8s-*7`) zJ6{q^dnqVeV~~25LE!WRB7!4Mq~oNaKj9J}NQ)B+oCQC4wdm(p*&nnM1PzWw08E+yH<=IkXykgApum@iG^Bq12GRP1(PY)FA z4*07A1$CjdXB`V~-}Dl#-~&JKxeh#Tf9}N)<>3YsDN{oedI%JhMK8~SuMg(`|JhNy z8@lF!6dPUc#$=Jj$sk0I19LkA!F6wZ6Kwf|zx1abgAXHrXIs%GBBAhHKTu)YGBsK*e4NHt+ziIBwhh8{UOIXKGSDz8p7!JNw2wlVHKz znu`-m986GgQRvNQvMYEZGr{M1ld<*$Q)opWQ6HRWqAdqn`Sb{oWT?SFU|*f1R1`ef znF(z0&JVim#fam}W|F7*A-%_TCmSW}2p$LCDr@Bo|Jz{|o$k zzxRWYr*Ya+U<}N~5jvGIyc(!63Zlg&?RgM@4FIBLbpk=0upo>&2pC`iLl}&gb=@(i zkhsW^2|1kVyWWt462A@y(; z^BKoMz!`pmtZogvrkBMbgO5>w54f~PskNb=IRY$vg2F`GyNOh(hp^2vv;KJ|%f)!) z`X0`$WCV4{GQ1sl@MCCVY#WP=r`i_#NG8eXNpzO;$-KxWhRrfbONhV-ApAb(oEdsR z`v!2q2`?Hv>C{e%|+ zC6hP>m5jPqJwNo&qmhgAw|C$Dw(Xr+E_r3P7B=YzPx8pfqZ0&4Vr~gFLOjtxuJaQY|_1Ze@5L%Ei zfdV|~4A~UW%FOulTcFNx|L!0B{san{A7KBJUlu4t9A0;56|mZvc3xLtex`^3!A^&4 z27~8aGfzK#c)Kq{4*%DG`SZ?2xZ&Cxwrj4*D}-Y&Ft0BP3jH}HEqT^hJ-X4w=ko%z zMVK1R;DrVq!;cN}9Gd!|1;@#TO<*{3PEJ+;PJYl6kMY&UMRios8~StjneFZaeKfRht5G{Z*`jRq*e)AMzBfmv2Yd+HtBc0(n}?xLFs@wrhS zB}fY>rHxmgvl&NY=bn!fwHi(a`-RxMBbglkOy((^Os3(=?7w2$W;#tb3d4@h@bcS% zj^L`@imZ?qM_~$|=n{FVj)sh6#qZHk8l;Z+qtF4(OWXK0yr_>!;6zp(t_kw+P%ECL zi|jUAg!eW?q(^vD-KF1cFQtc#d5}y5M*PXO4m~(ixzvlpijTumH=b zVYd4ktWbm?0GAyY0hnm50+&|R>6r->CR%H+Jl6#Z96w?yvjmQDZd4veZ8H+f?~Y|s z$aN{_uQS$ zkBps@5$y;RqlAG?+x>*bs}P^Lq5rN4C#@ z{`1>k{^eg)2KEAl@I~2KnK1R0A3JBXh6NXGd~3rHM`bu|H&_VF=+QY0Iq$vT1TW|7 zd0;#I{)NM}$OXL=EWoRDXJi5+p7;nD$eMG~jem?&YxF>#vT2cV-rxVDKWw0Ib>=8o z-lvxtOBuQ_+~YV|xZ&_GK%{6u>VJjDe+zO=O`wooa(D!%!|4G&nE46!ef=BTpZ� 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/image/employee.png b/src/main/resources/image/employee.png new file mode 100644 index 0000000000000000000000000000000000000000..f1788ca316e8503d082fc783405e6c43a9225fc9 GIT binary patch literal 687312 zcmZU&1yEhluQ!Uj6?b=vJ4K4SdvVu;9^AdSyL*dMwD`f@-QDHj4zK@v@4MeO@2#2a zWGDG0d#&s}li7(>QIbYQB1D3KfIyX%kx+wxfchsxwIRU$)94@r$sr(+Ds9BYRb<7* z$yHn&Ep6;AARuHSQ`6x!)K_rBr#eq!(=Z?z3VB5ZAWK50q3C7ci6{_d=_%l#ev%Ln zgX+mCp`r?kDUsq}F}@Wo!@>HHB#3?|7zjp9^$3!5I;?)&^!BOloJ`O4`)HWbeOzy1 zg}}zC(a3_Tgh*fxgz)4@XTpN|Zb|MeMXUVtIH41xIT1X78Cr_qQ&LuT{Y5yYjvy(d zf7tA0Jd_L=8Zf7zV(s!*3|9HvVjV2J3$#J<82R)f{#;X2-%qy7AXma2f$B|AMdfdJ z9kCHbN~0b}%rs{Y5DdtXya^nZJuGe5qs^SUM^qbE%)}BLi8K|76Qc{kVjl?xZh>Xf zO2Zfy;>1LxvA+n7WbXdrPtM6BCgn=))9SalVUv7^itK)z zZ}X4pEHC@4Gh&x!=M~5PQn38ZqryKZl)E&OwD?ynEBHEhG*V+&-@oc0cz%ab zOB`+J;ix0r4-u4Qz&wY01yMM`a0RPx3%0`41Z8iddBU`PXZ1u3?}2B762(9( z`mP~^H5(E`AwP^}Dn^>Xij5K`TCXBV9ZH=*`~$8!2&s_r7fMC2ZNXLn*0k3#(mbS4 zU|S*NG4ma)6v#XS?yQFvtdN1&uuaYfWe9=1GvI`l1Me6pzhnDEz7CNeQ48|eW_h9! zjDe!ZjEb9;)-BAS=V64wiI9^RQDIVn{e`K)U4ga`V=KNP=2i%&nm?n%g){M8uW(}; z@>qroy%WC^s*}_pd^6tRdrOgHVf&2u@fALj6p>k^^MDf@J3a+1a&kNa=zM^9NWXs_ zbZ07TT(2Xrh6@KZz63@SGP!B|Lf}pt_{;sKTn|W22MB5GyWUgfojavuY!LYI}-ys(QL| zdT?64(0)wchL|g^TqjX;`&S!sdz^wAG|LBstVy5^O*URpdqF>D206Rc#`N3xo{Fn51z0AP|39eSv&& zd1rigfByol9wHN>AL1QC7cL=QW>REQZxX$ekeD}PUu+@OB!wP}IH+Kjc+IoNvlmZW zMms}mr%0&yqR6elU&de7R2Dx$Z<%KqJy9{?kZm@Rc{p5gvAibI7&J}62SuJrEX%ij#j2tlh(!}@XYv3|4iykn={sOaAIAs0dVL+B%O_& zEyNRVzhDmya0O%owpMkQug^y>9M;s&-_NyIc~`of>wp-1=fKu08MmB^*2VgP>+Ix) z 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®zO1r8xXh|M3FGgkllO6CzL#Q9Ou2)%$pxDezr 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

  • =t-m3os>WGl~c1_%p3p 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
      Jnl0NYrSKql?zT)?cYPU-~8cue+- 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≫aZ^GsT3C>fn2S)k8 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 I_e+3-%)SO~G5xdlR< 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

      QIVH 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 IM6N<$f>+z&sQ>@~ literal 0 HcmV?d00001 diff --git a/src/main/resources/image/god.png b/src/main/resources/image/god.png new file mode 100644 index 0000000000000000000000000000000000000000..48ef3b5a5d7cfd25531fc867a5d80cdfd04c1f0a GIT binary patch literal 802951 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�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�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≶Wvf?o5sXG@|9B z)olEC9;GFn*5!d!PK3ue7Vnw#PkWiL2LIRmiYy_cEfSEt6mJS+qig<#9x3KDTlYdA zJ^i@QaicH8@@xk@aWc6p)I*?6gCuJjrZDl&{wXIkYD``esLA)z

      @KE%@t>S0T6}Ve_VqSt+FjVIt!QAX@-s9g=FG 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%0AwnboNcBI0vHvy`|F{5e=21#c}5TO*zObMl4iV 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.=P~_Oyj`df-Lz01OUG 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 z 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�OO5dpn3)AH0SU zou}f3H;e{MFeHPJ%6^vm1=HY=sP%>g+Tb&VNLDt3ZOJ(RAb6IFdD2p`M;zNKlUY&< z_HIcdTg57^Ck=P9RDxC*7Cy z7fB@@RLb<#=U;i2ebL180r+5p9KbZj`~`dXPeX58r!Q4p3P82DM*;U#doc@Gu)VP0 z8p}Zrzdl?7bjX0oN-nZjtXz^J-=pJV8A7+BQX02$KTH8x5o0$*uiasM0u*%*0yqO6 zqg!p6K~6T$3l^MnqlXE)r-dcgEklaa_Z_M^bPzM#w}1CLFNZI@d?}ox-$@~R?e6Ud2oZ1bC{wsL#;KDg z*rG(Zv7?97JCs


      I58~2_VYODdyQD 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

      ;9rFtsjt@A6n7pRz!?RlzSUWS*O-?RP%B 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 Date: Sun, 1 Dec 2024 17:39:33 +0900 Subject: [PATCH 390/563] =?UTF-8?q?feat:=20=EA=B3=84=EC=95=BD=EC=84=9C,=20?= =?UTF-8?q?=EB=B0=9C=EC=A3=BC=EC=84=9C,=20=EC=88=98=EC=A3=BC=EC=84=9C=20?= =?UTF-8?q?=EC=95=8C=EB=9E=8C=20=EB=93=B1=EB=A1=9D=20=EC=99=84=EB=A3=8C=20?= =?UTF-8?q?(#182)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/AlarmCommandService.java | 13 ++++- .../domain/aggregate/entity/Alarm.java | 3 ++ .../service/AlarmCommandServiceImpl.java | 49 +++++++++++++++++-- .../alarm/query/dto/AlarmSelectDTO.java | 2 + .../alarm/query/dto/AlarmSelectReadDTO.java | 1 + .../alarm/query/dto/AlarmSelectUnreadDTO.java | 1 + .../query/service/AlarmQueryServiceImpl.java | 7 +++ .../alarm/scheduler/AlarmScheduler.java | 12 +++-- .../service/ContractCommandServiceImpl.java | 12 ++++- .../service/OrderCommandServiceImpl.java | 8 ++- .../controller/PurchaseOrderController.java | 2 +- .../PurchaseOrderCommandServiceImpl.java | 11 ++++- .../alarm/query/repository/AlarmMapper.xml | 34 +++++++++---- 13 files changed, 130 insertions(+), 25 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/application/service/AlarmCommandService.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/service/AlarmCommandService.java index d74db3e9..eab3621d 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/application/service/AlarmCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/service/AlarmCommandService.java @@ -4,18 +4,27 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import stanl_2.final_backend.domain.alarm.command.application.dto.AlarmRegistDTO; import stanl_2.final_backend.domain.alarm.command.domain.aggregate.entity.Alarm; +import stanl_2.final_backend.domain.contract.command.domain.aggregate.entity.Contract; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeAlarmDTO; +import stanl_2.final_backend.domain.order.command.domain.aggregate.entity.Order; +import stanl_2.final_backend.domain.purchase_order.command.domain.aggregate.entity.PurchaseOrder; public interface AlarmCommandService { SseEmitter subscribe(AlarmRegistDTO alarmRegistDTO, HttpServletResponse response); void sendToClient(SseEmitter emitter, String emitterId, Object data); - void send(String memberLoginId, String message, String redirectUrl, String type, String createdAt); + void send(String memberLoginId, String message, String redirectUrl, String tag, String type, String createdAt); - Alarm createAlarm(String memberId, String message, String redirectUrl, String type, String createdAt); + Alarm createAlarm(String memberId, String message, String redirectUrl, String tag, String type, String createdAt); void sendNoticeAlarm(NoticeAlarmDTO noticeAlarmDTO); Boolean updateReadStatus(String alarmId); + + void sendContractAlarm(Contract contract); + + void sendPurchaseOrderAlarm(PurchaseOrder purchaseOrder); + + void sendOrderAlarm(Order order); } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/aggregate/entity/Alarm.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/aggregate/entity/Alarm.java index 805a7484..29538ff1 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/aggregate/entity/Alarm.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/aggregate/entity/Alarm.java @@ -35,6 +35,9 @@ public class Alarm { @Column(name = "ALR_TYPE", nullable = false) private String type = "NOTICE"; + @Column(name = "ALR_TAG", nullable = false) + private String tag; + @Column(name = "ALR_READ_STAT", nullable = false) private Boolean readStatus; diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java index 35b6f4b5..8ab34ade 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java @@ -14,9 +14,12 @@ import stanl_2.final_backend.domain.alarm.command.domain.repository.EmitterRepository; import stanl_2.final_backend.domain.alarm.common.exception.AlarmCommonException; import stanl_2.final_backend.domain.alarm.common.exception.AlarmErrorCode; +import stanl_2.final_backend.domain.contract.command.domain.aggregate.entity.Contract; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeAlarmDTO; +import stanl_2.final_backend.domain.order.command.domain.aggregate.entity.Order; +import stanl_2.final_backend.domain.purchase_order.command.domain.aggregate.entity.PurchaseOrder; import stanl_2.final_backend.domain.schedule.common.exception.ScheduleCommonException; import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; @@ -106,9 +109,9 @@ public void sendToClient(SseEmitter emitter, String emitterId, Object data) { @Override @Transactional - public void send(String memberId, String message, String redirectUrl, String type, String createdAt){ + public void send(String memberId, String message, String redirectUrl, String tag, String type, String createdAt){ - Alarm alarm = alarmRepository.save(createAlarm(memberId, message, redirectUrl, type, createdAt)); + Alarm alarm = alarmRepository.save(createAlarm(memberId, message, redirectUrl, tag, type, createdAt)); Map sseEmitters = emitterRepository.findAllEmitterStartWithByMemberId(memberId); sseEmitters.forEach( @@ -121,12 +124,13 @@ public void send(String memberId, String message, String redirectUrl, String typ @Override @Transactional - public Alarm createAlarm(String memberId, String message, String redirectUrl, String type, String createdAt) { + public Alarm createAlarm(String memberId, String message, String redirectUrl, String tag, String type, String createdAt) { Alarm alarm = new Alarm(); alarm.setMemberId(memberId); alarm.setMessage(message); alarm.setRedirectUrl(redirectUrl); + alarm.setTag(tag); alarm.setType(type); alarm.setReadStatus(false); alarm.setCreatedAt(createdAt); @@ -148,10 +152,47 @@ public void sendNoticeAlarm(NoticeAlarmDTO noticeAlarmDTO){ String redirectUrl = "/api/v1/notice/" + noticeAlarmDTO.getNoticeId(); String createdAt = getCurrentTime(); - send(memberId, message, redirectUrl, type, createdAt); + send(memberId, message, redirectUrl, tag, type, createdAt); }); } + @Override + @Transactional + public void sendContractAlarm(Contract contract) { + + String type = "CONTRACT"; + String tag = "계약서"; + String message = contract.getCustomerName() +" 고객님의 계약서가 승인되었습니다."; + String redirectUrl = "/api/v1/employee/" + contract.getContractId(); + String createdAt = getCurrentTime(); + + send(contract.getMemberId(), message, redirectUrl, tag, type, createdAt); + } + + @Override + public void sendPurchaseOrderAlarm(PurchaseOrder purchaseOrder) { + + String type = "CONTRACT"; + String tag = "발주서"; + String message = purchaseOrder.getTitle() +" 발주서가 승인되었습니다."; + String redirectUrl = "/api/v1/purchase-order/admin/" + purchaseOrder.getPurchaseOrderId(); + String createdAt = getCurrentTime(); + + send(purchaseOrder.getMemberId(), message, redirectUrl, tag, type, createdAt); + } + + @Override + public void sendOrderAlarm(Order order) { + + String type = "CONTRACT"; + String tag = "수주서"; + String message = order.getTitle() +" 수주서가 승인되었습니다."; + String redirectUrl = "/api/v1/order/employee/" + order.getOrderId(); + String createdAt = getCurrentTime(); + + send(order.getMemberId(), message, redirectUrl, tag, type, createdAt); + } + @Override @Transactional public Boolean updateReadStatus(String alarmId) { diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java index 5e907885..41ed3693 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java @@ -13,8 +13,10 @@ public class AlarmSelectDTO { private String message; private String type; + private String tag; private String redirectUrl; private Boolean readStatus; + private String createdAt; private String memberLoginId; } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectReadDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectReadDTO.java index a18d0c02..5b0625fb 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectReadDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectReadDTO.java @@ -10,6 +10,7 @@ public class AlarmSelectReadDTO { private String message; private String type; + private String tag; private String redirectUrl; private Boolean readStatus; diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectUnreadDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectUnreadDTO.java index b488f5df..036fd1a6 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectUnreadDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectUnreadDTO.java @@ -10,6 +10,7 @@ public class AlarmSelectUnreadDTO { private String message; private String type; + private String tag; private String redirectUrl; private Boolean readStatus; diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryServiceImpl.java index 32c3a413..08e89b66 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryServiceImpl.java @@ -39,6 +39,13 @@ public AlarmSelectTypeDTO selectMemberByAlarmType(String memberLoginId) { AlarmSelectTypeDTO alarmSelectTypeDTO = alarmMapper.findNumberOfAlarmsByType(memberId); + if(alarmSelectTypeDTO == null){ + AlarmSelectTypeDTO alarmNullSelectTypeDTO + = new AlarmSelectTypeDTO(0,0,0); + + return alarmNullSelectTypeDTO; + } + return alarmSelectTypeDTO; } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java index 1fe3b13a..df8fda98 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.alarm.scheduler; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @@ -15,6 +16,7 @@ @Service("AlarmSchdulerService") +@Slf4j public class AlarmScheduler { private final AlarmCommandService alarmCommandService; @@ -31,7 +33,7 @@ public AlarmScheduler(AlarmCommandService alarmCommandService, ScheduleQueryServ } // @Scheduled(cron = "0 0 2 * * *") // 매일 새벽 2시에 실행) - @Scheduled(cron = "0 8 22 * * *") + @Scheduled(cron = "0 31 11 * * *") @Transactional public void alarmTodaySchedule(){ @@ -46,12 +48,12 @@ public void alarmTodaySchedule(){ String memberId = schedule.getMemberId(); String type = "SCHEDULE"; - String message = "[" + type + "] 금일 " + Hour + "시 " + Minute + "분에 '" - + schedule.getName() + "' 일정이 있습니다"; - String redirectUrl = "/api/v1/schedule/" + schedule.getMemberId(); + String tag = schedule.getTag(); + String message = "금일 " + Hour + "시 " + Minute + "분에 '" + schedule.getName() + "' 일정이 있습니다"; + String redirectUrl = "/api/v1/schedule"; String createdAt = getCurrentTime(); - alarmCommandService.send(memberId, message, redirectUrl, type, createdAt); + alarmCommandService.send(memberId, message, redirectUrl, tag, type, createdAt); }); } } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index ab977674..d3420639 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -6,6 +6,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.alarm.command.application.service.AlarmCommandService; import stanl_2.final_backend.domain.contract.command.application.dto.*; import stanl_2.final_backend.domain.contract.command.application.service.ContractCommandService; import stanl_2.final_backend.domain.contract.command.domain.aggregate.entity.Contract; @@ -49,9 +50,15 @@ public class ContractCommandServiceImpl implements ContractCommandService { private final ModelMapper modelMapper; private final AESUtils aesUtils; private final S3FileService s3FileService; + private final AlarmCommandService alarmCommandService; @Autowired - public ContractCommandServiceImpl(ContractRepository contractRepository, UpdateHistoryRepository updateHistoryRepository, AuthQueryService authQueryService, CustomerQueryService customerQueryService, MemberQueryService memberQueryService, CustomerCommandService customerCommandService, ProductQueryService productQueryService, ProductCommandService productCommandService, SalesHistoryCommandService salesHistoryCommandService, ModelMapper modelMapper, AESUtils aesUtils, S3FileService s3FileService) { + public ContractCommandServiceImpl(ContractRepository contractRepository, UpdateHistoryRepository updateHistoryRepository, + AuthQueryService authQueryService, CustomerQueryService customerQueryService, + MemberQueryService memberQueryService, CustomerCommandService customerCommandService, + ProductQueryService productQueryService, ProductCommandService productCommandService, + SalesHistoryCommandService salesHistoryCommandService, ModelMapper modelMapper, + AESUtils aesUtils, S3FileService s3FileService, AlarmCommandService alarmCommandService) { this.contractRepository = contractRepository; this.updateHistoryRepository = updateHistoryRepository; this.authQueryService = authQueryService; @@ -64,6 +71,7 @@ public ContractCommandServiceImpl(ContractRepository contractRepository, UpdateH this.modelMapper = modelMapper; this.aesUtils = aesUtils; this.s3FileService = s3FileService; + this.alarmCommandService = alarmCommandService; } private String getCurrentTime() { @@ -253,6 +261,8 @@ public void modifyContractStatus(ContractStatusModifyDTO contractStatusModifyDTO contractRepository.save(contract); + alarmCommandService.sendContractAlarm(contract); + if (contractStatusModifyDTO.getStatus().equals("APPROVED")) { // 판매 내역 등록 salesHistoryCommandService.registerSalesHistory(contract.getContractId()); diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java index 027670ae..56b6aaa8 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java @@ -3,6 +3,7 @@ import org.modelmapper.ModelMapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.alarm.command.application.service.AlarmCommandService; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.order.command.application.dto.OrderModifyDTO; import stanl_2.final_backend.domain.order.command.application.dto.OrderRegistDTO; @@ -23,11 +24,14 @@ public class OrderCommandServiceImpl implements OrderCommandService { private final OrderRepository orderRepository; private final AuthQueryService authQueryService; private final ModelMapper modelMapper; + private final AlarmCommandService alarmCommandService; - public OrderCommandServiceImpl(OrderRepository orderRepository, AuthQueryService authQueryService, ModelMapper modelMapper) { + public OrderCommandServiceImpl(OrderRepository orderRepository, AuthQueryService authQueryService, + ModelMapper modelMapper, AlarmCommandService alarmCommandService) { this.orderRepository = orderRepository; this.authQueryService = authQueryService; this.modelMapper = modelMapper; + this.alarmCommandService = alarmCommandService; } private String getCurrentTime() { @@ -106,5 +110,7 @@ public void modifyOrderStatus(OrderStatusModifyDTO orderStatusModifyDTO) { order.setAdminId(adminId); orderRepository.save(order); + + alarmCommandService.sendOrderAlarm(order); } } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java index 4a415958..d498a5b2 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java @@ -91,7 +91,7 @@ public ResponseEntity deletePurchaseOrder(@PathVar @ApiResponse(responseCode = "200", description = "발주서 승인 상태 수정 성공", content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) }) - @PutMapping("stauts/{purchaseOrderId}") + @PutMapping("status/{purchaseOrderId}") public ResponseEntity putPurchaseOrderStatus(@PathVariable String purchaseOrderId, PurchaseOrderStatusModifyDTO purchaseOrderStatusModifyDTO, Principal principal) { diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java index 78d3b340..b7283178 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java @@ -1,9 +1,11 @@ package stanl_2.final_backend.domain.purchase_order.command.domain.service; +import lombok.extern.slf4j.Slf4j; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.alarm.command.application.service.AlarmCommandService; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.order.command.domain.aggregate.entity.Order; import stanl_2.final_backend.domain.order.command.domain.repository.OrderRepository; @@ -23,19 +25,23 @@ import java.time.format.DateTimeFormatter; @Service +@Slf4j public class PurchaseOrderCommandServiceImpl implements PurchaseOrderCommandService { private final PurchaseOrderRepository purchaseOrderRepository; private final OrderRepository orderRepository; private final AuthQueryService authQueryService; private final ModelMapper modelMapper; + private final AlarmCommandService alarmCommandService; @Autowired - public PurchaseOrderCommandServiceImpl(PurchaseOrderRepository purchaseOrderRepository, OrderRepository orderRepository, AuthQueryService authQueryService, ModelMapper modelMapper) { + public PurchaseOrderCommandServiceImpl(PurchaseOrderRepository purchaseOrderRepository, OrderRepository orderRepository, + AuthQueryService authQueryService, ModelMapper modelMapper, AlarmCommandService alarmCommandService) { this.purchaseOrderRepository = purchaseOrderRepository; this.orderRepository = orderRepository; this.authQueryService = authQueryService; this.modelMapper = modelMapper; + this.alarmCommandService = alarmCommandService; } private String getCurrentTime() { @@ -130,5 +136,8 @@ public void modifyPurchaseOrderStatus(PurchaseOrderStatusModifyDTO purchaseOrder purchaseOrder.setAdminId(adminId); purchaseOrderRepository.save(purchaseOrder); + + alarmCommandService.sendPurchaseOrderAlarm(purchaseOrder); } } + diff --git a/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml b/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml index 1edaf1c8..a633cc56 100644 --- a/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml @@ -20,13 +20,16 @@ + + - SELECT a.alr_msg, + a.alr_tag, a.alr_type, a.alr_url, - a.alr_read_stat + a.alr_read_stat, + a.created_at FROM tb_alarm a WHERE a.mem_id = #{memberId} AND a.alr_type = #{type} From c8b2cae015ada6bc03afb6c326849e63145d7e74 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Sun, 1 Dec 2024 18:20:27 +0900 Subject: [PATCH 391/563] =?UTF-8?q?feat:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=A0=84=EC=B2=B4=20=EC=95=8C=EB=A6=BC=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20&=20=EB=B0=9C=EC=A3=BC=EC=84=9C=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EC=B2=98=EB=A6=AC=20=EC=99=84=EB=A3=8C=20(#182)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/AlarmCommandServiceImpl.java | 27 +++++++++++++++++-- .../controller/PurchaseOrderController.java | 2 +- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java index 8ab34ade..ae0ed3d7 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java @@ -27,6 +27,8 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; @@ -142,13 +144,34 @@ public Alarm createAlarm(String memberId, String message, String redirectUrl, St @Transactional public void sendNoticeAlarm(NoticeAlarmDTO noticeAlarmDTO){ - List memberIdList = memberQueryService.selectMemberByRole(noticeAlarmDTO.getTag()); + List memberIdList = new ArrayList<>(); + + if(noticeAlarmDTO.getTag().equals("all")){ + // 결과 합치기 + memberIdList.addAll(memberQueryService.selectMemberByRole("employee")); + memberIdList.addAll(memberQueryService.selectMemberByRole("admin")); + memberIdList.addAll(memberQueryService.selectMemberByRole("god")); + // 중복 제거 + memberIdList = new ArrayList<>(new HashSet<>(memberIdList)); + } else { + memberQueryService.selectMemberByRole(noticeAlarmDTO.getTag()); + } memberIdList.forEach(member -> { String memberId = member; String type = "NOTICE"; String tag = noticeAlarmDTO.getClassification(); - String message = "[" + tag + "] 영업 관리자 대상 공지사항이 등록되었습니다."; + + String target = null; + if(noticeAlarmDTO.getTag().equals("all")){ + target = "전체"; + } else if(noticeAlarmDTO.getTag().equals("admin")) { + target = "영업관리자"; + } else { + target = "영업담당자"; + } + + String message = target + " 대상 공지사항이 등록되었습니다."; String redirectUrl = "/api/v1/notice/" + noticeAlarmDTO.getNoticeId(); String createdAt = getCurrentTime(); diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java index d498a5b2..ab4207ad 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java @@ -93,7 +93,7 @@ public ResponseEntity deletePurchaseOrder(@PathVar }) @PutMapping("status/{purchaseOrderId}") public ResponseEntity putPurchaseOrderStatus(@PathVariable String purchaseOrderId, - PurchaseOrderStatusModifyDTO purchaseOrderStatusModifyDTO, + @RequestBody PurchaseOrderStatusModifyDTO purchaseOrderStatusModifyDTO, Principal principal) { purchaseOrderStatusModifyDTO.setPurchaseOrderId(purchaseOrderId); From ae211db5140cfce7bf7f2a73eee066239d0c2c76 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Sun, 1 Dec 2024 19:11:07 +0900 Subject: [PATCH 392/563] =?UTF-8?q?feat:=20ApiService=20putParams=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/command/application/dto/SigninResponseDTO.java | 1 + .../command/domain/service/AuthCommandServiceImpl.java | 3 ++- .../final_backend/domain/member/query/dto/MemberDTO.java | 1 + src/main/resources/application-prod.yml | 2 +- .../domain/member/query/repository/MemberMapper.xml | 5 ++++- 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninResponseDTO.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninResponseDTO.java index 4eae9544..032e1178 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninResponseDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninResponseDTO.java @@ -12,4 +12,5 @@ public class SigninResponseDTO { private String name; private String role; private String auth; + private String imageUrl; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java index c8c5715c..d965219b 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java @@ -133,7 +133,8 @@ public SigninResponseDTO signin(SigninRequestDTO signinRequestDTO) throws Genera accessToken, refreshToken, aesUtils.decrypt(memberDetails.getMember().getName()), memberDetails.getMember().getPosition(), - firstRole + firstRole, + aesUtils.decrypt(memberDetails.getMember().getImageUrl()) ); } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java index dc5b0da3..02b6527d 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java @@ -30,4 +30,5 @@ public class MemberDTO { private String account; private String centerId; private String createdAt; + private String updatedAt; } diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 72c1ffe0..7eb65804 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -11,7 +11,7 @@ spring: timeout: 5000ms jpa: hibernate: - ddl-auto: create + ddl-auto: update open-in-view: false logging: diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml index 5c9479d0..31bb6116 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml @@ -24,6 +24,7 @@ + From d822ab22b96c8d729e3ef2f08bd5ed876f1eac1c Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Sun, 1 Dec 2024 19:11:07 +0900 Subject: [PATCH 393/563] =?UTF-8?q?fix:=20=ED=9A=8C=EC=9B=90=20S3=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=A0=81=EC=9A=A9(#192)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/command/application/dto/SigninResponseDTO.java | 1 + .../command/domain/service/AuthCommandServiceImpl.java | 3 ++- .../final_backend/domain/member/query/dto/MemberDTO.java | 1 + src/main/resources/application-prod.yml | 2 +- .../domain/member/query/repository/MemberMapper.xml | 5 ++++- 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninResponseDTO.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninResponseDTO.java index 4eae9544..032e1178 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninResponseDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/SigninResponseDTO.java @@ -12,4 +12,5 @@ public class SigninResponseDTO { private String name; private String role; private String auth; + private String imageUrl; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java index c8c5715c..d965219b 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java @@ -133,7 +133,8 @@ public SigninResponseDTO signin(SigninRequestDTO signinRequestDTO) throws Genera accessToken, refreshToken, aesUtils.decrypt(memberDetails.getMember().getName()), memberDetails.getMember().getPosition(), - firstRole + firstRole, + aesUtils.decrypt(memberDetails.getMember().getImageUrl()) ); } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java index dc5b0da3..02b6527d 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberDTO.java @@ -30,4 +30,5 @@ public class MemberDTO { private String account; private String centerId; private String createdAt; + private String updatedAt; } diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 72c1ffe0..7eb65804 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -11,7 +11,7 @@ spring: timeout: 5000ms jpa: hibernate: - ddl-auto: create + ddl-auto: update open-in-view: false logging: diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml index 5c9479d0..31bb6116 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml @@ -24,6 +24,7 @@ + From 26fe1501ed1725f52ee8208356866e99f86e4218 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Sun, 1 Dec 2024 22:53:23 +0900 Subject: [PATCH 394/563] =?UTF-8?q?feat:=20=EC=88=98=EC=A3=BC=EC=84=9C=20?= =?UTF-8?q?=EC=97=91=EC=85=80=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=88=98=EC=A0=95=20(#193)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ContractQueryServiceImpl.java | 1 + .../service/OrderCommandServiceImpl.java | 15 +++- .../query/controller/OrderController.java | 28 ++++++- .../domain/order/query/dto/OrderExcelDTO.java | 28 +++++++ .../order/query/dto/OrderSelectSearchDTO.java | 6 +- .../order/query/repository/OrderMapper.java | 7 +- .../query/service/OrderQueryService.java | 6 +- .../query/service/OrderQueryServiceImpl.java | 45 +++++++++++- .../controller/PurchaseOrderController.java | 4 +- .../PurchaseOrderCommandServiceImpl.java | 2 + .../order/query/repository/OrderMapper.xml | 73 +++++++++++++++++-- 11 files changed, 197 insertions(+), 18 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderExcelDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java index 4b07b0f0..bfe8f828 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java @@ -286,6 +286,7 @@ public Page selectBySearch(ContractSearchDTO contractSearchDT } @Override + @Transactional(readOnly = true) public void exportContractToExcel(HttpServletResponse response) { List contractExcels = contractMapper.findContractForExcel(); diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java index 027670ae..7c4217c8 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.order.command.domain.service; +import org.apache.commons.lang3.StringEscapeUtils; import org.modelmapper.ModelMapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -12,6 +13,7 @@ import stanl_2.final_backend.domain.order.command.domain.repository.OrderRepository; import stanl_2.final_backend.domain.order.common.exception.OrderCommonException; import stanl_2.final_backend.domain.order.common.exception.OrderErrorCode; +import stanl_2.final_backend.domain.s3.command.application.service.S3FileService; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -23,11 +25,13 @@ public class OrderCommandServiceImpl implements OrderCommandService { private final OrderRepository orderRepository; private final AuthQueryService authQueryService; private final ModelMapper modelMapper; + private final S3FileService s3FileService; - public OrderCommandServiceImpl(OrderRepository orderRepository, AuthQueryService authQueryService, ModelMapper modelMapper) { + public OrderCommandServiceImpl(OrderRepository orderRepository, AuthQueryService authQueryService, ModelMapper modelMapper, S3FileService s3FileService) { this.orderRepository = orderRepository; this.authQueryService = authQueryService; this.modelMapper = modelMapper; + this.s3FileService = s3FileService; } private String getCurrentTime() { @@ -41,7 +45,11 @@ public void registerOrder(OrderRegistDTO orderRegistDTO) { String memberId = authQueryService.selectMemberIdByLoginId(orderRegistDTO.getMemberId()); + String unescapedHtml = StringEscapeUtils.unescapeJson(orderRegistDTO.getContent()); + String updatedS3Url = s3FileService.uploadHtml(unescapedHtml, orderRegistDTO.getTitle()); + orderRegistDTO.setMemberId(memberId); + orderRegistDTO.setContent(updatedS3Url); Order order = modelMapper.map(orderRegistDTO, Order.class); @@ -60,6 +68,11 @@ public OrderModifyDTO modifyOrder(OrderModifyDTO orderModifyDTO) { throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); } + String unescapedHtml = StringEscapeUtils.unescapeJson(orderModifyDTO.getContent()); + String updatedS3Url = s3FileService.uploadHtml(unescapedHtml, orderModifyDTO.getTitle()); + + orderModifyDTO.setContent(updatedS3Url); + Order updateOrder = modelMapper.map(orderModifyDTO, Order.class); updateOrder.setCreatedAt(order.getCreatedAt()); updateOrder.setUpdatedAt(order.getUpdatedAt()); diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java b/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java index af2458c4..7cedae20 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java @@ -5,9 +5,12 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -17,6 +20,7 @@ import stanl_2.final_backend.domain.order.query.dto.OrderSelectSearchDTO; import stanl_2.final_backend.domain.order.query.service.OrderQueryService; +import java.security.GeneralSecurityException; import java.security.Principal; @RestController("queryOrderController") @@ -151,12 +155,16 @@ public ResponseEntity getDetailOrder(@PathVariable("orderI }) @GetMapping("search") public ResponseEntity getSearchOrder(@RequestParam(required = false) String title, + @RequestParam(required = false) String orderId, @RequestParam(required = false) String status, @RequestParam(required = false) String adminId, @RequestParam(required = false) String searchMemberId, @RequestParam(required = false) String startDate, @RequestParam(required = false) String endDate, - @PageableDefault(size = 10) Pageable pageable) { + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder, + @RequestParam(required = false) String productName, + @PageableDefault(size = 10) Pageable pageable) throws GeneralSecurityException { OrderSelectSearchDTO orderSelectSearchDTO = new OrderSelectSearchDTO(); orderSelectSearchDTO.setTitle(title); @@ -165,6 +173,13 @@ public ResponseEntity getSearchOrder(@RequestParam(require orderSelectSearchDTO.setSearchMemberId(searchMemberId); orderSelectSearchDTO.setStartDate(startDate); orderSelectSearchDTO.setEndDate(endDate); + orderSelectSearchDTO.setProductName(productName); + orderSelectSearchDTO.setOrderId(orderId); + + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } Page responseOrders = orderQueryService.selectSearchOrders(orderSelectSearchDTO, pageable); @@ -174,4 +189,15 @@ public ResponseEntity getSearchOrder(@RequestParam(require .result(responseOrders) .build()); } + + @Operation(summary = "수주서 엑셀 다운로드") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "수주서 엑셀 다운로드 성공", + content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) + }) + @GetMapping("excel") + public void exportOrder(HttpServletResponse response) { + + orderQueryService.exportOrder(response); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderExcelDTO.java b/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderExcelDTO.java new file mode 100644 index 00000000..e6ca3736 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderExcelDTO.java @@ -0,0 +1,28 @@ +package stanl_2.final_backend.domain.order.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import stanl_2.final_backend.global.excel.ExcelColumnName; + +@Getter +@AllArgsConstructor +public class OrderExcelDTO { + + @ExcelColumnName(name = "수주서 번호") + private String orderId; + + @ExcelColumnName(name = "수주서명") + private String title; + + @ExcelColumnName(name = "승인 상태") + private String status; + + @ExcelColumnName(name = "제품명") + private String carName; + + @ExcelColumnName(name = "수주자") + private String memberId; + + @ExcelColumnName(name = "수주일") + private String createdAt; +} diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectSearchDTO.java index fa1487b4..35f285c7 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectSearchDTO.java @@ -1,9 +1,6 @@ package stanl_2.final_backend.domain.order.query.dto; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import lombok.*; @AllArgsConstructor @NoArgsConstructor @@ -22,4 +19,5 @@ public class OrderSelectSearchDTO { private String productName; private String startDate; private String endDate; + private String createdAt; } diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java b/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java index 238694d1..8440b36d 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java @@ -2,6 +2,7 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; +import stanl_2.final_backend.domain.order.query.dto.OrderExcelDTO; import stanl_2.final_backend.domain.order.query.dto.OrderSelectAllDTO; import stanl_2.final_backend.domain.order.query.dto.OrderSelectIdDTO; import stanl_2.final_backend.domain.order.query.dto.OrderSelectSearchDTO; @@ -30,7 +31,11 @@ List findSearchOrderByMemberId(@Param("offset") int offset List findSearchOrder(@Param("offset") int offset, @Param("pageSize") int pageSize, - @Param("orderSelectSearchDTO") OrderSelectSearchDTO orderSelectSearchDTO); + @Param("orderSelectSearchDTO") OrderSelectSearchDTO orderSelectSearchDTO, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); int findOrderSearchCount(OrderSelectSearchDTO orderSelectSearchDTO); + + List findOrderForExcel(); } diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java index ae23d4aa..1a0826ee 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java @@ -1,11 +1,13 @@ package stanl_2.final_backend.domain.order.query.service; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import stanl_2.final_backend.domain.order.query.dto.OrderSelectAllDTO; import stanl_2.final_backend.domain.order.query.dto.OrderSelectIdDTO; import stanl_2.final_backend.domain.order.query.dto.OrderSelectSearchDTO; +import java.security.GeneralSecurityException; import java.util.Map; public interface OrderQueryService { @@ -19,5 +21,7 @@ public interface OrderQueryService { OrderSelectIdDTO selectDetailOrder(OrderSelectIdDTO orderSelectIdDTO); - Page selectSearchOrders(OrderSelectSearchDTO orderSelectSearchDTO, Pageable pageable); + Page selectSearchOrders(OrderSelectSearchDTO orderSelectSearchDTO, Pageable pageable) throws GeneralSecurityException; + + void exportOrder(HttpServletResponse response); } diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java index f31aa437..e4dc1d3a 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java @@ -1,20 +1,26 @@ package stanl_2.final_backend.domain.order.query.service; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import stanl_2.final_backend.domain.order.common.exception.OrderCommonException; import stanl_2.final_backend.domain.order.common.exception.OrderErrorCode; +import stanl_2.final_backend.domain.order.query.dto.OrderExcelDTO; import stanl_2.final_backend.domain.order.query.dto.OrderSelectAllDTO; import stanl_2.final_backend.domain.order.query.dto.OrderSelectIdDTO; import stanl_2.final_backend.domain.order.query.dto.OrderSelectSearchDTO; import stanl_2.final_backend.domain.order.query.repository.OrderMapper; +import stanl_2.final_backend.global.excel.ExcelUtilsV1; +import java.security.GeneralSecurityException; import java.util.List; @Service @@ -22,13 +28,17 @@ public class OrderQueryServiceImpl implements OrderQueryService { private final OrderMapper orderMapper; private final AuthQueryService authQueryService; + private final MemberQueryService memberQueryService; private final RedisTemplate redisTemplate; + private final ExcelUtilsV1 excelUtilsV1; @Autowired - public OrderQueryServiceImpl(OrderMapper orderMapper, AuthQueryService authQueryService, RedisTemplate redisTemplate) { + public OrderQueryServiceImpl(OrderMapper orderMapper, AuthQueryService authQueryService, MemberQueryService memberQueryService, RedisTemplate redisTemplate, ExcelUtilsV1 excelUtilsV1) { this.orderMapper = orderMapper; this.authQueryService = authQueryService; + this.memberQueryService = memberQueryService; this.redisTemplate = redisTemplate; + this.excelUtilsV1 = excelUtilsV1; } // 영업사원 조회 @@ -147,19 +157,48 @@ public OrderSelectIdDTO selectDetailOrder(OrderSelectIdDTO orderSelectIdDTO) { @Override @Transactional(readOnly = true) - public Page selectSearchOrders(OrderSelectSearchDTO orderSelectSearchDTO, Pageable pageable) { + public Page selectSearchOrders(OrderSelectSearchDTO orderSelectSearchDTO, Pageable pageable) throws GeneralSecurityException { int offset = Math.toIntExact(pageable.getOffset()); int pageSize = pageable.getPageSize(); - List orders = orderMapper.findSearchOrder(offset, pageSize, orderSelectSearchDTO); + + // 정렬 정보 가져오기 + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } + + List orders = orderMapper.findSearchOrder(offset, pageSize, orderSelectSearchDTO, sortField, sortOrder); if (orders == null || orders.isEmpty()) { throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); } + for (OrderSelectSearchDTO order : orders) { + if (order.getMemberId() != null) { + String memberName = memberQueryService.selectNameById(order.getMemberId()); + order.setMemberName(memberName); + } + } + Integer count = orderMapper.findOrderSearchCount(orderSelectSearchDTO); int totalOrder = (count != null) ? count : 0; return new PageImpl<>(orders, pageable, totalOrder); } + + @Override + @Transactional(readOnly = true) + public void exportOrder(HttpServletResponse response) { + List orderExcels = orderMapper.findOrderForExcel(); + + if (orderExcels == null) { + throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); + } + + excelUtilsV1.download(OrderExcelDTO.class, orderExcels, "orderExcel", response); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java index 4a415958..ab4207ad 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java @@ -91,9 +91,9 @@ public ResponseEntity deletePurchaseOrder(@PathVar @ApiResponse(responseCode = "200", description = "발주서 승인 상태 수정 성공", content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) }) - @PutMapping("stauts/{purchaseOrderId}") + @PutMapping("status/{purchaseOrderId}") public ResponseEntity putPurchaseOrderStatus(@PathVariable String purchaseOrderId, - PurchaseOrderStatusModifyDTO purchaseOrderStatusModifyDTO, + @RequestBody PurchaseOrderStatusModifyDTO purchaseOrderStatusModifyDTO, Principal principal) { purchaseOrderStatusModifyDTO.setPurchaseOrderId(purchaseOrderId); diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java index 78d3b340..95af2d84 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java @@ -129,6 +129,8 @@ public void modifyPurchaseOrderStatus(PurchaseOrderStatusModifyDTO purchaseOrder purchaseOrder.setStatus(purchaseOrderStatusModifyDTO.getStatus()); purchaseOrder.setAdminId(adminId); + System.out.println("dddddd: " + purchaseOrderStatusModifyDTO.getStatus()); + purchaseOrderRepository.save(purchaseOrder); } } diff --git a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml index 51554325..1f03a6f1 100644 --- a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml @@ -4,7 +4,7 @@ "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> - + @@ -12,6 +12,8 @@ + + @@ -28,14 +30,21 @@ + + + + + + + + + - SELECT a.ord_id, a.ord_ttl, a.ord_stat, + a.created_at, + a.mem_id, b.conr_ttl, d.mem_name AS admin_name, c.mem_name AS mem_name, @@ -243,11 +254,48 @@ AND a.mem_id = #{searchMemberId} + + AND e.prod_name = #{productName} + AND a.created_at BETWEEN #{orderSelectSearchDTO.startDate} AND #{orderSelectSearchDTO.endDate} - ORDER BY a.created_at DESC + + + + + + ORDER BY a.ord_ttl ${sortOrder} + + + ORDER BY a.ord_stat ${sortOrder} + + + ORDER BY a.ord_id ${orderId} + + + ORDER BY a.mem_id ${memberId} + + + ORDER BY a.admin_id ${adminId} + + + ORDER BY a.created_at ${createdAt} + + + ORDER BY e.prod_name ${productName} + + + ORDER BY a.created_at DESC + + + + + + ORDER BY a.created_at DESC + + LIMIT #{pageSize} OFFSET #{offset}; @@ -275,4 +323,19 @@ + + \ No newline at end of file From 637de37211aadc6abdd4c1d92a0110a455053f4f Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Mon, 2 Dec 2024 00:55:51 +0900 Subject: [PATCH 395/563] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20front=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=ED=95=98=EB=A9=B4=EC=84=9C=20=EC=83=9D?= =?UTF-8?q?=EA=B8=B4=20=EC=88=98=EC=A0=95=20=EC=82=AC=ED=95=AD=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=20(#182)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/aggregate/entity/Alarm.java | 2 +- .../service/AlarmCommandServiceImpl.java | 19 ++++++++++++++----- .../alarm/query/dto/AlarmSelectDTO.java | 1 + .../alarm/scheduler/AlarmScheduler.java | 17 ++++++++++++++--- .../alarm/query/repository/AlarmMapper.xml | 2 ++ 5 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/aggregate/entity/Alarm.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/aggregate/entity/Alarm.java index 29538ff1..a21a47af 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/aggregate/entity/Alarm.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/aggregate/entity/Alarm.java @@ -29,7 +29,7 @@ public class Alarm { @Column(name = "ALR_MSG", nullable = false) private String message; - @Column(name = "ALR_URL", nullable = false) + @Column(columnDefinition = "TEXT", name = "ALR_URL", nullable = false) private String redirectUrl; @Column(name = "ALR_TYPE", nullable = false) diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java index ae0ed3d7..7fc16218 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java @@ -160,7 +160,12 @@ public void sendNoticeAlarm(NoticeAlarmDTO noticeAlarmDTO){ memberIdList.forEach(member -> { String memberId = member; String type = "NOTICE"; - String tag = noticeAlarmDTO.getClassification(); + String tag = null; + if(noticeAlarmDTO.getClassification().equals("important")){ + tag = "중요"; + } else { + tag = "일반"; + } String target = null; if(noticeAlarmDTO.getTag().equals("all")){ @@ -172,7 +177,9 @@ public void sendNoticeAlarm(NoticeAlarmDTO noticeAlarmDTO){ } String message = target + " 대상 공지사항이 등록되었습니다."; - String redirectUrl = "/api/v1/notice/" + noticeAlarmDTO.getNoticeId(); + String redirectUrl = "/notice/detail?tag=" + noticeAlarmDTO.getTag() + "&classification=" + noticeAlarmDTO.getClassification() + + "¬iceTitle=" + noticeAlarmDTO.getTitle() + "¬iceContent=" + noticeAlarmDTO.getContent() + + "¬iceId=" + noticeAlarmDTO.getNoticeId(); String createdAt = getCurrentTime(); send(memberId, message, redirectUrl, tag, type, createdAt); @@ -186,31 +193,33 @@ public void sendContractAlarm(Contract contract) { String type = "CONTRACT"; String tag = "계약서"; String message = contract.getCustomerName() +" 고객님의 계약서가 승인되었습니다."; - String redirectUrl = "/api/v1/employee/" + contract.getContractId(); + String redirectUrl = "/contract/list"; String createdAt = getCurrentTime(); send(contract.getMemberId(), message, redirectUrl, tag, type, createdAt); } @Override + @Transactional public void sendPurchaseOrderAlarm(PurchaseOrder purchaseOrder) { String type = "CONTRACT"; String tag = "발주서"; String message = purchaseOrder.getTitle() +" 발주서가 승인되었습니다."; - String redirectUrl = "/api/v1/purchase-order/admin/" + purchaseOrder.getPurchaseOrderId(); + String redirectUrl = "/purchase-order/list"; String createdAt = getCurrentTime(); send(purchaseOrder.getMemberId(), message, redirectUrl, tag, type, createdAt); } @Override + @Transactional public void sendOrderAlarm(Order order) { String type = "CONTRACT"; String tag = "수주서"; String message = order.getTitle() +" 수주서가 승인되었습니다."; - String redirectUrl = "/api/v1/order/employee/" + order.getOrderId(); + String redirectUrl = "/order/list"; String createdAt = getCurrentTime(); send(order.getMemberId(), message, redirectUrl, tag, type, createdAt); diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java index 41ed3693..d17b39a5 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java @@ -11,6 +11,7 @@ @Setter public class AlarmSelectDTO { + private String alarmId; private String message; private String type; private String tag; diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java index df8fda98..f92e1da6 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java @@ -33,7 +33,7 @@ public AlarmScheduler(AlarmCommandService alarmCommandService, ScheduleQueryServ } // @Scheduled(cron = "0 0 2 * * *") // 매일 새벽 2시에 실행) - @Scheduled(cron = "0 31 11 * * *") + @Scheduled(cron = "0 59 23 * * *") @Transactional public void alarmTodaySchedule(){ @@ -48,9 +48,20 @@ public void alarmTodaySchedule(){ String memberId = schedule.getMemberId(); String type = "SCHEDULE"; - String tag = schedule.getTag(); + + String tag = null; + if(schedule.getTag().equals("MEETING")){ + tag = "미팅"; + } else if(schedule.getTag().equals("SESSION")){ + tag = "회의"; + } else if(schedule.getTag().equals("VACATION")){ + tag = "휴가"; + } else{ + tag = "교육"; + } + String message = "금일 " + Hour + "시 " + Minute + "분에 '" + schedule.getName() + "' 일정이 있습니다"; - String redirectUrl = "/api/v1/schedule"; + String redirectUrl = "/schedule"; String createdAt = getCurrentTime(); alarmCommandService.send(memberId, message, redirectUrl, tag, type, createdAt); diff --git a/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml b/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml index a633cc56..f8d2a9b3 100644 --- a/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml @@ -51,6 +51,7 @@ AND a.alr_read_stat = false + @@ -60,6 +61,7 @@ - SELECT a.pur_ord_id, a.pur_ord_ttl, a.pur_ord_stat, + a.created_at, + a.mem_id, b.ord_ttl, d.mem_name AS admin_name, c.mem_name AS mem_name, @@ -239,31 +242,70 @@ AND a.created_at BETWEEN #{purchaseOrderSelectSearchDTO.startDate} AND #{purchaseOrderSelectSearchDTO.endDate} - ORDER BY a.created_at DESC + + + + + + ORDER BY a.pur_ord_id ${sortOrder} + + + ORDER BY a.pur_ord_ttl ${sortOrder} + + + ORDER BY a.pur_ord_stat ${sortOrder} + + + ORDER BY f.prod_name ${sortOrder} + + + ORDER BY c.mem_name ${sortOrder} + + + ORDER BY a.created_at ${sortOrder} + + + ORDER BY a.created_at DESC + + + + + + ORDER BY a.created_at DESC + + LIMIT #{pageSize} OFFSET #{offset}; - SELECT COUNT(*) AS cnt FROM tb_purchase_order a a.active = TRUE - - AND a.ord_ttl LIKE CONCAT('%', #{title}, '%') + + AND a.pur_ord_ttl LIKE CONCAT('%', #{purchaseOrderSelectSearchDTO.title}, '%') - - AND a.ord_stat = #{status} + + AND a.pur_ord_stat = #{purchaseOrderSelectSearchDTO.status} - - AND a.admin_id = #{adminId} + + AND a.admin_id = #{purchaseOrderSelectSearchDTO.adminId} - - AND a.mem_id = #{searchMemberId} + + AND a.mem_id = #{purchaseOrderSelectSearchDTO.searchMemberId} - - AND a.created_at BETWEEN #{startDate} AND #{endDate} + + AND a.created_at BETWEEN #{purchaseOrderSelectSearchDTO.startDate} AND #{purchaseOrderSelectSearchDTO.endDate} + + \ No newline at end of file From d68afc661374a3a278576d3faa3edc31185ff20b Mon Sep 17 00:00:00 2001 From: giuseog Date: Mon, 2 Dec 2024 03:05:08 +0900 Subject: [PATCH 397/563] =?UTF-8?q?feat:=20mybatis=EC=99=80=20=EC=8B=B8?= =?UTF-8?q?=EC=9A=B0=EB=8A=94=20=EC=A4=91(#183)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/CustomerQueryServiceImpl.java | 5 +- .../controller/SalesHistoryController.java | 50 ++++- .../dto/SalesHistoryStatisticsAverageDTO.java | 8 +- .../query/repository/SalesHistoryMapper.java | 16 +- .../service/SalesHistoryQueryServiceImpl.java | 43 +++- src/main/resources/application-prod.yml | 2 +- .../query/repository/SalesHistoryMapper.xml | 201 +++++++++++++++++- 7 files changed, 295 insertions(+), 30 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java index 0fb06c97..5109097d 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java @@ -116,8 +116,9 @@ public String selectCustomerNameById(String customerId) throws GeneralSecurityEx CustomerDTO customerInfoDTO = customerMapper.selectCustomerInfoById(customerId); - String customerName = aesUtils.decrypt(customerInfoDTO.getName()); +// String customerName = aesUtils.decrypt(customerInfoDTO.getName()); - return customerName; +// return customerName; + return customerInfoDTO.getName(); } } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java index 561bd361..6220599d 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java @@ -8,7 +8,9 @@ import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -40,11 +42,18 @@ public SalesHistoryController(SalesHistoryQueryService salesHistoryQueryService) }) @GetMapping("/employee") public ResponseEntity getAllSalesHistoryByEmployee(Principal principal, - @PageableDefault(size = 20) Pageable pageable){ + @PageableDefault(size = 20) Pageable pageable, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder){ SalesHistorySelectDTO salesHistorySelectDTO = new SalesHistorySelectDTO(); salesHistorySelectDTO.setSearcherName(principal.getName()); + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } + Page responseSalesHistory = salesHistoryQueryService.selectAllSalesHistoryByEmployee(salesHistorySelectDTO, pageable); return ResponseEntity.ok(SalesHistoryResponseMessage.builder() @@ -62,7 +71,14 @@ public ResponseEntity getAllSalesHistoryByEmployee( content = @Content(mediaType = "application/json")) }) @GetMapping("") - public ResponseEntity getAllSalesHistory(@PageableDefault(size = 20) Pageable pageable){ + public ResponseEntity getAllSalesHistory(@PageableDefault(size = 20) Pageable pageable, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder){ + + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } Page responseSalesHistory = salesHistoryQueryService.selectAllSalesHistory(pageable); @@ -106,10 +122,17 @@ public ResponseEntity getSalesHistoryDetail(@PathVa @PostMapping("/employee/search") public ResponseEntity getSalesHistorySearchByEmployee(@RequestBody SalesHistorySearchDTO salesHistorySearchDTO ,Principal principal, - @PageableDefault(size = 20) Pageable pageable){ + @PageableDefault(size = 20) Pageable pageable, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder){ salesHistorySearchDTO.setSearcherName(principal.getName()); + + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } Page responseSalesHistory = salesHistoryQueryService.selectSalesHistorySearchByEmployee(salesHistorySearchDTO, pageable); return ResponseEntity.ok(SalesHistoryResponseMessage.builder() @@ -128,8 +151,15 @@ public ResponseEntity getSalesHistorySearchByEmploy }) @PostMapping("/search") public ResponseEntity getSalesHistoryBySearch(@RequestBody SalesHistorySearchDTO salesHistorySearchDTO, - @PageableDefault(size = 20) Pageable pageable){ + @PageableDefault(size = 20) Pageable pageable, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder){ + + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } Page responseSalesHistory = salesHistoryQueryService.selectSalesHistoryBySearch(salesHistorySearchDTO, pageable); return ResponseEntity.ok(SalesHistoryResponseMessage.builder() @@ -440,7 +470,7 @@ public void exporSalesHistory(HttpServletResponse response){ salesHistoryQueryService.exportSalesHistoryToExcel(response); } - @Operation(summary = "전체 통계(실적,수당,매출액) 월별 검색(전체)") + @Operation(summary = "전체 통계(실적,수당,매출액) 월별 검색(관리자, 담당자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), @@ -456,12 +486,12 @@ public ResponseEntity getAllStatisticsByMonth(@Requ return ResponseEntity.ok(SalesHistoryResponseMessage.builder() .httpStatus(200) - .msg("전체 통계(실적,수당,매출액) 월별 검색(전체) 성공") + .msg("전체 통계(실적,수당,매출액) 월별 검색(관리자, 담당자) 성공") .result(responseSalesHistory) .build()); } - @Operation(summary = "전체 통계(실적,수당,매출액) 연도 별 검색(전체)") + @Operation(summary = "전체 통계(실적,수당,매출액) 연도 별 검색(관리자, 담당자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), @@ -477,12 +507,12 @@ public ResponseEntity getAllStatisticsByYear(@Reque Page responseSalesHistory = salesHistoryQueryService.selectAllStatisticsByYear(salesHistoryRankedDataDTO, pageable); return ResponseEntity.ok(SalesHistoryResponseMessage.builder() .httpStatus(200) - .msg("전체 통계(실적,수당,매출액) 연도 별 검색(전체)") + .msg("전체 통계(실적,수당,매출액) 연도 별 검색(관리자, 담당자)") .result(responseSalesHistory) .build()); } - @Operation(summary = "전체 통계(실적,수당,매출액) 조회기간 별 검색(전체)") + @Operation(summary = "전체 통계(실적,수당,매출액) 조회기간 별 검색(관리자, 담당자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), @@ -498,7 +528,7 @@ public ResponseEntity getAllStatisticsBySearch(@Req Page responseSalesHistory = salesHistoryQueryService.selectAllStatisticsBySearch(salesHistoryRankedDataDTO, pageable); return ResponseEntity.ok(SalesHistoryResponseMessage.builder() .httpStatus(200) - .msg("전체 통계(실적,수당,매출액) 조회기간 별 검색(전체)") + .msg("전체 통계(실적,수당,매출액) 조회기간 별 검색(관리자, 담당자)") .result(responseSalesHistory) .build()); } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsAverageDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsAverageDTO.java index 8ad31b3c..22acf040 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsAverageDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsAverageDTO.java @@ -12,7 +12,9 @@ @Getter @Setter public class SalesHistoryStatisticsAverageDTO { - private Double averageTotalSales; - private Double averageTotalIncentive; - private Double averageTotalPerformance; + private BigDecimal averageTotalSales; + private BigDecimal averageTotalIncentive; + private BigDecimal averageTotalPerformance; + private String month; + private String year; } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java index ba6d691c..9179e8b8 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java @@ -11,12 +11,16 @@ public interface SalesHistoryMapper { List findSalesHistoryByEmployee(@Param("size") int size , @Param("offset") int offset - , @Param("searcherId") String searcherId); + , @Param("searcherId") String searcherId, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); int findSalesHistoryCountByEmployee(@Param("searcherId") String searcherId); List findAllSalesHistory(@Param("size") int size - , @Param("offset") int offset); + , @Param("offset") int offset, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); SalesHistorySelectDTO findSalesHistoryDetail(@Param("salesHistorySelectDTO") SalesHistorySelectDTO salesHistorySelectDTO); @@ -74,13 +78,17 @@ List findStatisticsCenterBySearchYear(@Param("size") List findSalesHistorySearchByEmployee(@Param("size") int size , @Param("offset") int offset, - @Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); + @Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); int findSalesHistorySearchCountByEmployee(@Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); List findSalesHistoryBySearch(@Param("size") int size , @Param("offset") int offset, - @Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); + @Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); int findSalesHistoryCountBySearch(@Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java index 4d82f07e..9aaa3414 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java @@ -5,6 +5,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.center.query.service.CenterQueryService; @@ -49,9 +50,17 @@ public Page selectAllSalesHistoryByEmployee(SalesHistoryS int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } + String searcherId = authQueryService.selectMemberIdByLoginId(salesHistorySelectDTO.getSearcherName()); - List salesHistoryList = salesHistoryMapper.findSalesHistoryByEmployee(size,offset, searcherId); + List salesHistoryList = salesHistoryMapper.findSalesHistoryByEmployee(size,offset, searcherId, sortField, sortOrder); int total = salesHistoryMapper.findSalesHistoryCountByEmployee(searcherId); @@ -100,9 +109,19 @@ public Page selectSalesHistorySearchByEmployee(SalesHisto int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } + + System.out.println("sortField & sortOrder: " + sortField + " & " + sortOrder); + salesHistorySearchDTO.setSearcherName(authQueryService.selectMemberIdByLoginId(salesHistorySearchDTO.getSearcherName())); - List salesHistoryList = salesHistoryMapper.findSalesHistorySearchByEmployee(size,offset, salesHistorySearchDTO); + List salesHistoryList = salesHistoryMapper.findSalesHistorySearchByEmployee(size,offset, salesHistorySearchDTO, sortField, sortOrder); int total = salesHistoryMapper.findSalesHistorySearchCountByEmployee(salesHistorySearchDTO); @@ -136,7 +155,15 @@ public Page selectSalesHistoryBySearch(SalesHistorySearch int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); - List salesHistoryList = salesHistoryMapper.findSalesHistoryBySearch(size,offset, salesHistorySearchDTO); + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } + + List salesHistoryList = salesHistoryMapper.findSalesHistoryBySearch(size,offset, salesHistorySearchDTO, sortField, sortOrder); int total = salesHistoryMapper.findSalesHistoryCountBySearch(salesHistorySearchDTO); @@ -171,7 +198,15 @@ public Page selectAllSalesHistory(Pageable pageable) { int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); - List salesHistoryList = salesHistoryMapper.findAllSalesHistory(size,offset); + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } + + List salesHistoryList = salesHistoryMapper.findAllSalesHistory(size,offset, sortField, sortOrder); int total = salesHistoryMapper.findSalesHistoryCount(); diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 7eb65804..10a0c066 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -17,7 +17,7 @@ spring: logging: level: org.springframework.security: WARN - + org.mybatis: DEBUG mybatis: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml index dbcea431..265cc714 100644 --- a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml @@ -62,6 +62,8 @@ + + @@ -79,7 +81,47 @@ a.created_at FROM tb_sales_history a WHERE a.active = TRUE AND a.mem_id = #{searcherId} - ORDER BY a.created_at DESC + + + + + + ORDER BY a.sal_hist_id ${sortOrder} + + + ORDER BY a.sal_hist_ince ${sortOrder} + + + ORDER BY a.sal_hist_no_of_veh ${sortOrder} + + + ORDER BY a.sal_hist_tota_sale ${sortOrder} + + + ORDER BY a.conr_id ${sortOrder} + + + ORDER BY a.cust_id ${sortOrder} + + + ORDER BY a.prod_id ${sortOrder} + + + ORDER BY a.mem_id ${sortOrder} + + + ORDER BY a.cent_id ${sortOrder} + + + ORDER BY a.created_at DESC + + + + + + ORDER BY a.created_at DESC + + LIMIT #{size} OFFSET #{offset} @@ -119,7 +161,47 @@ AND a.created_at BETWEEN #{salesHistorySearchDTO.startDate} AND #{salesHistorySearchDTO.endDate} - ORDER BY a.created_at DESC + + + + + + ORDER BY a.sal_hist_id ${sortOrder} + + + ORDER BY a.sal_hist_ince ${sortOrder} + + + ORDER BY a.sal_hist_no_of_veh ${sortOrder} + + + ORDER BY a.sal_hist_tota_sale ${sortOrder} + + + ORDER BY a.conr_id ${sortOrder} + + + ORDER BY a.cust_id ${sortOrder} + + + ORDER BY a.prod_id ${sortOrder} + + + ORDER BY a.mem_id ${sortOrder} + + + ORDER BY a.cent_id ${sortOrder} + + + ORDER BY a.created_at DESC + + + + + + ORDER BY a.created_at DESC + + LIMIT #{size} OFFSET #{offset} @@ -167,7 +249,47 @@ AND a.active = TRUE - ORDER BY a.created_at desc + + + + + + ORDER BY a.sal_hist_id ${sortOrder} + + + ORDER BY a.sal_hist_ince ${sortOrder} + + + ORDER BY a.sal_hist_no_of_veh ${sortOrder} + + + ORDER BY a.sal_hist_tota_sale ${sortOrder} + + + ORDER BY a.conr_id ${sortOrder} + + + ORDER BY a.cust_id ${sortOrder} + + + ORDER BY a.prod_id ${sortOrder} + + + ORDER BY a.mem_id ${sortOrder} + + + ORDER BY a.cent_id ${sortOrder} + + + ORDER BY a.created_at DESC + + + + + + ORDER BY a.created_at DESC + + LIMIT #{size} OFFSET #{offset} @@ -209,7 +331,47 @@ a.created_at FROM tb_sales_history a WHERE a.active = TRUE - ORDER BY a.created_at DESC + + + + + + ORDER BY a.sal_hist_id ${sortOrder} + + + ORDER BY a.sal_hist_ince ${sortOrder} + + + ORDER BY a.sal_hist_no_of_veh ${sortOrder} + + + ORDER BY a.sal_hist_tota_sale ${sortOrder} + + + ORDER BY a.conr_id ${sortOrder} + + + ORDER BY a.cust_id ${sortOrder} + + + ORDER BY a.prod_id ${sortOrder} + + + ORDER BY a.mem_id ${sortOrder} + + + ORDER BY a.cent_id ${sortOrder} + + + ORDER BY a.created_at DESC + + + + + + ORDER BY a.created_at DESC + + LIMIT #{size} OFFSET #{offset} @@ -404,6 +566,12 @@ + @@ -83,17 +82,17 @@ From ebe66458bd35e957f22fa90feb13a56bc1efdd06 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 2 Dec 2024 10:31:50 +0900 Subject: [PATCH 400/563] =?UTF-8?q?fix:=20=EA=B3=A0=EA=B0=9D=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=A0=95=EB=A0=AC=20=EB=B2=84=ED=8A=BC(#1?= =?UTF-8?q?97)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/CustomerController.java | 16 ++++++++--- .../service/CustomerQueryServiceImpl.java | 16 +++++++++-- .../query/repository/CustomerMapper.xml | 27 ++++++++++++++++++- 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java b/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java index 908e24af..7b6cf63f 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java @@ -11,6 +11,8 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import stanl_2.final_backend.domain.customer.common.response.CustomerResponseMessage; @@ -86,15 +88,21 @@ public ResponseEntity getCustomers( }) @GetMapping("/search") public ResponseEntity searchCustomer( - @RequestParam(defaultValue = "0") int page, - @RequestParam(defaultValue = "10") int size, @RequestParam(required = false) String customerId, @RequestParam(required = false) String name, @RequestParam(required = false) String sex, - @RequestParam(required = false) String phone + @RequestParam(required = false) String phone, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder, + @PageableDefault(size = 10) Pageable pageable ) throws GeneralSecurityException { - Pageable pageable = PageRequest.of(page, size); + // 정렬 추가 + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } + CustomerSearchDTO customerSearchDTO = new CustomerSearchDTO(customerId , name, sex, phone); Page customerDTOPage = customerQueryService.findCustomerByCondition(pageable, customerSearchDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java index 2c281c82..fb6fd387 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java @@ -6,6 +6,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.customer.common.exception.CustomerCommonException; @@ -78,9 +79,17 @@ public Page selectCustomerList(Pageable pageable) throws GeneralSec @Override @Transactional(readOnly = true) public Page findCustomerByCondition(Pageable pageable, CustomerSearchDTO customerSearchDTO) throws GeneralSecurityException { - int page = pageable.getPageNumber(); + int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); - int offset = page*size; + + // 정렬 정보 가져오기 + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } Map params = new HashMap<>(); params.put("offset", offset); @@ -90,6 +99,9 @@ public Page findCustomerByCondition(Pageable pageable, Custom params.put("sex", customerSearchDTO.getSex()); params.put("phone", aesUtils.encrypt(customerSearchDTO.getPhone())); + params.put("sortField", sortField); + params.put("sortOrder", sortOrder); + List customerList = customerMapper.findCustomerByConditions(params); Integer count = customerMapper.findCustomerCnt(params); diff --git a/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml b/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml index 5be0d1ba..ea04c147 100644 --- a/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml @@ -114,7 +114,32 @@ AND a.cust_id = #{ customerId } - ORDER BY a.created_at DESC + + + + + + ORDER BY a.cust_name ${ sortOrder } + + + ORDER BY a.cust_sex ${ sortOrder } + + + ORDER BY a.cust_pho ${ sortOrder } + + + ORDER BY a.cust_id ${ sortOrder } + + + ORDER BY a.created_at DESC + + + + + + ORDER BY a.created_at DESC + + LIMIT #{ size } OFFSET #{ offset } From 7d68331a9128b8583e6e976db2759f8e9e616c58 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 2 Dec 2024 11:20:41 +0900 Subject: [PATCH 401/563] =?UTF-8?q?fix:=20=EB=A1=9C=EA=B7=B8=20=EC=A0=95?= =?UTF-8?q?=EB=A0=AC=20(#197)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log/query/controller/LogController.java | 17 +++++++--- .../log/query/repository/LogMapper.java | 4 ++- .../query/service/LogQueryServiceImpl.java | 12 ++++++- .../domain/log/query/repository/LogMapper.xml | 33 ++++++++++++++++++- 4 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java b/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java index 4a6c0396..00f16bdf 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java @@ -11,6 +11,8 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -43,18 +45,25 @@ public LogController(LogQueryService logQueryService) { }) @GetMapping("") public ResponseEntity getLogs( - @RequestParam(defaultValue = "0") int page, - @RequestParam(defaultValue = "10") int size, @RequestParam(required = false) String logId, @RequestParam(required = false) String ipAddress, @RequestParam(required = false) String requestTime_start, @RequestParam(required = false) String requestTime_end, @RequestParam(required = false) String status, @RequestParam(required = false) String method, - @RequestParam(required = false) String uri + @RequestParam(required = false) String uri, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder, + @PageableDefault(size = 10) Pageable pageable ){ - Pageable pageable = PageRequest.of(page, size); + // 정렬 추가 + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } + + LogSearchDTO searchLogDTO = new LogSearchDTO(logId, ipAddress, requestTime_start, requestTime_end, status, method, uri); Page logDTOPage = logQueryService.selectLogs(pageable, searchLogDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/repository/LogMapper.java b/src/main/java/stanl_2/final_backend/domain/log/query/repository/LogMapper.java index 80901960..ddb5afa6 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/repository/LogMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/repository/LogMapper.java @@ -13,7 +13,9 @@ public interface LogMapper { List findLogs( @Param("offset") int offset, @Param("size") int size, - @Param("searchLogDTO") LogSearchDTO searchLogDTO + @Param("searchLogDTO") LogSearchDTO searchLogDTO, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder ); int findLogsCnt(); diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java index d11c86f8..0d639114 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java @@ -6,6 +6,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.log.common.exception.LogCommonException; @@ -39,7 +40,16 @@ public Page selectLogs(Pageable pageable, LogSearchDTO searchLogDTO) { int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); - List logs = logMapper.findLogs(offset, size, searchLogDTO); + // 정렬 정보 가져오기 + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } + + List logs = logMapper.findLogs(offset, size, searchLogDTO, sortField, sortOrder); int totalElements = logMapper.findLogsCnt(); return new PageImpl<>(logs, pageable, totalElements); diff --git a/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml b/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml index c3e31b9e..19362655 100644 --- a/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml @@ -75,7 +75,38 @@ AND a.uri LIKE CONCAT('%', #{ searchLogDTO.uri }, '%') - ORDER BY a.request_time DESC + + + + + + ORDER BY a.log_id ${ sortOrder } + + + ORDER BY a.ip_address ${ sortOrder } + + + ORDER BY a.request_time ${ sortOrder } + + + ORDER BY a.status ${ sortOrder } + + + ORDER BY a.method ${ sortOrder } + + + ORDER BY a.uri ${ sortOrder } + + + ORDER BY a.request_time DESC + + + + + + ORDER BY a.request_time DESC + + LIMIT #{ size } OFFSET #{ offset } From 018bd905cd98880b2ee71471f8fbf81a84a66c0d Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 2 Dec 2024 12:17:24 +0900 Subject: [PATCH 402/563] =?UTF-8?q?fix:=20LOG=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=20=EA=B0=9C=EC=88=98=20Mapper=20=EC=88=98=EC=A0=95(#197)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log/query/repository/LogMapper.java | 2 +- .../query/service/LogQueryServiceImpl.java | 2 +- .../domain/log/query/repository/LogMapper.xml | 30 +++++++++++++++++-- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/repository/LogMapper.java b/src/main/java/stanl_2/final_backend/domain/log/query/repository/LogMapper.java index ddb5afa6..a4d396aa 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/repository/LogMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/repository/LogMapper.java @@ -18,7 +18,7 @@ List findLogs( @Param("sortOrder") String sortOrder ); - int findLogsCnt(); + int findLogsCnt(@Param("searchLogDTO") LogSearchDTO searchLogDTO); List findLogForExcel(); } diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java index 0d639114..6785be94 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/service/LogQueryServiceImpl.java @@ -51,7 +51,7 @@ public Page selectLogs(Pageable pageable, LogSearchDTO searchLogDTO) { List logs = logMapper.findLogs(offset, size, searchLogDTO, sortField, sortOrder); - int totalElements = logMapper.findLogsCnt(); + int totalElements = logMapper.findLogsCnt(searchLogDTO); return new PageImpl<>(logs, pageable, totalElements); } diff --git a/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml b/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml index 19362655..f14fbbed 100644 --- a/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml @@ -37,6 +37,29 @@ SELECT COUNT(*) AS cnt FROM tb_log a + + + AND a.log_id LIKE CONCAT('%', #{ searchLogDTO.logId }, '%') + + + AND a.ip_address LIKE CONCAT('%', #{ searchLogDTO.ipAddress }, '%') + + + AND a.request_time >= #{ searchLogDTO.requestTime_start } + + + AND a.request_time <= #{ searchLogDTO.requestTime_end } + + + AND a.status LIKE CONCAT('%', #{ searchLogDTO.status }, '%') + + + AND a.method LIKE CONCAT('%', #{ searchLogDTO.method }, '%') + + + AND a.uri LIKE CONCAT('%', #{ searchLogDTO.uri }, '%') + + @@ -954,7 +961,20 @@ AND a.active = TRUE - GROUP BY LEFT(a.created_at, 7) + + + GROUP BY a.mem_id + + + GROUP BY a.cent_id + + + + , LEFT(a.created_at, 7) + + + , LEFT(a.created_at, 4) + ORDER BY total_incentive DESC @@ -985,7 +1005,20 @@ AND a.active = TRUE - GROUP BY LEFT(a.created_at, 4) + + + GROUP BY a.mem_id + + + GROUP BY a.cent_id + + + + , LEFT(a.created_at, 7) + + + , LEFT(a.created_at, 4) + ORDER BY total_incentive DESC @@ -1016,6 +1049,14 @@ AND a.active = TRUE + + + GROUP BY a.mem_id + + + GROUP BY a.cent_id + + ORDER BY total_incentive DESC @@ -1033,4 +1074,6 @@ LIMIT #{size} OFFSET #{offset} + + From 1f4976046852e79ab1f8c5f268834a5be0772bd5 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Mon, 2 Dec 2024 15:02:28 +0900 Subject: [PATCH 405/563] =?UTF-8?q?feat:=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EB=B0=8F=20=ED=9A=8C=EC=9B=90=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=B4=EC=84=9C=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#182)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/AlarmController.java | 50 ------------------- .../alarm/query/dto/AlarmSelectDTO.java | 1 + .../alarm/query/repository/AlarmMapper.xml | 4 +- 3 files changed, 4 insertions(+), 51 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/controller/AlarmController.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/controller/AlarmController.java index eab418c8..1f6a9e06 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/controller/AlarmController.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/controller/AlarmController.java @@ -78,54 +78,4 @@ public ResponseEntity selectReadAlarm(Principal principal, .result(allAlarms) .build()); } - -// @Operation(summary = "회원 읽은 알림 상세 조회") -// @ApiResponses(value = { -// @ApiResponse(responseCode = "200", description = "회원 읽은 알림 상세 조회 완료", -// content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) -// }) -// @GetMapping("/read/{type}") -// public ResponseEntity selectReadAlarm(Principal principal, -// @PathVariable String type, -// @PageableDefault(size = 8) Pageable pageable){ -// -// String memberLoginId = principal.getName(); -// AlarmSelectReadDTO alarmSelectReadDTO = new AlarmSelectReadDTO(); -// alarmSelectReadDTO.setMemberLoginId(memberLoginId); -// alarmSelectReadDTO.setType(type); -// -// Page allReadAlarms -// = alarmQueryService.selectReadAlarmByType(alarmSelectReadDTO , pageable); -// -// return ResponseEntity.ok(AlarmResponseMessage.builder() -// .httpStatus(200) -// .msg("성공") -// .result(allReadAlarms) -// .build()); -// } - -// @Operation(summary = "회원 읽지 않은 알림 상세 조회") -// @ApiResponses(value = { -// @ApiResponse(responseCode = "200", description = "회원 읽은 알림 상세 조회 완료", -// content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))}) -// }) -// @GetMapping("/unread/{type}") -// public ResponseEntity selectUnreadAlarm(Principal principal, -// @PathVariable String type, -// @PageableDefault(size = 8) Pageable pageable){ -// String memberLoginId = principal.getName(); -// AlarmSelectUnreadDTO alarmSelectUnreadDTO = new AlarmSelectUnreadDTO(); -// alarmSelectUnreadDTO.setMemberLoginId(memberLoginId); -// alarmSelectUnreadDTO.setType(type); -// -// Page allUnreadAlarms -// = alarmQueryService.selectUnreadAlarmByType(alarmSelectUnreadDTO, pageable); -// -// return ResponseEntity.ok(AlarmResponseMessage.builder() -// .httpStatus(200) -// .msg("성공") -// .result(allUnreadAlarms) -// .build()); -// } - } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java index d17b39a5..b4cb49b9 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java @@ -20,4 +20,5 @@ public class AlarmSelectDTO { private String createdAt; private String memberLoginId; + private String memberId; } diff --git a/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml b/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml index f8d2a9b3..5d2cd931 100644 --- a/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml @@ -58,6 +58,7 @@ + + + + \ No newline at end of file diff --git a/src/main/resources/templates/mail.html b/src/main/resources/templates/mail.html new file mode 100644 index 00000000..964945de --- /dev/null +++ b/src/main/resources/templates/mail.html @@ -0,0 +1,60 @@ + + + + + + + Email Confirmation + + + +

      +

      STANL2 본인 확인

      +

      안녕하세요,

      +

      본인 확인을 위해 아래 인증 코드를 입력해 주세요:

      +
      + + [인증 코드] +
      +

      감사합니다.

      +
      + + + From 4220fc2ee8bd4de45a7dca0e4019d07cf57e58e4 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 2 Dec 2024 15:19:49 +0900 Subject: [PATCH 407/563] =?UTF-8?q?feat:=20=EC=9E=84=EC=8B=9C=EB=B9=84?= =?UTF-8?q?=EB=B0=80=EB=B2=88=ED=98=B8=20=EC=9E=AC=EB=B0=9C=EA=B8=89=20?= =?UTF-8?q?=EC=A7=84=ED=96=89=EC=A4=91(#199)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/MemberController.java | 21 ++++++++++++++ .../member/query/repository/MemberMapper.java | 2 ++ .../query/service/MemberQueryService.java | 2 ++ .../query/service/MemberQueryServiceImpl.java | 9 ++++++ .../member/query/repository/MemberMapper.xml | 28 +++++++++++++++++++ 5 files changed, 62 insertions(+) diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java b/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java index 9db7a52c..c9820774 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java @@ -127,4 +127,25 @@ public ResponseEntity getMemberByOrganizationId(@PathVari } + @Operation(summary = "회원 id로 정보 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = MemberResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("/memberInfo/{memberId}") + public ResponseEntity getMemberInfoById(@PathVariable("memberId") String memberId) throws GeneralSecurityException { + + MemberDTO memberInfo = memberQueryService.selectMemberInfoById(memberId); + + return ResponseEntity.ok(MemberResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(memberInfo) + .build()); + } + + + } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java b/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java index 46c032c5..1a4ad4aa 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java @@ -18,4 +18,6 @@ public interface MemberMapper { List findMembersByCenterList(@Param("centerList") List centerList); List findMembersByOrganizationId(@Param("organizationId") String organizationId); + + MemberDTO findMemberInfoBymemberId(String memberId); } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java index f1776560..684f6772 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java @@ -17,5 +17,7 @@ public interface MemberQueryService { List selectMemberByOrganizationId(String organizationId) throws GeneralSecurityException; + MemberDTO selectMemberInfoById(String memberId); + // MemberDetailDTO selectMemberDetail(String name) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index 8d2759c3..c4d22029 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -127,4 +127,13 @@ public List selectMemberByOrganizationId(String organizationId) throw return memberList; } + + @Override + @Transactional(readOnly = true) + public MemberDTO selectMemberInfoById(String memberId) { + + MemberDTO memberDTO = memberMapper.findMemberInfoBymemberId(memberId); + + return memberDTO; + } } diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml index 31bb6116..6124f16d 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml @@ -96,4 +96,32 @@ WHERE a.active = true and a.org_cha_id = #{ organizationId } + + + \ No newline at end of file From cf85ac52ca26b1e22ac7ddb6cbbe2726dfdb8737 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 2 Dec 2024 15:47:23 +0900 Subject: [PATCH 408/563] =?UTF-8?q?feat:=20smtp=EB=A5=BC=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=ED=95=9C=20=EC=9D=B8=EC=A6=9D=EB=B2=88=ED=98=B8=20?= =?UTF-8?q?=EC=A0=84=EC=86=A1(#199)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 ++ .../command/application/controller/AuthController.java | 3 ++- .../command/application/service/AuthCommandService.java | 3 ++- .../command/domain/service/AuthCommandServiceImpl.java | 7 ++++--- .../stanl_2/final_backend/global/mail/MailServiceImpl.java | 5 +++++ 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 97070e89..49e312ff 100644 --- a/build.gradle +++ b/build.gradle @@ -73,6 +73,8 @@ dependencies { // SMTP implementation 'org.springframework.boot:spring-boot-starter-mail' + implementation 'ognl:ognl:3.2.20' + } tasks.named('test') { diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java index fd505833..ceb2b528 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.mail.MessagingException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; @@ -147,7 +148,7 @@ public ResponseEntity refresh(@RequestBody RefreshDTO ref content = {@Content(schema = @Schema(implementation = MemberResponseMessage.class))}) }) @PostMapping("checkmail") - public ResponseEntity checkMail(@RequestBody CheckMailDTO checkMailDTO) throws GeneralSecurityException { + public ResponseEntity checkMail(@RequestBody CheckMailDTO checkMailDTO) throws GeneralSecurityException, MessagingException { authCommandService.sendEmail(checkMailDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java index 982bc069..6ac06f2e 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.member.command.application.service; +import jakarta.mail.MessagingException; import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.member.command.application.dto.*; @@ -14,5 +15,5 @@ public interface AuthCommandService { SigninResponseDTO signin(SigninRequestDTO signinRequestDTO) throws GeneralSecurityException; - void sendEmail(CheckMailDTO checkMailDTO) throws GeneralSecurityException; + void sendEmail(CheckMailDTO checkMailDTO) throws GeneralSecurityException, MessagingException; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java index 387bd2ef..3355e7e8 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java @@ -3,6 +3,7 @@ import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; +import jakarta.mail.MessagingException; import lombok.extern.slf4j.Slf4j; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; @@ -27,6 +28,7 @@ import stanl_2.final_backend.global.exception.GlobalCommonException; import stanl_2.final_backend.global.exception.GlobalErrorCode; import stanl_2.final_backend.global.mail.MailService; +import stanl_2.final_backend.global.redis.RedisService; import stanl_2.final_backend.global.security.service.MemberDetails; import stanl_2.final_backend.global.utils.AESUtils; @@ -144,11 +146,10 @@ public SigninResponseDTO signin(SigninRequestDTO signinRequestDTO) throws Genera @Override @Transactional - public void sendEmail(CheckMailDTO checkMailDTO) throws GeneralSecurityException { + public void sendEmail(CheckMailDTO checkMailDTO) throws GeneralSecurityException, MessagingException { String email = authQueryService.findEmail(checkMailDTO); - - + mailService.sendEmail(email); } private String generateAccessToken(String username, String authorities, SecretKey secretKey) { diff --git a/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java b/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java index e83c87d2..78c5f93b 100644 --- a/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java @@ -45,6 +45,7 @@ private String createCode() { /* Thymeleaf 기반의 html 파일에 값 넣고 연결하는 메소드 */ private String setContext(String code) { + // thymeleaf 기반의 html 파일에 값을 넣고 연결 Context context = new Context(); // templateengine과 classloadertemplateresolver를 활용하여 resource/template에 위치한 mail.html 연결 @@ -65,6 +66,7 @@ private String setContext(String code) { } private MimeMessage createEmailForm(String email) throws MessagingException { + String authCode = createCode(); // MimeMessage에 코드, 송신 이메일, 내용 보관 @@ -83,12 +85,15 @@ private MimeMessage createEmailForm(String email) throws MessagingException { /* 만든 메일 전송 */ @Override public void sendEmail(String toEmail) throws MessagingException { + // redis에 해당 이메일이 있으면 db에서 삭제 if(redisService.getKey(toEmail) != null) { redisService.clearMailCache(toEmail); } + System.out.println("!!!!!!2"); MimeMessage emailForm = createEmailForm(toEmail); + System.out.println("!!!!!!3"); mailSender.send(emailForm); } From b9b115401575cfdeb6ebb72ab1f4b9ced1a7da70 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 2 Dec 2024 16:19:44 +0900 Subject: [PATCH 409/563] =?UTF-8?q?feat:=20smtp=EB=A5=BC=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=ED=95=9C=20=EC=9D=B8=EC=A6=9D=EB=B2=88=ED=98=B8=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8(#199)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AuthController.java | 27 ++++++++++++++++--- .../command/application/dto/CheckNumDTO.java | 15 +++++++++++ .../service/AuthCommandService.java | 2 ++ .../service/AuthCommandServiceImpl.java | 17 ++++++++++-- .../common/exception/MemberErrorCode.java | 1 + .../query/service/AuthQueryService.java | 4 +-- .../query/service/AuthQueryServiceImpl.java | 4 +-- .../global/mail/MailServiceImpl.java | 3 +-- 8 files changed, 60 insertions(+), 13 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/member/command/application/dto/CheckNumDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java index ceb2b528..4d2fa754 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java @@ -13,6 +13,8 @@ import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.member.command.application.dto.*; import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; +import stanl_2.final_backend.domain.member.common.exception.MemberCommonException; +import stanl_2.final_backend.domain.member.common.exception.MemberErrorCode; import stanl_2.final_backend.domain.member.common.response.MemberResponseMessage; import java.security.GeneralSecurityException; @@ -153,11 +155,28 @@ public ResponseEntity checkMail(@RequestBody CheckMailDTO authCommandService.sendEmail(checkMailDTO); return ResponseEntity.ok(MemberResponseMessage.builder() - .httpStatus(200) - .msg("성공") - .result(null) - .build()); + .httpStatus(200) + .msg("성공") + .result(null) + .build()); } + @Operation(summary = "임시 비밀번호 재발급") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = MemberResponseMessage.class))}) + }) + @PostMapping("checknum") + public ResponseEntity checkMail(@RequestBody CheckNumDTO checkNumDTO) throws GeneralSecurityException, MessagingException { + + authCommandService.checkNum(checkNumDTO); + + return ResponseEntity.ok(MemberResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(null) + .build()); + } + } diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/CheckNumDTO.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/CheckNumDTO.java new file mode 100644 index 00000000..369caf0f --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/dto/CheckNumDTO.java @@ -0,0 +1,15 @@ +package stanl_2.final_backend.domain.member.command.application.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class CheckNumDTO { + private String loginId; + private String number; +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java index 6ac06f2e..bbd2435d 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java @@ -16,4 +16,6 @@ public interface AuthCommandService { SigninResponseDTO signin(SigninRequestDTO signinRequestDTO) throws GeneralSecurityException; void sendEmail(CheckMailDTO checkMailDTO) throws GeneralSecurityException, MessagingException; + + void checkNum(CheckNumDTO checkNumDTO) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java index 3355e7e8..d17d722c 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java @@ -23,6 +23,8 @@ import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.MemberRole; import stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; import stanl_2.final_backend.domain.member.command.domain.repository.MemberRoleRepository; +import stanl_2.final_backend.domain.member.common.exception.MemberCommonException; +import stanl_2.final_backend.domain.member.common.exception.MemberErrorCode; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.s3.command.application.service.S3FileService; import stanl_2.final_backend.global.exception.GlobalCommonException; @@ -42,6 +44,7 @@ @Service("commandAuthService") public class AuthCommandServiceImpl implements AuthCommandService { + private final RedisService redisService; @Value("${jwt.secret-key}") private String jwtSecretKey; @@ -64,7 +67,7 @@ public AuthCommandServiceImpl(MemberRepository memberRepository, AuthQueryService authQueryService, AESUtils aesUtils, S3FileService s3FileService, - MailService mailService) { + MailService mailService, RedisService redisService) { this.memberRepository = memberRepository; this.memberRoleRepository = memberRoleRepository; this.passwordEncoder = passwordEncoder; @@ -74,6 +77,7 @@ public AuthCommandServiceImpl(MemberRepository memberRepository, this.aesUtils = aesUtils; this.s3FileService = s3FileService; this.mailService = mailService; + this.redisService = redisService; } @Override @@ -147,11 +151,20 @@ public SigninResponseDTO signin(SigninRequestDTO signinRequestDTO) throws Genera @Override @Transactional public void sendEmail(CheckMailDTO checkMailDTO) throws GeneralSecurityException, MessagingException { - String email = authQueryService.findEmail(checkMailDTO); + String email = authQueryService.findEmail(checkMailDTO.getLoginId()); mailService.sendEmail(email); } + @Override + public void checkNum(CheckNumDTO checkNumDTO) throws GeneralSecurityException { + String email = authQueryService.findEmail(checkNumDTO.getLoginId()); + + if (checkNumDTO.getNumber() != null && !checkNumDTO.getNumber().equals(String.valueOf(redisService.getKey(email)))) { + throw new MemberCommonException(MemberErrorCode.NUMBER_NOT_FOUND); + } + } + private String generateAccessToken(String username, String authorities, SecretKey secretKey) { return Jwts.builder() .setIssuer("STANL2") diff --git a/src/main/java/stanl_2/final_backend/domain/member/common/exception/MemberErrorCode.java b/src/main/java/stanl_2/final_backend/domain/member/common/exception/MemberErrorCode.java index bbec0dac..4c2e71ae 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/common/exception/MemberErrorCode.java +++ b/src/main/java/stanl_2/final_backend/domain/member/common/exception/MemberErrorCode.java @@ -40,6 +40,7 @@ public enum MemberErrorCode { */ MEMBER_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "회원 데이터를 찾지 못했습니다"), MEMBER_ID_NOT_FOUND(404002, HttpStatus.NOT_FOUND, "회원 pk값을 찾지 못했습니다."), + NUMBER_NOT_FOUND(40403, HttpStatus.NOT_FOUND, "인증번호가 맞지 않습니다."), /** diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryService.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryService.java index adabd0c0..4a06509a 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryService.java @@ -1,11 +1,9 @@ package stanl_2.final_backend.domain.member.query.service; -import stanl_2.final_backend.domain.member.command.application.dto.CheckMailDTO; - import java.security.GeneralSecurityException; public interface AuthQueryService { String selectMemberIdByLoginId(String name); - String findEmail(CheckMailDTO checkMailDTO) throws GeneralSecurityException; + String findEmail(String loginId) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java index 8652b5c2..5dca89da 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/AuthQueryServiceImpl.java @@ -41,9 +41,9 @@ public String selectMemberIdByLoginId(String loginId){ @Override @Transactional(readOnly = true) - public String findEmail(CheckMailDTO checkMailDTO) throws GeneralSecurityException { + public String findEmail(String loginId) throws GeneralSecurityException { - String email = authMapper.findEmailByLoginId(checkMailDTO.getLoginId()); + String email = authMapper.findEmailByLoginId(loginId); email = aesUtils.decrypt(email); diff --git a/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java b/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java index 78c5f93b..9dd6a098 100644 --- a/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java @@ -90,10 +90,9 @@ public void sendEmail(String toEmail) throws MessagingException { if(redisService.getKey(toEmail) != null) { redisService.clearMailCache(toEmail); } - System.out.println("!!!!!!2"); MimeMessage emailForm = createEmailForm(toEmail); - System.out.println("!!!!!!3"); + mailSender.send(emailForm); } From 157c9423c67731e3dce37a0c677cd376e67e5cee Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Mon, 2 Dec 2024 16:40:20 +0900 Subject: [PATCH 410/563] =?UTF-8?q?feat:=20=EB=B0=9C=EC=A3=BC=EC=84=9C=20?= =?UTF-8?q?=EC=97=91=EC=85=80=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=EC=8A=B9=EC=9D=B8=EC=97=AC=EB=B6=80,=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20(#195)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../order/query/repository/OrderMapper.java | 2 + .../query/service/OrderQueryService.java | 2 + .../query/service/OrderQueryServiceImpl.java | 8 ++ .../controller/PurchaseOrderController.java | 12 --- .../dto/PurchaseOrderModifyDTO.java | 1 - .../dto/PurchaseOrderStatusModifyDTO.java | 2 - .../PurchaseOrderCommandServiceImpl.java | 19 ++-- .../controller/PurchaseOrderController.java | 35 +++++-- .../dto/PurchaseOrderExcelDTO.java | 4 +- .../query/repository/PurchaseOrderMapper.java | 10 +- .../service/PurchaseOrderQueryService.java | 8 +- .../PurchaseOrderQueryServiceImpl.java | 96 ++++++++++++++++--- .../order/query/repository/OrderMapper.xml | 7 ++ .../query/repository/PurchaseOrderMapper.xml | 75 ++++++++++++--- 14 files changed, 219 insertions(+), 62 deletions(-) rename src/main/java/stanl_2/final_backend/domain/purchase_order/{command/application => query}/dto/PurchaseOrderExcelDTO.java (87%) diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java b/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java index 8440b36d..bf2b6bcb 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java @@ -38,4 +38,6 @@ List findSearchOrder(@Param("offset") int offset, int findOrderSearchCount(OrderSelectSearchDTO orderSelectSearchDTO); List findOrderForExcel(); + + String selectByContractId(String orderId); } diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java index 1a0826ee..edb18f4e 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java @@ -23,5 +23,7 @@ public interface OrderQueryService { Page selectSearchOrders(OrderSelectSearchDTO orderSelectSearchDTO, Pageable pageable) throws GeneralSecurityException; + String selectByContractId(String orderId); + void exportOrder(HttpServletResponse response); } diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java index e4dc1d3a..2b4d33c2 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java @@ -190,6 +190,14 @@ public Page selectSearchOrders(OrderSelectSearchDTO orderS return new PageImpl<>(orders, pageable, totalOrder); } + @Override + public String selectByContractId(String orderId) { + + String contractId = orderMapper.selectByContractId(orderId); + + return contractId; + } + @Override @Transactional(readOnly = true) public void exportOrder(HttpServletResponse response) { diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java index 7c6c6db8..1c3cd1d0 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/controller/PurchaseOrderController.java @@ -112,16 +112,4 @@ public ResponseEntity putPurchaseOrderStatus(@Path .result(null) .build()); } - - @Operation(summary = "엑셀 다운로드") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "엑셀 다운로드 성공", - content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) - }) - @PutMapping("excel") - public ResponseEntity exportPurchaseOrder(HttpServletResponse response) { - - purchaseOrderQueryService.exportPurchaseOrder(response); - } - } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderModifyDTO.java index 12abc2cf..e4e5a8ff 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderModifyDTO.java @@ -10,6 +10,5 @@ public class PurchaseOrderModifyDTO { private String purchaseOrderId; private String title; private String content; - private String orderId; private String memberId; } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderStatusModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderStatusModifyDTO.java index af08bcdb..1ce6c560 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderStatusModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderStatusModifyDTO.java @@ -8,8 +8,6 @@ @Getter public class PurchaseOrderStatusModifyDTO { private String purchaseOrderId; - private String title; private String status; - private String content; private String adminId; } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java index 177f1374..48e695db 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.purchase_order.command.domain.service; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringEscapeUtils; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; @@ -24,6 +25,7 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +@Slf4j @Service public class PurchaseOrderCommandServiceImpl implements PurchaseOrderCommandService { @@ -64,7 +66,13 @@ public void registerPurchaseOrder(PurchaseOrderRegistDTO purchaseOrderRegistDTO) throw new OrderCommonException(OrderErrorCode.ORDER_STATUS_NOT_APPROVED); } + String unescapedHtml = StringEscapeUtils.unescapeJson(purchaseOrderRegistDTO.getContent()); + String updatedS3Url = s3FileService.uploadHtml(unescapedHtml, purchaseOrderRegistDTO.getTitle()); + + PurchaseOrder purchaseOrder = modelMapper.map(purchaseOrderRegistDTO, PurchaseOrder.class); + purchaseOrder.setMemberId(memberId); + purchaseOrder.setContent(updatedS3Url); purchaseOrderRepository.save(purchaseOrder); } @@ -81,8 +89,7 @@ public PurchaseOrderModifyDTO modifyPurchaseOrder(PurchaseOrderModifyDTO purchas .orElseThrow(() -> new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND)); // 수주서가 존재하는지 확인 - Order order = orderRepository.findByOrderIdAndMemberId( - purchaseOrderModifyDTO.getOrderId(), purchaseOrderModifyDTO.getMemberId()); + Order order = orderRepository.findByOrderIdAndMemberId(purchaseOrder.getOrderId(), memberId); if (order == null) { throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); } @@ -94,13 +101,15 @@ public PurchaseOrderModifyDTO modifyPurchaseOrder(PurchaseOrderModifyDTO purchas String unescapedHtml = StringEscapeUtils.unescapeJson(purchaseOrderModifyDTO.getContent()); String updatedS3Url = s3FileService.uploadHtml(unescapedHtml, purchaseOrderModifyDTO.getTitle()); - purchaseOrderModifyDTO.setContent(updatedS3Url); PurchaseOrder updatePurchaseOrder = modelMapper.map(purchaseOrderModifyDTO, PurchaseOrder.class); updatePurchaseOrder.setCreatedAt(purchaseOrder.getCreatedAt()); updatePurchaseOrder.setUpdatedAt(purchaseOrder.getUpdatedAt()); updatePurchaseOrder.setStatus(purchaseOrder.getStatus()); updatePurchaseOrder.setActive(purchaseOrder.getActive()); + updatePurchaseOrder.setContent(updatedS3Url); + updatePurchaseOrder.setOrderId(purchaseOrder.getOrderId()); + updatePurchaseOrder.setMemberId(memberId); purchaseOrderRepository.save(updatePurchaseOrder); @@ -134,10 +143,6 @@ public void modifyPurchaseOrderStatus(PurchaseOrderStatusModifyDTO purchaseOrder PurchaseOrder purchaseOrder = purchaseOrderRepository.findByPurchaseOrderId(purchaseOrderStatusModifyDTO.getPurchaseOrderId()) .orElseThrow(() -> new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND)); - String unescapedHtml = StringEscapeUtils.unescapeJson(purchaseOrderStatusModifyDTO.getContent()); - String updatedS3Url = s3FileService.uploadHtml(unescapedHtml, purchaseOrderStatusModifyDTO.getTitle()); - - purchaseOrder.setContent(updatedS3Url); purchaseOrder.setStatus(purchaseOrderStatusModifyDTO.getStatus()); purchaseOrder.setAdminId(adminId); diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java index e338138e..1a427994 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -19,6 +20,7 @@ import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectSearchDTO; import stanl_2.final_backend.domain.purchase_order.query.service.PurchaseOrderQueryService; +import java.security.GeneralSecurityException; import java.security.Principal; @RestController("PurchaseOrderQueryController") @@ -82,13 +84,21 @@ public ResponseEntity getAllPurchaseOrdersAdmin(Pr }) @GetMapping("admin/search") public ResponseEntity getSearchPurchaseOrdersAdmin(@RequestParam(required = false) String title, - @RequestParam(required = false) String status, - @RequestParam(required = false) String adminId, - @RequestParam(required = false) String searchMemberId, - @RequestParam(required = false) String startDate, - @RequestParam(required = false) String endDate, + @RequestParam(required = false) String status, + @RequestParam(required = false) String adminId, + @RequestParam(required = false) String searchMemberId, + @RequestParam(required = false) String startDate, + @RequestParam(required = false) String endDate, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder, Principal principal, - @PageableDefault(size = 10) Pageable pageable) { + @PageableDefault(size = 10) Pageable pageable) throws GeneralSecurityException { + + // 정렬 추가 + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO = new PurchaseOrderSelectSearchDTO(); purchaseOrderSelectSearchDTO.setTitle(title); @@ -161,7 +171,7 @@ public ResponseEntity getSearchPurchaseOrders(@Req @RequestParam(required = false) String endDate, @RequestParam(required = false) String sortField, @RequestParam(required = false) String sortOrder, - @PageableDefault(size = 10) Pageable pageable) { + @PageableDefault(size = 10) Pageable pageable) throws GeneralSecurityException { // 정렬 추가 if (sortField != null && sortOrder != null) { @@ -185,5 +195,16 @@ public ResponseEntity getSearchPurchaseOrders(@Req .result(responsePurchaseOrder) .build()); } + + @Operation(summary = "엑셀 다운로드") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "엑셀 다운로드 성공", + content = {@Content(schema = @Schema(implementation = PurchaseOrderResponseMessage.class))}) + }) + @GetMapping("excel") + public void exportPurchaseOrder(HttpServletResponse response) throws GeneralSecurityException { + + purchaseOrderQueryService.exportPurchaseOrder(response); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderExcelDTO.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderExcelDTO.java similarity index 87% rename from src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderExcelDTO.java rename to src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderExcelDTO.java index 3cfe7f33..af2d3b13 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderExcelDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/dto/PurchaseOrderExcelDTO.java @@ -1,10 +1,12 @@ -package stanl_2.final_backend.domain.purchase_order.command.application.dto; +package stanl_2.final_backend.domain.purchase_order.query.dto; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.Setter; import stanl_2.final_backend.global.excel.ExcelColumnName; @Getter +@Setter @AllArgsConstructor public class PurchaseOrderExcelDTO { diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java index c24de0d7..0356bb02 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java @@ -2,7 +2,7 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; -import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderExcelDTO; +import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderExcelDTO; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectAllDTO; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectIdDTO; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectSearchDTO; @@ -27,9 +27,13 @@ List findSearchPurchaseOrder(@Param("offset") int @Param("sortField") String sortField, @Param("sortOrder") String sortOrder); - List findSearchPurchaseOrderMemberId(int offset, int pageSize, PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO); + List findSearchPurchaseOrderMemberId(@Param("offset") int offset, + @Param("pageSize") int pageSize, + @Param("PurchaseOrderSelectSearchDTO") PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); - Integer findSearchPurchaseOrderCountMemberId(PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO); + int findSearchPurchaseOrderCountMemberId(@Param("PurchaseOrderSelectSearchDTO") PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO); PurchaseOrderSelectIdDTO findPurchaseOrderByPurchaseOrderId(String purchaseOrderId); diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java index 536415f7..98679a65 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryService.java @@ -7,18 +7,20 @@ import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectIdDTO; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectSearchDTO; +import java.security.GeneralSecurityException; + public interface PurchaseOrderQueryService { PurchaseOrderSelectIdDTO selectDetailPurchaseOrderAdmin(PurchaseOrderSelectIdDTO purchaseOrderSelectIdDTO); Page selectAllPurchaseOrderAdmin(Pageable pageable, PurchaseOrderSelectAllDTO purchaseOrderSelectAllDTO); - Page selectSearchPurchaseOrderAdmin(PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO, Pageable pageable); + Page selectSearchPurchaseOrderAdmin(PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO, Pageable pageable) throws GeneralSecurityException; - Page selectSearchPurchaseOrder(PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO, Pageable pageable); + Page selectSearchPurchaseOrder(PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO, Pageable pageable) throws GeneralSecurityException; Page selectAllPurchaseOrder(Pageable pageable, PurchaseOrderSelectAllDTO purchaseOrderSelectAllDTO); PurchaseOrderSelectIdDTO selectDetailPurchaseOrder(PurchaseOrderSelectIdDTO purchaseOrderSelectIdDTO); - void exportPurchaseOrder(HttpServletResponse response); + void exportPurchaseOrder(HttpServletResponse response) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java index 36f2327c..ab799e3f 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java @@ -1,8 +1,10 @@ package stanl_2.final_backend.domain.purchase_order.query.service; import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringEscapeUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; @@ -10,8 +12,13 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.member.query.dto.MemberDTO; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; -import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderExcelDTO; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; +import stanl_2.final_backend.domain.order.command.domain.aggregate.entity.Order; +import stanl_2.final_backend.domain.order.query.service.OrderQueryService; +import stanl_2.final_backend.domain.product.query.service.ProductQueryService; +import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderExcelDTO; import stanl_2.final_backend.domain.purchase_order.common.exception.PurchaseOrderCommonException; import stanl_2.final_backend.domain.purchase_order.common.exception.PurchaseOrderErrorCode; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectAllDTO; @@ -19,22 +26,32 @@ import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectSearchDTO; import stanl_2.final_backend.domain.purchase_order.query.repository.PurchaseOrderMapper; import stanl_2.final_backend.global.excel.ExcelUtilsV1; +import stanl_2.final_backend.global.utils.AESUtils; +import java.security.GeneralSecurityException; import java.util.List; -@Service + +@Slf4j +@Service("purchaseOrderQueryService") public class PurchaseOrderQueryServiceImpl implements PurchaseOrderQueryService { private final PurchaseOrderMapper purchaseOrderMapper; private final AuthQueryService authQueryService; + private final MemberQueryService memberQueryService; private final RedisTemplate redisTemplate; private final ExcelUtilsV1 excelUtilsV1; + private final AESUtils aesUtils; @Autowired - public PurchaseOrderQueryServiceImpl(PurchaseOrderMapper purchaseOrderMapper, AuthQueryService authQueryService, RedisTemplate redisTemplate, ExcelUtilsV1 excelUtilsV1) { + public PurchaseOrderQueryServiceImpl(PurchaseOrderMapper purchaseOrderMapper, AuthQueryService authQueryService, + RedisTemplate redisTemplate, ExcelUtilsV1 excelUtilsV1, + MemberQueryService memberQueryService, AESUtils aesUtils) { this.purchaseOrderMapper = purchaseOrderMapper; this.authQueryService = authQueryService; this.redisTemplate = redisTemplate; this.excelUtilsV1 = excelUtilsV1; + this.memberQueryService = memberQueryService; + this.aesUtils = aesUtils; } // 영업 관리자 조회 @@ -88,23 +105,53 @@ public Page selectAllPurchaseOrderAdmin(Pageable page @Override @Transactional(readOnly = true) - public Page selectSearchPurchaseOrderAdmin(PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO, Pageable pageable) { + public Page selectSearchPurchaseOrderAdmin(PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO, Pageable pageable) throws GeneralSecurityException { String memberId = authQueryService.selectMemberIdByLoginId(purchaseOrderSelectSearchDTO.getMemberId()); purchaseOrderSelectSearchDTO.setMemberId(memberId); + if ("대기".equals(purchaseOrderSelectSearchDTO.getStatus())) { + purchaseOrderSelectSearchDTO.setStatus("WAIT"); + } + if ("승인".equals(purchaseOrderSelectSearchDTO.getStatus())) { + purchaseOrderSelectSearchDTO.setStatus("APPROVED"); + } + if ("취소".equals(purchaseOrderSelectSearchDTO.getStatus())) { + purchaseOrderSelectSearchDTO.setStatus("CANCEL"); + } + int offset = Math.toIntExact(pageable.getOffset()); int pageSize = pageable.getPageSize(); - List purchaseOrders = purchaseOrderMapper.findSearchPurchaseOrderMemberId(offset, pageSize, purchaseOrderSelectSearchDTO); + + // 정렬 정보 가져오기 + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } + + List purchaseOrders = purchaseOrderMapper.findSearchPurchaseOrderMemberId(offset, pageSize, purchaseOrderSelectSearchDTO, sortField, sortOrder); if (purchaseOrders == null) { throw new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND); } - Integer count = purchaseOrderMapper.findSearchPurchaseOrderCountMemberId(purchaseOrderSelectSearchDTO); - int totalPurchaseOrder = (count != null) ? count : 0; + for (PurchaseOrderSelectSearchDTO purchaseOrder : purchaseOrders) { + if (purchaseOrder.getMemberId() != null) { + String memberName = memberQueryService.selectNameById(purchaseOrder.getMemberId()); + purchaseOrder.setMemberName(memberName); + } + } - return new PageImpl<>(purchaseOrders, pageable, totalPurchaseOrder); + int count = purchaseOrderMapper.findSearchPurchaseOrderCountMemberId(purchaseOrderSelectSearchDTO); + + if (count == 0) { + throw new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND); + } + + return new PageImpl<>(purchaseOrders, pageable, count); } // 영업 담장자 조회 @@ -150,7 +197,17 @@ public Page selectAllPurchaseOrder(Pageable pageable, @Override @Transactional(readOnly = true) - public Page selectSearchPurchaseOrder(PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO, Pageable pageable) { + public Page selectSearchPurchaseOrder(PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO, Pageable pageable) throws GeneralSecurityException { + + if ("대기".equals(purchaseOrderSelectSearchDTO.getStatus())) { + purchaseOrderSelectSearchDTO.setStatus("WAIT"); + } + if ("승인".equals(purchaseOrderSelectSearchDTO.getStatus())) { + purchaseOrderSelectSearchDTO.setStatus("APPROVED"); + } + if ("취소".equals(purchaseOrderSelectSearchDTO.getStatus())) { + purchaseOrderSelectSearchDTO.setStatus("CANCEL"); + } int offset = Math.toIntExact(pageable.getOffset()); int pageSize = pageable.getPageSize(); @@ -170,22 +227,39 @@ public Page selectSearchPurchaseOrder(PurchaseOrde throw new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND); } + for (PurchaseOrderSelectSearchDTO purchaseOrder : purchaseOrders) { + if (purchaseOrder.getMemberId() != null) { + String memberName = memberQueryService.selectNameById(purchaseOrder.getMemberId()); + purchaseOrder.setMemberName(memberName); + } + } + int count = purchaseOrderMapper.findSearchPurchaseOrderCount(purchaseOrderSelectSearchDTO); + if (count == 0) { throw new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND); } - System.out.println("dkjdkdk:"); + return new PageImpl<>(purchaseOrders, pageable, count); } @Override @Transactional(readOnly = true) - public void exportPurchaseOrder(HttpServletResponse response) { + public void exportPurchaseOrder(HttpServletResponse response) throws GeneralSecurityException { List purchaseOrderExcels = purchaseOrderMapper.findPurchaseOrderForExcel(); + if(purchaseOrderExcels == null) { throw new PurchaseOrderCommonException(PurchaseOrderErrorCode.PURCHASE_ORDER_NOT_FOUND); } + + for(int i=0;i + + \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml b/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml index a25ac35a..a6084d76 100644 --- a/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml @@ -25,10 +25,20 @@ + + + + + + + + + + - a.pur_ord_id, a.pur_ord_ttl, a.pur_ord_stat, - b.ord_ttl, - d.mem_name AS admin_name, - c.mem_name AS mem_name, - f.prod_name + a.created_at, + a.mem_id, + b.ord_ttl FROM tb_purchase_order a LEFT JOIN tb_order b ON a.ord_id = b.ord_id @@ -122,22 +130,52 @@ AND a.created_at BETWEEN #{purchaseOrderSelectSearchDTO.startDate} AND #{purchaseOrderSelectSearchDTO.endDate} - ORDER BY a.created_at DESC + + + + + + ORDER BY a.pur_ord_id ${sortOrder} + + + ORDER BY a.pur_ord_ttl ${sortOrder} + + + ORDER BY a.pur_ord_stat ${sortOrder} + + + ORDER BY f.prod_name ${sortOrder} + + + ORDER BY c.mem_name ${sortOrder} + + + ORDER BY a.created_at ${sortOrder} + + + ORDER BY a.created_at DESC + + + + + + ORDER BY a.created_at DESC + + LIMIT #{pageSize} OFFSET #{offset}; - SELECT COUNT(*) AS cnt FROM tb_purchase_order a - a.mem_id = #{purchaseOrderSelectSearchDTO.memberId} - AND a.active = TRUE + a.active = TRUE - AND a.ord_ttl LIKE CONCAT('%', #{purchaseOrderSelectSearchDTO.title}, '%') + AND a.pur_ord_ttl LIKE CONCAT('%', #{purchaseOrderSelectSearchDTO.title}, '%') - AND a.ord_stat = #{purchaseOrderSelectSearchDTO.status} + AND a.pur_ord_stat = #{purchaseOrderSelectSearchDTO.status} AND a.admin_id = #{purchaseOrderSelectSearchDTO.adminId} @@ -210,8 +248,6 @@ a.created_at, a.mem_id, b.ord_ttl, - d.mem_name AS admin_name, - c.mem_name AS mem_name, f.prod_name FROM tb_purchase_order a LEFT JOIN tb_order b @@ -306,6 +342,15 @@ a.pur_ord_ttl, a.pur_ord_stat, a.pur_ord_id, - a.created_at + a.created_at, + b.mem_name, + d.conr_car_name + FROM tb_purchase_order a + LEFT JOIN tb_member b + ON a.mem_id = b.mem_id + LEFT JOIN tb_order c + ON a.ord_id = c.ord_id + LEFT JOIN tb_contract d + ON c.conr_id = d.conr_id; \ No newline at end of file From 5f2555bdb0f7cc05c82dccdccc9c5fa6fc1a86df Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 2 Dec 2024 16:43:04 +0900 Subject: [PATCH 411/563] =?UTF-8?q?fix:=20=ED=8C=8C=EC=9D=BC=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=EA=B4=80=EB=A0=A8=20=EC=88=98=EC=A0=95=20=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20(#201)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/NoticeController.java | 37 +++++++++++++++---- .../application/dto/NoticeModifyDTO.java | 2 + 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java index e5fcaf9d..b1962bcb 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java @@ -27,9 +27,11 @@ public class NoticeController { private final AuthQueryService authQueryService; private final S3FileServiceImpl s3FileService; + private final NoticeModifyDTO noticeModifyDTO; @Autowired - public NoticeController(NoticeCommandService noticeCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService){ + public NoticeController(NoticeCommandService noticeCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService, NoticeModifyDTO noticeModifyDTO){ + this.noticeModifyDTO = noticeModifyDTO; this.noticeCommandService = noticeCommandService; this.authQueryService =authQueryService; this.s3FileService = s3FileService; @@ -42,11 +44,20 @@ public NoticeController(NoticeCommandService noticeCommandService, AuthQueryServ }) @PostMapping(value = "") public ResponseEntity postNotice(@RequestPart("dto") NoticeRegistDTO noticeRegistDTO, // JSON 데이터 - @RequestPart("file") MultipartFile file, + @RequestPart(value = "file", required = false) MultipartFile file, Principal principal){ String memberId =authQueryService.selectMemberIdByLoginId(principal.getName()); noticeRegistDTO.setMemberId(memberId); - noticeRegistDTO.setFileUrl(s3FileService.uploadOneFile(file)); + if (file != null && !file.isEmpty()) { + noticeRegistDTO.setFileUrl(s3FileService.uploadOneFile(file)); + System.out.println("response:1"); + }else if(file==null || file.isEmpty()){ + System.out.println("response:2"); + noticeRegistDTO.setFileUrl(null); + } else { + System.out.println("response:3"); + noticeRegistDTO.setFileUrl(null); + } noticeCommandService.registerNotice(noticeRegistDTO, principal); return ResponseEntity.ok(NoticeResponseMessage.builder() .httpStatus(200) @@ -63,13 +74,23 @@ public ResponseEntity postNotice(@RequestPart("dto") Noti @PutMapping("{noticeId}") public ResponseEntity modifyNotice(Principal principal, @PathVariable String noticeId, - @RequestBody NoticeModifyDTO noticeModifyRequestDTO){ + @RequestPart("dto") NoticeModifyDTO noticeModifyDTO, // JSON 데이터 + @RequestPart(value = "file", required = false) MultipartFile file){ String memberLoginId = principal.getName(); - noticeModifyRequestDTO.setMemberLoginId(memberLoginId); - noticeModifyRequestDTO.setNoticeId(noticeId); - - NoticeModifyDTO noticeModifyDTO = noticeCommandService.modifyNotice(noticeId,noticeModifyRequestDTO,principal); + noticeModifyDTO.setMemberId(memberLoginId); + noticeModifyDTO.setContent(noticeModifyDTO.getContent()); + if (file != null && !file.isEmpty()) { + noticeModifyDTO.setFileUrl(s3FileService.uploadOneFile(file)); + System.out.println("1"); + }else if(file==null || file.isEmpty()) { + System.out.println("2"); + noticeModifyDTO.setFileUrl(null); + } else { + System.out.println("3"); + noticeModifyDTO.setFileUrl(s3FileService.uploadOneFile(file)); + } + noticeCommandService.modifyNotice(noticeId,noticeModifyDTO, principal); return ResponseEntity.ok(NoticeResponseMessage.builder() .httpStatus(200) .msg("성공") diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java index b1c518cf..e91cc24e 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeModifyDTO.java @@ -4,7 +4,9 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.springframework.stereotype.Component; +@Component @AllArgsConstructor @NoArgsConstructor @Setter From 23d93974eb7f10c3a3d25a7d96d065d89eeaf4f5 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Mon, 2 Dec 2024 16:43:36 +0900 Subject: [PATCH 412/563] =?UTF-8?q?fix:=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20import=20=EC=A7=80=EC=9A=B0=EA=B8=B0=20(#195)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/service/PurchaseOrderQueryServiceImpl.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java index ab799e3f..d43ecba8 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java @@ -4,7 +4,6 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringEscapeUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; @@ -12,12 +11,8 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import stanl_2.final_backend.domain.member.query.dto.MemberDTO; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.member.query.service.MemberQueryService; -import stanl_2.final_backend.domain.order.command.domain.aggregate.entity.Order; -import stanl_2.final_backend.domain.order.query.service.OrderQueryService; -import stanl_2.final_backend.domain.product.query.service.ProductQueryService; import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderExcelDTO; import stanl_2.final_backend.domain.purchase_order.common.exception.PurchaseOrderCommonException; import stanl_2.final_backend.domain.purchase_order.common.exception.PurchaseOrderErrorCode; From 00cd906f9bf646fbf6b97f134f9290d1b506af63 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 2 Dec 2024 17:05:41 +0900 Subject: [PATCH 413/563] =?UTF-8?q?feat:=20=EC=83=88=EB=A1=9C=EC=9A=B4=20?= =?UTF-8?q?=EC=9E=84=EC=8B=9C=EB=B2=88=ED=98=B8=20=EC=9E=AC=EB=B0=9C?= =?UTF-8?q?=EA=B8=89(#199)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AuthController.java | 2 + .../service/AuthCommandService.java | 2 + .../service/AuthCommandServiceImpl.java | 44 +++++++++++++++++++ .../global/exception/GlobalErrorCode.java | 1 + .../global/mail/MailService.java | 3 ++ .../global/mail/MailServiceImpl.java | 24 +++++++++- 6 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java index 4d2fa754..09b2e40a 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/controller/AuthController.java @@ -172,6 +172,8 @@ public ResponseEntity checkMail(@RequestBody CheckNumDTO authCommandService.checkNum(checkNumDTO); + authCommandService.sendNewPwd(checkNumDTO.getLoginId()); + return ResponseEntity.ok(MemberResponseMessage.builder() .httpStatus(200) .msg("성공") diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java index bbd2435d..74558de9 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/application/service/AuthCommandService.java @@ -18,4 +18,6 @@ public interface AuthCommandService { void sendEmail(CheckMailDTO checkMailDTO) throws GeneralSecurityException, MessagingException; void checkNum(CheckNumDTO checkNumDTO) throws GeneralSecurityException; + + void sendNewPwd(String loginId) throws MessagingException, GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java index d17d722c..7b3253dd 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java @@ -37,6 +37,7 @@ import javax.crypto.SecretKey; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; +import java.security.SecureRandom; import java.util.Date; import java.util.stream.Collectors; @@ -48,6 +49,16 @@ public class AuthCommandServiceImpl implements AuthCommandService { @Value("${jwt.secret-key}") private String jwtSecretKey; + private static final String LOWERCASE = "abcdefghijklmnopqrstuvwxyz"; + private static final String UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private static final String DIGITS = "0123456789"; + private static final String SPECIAL_CHARACTERS = "!@#$%^&*()-_=+<>?"; + + // 임시 비밀번호의 최소 길이 + private static final int MIN_LENGTH = 12; + + private SecureRandom secureRandom = new SecureRandom(); + private final MemberRepository memberRepository; private final MemberRoleRepository memberRoleRepository; private final PasswordEncoder passwordEncoder; @@ -165,6 +176,39 @@ public void checkNum(CheckNumDTO checkNumDTO) throws GeneralSecurityException { } } + @Override + @Transactional + public void sendNewPwd(String loginId) throws MessagingException, GeneralSecurityException { + StringBuilder password = new StringBuilder(); + + // 반드시 포함할 문자들 + password.append(randomChar(LOWERCASE)); // 소문자 + password.append(randomChar(UPPERCASE)); // 대문자 + password.append(randomChar(DIGITS)); // 숫자 + password.append(randomChar(SPECIAL_CHARACTERS)); // 특수문자 + + // 나머지 비밀번호 길이 채우기 (MIN_LENGTH 까지) + String allChars = LOWERCASE + UPPERCASE + DIGITS + SPECIAL_CHARACTERS; + for (int i = password.length(); i < MIN_LENGTH; i++) { + password.append(randomChar(allChars)); + } + + String hashPwd = passwordEncoder.encode(password); + + Member newPwdMem = memberRepository.findByLoginId(loginId); + + mailService.sendPwdEmail(aesUtils.decrypt(newPwdMem.getEmail()), password); + + newPwdMem.setPassword(hashPwd); + + memberRepository.save(newPwdMem); + } + + private char randomChar(String charSet) { + int randomIndex = secureRandom.nextInt(charSet.length()); + return charSet.charAt(randomIndex); + } + private String generateAccessToken(String username, String authorities, SecretKey secretKey) { return Jwts.builder() .setIssuer("STANL2") diff --git a/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java b/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java index 704d2b2d..dc8a2cb3 100644 --- a/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java +++ b/src/main/java/stanl_2/final_backend/global/exception/GlobalErrorCode.java @@ -21,6 +21,7 @@ public enum GlobalErrorCode { "데이터 무결성 위반입니다. 필수 값이 누락되었거나 유효하지 않습니다."), REGISTER_FAIL(40006, HttpStatus.BAD_REQUEST, "회원 가입 실패!"), VALIDATION_FAIL(40007, HttpStatus.BAD_REQUEST, "유효성 검사 실패"), + NUMBER_VALIDATION_FAIL(40008, HttpStatus.BAD_REQUEST, "인증번호 인증 실패"), /** diff --git a/src/main/java/stanl_2/final_backend/global/mail/MailService.java b/src/main/java/stanl_2/final_backend/global/mail/MailService.java index 6b11c1db..22423ea3 100644 --- a/src/main/java/stanl_2/final_backend/global/mail/MailService.java +++ b/src/main/java/stanl_2/final_backend/global/mail/MailService.java @@ -9,4 +9,7 @@ public interface MailService { // 보낸 이메일과 인증번호 일치하는지 확인 Boolean verifyEmailCode(String email, String code); + + + void sendPwdEmail(String decrypt, StringBuilder password) throws MessagingException; } diff --git a/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java b/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java index 9dd6a098..f39d1f32 100644 --- a/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java @@ -10,6 +10,8 @@ import org.thymeleaf.context.Context; import org.thymeleaf.templatemode.TemplateMode; import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; +import stanl_2.final_backend.global.exception.GlobalCommonException; +import stanl_2.final_backend.global.exception.GlobalErrorCode; import stanl_2.final_backend.global.redis.RedisService; import java.security.SecureRandom; @@ -82,6 +84,18 @@ private MimeMessage createEmailForm(String email) throws MessagingException { return message; } + private MimeMessage createNewPwdEmailForm(String email, String newPwd) throws MessagingException { + + // MimeMessage에 코드, 송신 이메일, 내용 보관 + MimeMessage message = mailSender.createMimeMessage(); + message.addRecipients(MimeMessage.RecipientType.TO, email); + message.setSubject("STANL2 본인 확인"); + message.setFrom(configEmail); + message.setText(setContext(newPwd), "utf-8", "html"); + + return message; + } + /* 만든 메일 전송 */ @Override public void sendEmail(String toEmail) throws MessagingException { @@ -100,9 +114,17 @@ public void sendEmail(String toEmail) throws MessagingException { @Override public Boolean verifyEmailCode(String email, String code) { if(!code.equals(redisService.getKey(email))){ - System.out.println("시바련ㄴ아 틀렸어"); + throw new GlobalCommonException(GlobalErrorCode.NUMBER_VALIDATION_FAIL); } return true; } + + @Override + public void sendPwdEmail(String email, StringBuilder password) throws MessagingException { + + MimeMessage emailForm = createNewPwdEmailForm(email, password.toString()); + + mailSender.send(emailForm); + } } From f3c2229f4343e46c1b1aab0cc405f33d4f6caaf1 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Mon, 2 Dec 2024 17:18:19 +0900 Subject: [PATCH 414/563] =?UTF-8?q?fix:=20pr=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/order/query/service/OrderQueryService.java | 1 - .../domain/order/query/service/OrderQueryServiceImpl.java | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java index edb18f4e..010008f5 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java @@ -8,7 +8,6 @@ import stanl_2.final_backend.domain.order.query.dto.OrderSelectSearchDTO; import java.security.GeneralSecurityException; -import java.util.Map; public interface OrderQueryService { Page selectAllEmployee(String loginId, Pageable pageable); diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java index 2b4d33c2..c8e16edd 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java @@ -191,6 +191,7 @@ public Page selectSearchOrders(OrderSelectSearchDTO orderS } @Override + @Transactional(readOnly = true) public String selectByContractId(String orderId) { String contractId = orderMapper.selectByContractId(orderId); From 155d7e340e48ab8d4b58ef074c7ad481cdfcd325 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 2 Dec 2024 17:27:02 +0900 Subject: [PATCH 415/563] =?UTF-8?q?feat:=20=EC=83=88=EB=A1=9C=EC=9A=B4=20?= =?UTF-8?q?=EC=9E=84=EC=8B=9C=EB=B2=88=ED=98=B8=20=EC=A0=84=EC=86=A1=20htm?= =?UTF-8?q?l=20=EC=B6=94=EA=B0=80(#199)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/mail/MailServiceImpl.java | 23 ++++++- src/main/resources/templates/pwdMail.html | 61 +++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/templates/pwdMail.html diff --git a/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java b/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java index f39d1f32..ecf00e27 100644 --- a/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java @@ -67,6 +67,27 @@ private String setContext(String code) { return templateEngine.process("mail", context); } + private String setPwdContext(String newPwd) { + + // thymeleaf 기반의 html 파일에 값을 넣고 연결 + Context context = new Context(); + // templateengine과 classloadertemplateresolver를 활용하여 resource/template에 위치한 mail.html 연결 + TemplateEngine templateEngine = new TemplateEngine(); + ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver(); + + // 매개변수 저장 + context.setVariable("tempPassword", newPwd); + + templateResolver.setPrefix("templates/"); + templateResolver.setSuffix(".html"); + templateResolver.setTemplateMode(TemplateMode.HTML); + templateResolver.setCacheable(false); + + templateEngine.setTemplateResolver(templateResolver); + + return templateEngine.process("pwdMail", context); + } + private MimeMessage createEmailForm(String email) throws MessagingException { String authCode = createCode(); @@ -91,7 +112,7 @@ private MimeMessage createNewPwdEmailForm(String email, String newPwd) throws Me message.addRecipients(MimeMessage.RecipientType.TO, email); message.setSubject("STANL2 본인 확인"); message.setFrom(configEmail); - message.setText(setContext(newPwd), "utf-8", "html"); + message.setText(setPwdContext(newPwd), "utf-8", "html"); return message; } diff --git a/src/main/resources/templates/pwdMail.html b/src/main/resources/templates/pwdMail.html new file mode 100644 index 00000000..c42cf423 --- /dev/null +++ b/src/main/resources/templates/pwdMail.html @@ -0,0 +1,61 @@ + + + + + + + Temporary Password + + + +
      +

      STANL2 임시 비밀번호 발급

      +

      안녕하세요,

      +

      요청하신 임시 비밀번호가 발급되었습니다. 아래의 비밀번호를 사용해 로그인하신 후, 꼭 비밀번호를 변경해 주세요:

      +
      + + [임시 비밀번호] +
      +

      감사합니다.

      +
      + + + From 464349688a567ef9af9309058c0b6aa493f4567d Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 2 Dec 2024 17:40:49 +0900 Subject: [PATCH 416/563] =?UTF-8?q?fix:=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=A0=9C=EA=B1=B0(#1?= =?UTF-8?q?99)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../final_backend/global/mail/MailConfig.java | 4 ++-- .../final_backend/global/mail/MailService.java | 4 ---- .../final_backend/global/mail/MailServiceImpl.java | 12 ------------ 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/global/mail/MailConfig.java b/src/main/java/stanl_2/final_backend/global/mail/MailConfig.java index 764e7168..ace508e5 100644 --- a/src/main/java/stanl_2/final_backend/global/mail/MailConfig.java +++ b/src/main/java/stanl_2/final_backend/global/mail/MailConfig.java @@ -11,10 +11,10 @@ @Configuration public class MailConfig { - @Value("${EMAIL_ID}") + @Value("${spring.mail.host.port.username}") private String email; - @Value("${EMAIL_PWD}") + @Value("${spring.mail.host.port.password}") private String password; @Bean diff --git a/src/main/java/stanl_2/final_backend/global/mail/MailService.java b/src/main/java/stanl_2/final_backend/global/mail/MailService.java index 22423ea3..fbc3c6be 100644 --- a/src/main/java/stanl_2/final_backend/global/mail/MailService.java +++ b/src/main/java/stanl_2/final_backend/global/mail/MailService.java @@ -7,9 +7,5 @@ public interface MailService { /* 메일 전송 */ void sendEmail(String toEmail) throws MessagingException; - // 보낸 이메일과 인증번호 일치하는지 확인 - Boolean verifyEmailCode(String email, String code); - - void sendPwdEmail(String decrypt, StringBuilder password) throws MessagingException; } diff --git a/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java b/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java index ecf00e27..84e5e076 100644 --- a/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java @@ -10,8 +10,6 @@ import org.thymeleaf.context.Context; import org.thymeleaf.templatemode.TemplateMode; import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; -import stanl_2.final_backend.global.exception.GlobalCommonException; -import stanl_2.final_backend.global.exception.GlobalErrorCode; import stanl_2.final_backend.global.redis.RedisService; import java.security.SecureRandom; @@ -131,16 +129,6 @@ public void sendEmail(String toEmail) throws MessagingException { mailSender.send(emailForm); } - - @Override - public Boolean verifyEmailCode(String email, String code) { - if(!code.equals(redisService.getKey(email))){ - throw new GlobalCommonException(GlobalErrorCode.NUMBER_VALIDATION_FAIL); - } - - return true; - } - @Override public void sendPwdEmail(String email, StringBuilder password) throws MessagingException { From a797673687d58975455732361e3ef0ae58810366 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 2 Dec 2024 17:44:54 +0900 Subject: [PATCH 417/563] =?UTF-8?q?fix:=20=ED=8C=8C=EC=9D=BC=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=EA=B4=80=EB=A0=A8=20=EC=88=98=EC=A0=95=20=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20(#201)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/controller/NoticeController.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java index b1962bcb..04aca076 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java @@ -76,21 +76,24 @@ public ResponseEntity modifyNotice(Principal principal, @PathVariable String noticeId, @RequestPart("dto") NoticeModifyDTO noticeModifyDTO, // JSON 데이터 @RequestPart(value = "file", required = false) MultipartFile file){ + System.out.println("=============================="); String memberLoginId = principal.getName(); + System.out.println("=============================="); noticeModifyDTO.setMemberId(memberLoginId); + System.out.println("=============================="); noticeModifyDTO.setContent(noticeModifyDTO.getContent()); - if (file != null && !file.isEmpty()) { + System.out.println("response:1"); noticeModifyDTO.setFileUrl(s3FileService.uploadOneFile(file)); - System.out.println("1"); }else if(file==null || file.isEmpty()) { - System.out.println("2"); + System.out.println("response:2"); noticeModifyDTO.setFileUrl(null); } else { - System.out.println("3"); + System.out.println("response:3"); noticeModifyDTO.setFileUrl(s3FileService.uploadOneFile(file)); } noticeCommandService.modifyNotice(noticeId,noticeModifyDTO, principal); + return ResponseEntity.ok(NoticeResponseMessage.builder() .httpStatus(200) .msg("성공") From 4fce7de5152712ecbf92e7c67a31251397cd0988 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 2 Dec 2024 17:45:32 +0900 Subject: [PATCH 418/563] =?UTF-8?q?fix:=20=ED=8C=8C=EC=9D=BC=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=EA=B4=80=EB=A0=A8=20=EC=88=98=EC=A0=95=20=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20(#204)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ProblemController.java | 39 ++++++++++++++++--- .../application/dto/ProblemModifyDTO.java | 2 + 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java index 384e090d..45fb57e4 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java @@ -10,7 +10,6 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; -import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; import stanl_2.final_backend.domain.problem.command.application.dto.ProblemModifyDTO; import stanl_2.final_backend.domain.problem.command.application.dto.ProblemRegistDTO; import stanl_2.final_backend.domain.problem.command.application.service.ProblemCommandService; @@ -25,11 +24,13 @@ public class ProblemController { private final ProblemCommandService problemCommandService; private final AuthQueryService authQueryService; private final S3FileServiceImpl s3FileService; + private final ProblemModifyDTO problemModifyDTO; @Autowired - public ProblemController(ProblemCommandService problemCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService) { + public ProblemController(ProblemCommandService problemCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService,ProblemModifyDTO problemModifyDTO ) { this.problemCommandService = problemCommandService; this.authQueryService = authQueryService; this.s3FileService = s3FileService; + this.problemModifyDTO =problemModifyDTO; } @Operation(summary = "문제사항 작성") @@ -43,7 +44,16 @@ public ResponseEntity postProblem(@RequestPart("dto") Pr Principal principal){ String memberId = authQueryService.selectMemberIdByLoginId(principal.getName()); problemRegistDTO.setMemberId(memberId); - problemRegistDTO.setFileUrl(s3FileService.uploadOneFile(file)); + if (file != null && !file.isEmpty()) { + problemRegistDTO.setFileUrl(s3FileService.uploadOneFile(file)); + System.out.println("response:1"); + }else if(file==null || file.isEmpty()){ + System.out.println("response:2"); + problemRegistDTO.setFileUrl(null); + } else { + System.out.println("response:3"); + problemRegistDTO.setFileUrl(null); + } problemCommandService.registerProblem(problemRegistDTO,principal); return ResponseEntity.ok(ProblemResponseMessage.builder() .httpStatus(200) @@ -59,9 +69,26 @@ public ResponseEntity postProblem(@RequestPart("dto") Pr }) @PutMapping("{problemId}") public ResponseEntity modifyProblem(@PathVariable String problemId, - @RequestBody ProblemModifyDTO problemModifyRequestDTO, Principal principal){ - - ProblemModifyDTO problemModifyDTO = problemCommandService.modifyProblem(problemId,problemModifyRequestDTO,principal); + @RequestBody ProblemModifyDTO problemModifyRequestDTO, + @RequestPart(value = "file", required = false) MultipartFile file, + Principal principal){ + System.out.println("=============================="); + String memberLoginId = principal.getName(); + System.out.println("=============================="); + problemModifyRequestDTO.setMemberId(memberLoginId); + System.out.println("=============================="); + problemModifyRequestDTO.setContent(problemModifyRequestDTO.getContent()); + if (file != null && !file.isEmpty()) { + System.out.println("response:1"); + problemModifyRequestDTO.setFileUrl(s3FileService.uploadOneFile(file)); + }else if(file==null || file.isEmpty()) { + System.out.println("response:2"); + problemModifyRequestDTO.setFileUrl(null); + } else { + System.out.println("response:3"); + problemModifyRequestDTO.setFileUrl(s3FileService.uploadOneFile(file)); + } + problemCommandService.modifyProblem(problemId,problemModifyRequestDTO,principal); return ResponseEntity.ok(ProblemResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java index d114da4c..55a70aa8 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java @@ -4,11 +4,13 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.springframework.stereotype.Component; @AllArgsConstructor @NoArgsConstructor @Setter @Getter +@Component public class ProblemModifyDTO { private String title; From 5dc07c47a2b4e3fcc911c93a1a9d74a8e78aa5c2 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 2 Dec 2024 17:45:42 +0900 Subject: [PATCH 419/563] =?UTF-8?q?fix:=20=ED=8C=8C=EC=9D=BC=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=EA=B4=80=EB=A0=A8=20=EC=88=98=EC=A0=95=20=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20(#203)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PromotionController.java | 41 +++++++++++++++---- .../application/dto/PromotionModifyDTO.java | 2 + 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java index 5032ee30..796d8918 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java @@ -10,7 +10,6 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; -import stanl_2.final_backend.domain.problem.command.application.dto.ProblemRegistDTO; import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionModifyDTO; import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionRegistDTO; import stanl_2.final_backend.domain.promotion.command.application.service.PromotionCommandService; @@ -25,12 +24,14 @@ public class PromotionController { private final PromotionCommandService promotionCommandService; private final AuthQueryService authQueryService; private final S3FileServiceImpl s3FileService; + private final PromotionModifyDTO promotionModifyDTO; @Autowired - public PromotionController(PromotionCommandService promotionCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService) { + public PromotionController(PromotionCommandService promotionCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService, PromotionModifyDTO promotionModifyDTO) { this.promotionCommandService = promotionCommandService; this.authQueryService =authQueryService; this.s3FileService = s3FileService; + this.promotionModifyDTO = promotionModifyDTO; } @Operation(summary = "프로모션 작성") @@ -44,7 +45,16 @@ public ResponseEntity postNotice(@RequestPart("dto") P Principal principal){ String memberId = authQueryService.selectMemberIdByLoginId(principal.getName()); promotionRegistDTO.setMemberId(memberId); - promotionRegistDTO.setFileUrl(s3FileService.uploadOneFile(file)); + if (file != null && !file.isEmpty()) { + promotionRegistDTO.setFileUrl(s3FileService.uploadOneFile(file)); + System.out.println("response:1"); + }else if(file==null || file.isEmpty()){ + System.out.println("response:2"); + promotionRegistDTO.setFileUrl(null); + } else { + System.out.println("response:3"); + promotionRegistDTO.setFileUrl(null); + } promotionCommandService.registerPromotion(promotionRegistDTO,principal); return ResponseEntity.ok(PromotionResponseMessage.builder() .httpStatus(200) @@ -59,10 +69,27 @@ public ResponseEntity postNotice(@RequestPart("dto") P content = {@Content(schema = @Schema(implementation = PromotionResponseMessage.class))}) }) @PutMapping("{promotionId}") - public ResponseEntity modifyNotice(@PathVariable String promotionId, - @RequestBody PromotionModifyDTO promotionModifyRequestDTO, Principal principal){ - - PromotionModifyDTO promotionModifyDTO = promotionCommandService.modifyPromotion(promotionId,promotionModifyRequestDTO,principal); + public ResponseEntity modifyNotice( + Principal principal, + @PathVariable String promotionId, + @RequestBody PromotionModifyDTO promotionModifyRequestDTO, + @RequestPart(value = "file", required = false) MultipartFile file){ + System.out.println("=============================="); + String memberLoginId = principal.getName(); + promotionModifyDTO.setMemberId(memberLoginId); + System.out.println("=============================="); + promotionModifyDTO.setContent(promotionModifyDTO.getContent()); + if (file != null && !file.isEmpty()) { + System.out.println("response:1"); + promotionModifyDTO.setFileUrl(s3FileService.uploadOneFile(file)); + }else if(file==null || file.isEmpty()) { + System.out.println("response:2"); + promotionModifyDTO.setFileUrl(null); + } else { + System.out.println("response:3"); + promotionModifyDTO.setFileUrl(s3FileService.uploadOneFile(file)); + } + promotionCommandService.modifyPromotion(promotionId,promotionModifyRequestDTO,principal); return ResponseEntity.ok(PromotionResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java index 4ae4f152..50454695 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java @@ -4,11 +4,13 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.springframework.stereotype.Component; @AllArgsConstructor @NoArgsConstructor @Setter @Getter +@Component public class PromotionModifyDTO { private String title; From f930b53035b90b836a5673e06f8e3e6bbc13eac0 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 2 Dec 2024 17:45:56 +0900 Subject: [PATCH 420/563] =?UTF-8?q?fix:=20pr=20=EB=B0=98=EC=98=81(#199)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/command/domain/service/AuthCommandServiceImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java index 7b3253dd..96b730f3 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java @@ -168,6 +168,7 @@ public void sendEmail(CheckMailDTO checkMailDTO) throws GeneralSecurityException } @Override + @Transactional public void checkNum(CheckNumDTO checkNumDTO) throws GeneralSecurityException { String email = authQueryService.findEmail(checkNumDTO.getLoginId()); From 74d03f3a01b0dbce016985a8803a1cba39dd1844 Mon Sep 17 00:00:00 2001 From: giuseog Date: Mon, 2 Dec 2024 18:07:56 +0900 Subject: [PATCH 421/563] =?UTF-8?q?feat:=20=EA=B2=80=EC=83=89=20=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=EC=9D=84=20=EC=9C=84=ED=95=B4=20customer,=20salesHist?= =?UTF-8?q?ory=20mapper=20=EC=B6=94=EA=B0=80(#183)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/repository/CustomerMapper.java | 2 ++ .../query/service/CustomerQueryService.java | 2 ++ .../service/CustomerQueryServiceImpl.java | 14 +++++++++++ .../query/service/MemberQueryServiceImpl.java | 2 -- .../controller/SalesHistoryController.java | 5 ++-- .../query/dto/SalesHistorySearchDTO.java | 2 ++ .../service/SalesHistoryQueryService.java | 3 ++- .../service/SalesHistoryQueryServiceImpl.java | 10 +++++++- .../query/repository/CustomerMapper.xml | 18 ++++++++++++++ .../query/repository/SalesHistoryMapper.xml | 24 ++++++++++++++----- 10 files changed, 70 insertions(+), 12 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java b/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java index f4cb5f84..a060aade 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.java @@ -29,4 +29,6 @@ public interface CustomerMapper { int selectCustomerContractCnt(String customerId); List findCustomerForExcel(); + + List findCustomerInfoByName(String customerName); } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java index ee2fa419..a4cc866b 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryService.java @@ -13,6 +13,8 @@ public interface CustomerQueryService { CustomerDTO selectCustomerInfo(String customerId) throws GeneralSecurityException; + List selectCustomerId(String customerName) throws GeneralSecurityException; + Page selectCustomerList(Pageable pageable) throws GeneralSecurityException; Page findCustomerByCondition(Pageable pageable, CustomerSearchDTO customerSearchDTO) throws GeneralSecurityException; diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java index 8416550c..b1c5696a 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java @@ -20,6 +20,7 @@ import stanl_2.final_backend.global.utils.AESUtils; import java.security.GeneralSecurityException; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -134,6 +135,19 @@ public String selectCustomerNameById(String customerId) throws GeneralSecurityEx return customerInfoDTO.getName(); } + @Override + public List selectCustomerId(String customerName) throws GeneralSecurityException { + List customerInfoDTO = customerMapper.findCustomerInfoByName(customerName); + + List customerIds = new ArrayList<>(); + + customerInfoDTO.forEach(customerInfo -> { + customerIds.add(customerInfo.getCustomerId()); + }); + + return customerIds; + } + @Override @Transactional(readOnly = true) public Page selectCustomerContractInfo(String customerId, Pageable pageable) { diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index 16d4b855..bc7d116d 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -69,8 +69,6 @@ public List selectMemberByCenterId(String centerId){ List memberList = memberMapper.findMembersByCenterId(centerId); - - memberList.forEach(dto -> { try { dto.setName(selectNameById(dto.getMemberId())); diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java index 679f7d22..a6d09594 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java @@ -19,6 +19,7 @@ import stanl_2.final_backend.domain.sales_history.query.dto.*; import stanl_2.final_backend.domain.sales_history.query.service.SalesHistoryQueryService; +import java.security.GeneralSecurityException; import java.security.Principal; import java.util.List; import java.util.Map; @@ -153,7 +154,7 @@ public ResponseEntity getSalesHistorySearchByEmploy public ResponseEntity getSalesHistoryBySearch(@RequestBody SalesHistorySearchDTO salesHistorySearchDTO, @PageableDefault(size = 20) Pageable pageable, @RequestParam(required = false) String sortField, - @RequestParam(required = false) String sortOrder){ + @RequestParam(required = false) String sortOrder) throws GeneralSecurityException { if (sortField != null && sortOrder != null) { @@ -283,7 +284,7 @@ public ResponseEntity getStatisticsSearchYearByEmpl @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @GetMapping("statistics") + @PostMapping("statistics") public ResponseEntity getStatistics(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, @PageableDefault(size = 20) Pageable pageable){ diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java index 23878da2..6be458c8 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java @@ -18,4 +18,6 @@ public class SalesHistorySearchDTO { private List memberList; private List centerList; private String orderBy; + private String customerName; + private List customerList; } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java index c33b879d..db29832a 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java @@ -5,6 +5,7 @@ import org.springframework.data.domain.Pageable; import stanl_2.final_backend.domain.sales_history.query.dto.*; +import java.security.GeneralSecurityException; import java.util.List; public interface SalesHistoryQueryService { @@ -38,7 +39,7 @@ public interface SalesHistoryQueryService { Page selectSalesHistorySearchByEmployee(SalesHistorySearchDTO salesHistorySearchDTO, Pageable pageable); - Page selectSalesHistoryBySearch(SalesHistorySearchDTO salesHistorySearchDTO, Pageable pageable); + Page selectSalesHistoryBySearch(SalesHistorySearchDTO salesHistorySearchDTO, Pageable pageable) throws GeneralSecurityException; Page selectStatisticsAverageBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java index e3e9e54b..48cf8f38 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java @@ -19,6 +19,7 @@ import org.springframework.data.redis.core.RedisTemplate; import stanl_2.final_backend.global.excel.ExcelUtilsV1; +import java.security.GeneralSecurityException; import java.time.Duration; import java.util.List; @@ -151,7 +152,7 @@ public Page selectSalesHistorySearchByEmployee(SalesHisto } @Override - public Page selectSalesHistoryBySearch(SalesHistorySearchDTO salesHistorySearchDTO, Pageable pageable) { + public Page selectSalesHistoryBySearch(SalesHistorySearchDTO salesHistorySearchDTO, Pageable pageable) throws GeneralSecurityException { int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); @@ -163,6 +164,13 @@ public Page selectSalesHistoryBySearch(SalesHistorySearch sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; } + if(salesHistorySearchDTO.getCustomerName() != null){ + System.out.println("고객 리스트1: " + salesHistorySearchDTO.getCustomerList() + "검색어\n" + salesHistorySearchDTO.getCustomerName()); + + salesHistorySearchDTO.setCustomerList(customerQueryService.selectCustomerId(salesHistorySearchDTO.getCustomerName())); + System.out.println("고객 리스트: " + salesHistorySearchDTO.getCustomerList() + "검색어\n" + salesHistorySearchDTO.getCustomerName()); + } + List salesHistoryList = salesHistoryMapper.findSalesHistoryBySearch(size,offset, salesHistorySearchDTO, sortField, sortOrder); int total = salesHistoryMapper.findSalesHistoryCountBySearch(salesHistorySearchDTO); diff --git a/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml b/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml index 5be0d1ba..3da88efc 100644 --- a/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml @@ -195,4 +195,22 @@ a.mem_id FROM tb_customer_info a + + \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml index 0c706909..a14a34bd 100644 --- a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml @@ -232,22 +232,28 @@ a.created_at FROM tb_sales_history a + a.active = TRUE - a.mem_id IN + AND a.mem_id IN #{mem_id} - a.cent_id IN + AND a.cent_id IN #{cent_id} + + AND a.cust_id IN + + #{cust_id} + + AND a.created_at BETWEEN #{salesHistorySearchDTO.startDate} AND #{salesHistorySearchDTO.endDate} - AND a.active = TRUE @@ -298,22 +304,28 @@ COUNT(*) AS cnt FROM tb_sales_history a + a.active = TRUE - a.mem_id IN + AND a.mem_id IN #{mem_id} - a.cent_id IN + AND a.cent_id IN #{cent_id} + + AND a.cust_id IN + + #{cust_id} + + AND a.created_at BETWEEN #{salesHistorySearchDTO.startDate} AND #{salesHistorySearchDTO.endDate} - AND a.active = TRUE From 880dc7c195407f83e3957c045fa267e58c1a7846 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Mon, 2 Dec 2024 20:20:03 +0900 Subject: [PATCH 422/563] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EB=8B=A8=EC=97=90=20=EB=A7=9E=EA=B2=8C=20backend?= =?UTF-8?q?=20=EC=A0=84=EC=B2=B4=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#182)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/AlarmCommandService.java | 13 ++++--- .../domain/aggregate/entity/Alarm.java | 3 ++ .../service/AlarmCommandServiceImpl.java | 35 +++++++++++-------- .../alarm/query/dto/AlarmSelectDTO.java | 1 + .../alarm/scheduler/AlarmScheduler.java | 2 +- .../application/dto/ContractAlarmDTO.java | 18 ++++++++++ .../service/ContractCommandServiceImpl.java | 5 ++- .../query/service/MemberQueryService.java | 2 +- .../query/service/MemberQueryServiceImpl.java | 5 ++- .../application/dto/NoticeAlarmDTO.java | 2 ++ .../application/dto/OrderAlarmDTO.java | 18 ++++++++++ .../service/OrderCommandServiceImpl.java | 6 +++- .../dto/PurchaseOrderAlarmDTO.java | 18 ++++++++++ .../PurchaseOrderCommandServiceImpl.java | 6 +++- .../global/dataloader/MemberInitializer.java | 4 +-- .../security/config/RequestMatcherConfig.java | 2 +- .../alarm/query/repository/AlarmMapper.xml | 4 ++- 17 files changed, 115 insertions(+), 29 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractAlarmDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderAlarmDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderAlarmDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/application/service/AlarmCommandService.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/service/AlarmCommandService.java index eab3621d..bbff8e1c 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/application/service/AlarmCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/service/AlarmCommandService.java @@ -4,9 +4,12 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import stanl_2.final_backend.domain.alarm.command.application.dto.AlarmRegistDTO; import stanl_2.final_backend.domain.alarm.command.domain.aggregate.entity.Alarm; +import stanl_2.final_backend.domain.contract.command.application.dto.ContractAlarmDTO; import stanl_2.final_backend.domain.contract.command.domain.aggregate.entity.Contract; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeAlarmDTO; +import stanl_2.final_backend.domain.order.command.application.dto.OrderAlarmDTO; import stanl_2.final_backend.domain.order.command.domain.aggregate.entity.Order; +import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderAlarmDTO; import stanl_2.final_backend.domain.purchase_order.command.domain.aggregate.entity.PurchaseOrder; public interface AlarmCommandService { @@ -14,17 +17,17 @@ public interface AlarmCommandService { void sendToClient(SseEmitter emitter, String emitterId, Object data); - void send(String memberLoginId, String message, String redirectUrl, String tag, String type, String createdAt); + void send(String memberId, String adminId, String message, String redirectUrl, String tag, String type, String createdAt); - Alarm createAlarm(String memberId, String message, String redirectUrl, String tag, String type, String createdAt); + Alarm createAlarm(String memberId, String adminId, String message, String redirectUrl, String tag, String type, String createdAt); void sendNoticeAlarm(NoticeAlarmDTO noticeAlarmDTO); Boolean updateReadStatus(String alarmId); - void sendContractAlarm(Contract contract); + void sendContractAlarm(ContractAlarmDTO contractAlarmDTO); - void sendPurchaseOrderAlarm(PurchaseOrder purchaseOrder); + void sendPurchaseOrderAlarm(PurchaseOrderAlarmDTO purchaseOrderAlarmDTO); - void sendOrderAlarm(Order order); + void sendOrderAlarm(OrderAlarmDTO orderAlarmDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/aggregate/entity/Alarm.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/aggregate/entity/Alarm.java index a21a47af..e2d48ed8 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/aggregate/entity/Alarm.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/aggregate/entity/Alarm.java @@ -44,6 +44,9 @@ public class Alarm { @Column(name = "CREATED_AT", nullable = false) private String createdAt; + @Column(name = "ADMIN_ID", nullable = false) + private String adminId; + @Column(name = "MEM_ID", nullable = false) private String memberId; diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java index 7fc16218..4cccd4d7 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java @@ -14,11 +14,14 @@ import stanl_2.final_backend.domain.alarm.command.domain.repository.EmitterRepository; import stanl_2.final_backend.domain.alarm.common.exception.AlarmCommonException; import stanl_2.final_backend.domain.alarm.common.exception.AlarmErrorCode; +import stanl_2.final_backend.domain.contract.command.application.dto.ContractAlarmDTO; import stanl_2.final_backend.domain.contract.command.domain.aggregate.entity.Contract; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeAlarmDTO; +import stanl_2.final_backend.domain.order.command.application.dto.OrderAlarmDTO; import stanl_2.final_backend.domain.order.command.domain.aggregate.entity.Order; +import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderAlarmDTO; import stanl_2.final_backend.domain.purchase_order.command.domain.aggregate.entity.PurchaseOrder; import stanl_2.final_backend.domain.schedule.common.exception.ScheduleCommonException; import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; @@ -111,9 +114,10 @@ public void sendToClient(SseEmitter emitter, String emitterId, Object data) { @Override @Transactional - public void send(String memberId, String message, String redirectUrl, String tag, String type, String createdAt){ + public void send(String memberId, String adminId, String message, String redirectUrl, String tag, String type, + String createdAt){ - Alarm alarm = alarmRepository.save(createAlarm(memberId, message, redirectUrl, tag, type, createdAt)); + Alarm alarm = alarmRepository.save(createAlarm(memberId, adminId, message, redirectUrl, tag, type, createdAt)); Map sseEmitters = emitterRepository.findAllEmitterStartWithByMemberId(memberId); sseEmitters.forEach( @@ -126,10 +130,12 @@ public void send(String memberId, String message, String redirectUrl, String tag @Override @Transactional - public Alarm createAlarm(String memberId, String message, String redirectUrl, String tag, String type, String createdAt) { + public Alarm createAlarm(String memberId, String adminId, String message, String redirectUrl, String tag, String type, + String createdAt) { Alarm alarm = new Alarm(); alarm.setMemberId(memberId); + alarm.setAdminId(adminId); alarm.setMessage(message); alarm.setRedirectUrl(redirectUrl); alarm.setTag(tag); @@ -158,7 +164,7 @@ public void sendNoticeAlarm(NoticeAlarmDTO noticeAlarmDTO){ } memberIdList.forEach(member -> { - String memberId = member; + String targetId = member; String type = "NOTICE"; String tag = null; if(noticeAlarmDTO.getClassification().equals("important")){ @@ -182,47 +188,48 @@ public void sendNoticeAlarm(NoticeAlarmDTO noticeAlarmDTO){ + "¬iceId=" + noticeAlarmDTO.getNoticeId(); String createdAt = getCurrentTime(); - send(memberId, message, redirectUrl, tag, type, createdAt); + send(targetId, noticeAlarmDTO.getMemberId(), message, redirectUrl, tag, type, createdAt); }); } @Override @Transactional - public void sendContractAlarm(Contract contract) { + public void sendContractAlarm(ContractAlarmDTO contractAlarmDTO) { String type = "CONTRACT"; String tag = "계약서"; - String message = contract.getCustomerName() +" 고객님의 계약서가 승인되었습니다."; + String message = contractAlarmDTO.getCustomerName() +" 고객님의 계약서가 승인되었습니다."; String redirectUrl = "/contract/list"; String createdAt = getCurrentTime(); - send(contract.getMemberId(), message, redirectUrl, tag, type, createdAt); + send(contractAlarmDTO.getMemberId(), contractAlarmDTO.getAdminId(), message, redirectUrl, tag, type, createdAt); } @Override @Transactional - public void sendPurchaseOrderAlarm(PurchaseOrder purchaseOrder) { + public void sendPurchaseOrderAlarm(PurchaseOrderAlarmDTO purchaseOrderAlarmDTO) { String type = "CONTRACT"; String tag = "발주서"; - String message = purchaseOrder.getTitle() +" 발주서가 승인되었습니다."; + String message = purchaseOrderAlarmDTO.getTitle() +" 발주서가 승인되었습니다."; String redirectUrl = "/purchase-order/list"; String createdAt = getCurrentTime(); - send(purchaseOrder.getMemberId(), message, redirectUrl, tag, type, createdAt); + send(purchaseOrderAlarmDTO.getMemberId(), purchaseOrderAlarmDTO.getAdminId(), message, redirectUrl, + tag, type, createdAt); } @Override @Transactional - public void sendOrderAlarm(Order order) { + public void sendOrderAlarm(OrderAlarmDTO orderAlarmDTO) { String type = "CONTRACT"; String tag = "수주서"; - String message = order.getTitle() +" 수주서가 승인되었습니다."; + String message = orderAlarmDTO.getTitle() +" 수주서가 승인되었습니다."; String redirectUrl = "/order/list"; String createdAt = getCurrentTime(); - send(order.getMemberId(), message, redirectUrl, tag, type, createdAt); + send(orderAlarmDTO.getMemberId(), orderAlarmDTO.getAdminId(), message, redirectUrl, tag, type, createdAt); } @Override diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java index b4cb49b9..1df47c29 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java @@ -21,4 +21,5 @@ public class AlarmSelectDTO { private String memberLoginId; private String memberId; + private String adminId; } diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java index f92e1da6..0ae414e9 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java @@ -64,7 +64,7 @@ public void alarmTodaySchedule(){ String redirectUrl = "/schedule"; String createdAt = getCurrentTime(); - alarmCommandService.send(memberId, message, redirectUrl, tag, type, createdAt); + alarmCommandService.send(memberId, memberId, message, redirectUrl, tag, type, createdAt); }); } } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractAlarmDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractAlarmDTO.java new file mode 100644 index 00000000..de574b0a --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractAlarmDTO.java @@ -0,0 +1,18 @@ +package stanl_2.final_backend.domain.contract.command.application.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class ContractAlarmDTO { + + private String contractId; + private String customerName; + private String memberId; + private String adminId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index d3420639..e69ca541 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -261,7 +261,10 @@ public void modifyContractStatus(ContractStatusModifyDTO contractStatusModifyDTO contractRepository.save(contract); - alarmCommandService.sendContractAlarm(contract); + ContractAlarmDTO contractAlarmDTO = new ContractAlarmDTO(contract.getContractId(), contract.getCustomerName(), + contract.getMemberId(), contract.getAdminId()); + + alarmCommandService.sendContractAlarm(contractAlarmDTO); if (contractStatusModifyDTO.getStatus().equals("APPROVED")) { // 판매 내역 등록 diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java index 684f6772..bb1598aa 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java @@ -17,7 +17,7 @@ public interface MemberQueryService { List selectMemberByOrganizationId(String organizationId) throws GeneralSecurityException; - MemberDTO selectMemberInfoById(String memberId); + MemberDTO selectMemberInfoById(String memberId) throws GeneralSecurityException; // MemberDetailDTO selectMemberDetail(String name) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index c4d22029..9840a960 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -130,10 +130,13 @@ public List selectMemberByOrganizationId(String organizationId) throw @Override @Transactional(readOnly = true) - public MemberDTO selectMemberInfoById(String memberId) { + public MemberDTO selectMemberInfoById(String memberId) throws GeneralSecurityException { MemberDTO memberDTO = memberMapper.findMemberInfoBymemberId(memberId); + memberDTO.setName(aesUtils.decrypt(memberDTO.getName())); + memberDTO.setImageUrl(aesUtils.decrypt(memberDTO.getImageUrl())); + return memberDTO; } } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeAlarmDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeAlarmDTO.java index ea2ec01a..202926ee 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeAlarmDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeAlarmDTO.java @@ -21,4 +21,6 @@ public class NoticeAlarmDTO { private String memberLoginId; private String memberId; + + private String adminId; } diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderAlarmDTO.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderAlarmDTO.java new file mode 100644 index 00000000..4797c8a6 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderAlarmDTO.java @@ -0,0 +1,18 @@ +package stanl_2.final_backend.domain.order.command.application.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class OrderAlarmDTO { + + private String orderId; + private String title; + private String memberId; + private String adminId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java index 6e4c2584..806d0af0 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java @@ -6,6 +6,7 @@ import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.alarm.command.application.service.AlarmCommandService; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.order.command.application.dto.OrderAlarmDTO; import stanl_2.final_backend.domain.order.command.application.dto.OrderModifyDTO; import stanl_2.final_backend.domain.order.command.application.dto.OrderRegistDTO; import stanl_2.final_backend.domain.order.command.application.dto.OrderStatusModifyDTO; @@ -124,6 +125,9 @@ public void modifyOrderStatus(OrderStatusModifyDTO orderStatusModifyDTO) { orderRepository.save(order); - alarmCommandService.sendOrderAlarm(order); + OrderAlarmDTO orderAlarmDTO = new OrderAlarmDTO(order.getOrderId(), order.getTitle(), order.getMemberId(), + order.getAdminId()); + + alarmCommandService.sendOrderAlarm(orderAlarmDTO); } } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderAlarmDTO.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderAlarmDTO.java new file mode 100644 index 00000000..02277e50 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/application/dto/PurchaseOrderAlarmDTO.java @@ -0,0 +1,18 @@ +package stanl_2.final_backend.domain.purchase_order.command.application.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +public class PurchaseOrderAlarmDTO { + + private String purchaseOrderId; + private String title; + private String memberId; + private String adminId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java index a636f305..b69ed560 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java @@ -11,6 +11,7 @@ import stanl_2.final_backend.domain.order.command.domain.repository.OrderRepository; import stanl_2.final_backend.domain.order.common.exception.OrderCommonException; import stanl_2.final_backend.domain.order.common.exception.OrderErrorCode; +import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderAlarmDTO; import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderModifyDTO; import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderRegistDTO; import stanl_2.final_backend.domain.purchase_order.command.application.dto.PurchaseOrderStatusModifyDTO; @@ -138,7 +139,10 @@ public void modifyPurchaseOrderStatus(PurchaseOrderStatusModifyDTO purchaseOrder purchaseOrderRepository.save(purchaseOrder); - alarmCommandService.sendPurchaseOrderAlarm(purchaseOrder); + PurchaseOrderAlarmDTO purchaseOrderAlarmDTO = new PurchaseOrderAlarmDTO(purchaseOrder.getPurchaseOrderId(), + purchaseOrder.getTitle(), purchaseOrder.getMemberId(), purchaseOrder.getAdminId()); + + alarmCommandService.sendPurchaseOrderAlarm(purchaseOrderAlarmDTO); } } diff --git a/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java b/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java index a1900b93..de53f443 100644 --- a/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java +++ b/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java @@ -39,14 +39,14 @@ public void run(ApplicationArguments args) throws Exception { createOrUpdateMember( "employee", "employee", - "영업 관리자", + "영업 사원", "employee@stanl.com", 3, "FEMALE", "000000-0000003", "010-0000-0003", "신대방삼거리", - "영업 관리자", + "영업 사원", "고졸", "백엔드 개발자", "미필", diff --git a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java index 47698a59..15e60ec4 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java @@ -79,7 +79,7 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception // Member API .requestMatchers(HttpMethod.GET, "/api/v1/member/authorities").hasAnyRole("GOD") // CHECK (시스템관리자) - .requestMatchers(HttpMethod.GET, "/api/v1/member").hasAnyRole("member-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 회원 정보 조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasAnyRole("member-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 회원 정보 조회 (전체) // Log API .requestMatchers(HttpMethod.GET, "/api/v1/log").hasAnyRole("log-get", "GOD") // 로그 조회 (시스템 관리자) diff --git a/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml b/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml index 5d2cd931..a6a6079d 100644 --- a/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml @@ -59,6 +59,7 @@ + SELECT + a.sch_id, a.sch_name, a.sch_tag, a.sch_srt_at, From 4c856f02f9873cce0ecec9cc7262d4905491d58b Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Tue, 3 Dec 2024 02:06:18 +0900 Subject: [PATCH 426/563] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=EC=A1=B0=ED=9A=8C=EB=A5=BC=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EC=88=98=EC=A0=95=20(#182)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../final_backend/domain/alarm/query/dto/AlarmSelectDTO.java | 1 + src/main/resources/application-dev.yml | 2 +- src/main/resources/application-prod.yml | 2 +- .../domain/alarm/query/repository/AlarmMapper.xml | 4 +++- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java index 1df47c29..62f46136 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java @@ -22,4 +22,5 @@ public class AlarmSelectDTO { private String memberLoginId; private String memberId; private String adminId; + private String contentId; } diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 1057556f..25f34fa9 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,7 +1,7 @@ spring: jpa: hibernate: - ddl-auto: create + ddl-auto: update logging: level: diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index de12a564..8ab30fe5 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -11,7 +11,7 @@ spring: timeout: 5000ms jpa: hibernate: - ddl-auto: create + ddl-auto: update open-in-view: false logging: diff --git a/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml b/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml index a6a6079d..00745d2f 100644 --- a/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.xml @@ -60,6 +60,7 @@ + + SELECT + a.mem_id, + a.mem_login_id, + a.mem_name, + a.mem_ema, + a.mem_age, + a.mem_sex, + a.mem_iden_no, + a.mem_pho, + a.mem_emer_pho, + a.mem_adr, + a.mem_note, + a.mem_pos, + a.mem_grd, + a.mem_job_type, + a.mem_mil, + a.mem_bank_name, + a.mem_acc, + a.center_id + FROM tb_member a + WHERE a.mem_name LIKE CONCAT('%', #{ memberName }, '%') \ No newline at end of file From 22aa6b77305de994d3380648d116f0b72de46de1 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Tue, 3 Dec 2024 02:40:37 +0900 Subject: [PATCH 428/563] =?UTF-8?q?refactor:=20=EB=B0=9C=EC=A3=BC=EC=84=9C?= =?UTF-8?q?=20=EB=93=B1=EB=A1=9D=20=ED=9A=8C=EC=9B=90Id=20=EA=B0=92=20?= =?UTF-8?q?=EB=B9=BC=EA=B8=B0=20(#210)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/service/PurchaseOrderCommandServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java index 48e695db..5662a354 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/command/domain/service/PurchaseOrderCommandServiceImpl.java @@ -56,7 +56,8 @@ public void registerPurchaseOrder(PurchaseOrderRegistDTO purchaseOrderRegistDTO) String memberId = authQueryService.selectMemberIdByLoginId(purchaseOrderRegistDTO.getMemberId()); // 수주서가 존재하는지 확인 - Order order = orderRepository.findByOrderIdAndMemberId(purchaseOrderRegistDTO.getOrderId(), memberId); + Order order = orderRepository.findByOrderId(purchaseOrderRegistDTO.getOrderId()); + if (order == null) { throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); } @@ -69,7 +70,6 @@ public void registerPurchaseOrder(PurchaseOrderRegistDTO purchaseOrderRegistDTO) String unescapedHtml = StringEscapeUtils.unescapeJson(purchaseOrderRegistDTO.getContent()); String updatedS3Url = s3FileService.uploadHtml(unescapedHtml, purchaseOrderRegistDTO.getTitle()); - PurchaseOrder purchaseOrder = modelMapper.map(purchaseOrderRegistDTO, PurchaseOrder.class); purchaseOrder.setMemberId(memberId); purchaseOrder.setContent(updatedS3Url); From dcc9ba887ef207297165ae3ca0c6e1a8b3d38b1e Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Tue, 3 Dec 2024 09:39:39 +0900 Subject: [PATCH 429/563] =?UTF-8?q?feat:=20=EC=9E=84=EC=8B=9C=EB=B9=84?= =?UTF-8?q?=EB=B0=80=EB=B2=88=ED=98=B8=20=EC=9E=AC=EB=B0=9C=EA=B8=89(#199)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/command/domain/service/AuthCommandServiceImpl.java | 2 +- .../global/security/config/RequestMatcherConfig.java | 4 +++- .../global/security/filter/JWTTokenValidatorFilter.java | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java index 96b730f3..6cf6793d 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/command/domain/service/AuthCommandServiceImpl.java @@ -172,7 +172,7 @@ public void sendEmail(CheckMailDTO checkMailDTO) throws GeneralSecurityException public void checkNum(CheckNumDTO checkNumDTO) throws GeneralSecurityException { String email = authQueryService.findEmail(checkNumDTO.getLoginId()); - if (checkNumDTO.getNumber() != null && !checkNumDTO.getNumber().equals(String.valueOf(redisService.getKey(email)))) { + if (checkNumDTO.getNumber() != null && !checkNumDTO.getNumber().equals(redisService.getKey(email))) { throw new MemberCommonException(MemberErrorCode.NUMBER_NOT_FOUND); } } diff --git a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java index e53dc305..f932f1ce 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java @@ -15,7 +15,9 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception "/webjars/**", "/api/v1/auth/signin", "/api/v1/auth/signup", - "/api/v1/auth/refresh" + "/api/v1/auth/refresh", + "/api/v1/auth/checkmail", + "/api/v1/auth/checknum" ).permitAll() // Alarm API diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java index dc924a43..8e43735f 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java @@ -169,6 +169,8 @@ private boolean isExemptedPath(HttpServletRequest request) { String path = request.getServletPath(); return path.equals("/api/v1/auth/signin") || path.equals("/api/v1/auth/signup") || + path.equals("/api/v1/auth/checkmail") || + path.equals("/api/v1/auth/checknum") || path.startsWith("/swagger-ui") || path.startsWith("/v3/api-docs") || path.startsWith("/swagger-resources") || From a26038eb663be0affd3d01c6035a3d11def8a359 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 3 Dec 2024 12:31:42 +0900 Subject: [PATCH 430/563] =?UTF-8?q?fix:=20=EA=B8=80=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=EC=9E=90=20=EC=88=98=EC=A0=95=20=EC=82=AC=ED=95=AD=20(#213)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/application/controller/NoticeController.java | 4 ++-- .../notices/command/application/dto/NoticeRegistDTO.java | 2 ++ .../command/domain/service/NoticeCommandServiceImpl.java | 3 ++- .../command/domain/aggregate/service/ProblemServiceImpl.java | 1 - 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java index 7c974aa7..656aff42 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java @@ -46,8 +46,8 @@ public NoticeController(NoticeCommandService noticeCommandService, AuthQueryServ public ResponseEntity postNotice(@RequestPart("dto") NoticeRegistDTO noticeRegistDTO, // JSON 데이터 @RequestPart(value = "file", required = false) MultipartFile file, Principal principal){ - String memberId =authQueryService.selectMemberIdByLoginId(principal.getName()); - noticeRegistDTO.setMemberId(memberId); + String memberLoginId = principal.getName(); + noticeRegistDTO.setMemberLoginId(memberLoginId); if (file != null && !file.isEmpty()) { noticeRegistDTO.setFileUrl(s3FileService.uploadOneFile(file)); System.out.println("response:1"); diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java index ef148d49..b6f42509 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/dto/NoticeRegistDTO.java @@ -20,5 +20,7 @@ public class NoticeRegistDTO { private String memberId; + private String memberLoginId; + private String fileUrl; } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java index 2addb0c1..3633409c 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java @@ -58,8 +58,9 @@ private String getCurrentTimestamp() { @Transactional public void registerNotice(NoticeRegistDTO noticeRegistDTO, Principal principal) { + redisService.clearNoticeCache(); - String memberId= principal.getName(); + String memberId = authQueryService.selectMemberIdByLoginId(noticeRegistDTO.getMemberLoginId()); noticeRegistDTO.setMemberId(memberId); try { diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java index 9d3bd58b..03da74eb 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java @@ -107,7 +107,6 @@ public void modifyStatus(String problemId,Principal principal) { if(!problem.getMemberId().equals(memberId)){ throw new ProblemCommonException(ProblemErrorCode.AUTHORIZATION_VIOLATION); } - System.out.println("==========test=================="); try { problem.setStatus("DONE"); problem.setUpdatedAt(getCurrentTimestamp()); From 0fdf86da86f0c9953f0dccad113ee4842e8d6076 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Tue, 3 Dec 2024 12:40:31 +0900 Subject: [PATCH 431/563] =?UTF-8?q?fix:=20=ED=9A=8C=EC=9B=90=20100?= =?UTF-8?q?=EB=AA=85=20=EB=8D=94=EB=AF=B8=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1(#216)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/dataloader/MemberInitializer.java | 172 ++++++++++++------ src/main/resources/application-prod.yml | 2 +- src/main/resources/image/default.png | Bin 0 -> 166279 bytes 3 files changed, 117 insertions(+), 57 deletions(-) create mode 100644 src/main/resources/image/default.png diff --git a/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java b/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java index a1900b93..e733dd0d 100644 --- a/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java +++ b/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java @@ -18,6 +18,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; +import java.util.Random; @Slf4j @Component @@ -36,93 +37,152 @@ public MemberInitializer(AuthCommandService authCommandService, @Override public void run(ApplicationArguments args) throws Exception { + // 우리 계정 createOrUpdateMember( - "employee", - "employee", - "영업 관리자", - "employee@stanl.com", - 3, - "FEMALE", - "000000-0000003", - "010-0000-0003", - "신대방삼거리", - "영업 관리자", - "고졸", - "백엔드 개발자", + "god", + "god", + "신하늘", + "god@stanl.com", + 0, + "MALE", + "000000-0000000", + "010-0000-0000", + "서울 동작구 보라매로 87", + "시스템 관리자", + "중졸", + "REGULAR", "미필", "한국은행", - "000-0000-000000-00003", - "CEN_000000001", - "ORG_000000001", - "EMPLOYEE", - loadImage("employee.png") + "000-0000-000000-00000", + "CEN_000000000", + "ORG_000000000", + "GOD", + loadImage("god.png") ); + // 심사위원 1 계정 createOrUpdateMember( - "admin", - "admin", - "영업 관리자", - "admin@stanl.com", - 2, - "FEMALE", - "000000-0000002", - "010-0000-0002", - "신대방삼거리", - "영업 관리자", - "고졸", - "백엔드 개발자", + "god1", + "god1", + "이름1", + "god@stanl.com", + 0, + "MALE", + "000000-0000000", + "010-0000-0000", + "서울 동작구 보라매로 87", + "심사위원", + "중졸", + "TEMPORARY", "미필", "한국은행", "000-0000-000000-00000", - "CEN_000000001", - "ORG_000000001", - "ADMIN", - loadImage("admin.png") + "CEN_000000000", + "ORG_000000000", + "GOD", + loadImage("god.png") ); + // 심사위원 2 계정 createOrUpdateMember( - "director", - "director", - "영업 담당자", - "director@stanl.com", - 1, + "god2", + "god2", + "이름2", + "god@stanl.com", + 0, "MALE", - "000000-0000001", - "010-0000-0001", - "STANL2", - "영업담당자", - "고졸", - "영업 담당자", + "000000-0000000", + "010-0000-0000", + "서울 동작구 보라매로 87", + "심사위원", + "중졸", + "TEMPORARY", "미필", "한국은행", "000-0000-000000-00000", - "CEN_000000001", - "ORG_000000001", - "DIRECTOR", - loadImage("director.png") + "CEN_000000000", + "ORG_000000000", + "GOD", + loadImage("god.png") ); + // 심사위원 3 계정 createOrUpdateMember( - "god", - "god", - "시스템관리자", + "god3", + "god3", + "이름3", "god@stanl.com", 0, "MALE", "000000-0000000", "010-0000-0000", - "신대방삼거리", - "시스템관리자", + "서울 동작구 보라매로 87", + "심사위원", "중졸", - "백엔드 개발자", + "TEMPORARY", "미필", "한국은행", "000-0000-000000-00000", - "CEN_000000001", - "ORG_000000001", + "CEN_000000000", + "ORG_000000000", "GOD", loadImage("god.png") ); + + Random random = new Random(); + String[] positions = {"Staff", "Manager", "Director", "Executive"}; + String[] grades = {"A", "B", "C", "D"}; + String[] jobTypes = {"REGULAR", "TEMPORARY"}; + String[] militaryStatus = {"fulfilled", "exemption", "unfulfilled"}; + String[] genders = {"MALE", "FEMALE"}; + String[] roles = {"EMPLOYEE", "ADMIN", "DIRECTOR"}; + String[] lastNames = {"김", "이", "박", "최", "정", "강", "조", "윤", "장", "임"}; + String[] firstNames = {"민수", "지훈", "서연", "예준", "하은", "도현", "지원", "유진", "현우", "수아"}; + String[] addresses = { + "서울특별시 강남구 테헤란로", + "부산광역시 해운대구 센텀중앙로", + "대구광역시 수성구 범어로", + "인천광역시 남동구 미래로", + "광주광역시 서구 상무대로", + "대전광역시 유성구 대덕대로", + "울산광역시 남구 번영로", + "경기도 성남시 분당구 판교로", + "강원도 춘천시 중앙로", + "충청북도 청주시 상당구 상당로" + }; + + + for (int i = 1; i <= 100; i++) { + int centerId = random.nextInt(10) + 1; // Center ID between 0-10 + int orgId = random.nextInt(10) + 1; // Org ID between 0-10 + String sex = genders[i % 2]; + String name = lastNames[random.nextInt(lastNames.length)] + firstNames[random.nextInt(firstNames.length)]; + String address = addresses[random.nextInt(addresses.length)]; + + + createOrUpdateMember( + "user" + i, + "pass" + i, + name, + "user" + i + "@example.com", + 20 + random.nextInt(30), // Random age between 20-49 + sex, + String.format("123456-%07d", 1000000 + random.nextInt(9000000)), + "010-1234-567" + i, + address, + positions[random.nextInt(positions.length)], // Random position + grades[random.nextInt(grades.length)], // Random grade + jobTypes[random.nextInt(jobTypes.length)], // Random job type + militaryStatus[random.nextInt(militaryStatus.length)], // Random military status + "Bank" + centerId, + "123456789" + i, + String.format("CEN_%09d", centerId), + String.format("ORG_%09d", orgId), + roles[random.nextInt(roles.length)], // Random role + loadImage("default.png") + ); + } + } private MultipartFile loadImage(String fileName) throws IOException { diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 8ab30fe5..de12a564 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -11,7 +11,7 @@ spring: timeout: 5000ms jpa: hibernate: - ddl-auto: update + ddl-auto: create open-in-view: false logging: diff --git a/src/main/resources/image/default.png b/src/main/resources/image/default.png new file mode 100644 index 0000000000000000000000000000000000000000..60cef806b2c69c63dd102f01996c5485af4fb23b GIT binary patch literal 166279 zcmV)YK&-!sP)aMP;uC&)#YoC4gnKe58j?83Z6Yr|!uQz`i z9eeMHZEXB~wb60oxaNNx>VIak+s9Nw+pKk1o#T`l*Th9=&T!944qdiNk3nEd!Q1F*h{ebNj2r0r-UGn~Ur#_fjl zY!K%4QI*!YJq zF5f$B$IiF>{SLKn?(e(-Ul%YK8#~zYw=s;@lW+Ok{QJh2g!hgBU^=~dKlAS^f9G~w zchEH)@;bhJGp}Q&a~|P+<$E;szApDWAwDY@(Ag*H*ze6?Tz|k61h_nC`vcn?DJal} z>EOHuUJH%qiDQ}Po4jV?#JPn2?AW2ppd_f2@L#wU}*YdK=Hr9c+bWsjfRZh=_^AypkTE2`GD}A zw%ECV(5Ikq@OH-j{{|>zTlmlGzRw~OD*E9>hGy8{kd|c%M$1o!H(5aLcY=YrZf2%% z&~dZ&X}^EkHn~plSsmibu>EMCHI-T>i_5i-?U(<3X3KBc565yr!ITTR$1v|Hm;HlN zYPrsHBro@uVLVqlJdG`@+W3KUAIH7TH!|zP1qD-TaJxRpgo9ZEKmpqgzz+vu^uZ%- zq;p&O{NFx{%^DrULx#o~nAod!sFTV6Q#$tegyHO5j!%4;T^eQrf%%;vV#E~i`BPqE z^Fd93vgyN`VVh60d@%UB07AY#G^E|Z@tNU~chp8k&EJpw`f^{U-1}sLKrTBV1E9nA z4NU4flNFf8@4I@(q#wWYnUTWgvQU1{{qL{B^T>VAXk&cWV7d2y4EI0Z^x7s3&2F-Q zd`G7ZjT`rXj;`QwD5=5q3JCjj`~36#qHWeB&}(dxpn!)yvHOr{lluZq?fy`vcmGo+ zr#_OY>3`z$e;;IAPuq>pyZ8J{CMG}1f&~MO2OmGzJ`ZQkKgMT0&KGHPp8SB*uz~_v z*#R3F#S1sQPwhxRA@?54bU$(#0gV7aGXa5S$b)R0Asa(5_c6C0~$+aNGzc&J<8Ip2>mVJ_!_zc%8AmZkydce>-Iq+3obazU27J ztfyALAkVz;vW)E6C;ij=^rqy~K0V1%BM`#}`t<5oh#mwA@53SARU;@6Xh+7_g7G-}jO7Gn;m2bQ<~>*W##V2F$Acq`>{A;U z+9LylXc$JxLiWkdo-sK2v<%_dk4_lRjh{#O8OIpLciITn4Gv*H?4!Xs*udBx00O~b zT6%^iW!K<@0t$gd19=K|#`XsV_zt>vpTFfd7zzqGK(Id+khNURug@sEo!NuX$KR)n z_<(8LPJxy{IBq=Ofr*2U`DL*K6QJ|g&pPw0^W}=Gzat~N@lx!0Uk0a0dk%oY{wQpA zXVS!9-uyc`f8`aj^1`d+obxV|)6Q5WD^5KhcAlJi`UU#^^fNDnt&&qtJ6qD37WvKX zcgp0R|JE0sFI4`C{O`9zgF-GC7_^nG6`lp8*6oY z!Xbws1xF_j{IAiv??ubV))AmGvR8KQ8jfj0r*k{@R1qKvplmq#Wr?H4B!@dfjVD=W}uAkM=*edfEE|ImHw#hI+ffQ#b zi)`E=7Zj9kv12AIe7E@fp<-H} zx6SmPd=^k(EP$r*f+@;@clXB+W^#6uW#meeFY202%k8b0$p>d0f5J(UOr_PC(H~S*^v zy|GueZ65+K?7{j;tm~Kc8@lD?SJul@&%7*ey}ez!`^U6p9+`wQj?ww1)akcu>y!0w z?~oVPyeUsS`Mf;(*wga*o15@_1{El}dq$)O%%BG#Fa#&>21xAa9g$vA|ABGYxDEe} z?Rp>sX#FDw2o2yr2APb5|Ln%LJ;Qh>C>_95XoFM6^$kuMK{T2fgd}(nM0y#opsbiQ z0(}SHzc0p2XU`2Fnnngam<0t>LYUWM0K3t9kZUtJdu}k9`zV{3VY~m75d=Q13v<>B zFRYP{jt)8P^fRS*V9dDa9Hu}sG_DbE7{<-H{%60EB}bniC!BP;9Dm|zFk^Y#38%`j z$E}cM$DSm|EI(P6AHM>2f@E4+&=>wcc@vb)WP?ZfzkShm0ErFVPcUF` zmPS3Vtp!^ezJ#Ejz4*QpfV%@uN9XSC8$qiMr+;moJo@-ka@Rc%%41JFEpNWHMFE09 z@%o#a<;j1%D1Uk2ae45ur{tA4H_DpV)+<1)-?UTSTDMifV8h0p^7QJL3+8Z0N{Wf_O%U9oeTUNjDiahw&zsv9b zbg%sJ&-ckQFTN@pw(OAYJ%a#^A=!rI=DtDM)H|TIa|DcI0t{dZ*EPk~lh?q6AN}N)a@%k3 zl;8jHFY>^Hf0IZ5`jkBK$Uo$Phn|pM|Mo7q7W-eY>T`zXyQ0 zUT*#M@8!;OaQVGLp12(B5aw~Vx#PG!(;{hjQTk@%6l z${PCmCuNxN3B9N0y(il;-+|oc@cA0wO$Dk+GugY7zIKD`1ZxSkA9}5iT+h+lXPonVuY0GB^A7ctrd*79<-Mw<=d8=gJl4bJ7hRrgL8$hkgZm3aq zi(xqo$J{wEA+N39DsOGvE*my&2esWL8#nJzy18MaL08}0uuaxsxqfrE+S{ADmENv_ z?p?R32O1T6_Xt7Z%U}uzQ$ZsxVA{|K2LPFU`q0LBflY19z???yHQvJ53R?a#w0>h# z00zi|?LG4BvoFBOek0%hHXZshx%lFXWW|aVvUKTES-$*uIsSM^uw}=|iN~KJD)~I!T4UtN!+jTJw^`&;}hyA z@9O>S>6?U20A%;>?`1LW|H04e^RV3BAd}H(&H}(e^B1z~@&Vzqg2I;t2(&}}H~nl@ z|M--Q?s-RUy6tz8Y3r0f+xC>E~W5udLgKV=>Tn z@CkiYj1M^fp%WaLfMwAn@_w~!BEnDT` zhaZ(|zxM;V>hde(+%wOVW0t_-mMoE_3m3}bdGn;RrA1mBn`F_vg>u|6$IIe{OXcV# z$I9`?o*>JX9xJWQ9n#f4Pv*AHmBsUy$T3GRhtn>C^DdBu3l_pzPm&cUpC+fQI73cd zaVG2xIq%$6a=`@`<9V%;)6Y6r&O8^+eaYo=;)>Jc{0qT8&Ib#*_zH~!oq5&;0DzNl ztfOUa=K^VO>yqZC4(aSzfd4;E|EH<3RhBL}MmpN&N_|~}G&eL$S7(>3_LD?A0K8<+l38koWOZjC8u z&x;0P-vh|*m0kU0USzK9g<&@=d#(*ywy-N}rv|1rLHE+OZSR$H zS6(JR{OL^^wcfUC2n=LUHgD^bO|T7HdSu5Dc#WYxVnHp0AtK~Vld zCi@W7K9X^ce4&CuOzpb7c7xwaO?$;a9vGJ(%IB3ZH>(`ucJ%an%kraPPcIWBAJWt9jzUb zNoORJtd~S2E-lTi($LT(&CM;+m}!vCwhmba#&WfDGV-)1(mqkg0E##)dZO=$HopXp@FaEB>cLT3b4$ zDbpkw06}|8yCmZ&sZZ5OOI=1<>l>sl8ka;cEFrH?qT!IFlW`m?EsGY+m(x!@88YZB z`S#Tp%S|``Ozyt#9(n$iHPYSRBO_B2UrI>0KfMPp^B(C#i*`1&V?#@atbT3{ zTJwEIf48y)UNqwjw7p;wG#+#}9?)(X$NH)1chTBhDL?($E!Yui* zKnH&qE$>&z%wX33pqBBVnX{pRCTdKyPJ_cA;%5E}E!BU60nk2#ff1lJ>jNw386KBz zNQJGvqw@Ot&GOpoZ^$bzy(G8)_BV3bRbW!5oFvCDKSoYG=4d(b=w+~DWhq+TxlL`d zxO2WN1XJq*Ak1rNl`a586CACrsRbZV2WQMkI~~8S9?q7MfZs2%NEA*7fT*jJL@X|; zctWi)(766TL8_c_=D9L&{$g=D zy%G#WC7r4V3&=< zC?XM<-{TX%*DqeDQv&uHNk{wuiFPoO6=0ZW%1?g!BYENbRc<3i+15&F5*#T(ZmD>pCpUzaKfV z1I9GWl=j^E7_$bZK-plE``(j3-}jKzH+RYpetd(BO&bHHG`nuX$a3V3Ww7kF+wYL` zFSta`yMVIca=Gf7@5{B<{S>g{;E9g_ivJX{gptvIYBEtz|CH?NW5BRicI@0P zZ*SZr4?Of&`OR-`lUsiI3;E&qzAY=yJqwPwNRC^M7JT7+nb+DP^V`~FAt?9UmNsdJ z1Gk~IZ-ogbp&D^{E;^FjNUfVtEIglsm4 z1Og#R#FG*WMR-O9zeT^$HlWhX)o_qckx%1EW ztNr<&2j!mo9@ehO2OoJt9(d$$a{nWb%RLW1B6mITko@I=$K>quE|$CRe@w>qe54WR z{1J0sB{N?fzC?X^5Z$A@p?{gE!x#Y`PIe&Dz`(Th_fO&_Wj?`>tXsQQ9)v{r@%7i? zML$`(I$EWpxj{ObQqta32U_2vfUvl$Q#u5P%f)`Nd(eilwSL3kpG; zS4>%e-wgnwgHtL1L}EDiWH=@f06@TnV}NnE?Jk_JP3$nI)hT`(&dURU@B=8k0r3F9 zLinGi`g*XK4(aM@ms3tXNp85|$MWoR>%SPX*i@CX?i!59*)Z(NhIl)6ta)!NYh zhkCF6w}3*fT*=?&P)46MEW2-(A(#L#lg;X%33x1C09whu|;mY=~pTr z*p)QI-UpL{jZr$hBNMydRf#b&u^ZsA5Bp{f(qeS?J`G-u?Pa>`gq(cJnew|o-lKU6 z1eg31|1vF~25Nwbask$ZYb7W!gVSi40Fc9wTm8_#eWTL7qeotQrB>Wv z6n>Xe&26)a&+S4>9a1Ncf$C?{X=M|X8qEz2k_0FbeA0=8*sWG^+U(fJBi5Q)ICZUp zLJ0C83BV!~@Vnh$B?JTlgIm0IH~!x%9-CX-7MEBnEmB)vBUPo9QeK4fEvXP&wN-3Y zR9{Oix=3!j?RM$u9n$AS=`c9-jt@veTUiUa-Ax!Va%eZ~}E_v6)>C$NJ>w*EP3k4*BRzK=Oi*C0`-0RlgGHChh>6mD3V_*sd1-7Jii$xiN4-QUN zKPB@hfGNYzamtMO?9JN(&YjZB?bR5ikC)vaB5c;e@k0(8@c}%KH^)oB*Bx&LiNWW81}5;}VYrG6PFX zxkW0ADy5>RTx!ayq_(040AR=VK0RgtfD&~3CEzrS$4meSlVCQfXc*@Mi4pWG3yMTT zaw)*}^*7(p^mz{4+SNA!roi<2Y1szZ#;#2ruaiwM%6*o(_H_pZzLQ3gmIG3P1z%D1oEnkn;uK~2W2`zF9K%ogvmxj|top54@T_Ug` zT6Ax9m3XVFB!m_{Y{z;yS`;m?I-LifArvc?skI_H#SoxNy4iVsY12OPI%W z(Qz$m&dLgLRaJ<)x)S?UqUDBj+YR>MgPe%K;i3UAK)@}rkXPa%$Pu4Qx2dfxld7^( zLpD@agLT-k-lSJW5410n7yXJ05D{- z(eB;5rN4KVJo@kha??#W$|)xw2l^eC7BGUY)+XtMTxiATwgyN8NQCw}e2yoj6+qA! z3Q7hnAPpx^gZd}n@DZC$q67sjW6=K*u!Uf4t@x@44wd4DGy5zx5^_-zSkcA8S8I4Gq|l}7)~%30-Rm!kQi<(eHL72txY_*2LVVR z53bz>fOG>O$w)j{dU3ywS-e1g_46C$rRSfQUNlyF_TXKG+}+VVDBHS+W$Ug{uwA^z z?5UXgP~%{%l5CD_{JMdHDSgzj0eS;upip}hxXjr<-!#R5Ha2t6 zan{)@FP5`lXPm!E?d%IKlJhRSR4%yq3c2X=Yh>l6SIP~y{MI-ic9@+0K(4*+XVN)u ziClWsceRIM)umU-%1f@43opA`?V`(R*XZ(MEHA$FYPsQOzuG@cHva~EQI>ymXbd1S zX2QO4vs~6N+Xh= z(}5#k1OYIEkPE;8he?C#2kcg?R=`0m$_8qRi^Nh~tl;3Ts1Q4V!dhA)){T&nNUFh-3Z)v7DeQAgC+`1cms})&`qLj} z{rU|uG&HW=Y1?;>030TzXNWE1ds_HJ>l}Q|L4hs10t2NITYEb53@yBAM*<3_eGC+C zzU`0ltK098n}731x#`z`*l%3->)+iezy9r=a{phSGEQXN=o5Z>(=Pe#AMTc)11z}h zFK_+5{Nh)?)$gX6>wa<5ZSu39|62ZZ*8>0rb|)D@yZoE*McDqE!T7-V0Z`DYyBr2Q zzFQ{tQsRuuw(XncZ;wALmtAy$%x`a!c1VP_bW&Q9acKqsq(JW@aMUm?0$Lu!_k`Ci zNjO>>l%H{fxYvjEK7attt?NvHNO+9TTn6E&Y8HzG-~e7Yx(k$_62Mc77MaYzX@fMV zRbU8%B~+tDuPrG8cyL=Vh@v6|0RoBx>_EW)>zw8ITmq(m&$Wd`;wLMq=6_U4lmSRM zSp+i0hhsBHX|1Z3isEvxg)*rvs}YZtF@S)Y4Q!yQqzo-R*aiS0;PPn9fjtn(XjGDs zh{Pe8f&dCDm_$`+89;;%UJbcZqwx_k07?!I05L?S0dVouSYZGqUN_FmjnD3C{5Rx@ zAKUrC6kOISHMgxAKvN3ogzJTbYN$`ir59f;&pq?J3=dCeUd)cZG1=ZfF5M&3M$AE( zU(V>3Pf9m*NsfR46*T7*= z>fxva1O8SAeVz&$pA%j`_KD#4hz1jR3>rW4SPVR-aBK}g!r}xPfI|dqfM7$a?*us5 zq4S+!1`aGK85Xp+YOKMER^0+1P-cK7*#SQXtQG(T7zEgX9ggWJD^p;wmy{|X&;oE! zf>RiP7KGDz0Sb%})PM=pXwN|n9NP+q41g&F;KW|3DXmtPV9E(H2nIxhJ|h6?1^aR0 z*iLN2R-UcB6CmUT5cr(fp3Dc^g$RV~j)VolL_%N`0UOQ%?1jNnZw(n!jRJ+=Vg;kH zf{oNFWAHK#gJsapL@AzQnb;w#BDm+r9k)atdh~wT)wfHf->!bve%Nh9-TBgIHF&NT z&woe02s@nrp2@Mfex^Ky{Nv5EZ?5f6;m&0j+2kdDp0(zc4f5PeYvtKBZ^^SS!q%*n z=U-Vb&%bQgb1$#c-?+{5uov+8MXcj*FX(pbd0Sq3*)WcP`>a)(kI43yLpKI98P%jY;*zP!1~3gz?8doFNpDY&Jn`2@d* z%A&cgvaqvN+A?WrOva=MvVbf>~}aA~PHU`~L73yi@7Y2g7_c+rY`2@Gt70S?sj zY@r=kR#$+oL;JgI9&ua1dLn7y4A-g0(?AEN3(^ecO_uYGs^mO-wJxojA@D%7bSSpyqFk?j{ zw0+s`O~aWuOgldS{qGxl+VYIfn5F3lZ-2mY&n5Sq@-r=GI$r*M`9>*1Q<;&qTu_e_AcsxG(7D{pOuZ8O%v`2DrFw(Pgp*KL#6*KR$qPj~HJb{!H35@=L+Fo02sE5P~#Ze}D{uJtw#p z9|4UF0l#_45Xd@k&UOF}V-7rDvLyy2LtcOf4fiMxKwjM5rpF;*1n?hctTtY>_MG6iLbK5-Eh! z7tAh_*+&&g!MEl}$v2Mz7|ekc0uV~1?3(}uY+Es#4qFT>k!tAss=`94#&K$jivccp zk-|%h}8X60E-&5&~7I)B~x%rA!Mal)QgFPR$ zx-aRz8|RcHXhuQ`Xk<4$J|zl)$!D=^Mx+Dl9due;AL9ltumCb056;=JYK^U^lwfx! zjpqWea>3CR7+?fb1|*&MUj|!axL0Iv3!0ns9;pGST(~cpL|lILiyNi4dnXzMG6e>a z-<5$emc28&`ubU1mZjqk6BJC!{b>@wm@pv@d=ahr_UsQt%y-pZ0quYF^^Nk@ zb3kxEw)$JQ56C}OzbsGVcl{GR0R|`Elh@wdtTg~9m@<8cn@#&{3?f@|K>@%hbZSgb zXMmCUf5T%eK+0N@!?Jnv7J2x=2W7=^M*{=`(g`5wf>daYgBb)t;b959JrxuPd6@Y;f zfy}@GpmB&3K;*U?EWlUG_y9q}qMv;L3Q8IR6u}^9rxXJdAS=N>9$Y7TWjwfccMSuM zl~P0LM0teI+}{fT2|}hsaBc+ih=*B|HaY#o<7M^V|DnN1GDOyJ=pEm!);G?Yl18~W z9RQep51Fu*|E}da)TcS1F>PG_d=O><*w1HRU@jQU)arkdaXXV`B=#eL7HJqR9z(lnzI|k(+Soi#kZwmLF)FP?x$i&opvgYNr^5RQx$!dTC zs}C6!hm72YC!cyzHf`zA|HwbNT>EUGkUgdX0`uel2g`rqI{qnI5}k7*C_?}s3=GJo z4eRCpKi?@QEM6q_;ea$pgVG9zYJogR`MerDjA3cVOITR}CIBEP`qnq3qHwlU6wT2t z!76C`s-hxg3uSY_2+;DE;d@yDT96j@Z|cdmc+wYLq;K5mp4Ct$FE=!-9{)gWX3$>rMN zgF-fdsPO@Vk?3sKV`MJ>GC*OBH4i9Zs2!(e$BrGc`R#Rb>(75Ci#ystIiu1Pi)c4v zD;%mm$lx7XJUB;L0b~FeL2&_E_X2_dT6X3Dz#;r}B9{vd#pO~4aHyc1Crf#!_B>P( z94G<69zf6eq6gvB?6dbnyL+4#sm1@-!ZAEfw2Z!>#KUp1R9ex}yHy4xB55qs5)DSh z%5F>>vk|Qt!1LSbWYGVRM}8kzQnF5*cCZqFh_%)sUbi0r>5vjIBP$#+3earM)JrVn z!@V$M2-%H-02n|7^gn=>-^Bo78Lk1>X#pFm0`0G*1Yq2Nfkm=~8Y}jrfgQlX8CZ1U zJ`f1_*#-t>#l5dC1*>A*giHa_B+Q-~c8h|AP-?K3f`H=Ea>I4smsSo)YRt&IrUrmQ zR0D|YW@O94?7!k~{g;#!d<%@A1QZn-9JDzS2usKxlAy;2>P;HXt~Un2%2ey}GxaW= zxfU;`rJ`D@ORA)*m{zHYKKvhWvm`14-9|Z;hIl+uUJ6by~ZB?~! zhDr?r+8KezOCIn#B^>gK4@(~hMRC+*5K_k*(g0zI-Jt0PiG<>y>R<{U0AE9gq~i^+ zIvw+13_x-!JK)moz_C1Di3C{(A*Nv9arwk$_W%%_Uf{_089|TL@+VB z2^mz3b6f+C=iq&#B!byW%GBW*Ct%j45iqUR91I70DsV4c6&7(*>ZqKs>XJd+5ak1m zGJ-u8)~sZ((<-G^lTyP9P=OTjS=eJi=eL7N06fc>yv1NE zdoe5uN}33UrJLYUT8eGTG@!`=XhxEmM;nG8UwfTw-ndQrIkf>NubJTZg1yGP6=OLg zFvpJ!6wLM9FCP?`VQChGJzP*Y5;OlhKTa^ydFI;3KwtkcON7}k8E7G zUcPh1<@zEp6}djdWFxQU12DDNT2!nOfXO@znh_L8tvLkQUd>T{py4$H0ysVM1*}zQ zNkMDD5*Q?8_hSvIbs3ohTQly1R@6aO;RD_EfUa{e5(}0%)X_PVn4m!;FmR|b%>vos zK#Oe0HsxRtRrt+X1z>?Q_~5`^$QB!PyVVK`?xv$gbo65^!U05S3HxJ`jMhsg)hu<1 z2I*+&0>ITlQpBXAbv~9Xf|As_2ux9CEd*9a3i^ZU^fYS?gNw{o^oy<51=^`e8TY6E@ZOlnk{+B^pe%VClg1xWJ4&xJNWMfWTJ5 zm;#)e`4N;(1X;2T-1i#Fh7ufufz5K}D=>b*U?OuI*j3A12CIUClijT_0s)O_;{*W4 zUkv6@uJVRpK}N#?X${ec+<5&D<@Hxz127C}34y+GPG-a4BLU)IP*6!hhtFZp`R}ON zQX3QH92yjKx`P?^|Ey;IugTbt)J!m$m48w*pfE!XAr5EYC_J>ROh(G^Wu^{B3!>SI z0Er0bCuydumchGfP5b4DKpptw@Q4@AN^r0+U{+BIDNrnxbBd)R8#H9VP~!p$AZX!h zojRP@iKW}=fkqF4-s3pX!akQ5OoRRZMh*Z+6_PzvGMf-UK^YM6g~efWOF4j{94x{C zKw*KCfRBYxT*?r_!LSAe(}{YiPqu&^G^+zAA{pswUnp%&b5%AlXh;bW3neA&3rQVh zM8M5TNpY-;DX65A8305?EY&W62c(B1D7G3m0Lp`7G)hx_yT&GB!5A2ZMPD&!&>g3=lHkAmpF}I{*~yUZfK`$kWEM_y<0mpw3ApBr$%=^$JGiFgzDpeDZDkVa2 zaM}TCZn^T3RRD%p!7j&zRksIOVS0>#$7}^>I!pGw({9Q!!vSaV6`DHdDCf{TFagd? z@98Sc5UpyL@I!J(q zx{Q`aW687jmbq~HE@^4xvJ(IlgM>)Pg1L)TLXbhQi?OM$1&#+Dj^7fIB&0;6qyP}f zcs*oC9l(lli69tBSmP1Zl@5FkfpN^2=1eW<$!V;;PQv`4+QxMSH3ywOa^D~$U@a7l~c8^BEZ0Z6`S@&ul zNEpvP0r`{g0#rOUIqS5O<;53Yz&kaeMT<2Z81E^I?d%__oPGbeGyy>^XI<`ZI+fff zfP!{QP8>*J`gf9;a{u>}0PE9?`^?H;dAOGE@Q35*hr8WOW?rE2P>hG9Gaj?VdKMgZ z4NS|%t^Kl|S(PuoEIK7V$PG1&*ez?PX^ zda*D|nMKhmqjg3L9rlLtUop^dIHAoeepe8{5rC$LGdamBjL#l7oGex^UQbvm%GtGN z15kvtbXmYOv8`=sDmTNLkXNq5DpE;cT+Nil|hg_ zv@~`~J-{LyPKqA@6aavDu&sl#2mfE6Y?AioE|nwcNJ>+mlVKKYO-TdTMFarA0aA6b zIKHQFT|P}#@-R06Ov?tKtf?>@y0WMOW&~qBc4j%ca6PqPNL6Z-Ok_o6r4_hVV_j7R z0n%8{V-S!D3ihfRWh5LQVhk5&P}2Yb%0eb(5geG+$=Cu}1RDS{L$ZXh4PfE2Nz`Kp zfH~yk6ONbFtDk{WO#&1+AB9oFfJ2rk1x&U;=QYO>n5OIUoKchIOfWD_dm)VONlvh& zMsUa;N8j;J$m2O2Q$YGRWW;9%g)iF7n@vy{8ety$9wUdFSz-W$odATlz!2Wr*e$QW zzDZtP^Rk?B+_3=FBe8 z&!r{hTH=j?S@!NQPk}uVnw81mA_qya9s^Ul0Tc`_YMqH{20aZ)K_i=BjSB(TC|7Y0?P%3{opY1`PO{+zXlY@FjOyT z7YhrZWSPPkhssXM8=HCn3f=Msr(k=1tz2>8DoBNpG$$g`6bnmBEDEUrXT=NQ;vk`t zV)5dI@>BO}l8sFV7#Tea>{Y-^hnI~n97pf@NK07WptLmaKN*U93lEb!W(-y5+F;{q*BZ8~2>7&M-72p1TO2hzpMng|TeISoc) z1|-{CAUm2n#RmZJfVB{STsU?J$BuTwNZKYpiAi?2BGe?AWwkJF5hxTduw48hPW*H)UIQudqD);HZ(mptDI&91I91DCEDhhYJd3As7=F z4t2c4HB&+yDJYoxeJw!2L|;Wem=0tbHy+lmK}d$}1M)Vfu-mpnwr$@czl3aPP5=lJ zA?Zvfq&*&!3@a2f$ON701)Yr4S|kAnPPyFLYQ%J20;__87B6AaOE$*9k$ZlJReK(o zE8t){ExX4Y#;8GlCNK~bIGmWEK(@fvG7Xl{z{y)+oG*dEz&JoFXI*Mt2wNas?Te+e zZ2`7xRhmxio{oX4CmNOh2mDbPA2lG&Gx}6FJ>}F#&Aya}GH1j)HaftB-1_vP>v{s?j=yBl4K2{|1P+y}} z1tVywZvxe>M~lx1Tln&)_2o}NKE#5|FigVXC>H=0F@w(2;klkp>qh`$K%KwqT~kfx zaH`Wf;LKJFhb#LvAjn>Vus^AB0`^$=(JGUPgm7)F1|5&a#D?>$hNibN84934*bbuCPLmYI) zo(BK}hr9cgHB@L!i3~ChhLK<)Ts(6QX}|k-zn5)WyQO>Aurh|>DPxjZ1|W6ngPbAJ z2Qq9Ayt@htQ}1a}S<~qW3Z~^|I_{SchY1QZ1tyi5@?YLXT>e?xsU zfUZHWf%_BOD1EqXnz09f0jHzm^m8#LSK*$dQ-&;RY-k5q%+Ii83rpN~AnH0+l7`n9;~Q1``xmWQKJy zwN9qfsFN8YV*psxd0diYBZBoTuJgy+Zj;`fJ+iB3D9aY$;5rQYeSm^70B>y6ocK1k zt9_qtPXIASOCIQb7?^&~nBG3iBFyO%K8<hr-WNY`3^!AL%=*Xx%a{s+@!lF)TiFsjYEq#y$9;bMj z_%f#elA%KU0ELJJECDTj9MZv!--1{daLaN&8eXQONGBALHnsdRNMmId>VmU&%Eq^)(H1~z#; z>2wo7rw-C6p*a%~Uswyhh5%YzCOB0cM{MG|1NYrhUIRcVRjYzk7Lq-btE?y~ERm8@ ze1}y47^;{@Qdl50tWL$0YOn$Z9lZc=9{_^E;nXa{aumnCyXCoPoUJ!eR~TS!-=lg_hUjq69Isj zvqZ{D030xmT&*a^cQg#FX~9%#PZdW}5*UnJ74|;Gt#(OREifyXqyz5{-U%0LSvc_w zqjJ|Bzm+Xpw;DZPV_8tpx*Z?L=pjIX%X}KoUJNY|oa4|l*Pyeal^32Pt%;y?#(a|T zz&UFw#SL8{Dm(+CLcePDK; zy_{fBV`Sx383TCS>=(DxO3Iw<1;=PiCjkPHENk%Nm|>lAE#hTQ1m{-t!HH9FKqGM1 z+SsYtf|L_%UE7=I>1Y1V-Um`{$_Fk(L1=scN!TCP?n2f1+Jy=I56R*M2w33sO!i^_ zKLdB{eW=Ab7Z;UiS0C5c0My(LpN?&C+5J*m>ChIwy?MT7Lo#N-)MyT_iUb*#uo(lQ zAR{PGysW~+q${RMhr*B$05^bO9c0mhu0?Xpk`rWJmfqw8Ocs~KcWDIq|G5|JAnaAl2h&Ry>J;7wqjnZ zEb3~IhaR|Bwr<-cEFv^A$hI1%>_gF;IBt6G34n zc3=*i`f7o~*nykKlVdQERg^gUurUMiUfH~JNY-!flTAGXvK995!w<^oCoPfA`ml7Q zf^d2;g7N}!l@?0GS|c%At;F!)(`e=SklE`Hgmb6R3NWrfV5mcD(BOwdGxm@ziN?AG z5x2`IXv+KqX2;dA|C>X5Il`|-{1&4QqglvROSlK%^nT_9ICa1fWdyq=!<+>PEntEp z5IIRJ7y|=pW(B94z|Jo120$t2R~6J)K+n%WDwxgp@Brk z1zZlVN(UF5pA5s#fnks&0T};7$8!LLg7_W}N41xQGhEN>SRhLv6&AzVTf6k!DI@0c z`~eJ|_|7F61LF#bc)cDXY<}N9}C@D03c4$_WC4*Wv_|u)yKL z5(-$F9xQ<%0h1e?Q$fue&lIAU;=6IFjtfT+S8c~ zBA|Z-9&)Y3Eme5P9(=Y*u(DF>oOtjapQP+gNu!0U$3y43dN^+ymV~s>IT-*L#xPzM z^kqoCKM;iUV1(0n^5Ua7@>L96a{u#6fVnRyBX2u>Rp(Nt!)!!MQP7664$!KogS zgtEek?Fj@7Op60n4Yo%3a08)Q)HTm^_u zfLJr+4<$tlu94S8Fx1`^uo1sEit}ae0?x(eHl~^5B((q|&NxMI;JAlM$^$q%nFRln zzw3myZoe`M#v3>`g3ix;hH?fUu@4iUzV*$cr0A$3o!3%Fqt0QGoKj_c0V`M|j-ku| zEP<2sFxC*pvrMr;aog}-ShOhbiAOJ!2k*OA#wR9alryKXd?6(SyDi6OcKXlTOlF}| z$-!90UUML_;9##~%Bf1C9Ro)F^^;^5;z{H+ehWH%>Mn{CkD9PQ}Ai(Xa6 zxg>3xFvJm>K90u#7%`5(v}HqvM0GSI)0)XvxQ_7#hmGSNJlbPnt+D8LyM?niaTKXj zq5zE6x&~=)Y*AzPYkMOYPeU`VBLnBB9BP%0Hh>x!4uhyhXB(sg;|v4{jx;rbkhO4jBgR1HPy$w<8IrKsN6nGh z|Me{?U^zSnDFGN2bBubK9AKpr)8M+;8TPLK2gVeIy)=w75bnpr|Yoq7$3292#}Ei#-`pI81{yCR(HcI-LnVjaW9N zT9plGV39ox0E-131|SeL=ECNoRpsEOMeN3#w?yL!1O&zu7G{BhK|HgIPy77q+R);* zNj%voNyv;ar34%{6pBbCu7y>at!NQB8j?L6bWGM;U`_$MB?%VBx_KI-@Bmm@Xw?Rw zv{~Vp%=F~_fYWn~hl|;UC1qOwz|O8dR-$I_j&{k$q@%eB=TtAP&22Kji`kFN-eeZ1 zG2)U|$MG6G7hG2a5-386#dK%@S{OiXG6Mntt5fL+)iR@6l{vFDWf_;01sqdhColjA zj{4?oV+;kP0)vo60ERi=I7;S#F_ioli|H0gHP~GZq!V*Esw>I>Y&Lz)l#&cUrvM5` z_Dp~o#a(tu`t4{OoYEK#%cbY7lINd$1%Lo&n&fE5FCG+f6QKTGP%sO}eCeQI=p)v} z+am*D2b|cI@?m)D9qH~LkvBGMmcKmosN8z{?`7@U4RZbWzbi?gHFaK{%PO&&ayWk_ zfT054Yo#SXK(GP;9Mb3^5O|agq@8$C8AG6CAQ+&v3$uDKtNK(_N`SQop(VXFESFAj z0I2vmlapU3CgI!*95BUUxhzV`QF)wzg`@A79?iOm0T<^quxnK#2E)>z{se{QbQ@@O zr-A|*K`Y}0ST@m#!4wD%&Gqd%27=bXU}Li(FFM=itJAYjzb*}6X=qa*U; z&G2%v8K+T&+0CGu zf@9>kV^+xWWha9LEdfv*Eg49MSd2r!8*mNH$|%Sdf;d+laAq}%({LOGM@Q2689UfN ze3}#C;&VEd)JjzqtZTvEA%GvHQ{n7FnFD4(fGGS{fsxO^o{DdRIeeo)N{%X)%91MW zjj1Xt!{;ImDznr+B}5X}%|;{+>BKZ>#uk!3NSAo6?C)~-0}si%wd>@LU;jp$lR>oFwNmc~lEVSx3@X}Sj!J0+if24Pb{k^| zcyK`dIyxyHaGKqYcyT$_foaV;Z==plP^)RLoVkgBz!7v>fhk*fg|P#&0|JKT%yR$^ zyEWOB#jY#A7u1<`5#16Dd7$Ot80^Uig#Zo-e2(il04C*daFhnBrS0Fr@64zrF!QZO+nnQG9W zB$Jz>VPjAeV+ZVU;PK}}vK+VUr2PZ1$Px%tOltDFIcSN&N=PfTz+Q`8YMJLy$-!cD zJe!UNC2OGZGrM3D;V2|PTfQ>7D9 zsSQl21^=C{Yt|8-+>iMX?Dk}wguN#Y00c)#R)C!`e}RLdssT={YsqtA9fzWC&VjTj z&c-Ax01#ttOv(+$8H(^*;hZ8VDk{;OhQgx?qyT`yBq(mHu?DPXDL9sk=V;m(*iy>l zgrTAE;7YlS{qm(t+i3YaXzk0S7^`#(^=kISd*PXLb6u&I0Qn zBoplG=U^+`pD4#E;27ARDbWN60tH)P&1VQE^goONRSX7p$SEhDAxAG-t}Nl?<4;qt zASk%ln*v?!sI@|G+cas5Q^wH&x#VClCS?%}=#XR)96BArcV;K1<17_lr{~52U1T+N z98?7mY-;F&{5e*-7A%*Jxl5(JbBW9aYiVmEE8-Z5HmxSj1SucDl!K?(`$5)Vudyj3 zs3+0~A~;k+kSUM?Mp+gN4jlygGk(CS({ws`=DjnR#5o~B zmsx;CnE}9n>&PTn6__cq${>6OU~rPyhDb!^g&$0W<-@4IJ#J?Bd3AK7$H5!|)>3Ey zJ%+x{bix@r;k-=RX_f|jo;Pn9TKfh1-N(sSY)lLaLen!<*ho;~KqvwN0l;qaN)4zq zfuRagprp7GFL;SoX|mV2#1Eigmt(-q0xNVXW+Za3RXv!mh2)yI>%WD%BF zh}VE6WKA5`TwllW4o3141B_je5AzltC-W8@D|5jXIy#sVeYAGJHZ~gBn-Kt+!{OB- z*sNXYv~b#c$`!v>yDphskS&y3sf;1cOi{40LbU$HM-^(^K?|}#QW&66Dix*1P;CVU zfJ89>rKE&epG=o7&&rfaExYgJ1U(#H&uQu)AEGV>n8`3}Bmv2oV&FL%l3(9)i>!yd z?d=;jQu4=U&RG0BKw&2PETCZSmn$E*{L(>zqaAyOry&u=FK_TU+Qjn;3+oT?#(dczz2|xe?h=LI? zrP;^9FnGwE*_pZRbo?}&nhtK{9XM4I5GW`QIEs#Q8}c`;*aSI2&|nL};;-DEvl+SB zOTl8LXh~dwG$T)14V6`)X8*9LkI zx*c!O0aDcW4E(WWXBHz1pRk94z(AJ3dIt_TE=NpOpfxN7IFuDufGR^DSCCp;0aRY? zZsakdWCl2H8Z04`Y?1|Nl?ea@1jZ09T6wvA=c;Sv?9xx5g&PurwJiUC^4|MR$}-&={poyrRG?#3 zcU5;)=bUq-n{H@wP7)Or5mYb`6_AV|NJer75lIpSBrU&AQBhW$GwfC@LCbL^!mV~8jvx@CNnLaol(CSKwv1f? z#jubD03ag>CXh4{bKvt`d|X&a1cAe4?;({ff0%kEoT*)!%%$_e(PW4~QiR(^`f1oq zbx9HCe=-wiPoKjrZg6rX>aCOVzXlY3$@(7w3QjqwW;953`Fq?XFsQ{;fNiZdjVJtPVclx~#k z;3Fvb_*l6X2~NKQnL-|oS)ib4P?nQcLyVX~L-CS=OmY(p43abf+F3SE-k3ttkMkFE z6}04TDFf2ngy?AP;weZ^DR(XIM4*K(^{G)W48ZXt51_ zFWsI@CXv9$rRe)*<6=$GpMRt=#cSPNuehVTB81z}Z#un6!7VV1Q zXP~=afk;(8X4q~Yruac4C&1{aKKU#Fsa^?HeY)KqG@+}|h$~FpmtmPx_lQjP6 z`+jOYxx@dX_4MDiL|tjc}`Q zT_&4w`4Q=kG3^DdSb~JaQr>#wJ@o1M5?zN^F=_l%^m}oD6AY!Bmcrji;wK95Nodxu z0-;E;0A~VqLR2gg_!(47pqc_v`aqj=EZ<-4*8%`lTxQXo(6Aa+Zz}6jTh$uP$(Aa} zQp(Ed#x$)(QE@rq>3VqxWL^(nNA3{WMwwi$Jn1eteHSXP)@N|il}@LsW@t(qQ~fEi z($Q33N^Bgzw;Q3sWCD#EA(AeF=4@)7rl047;tY{tWLTkYOItR2K_$_lCL9~-68i|X zDdEzA)Eo-Ux71O$Ny1(NEmG{#kl&&tvd-j04Cvhtd-i;Ti`O3DR=v(`sW$&(P18&^;9j@MdOhCAbGZ$~-z`@fvf9?`K z-?#>?Ta=+7GoD5nivp#BXy~d($cu?Ud1@@mQt2ur%5|U{ktmRGTnCFI#6%%S4N+bX z(%ccqCjHIv#Uhg@i-15e1_d5fN=xYO(*)x1cSD{(5`(Sbn4s>b-Z0{p>^xd zbSGM&xTMlt1c5<>D+&>A?uG<|Sb{=KOroh5MMcK&q?NL@7LqV?7BAuF)jS3pv+k@Z3;Md;d@mH#R1L;3ADnxm9VFq7=nfATcUj zrOPfyLsRYJ9%UY)%=L+(Yo}IfcW4+w8wVjGB+O<^sAodYmrEh$p!RYXK|^|wp-nU< zkgS8}jplkp^SK&b7ejziA)D$~d;$xX%ZlCPLZn4S+NM&LSAfYAr{dg&>$r3Ow~nIo z0~O(^Twi7U|FIeA$sPZv;sQ@U_Fq~5>}&jw0ELH-|6g3c`8!;?_6SE$Ud7qVw{hge zd3^KD3GCao3va&G5BX_+lxE3wNI-F#j5q?h1cVYYgwkYnvw7(nsA$PWP@oHu6l*M@ zPjL-I*sZNsHknIPmo_-ZsM)VEU0H7N4qcSw;G~iqSYqNU7Re%rFa5C zLzxSSu1KamEh&eWGfW>rbqt#65Jd)|rY9{ijsAUJ!>Hk7(WPT|D>TaIiCvJZ(rJyJ z%dy@E4|huqmX*x9RIF7XqAcpO@v8Yjtm3EN7VPHow$jLfuF>)dP!%8*Hs zp_CT#JC?QJ@6(oSp#=&HE0B|0%vD5|l#N7w_9TLtg0G6vga%90mG9%zOo}X1!cHXH ziHQ_AI8(V4Q;_&gl0Vi!A&!8Mmz`sP5J4l3a!Z^g1YUk#^;1}!L5v~X(Sr0!dx8Md z__^olax{X6&(pLjO{ww|Fg#=v0tvZ|@jS1hLqkm5SAwyb4a!a$Ox(6*OKe=f0oQNU ztB8RvsS?H1IGV!*5S$ZUk(ZmGHGzjzvJR%0m3z$ zI(rkR&)u}{v(qO}W6tzR*7;aSP*5zPEHf6l2@%MPa~l|_tT{h6(%guAa~XWb7?g2P z5HgcUp3ajuom5fPXljei^GQS_7OBcQkoKuFQUy#!YJ^f#lcgA)xPlmgG7yS02p9wk zqKXCxegzjb1u4y`Kah?geP97X4PAk%rmc{jU1FI90YnAef{MzPT&+=6-JWhmN7Od& zgbF@R)VaK(hPO+1pd}eY2h`HPmq@EMJr!3Xjk{}FdM-hu$mw-ZS0oKZsz_R*=+b9Y zn{&JrnBp8&CbdKBaY=C*Jmwle$iL6eDzV`%5n%+z90|S_n>748!$#r7o_+ZK>PIN! z_o%g^Dv75kH5uUwBc<2GDi7lM{RIp*L|4Of=~kHb0sVbd8j3X}li`RlC@?5lHFavL z4O@K`Vl73v6=+&sN2buiK(j>)xl*lALO{&VD<>-`CMaYgif)mg%tyfBCVL9A?0^r+ zajD3Z5R5;?*FlO;sc=+*C_fTTNEgKG%}WD5Z) zd6SbDt7y^$Aul|S2tLo(z!Jn5G!4uX5{j4z4gck52@iqK6>7mpu?CF;$W2SetNnZ9 z=!s(lg>UiTyWcl3g+H03;FHGv&lvcVyZNsH3QxcP)35#C0t&b4|A1>ZeupE+uH*3W zt2lZ7HZI?MgbSCiW9QZ{@p|9xD9?;TX*%73R6mNz4D#aSGQ`*_i1(Nakss@^;G(h* zrO9#D8JQ++KDWeKBT+#AR3a)_(q+_;7W$TQ7b-Ke>~_T!)FG*(0*>mPm+ZF*c@hk? z1P)FnLW55gM@T|0cdA^a3{?w2BUSNH+tyuC!rd>KhF7R^W`aUK*+Uu)AUC(1esfDy zH)}^Xg1}Hi<0ngK)w-kk(?!JN?y(fFje)U0Vsyxe~f-hTTX z^zYXn9XoV!1YP*~#TMcTsva6ZWCVS3`r`rw8GbD3capRZBApqT0YHeC4uYDLBK^jw zjyq$R-}KiUeE!r(G-7MJN)I_gwX zjHW9>RumPXf~O{MHExVZG6aEwPceybfewKoJdA)R2|hmGMaJNXv~wkyZbTYgR}CK; zIdVAN_k+g$Duq${R>f~|3151Mt~?z@X&2gd9&AQ z(~IL{$QYuL;t5BJ*G05;AumNno@9+ql3nvPYHS$@0YWa>gw?fksXs#PvdS(<;yqWv zHH|3xI{#5ggE6bX57tA#gRi)sp%a8`FA|mDd z>*_ktZRpHhxE(6V8kC+aE~!QijU>OY93^GdD6gzVQ~L9AHv$<61s`iARMN5?0j18w zRaIvop;blpC4Z)x1alvvomhfqHE0&3E~hCYk*I0Z+2(4fD^fKI)_GZ=z67VlT18Er zI(Eb0L2sj5*B8<2#eQg2*UlQI5)}_fN=39+qAXIacB%?hKqR?%0YqS;wo-ozKE`Q^ zv^N$RbR#mA+DuMIPHLu&r_0aDH&;UO1$`FPN+{!^I*mL6Kn?*cGp&TcRZ1YM;x4b@ zHCe{S#EhaMV&L<{nl@!{6PLLaJ`=X}n-GlOqgkHZ{gpi+8}Nln8Ow{}tX!Mo6%bj` zay1SnD1`oP8dYQjTx3KZ0)ocXMZ3i+LJ;-R6Rq}l0vLqyPBKeeS(6c5Ax7fL&dj5w&@lTqzbz6d)-5SeisvA~^<4 z(_@hn9YQxjnFTLO6XPwgnCXxXgY+*V1T<*@<*`>&*0hG|d%PN}Db*MuN8hu@>_Gm#vY|)~f74S5x zX=V2X;th&5DD|j#g36GkMWA{D4T1G};w+{hK!|k7@Aui~&{00PB-Ao!gn3y7G{6Rt z)sjl9HmoEdRdBW9XL$FW5p*H?p?9yBOcX_ck&={QBkhw&|4n{f0Y+cE4VY0Spxg=d z4M=s_X?6CQTal{rWMwFnB_L2F$(^A4Wiu!m#9hQ{^40b&uqv#u-zA5?PfCK6oRt!l zlzoAzU5oih_dyk&3Zm+}Dt;0k8i5dXIqEZbHEhPq<$<5a7`KMpx@~Tyg2Oqsm_V9CuOPnPHkUuy*b;K}u@$NaZ~!sToA zIC$g&j-0-MvsWME>h0g+>Yc~9bN?YGPndwZic-`SWRn#rlMrpqP6{Xr1Oy5NabgHA z-WJ8{1Pv7(DOVgNAx;89asUuW314^tF(Dx+BnwcxbdFCgu*oP-mr7HT$+JmBBagt6 zr^!%!oUsF~SPh6JD5(BjjZ&&w)a~(!Hs)Q8NAdLA6}Zz7R$$QXORA@I!{L4tXV6%4{f;nw?y+6ZLABjP1{bWsclIHp@3TWgN7FDlHkUz+043h$)EmD@B=TlI{tyNfziUFO|f41!=n! zl%?XN7=qlLrs&sqfC0m6ufBl}?YmmMLGFi&sie;!y~a4cjtZMJ1XuGdRaBIyfw_sP z+%=OdEg29#(RHN`6<07V1OZC4n#MFrPrE-|D*{e_R=$;07v@Verkw9vLYCt&Fa4e| zzC;59&7p{NMO(&2ASib!DpDB=w+X38v0A}G#R!yDFiE!v0lN&A?2@36q|VMzxd9p^ z<018ruy})j!5tRL-!~jCfkF^BlMspONP>@#qwDG7=HlV)Vg-JW+gyjpFb5ox)N-$> zV{SK+=)$H%dyqkQHZL<3YseOE);~Z!f#K;+%AZP8{#UO6DCqc~4hm0>p1*$cA&wk7 zkAp`q;=;}EaPGzMc@4VgJbCTX)08)Qj4+i7CR^<6HpvMH3~&ZDGjbe zA~KWcqerWpRbs7)NQ#Z&iv97J!1s{ikVHyl7z%zR#FDyu6RjQ~CYlT)DcgplSb)yD z4jaTz6)dEYr==@s$Q?Vs#8yCaQ8ft~^r8$yYPudLK*&KsVX3hMrRz$iM^NJE5y(AJ zfKe`rVhlDTB3rS85?hKfC}k-h+~zOJD15P|_ACuYr5WpL>(Hu2E4m4-$wCTDHmyhZ z-WWAojU%CMETl=PZ(7jyaACP_zDr9ruxpq-yxV!a~*y=a19G=V^F zlfGM%7eXy1E7!pr>2pTT(Pa>GaD}Pt*++wq;%*<)zz*D2m6^@|+!zF}E9fXz;c@Y~ z1cnHnFOgx2JA@lMh-@5$s8G5E+!*3CjVfI62-Poy@Oj||2A*&-4Z0E%;jw}?x)5=0 z)iJn`8s$c^--F)0dypMp<$3(R3r+WC;>g zi8G-T8NY;5qCg`_(>OtdZ$z;%Ue_s9;GfB3tFjK4<)SYD_#Cf}bZX z>92GZ2pS32HY_Q!B%`QaPmH+>n#inzAh{1kc}nqB(jU$@qm1S5CW`ChiLCm9SQ>|R z{&?=PJ{nGRlvI~f%_UItlFdhu;g{1G3W|}gfwc0!H4A~esN9Eq8oae(#en*j@N1%qLpf;L2J6T zH$2>i%P89*od{2Gs5M3j9GrN9hk(M@iEzP7h7j!zgFjLw(JsV9IQ=os+)53+6)3pq zN_hC2#be2erKo@S5VyGjN-gQhHs@a(6auXLr`(k%U5O`S41vf0s-O_)+|nBaa-sG2 z{(wi1|B73;zsK9 z67m!23XlaPo136{kqM?{s3EL&;wsP1oiIIBt-De!A{~ztFvM|}isH`VGX-P=gVcO8 zP*o4g$5+jOPF9aQ+JM0_2Lu6ufsS%10>fZr;OdOj427KBJh~Y58dY{JcCAyxPl*-SYVKoW; z`t(Py7yDSzP`h^RFmmJwj34(AdiQ!6ZQFH4MP)5ro6fx4nQl)V+PChCE*)O9R&Om$ zluB01bSA+<^$lDKOezJb#xQ}A#=ki$#%4t1lbPgc*lt!H(&Uc#6|0fEk!tfR<*q1Z zqDC%#FJ)HzKC%UZtJmYVMlcn4$yIXEl~BMjxN$f_gESDvYYalayIdU2x`-qwL@9+x zmokRbJ|Rj|t0VY7arftSzc<2Mfmj-{k4!;;5Jk5lOn?v+f{4bFhYvE>GF217B+YjjPx17#Z2$xnG9_35 zQGk$0e=lEEit)V7D>mV$ixFc>!A3EP!UPp8B_KOiqA5Q5f6>MeB%!8p?Q!(YCAOLv zqfS9m{^U&apA+N698%4%S7o4b{~DvFNWUi{)?5j%+o?Eo6BHEnS6zWlY9A@E)9RdU zEzQ=ikSMUpHbbz<&UthJ$P&s*D`~Jbwj(Qzn63GY$x44_nx8FNt(b+Q_$M(m>R-=&JIjbRSxxvaHhFh}Q-TME7nl5)|6v)mL7| z=uu-ZaNuC{?Kc2@`o4_Lox5Y$J0sDvM_+X7^ddU6>0-yanzm#QrS?80rDX$q0?qiHB~K@HYk{SyRHr%&K`BYE zCzh^(2SH>9VhG-d7y|$;)h{$@q*iE^EqnMMD9z~9A872HS3A69`en3H65(}o*LQRG zk8r@i6=K4?F)kUp(_p1~1vddf83^|ajm>>fyeWm}jNc_~$1t*rP~INqfB?ZjfS{Gg z*Nu-1Ct&ctXo5vTEauFfh5O$+@&Jz7vUDAPeNfPP(*B-af%~3*jGm|CuL24IFwimZ zrhh}8Kj8A!dYnCf19!;`Zr%NsK%iNW{BhJWef0*$yf+N3$_r4HlZw)m7?f!d7)n%s zAh&^lAf`~+kd4TQ46&AJNwKLtx**P(BrQUv)V?d)w(fxDHFc=vYTdd$nz!I%x^zdIHf_+kV`sVv z{m{8x7gU#5qYdwSsds<$=jzj=HwF?E-X8KcUVf=RU#BHrd3gY4e*6i>jva?#L*GT8 z-Y?;uVI%S8;CJxqfI;ZlwHIFM`wBYp_1d-Vh}Lb|p$GR^GzXIY$BQ8H9;{3GKEZnWC`69b%_cT0^{>c2H%RP3Jp}H9HnkXfr3ClCD0*a z3I-G|G&eGG4HRMzzX~Wk2?YNU6#j%8H^0UC3-!2u<6DA) zVhex7O}Y*yMp6GQjvhFOw_odvy5cM}%}8)u2gMT-q@Ls@Fhm(c5MxkqF(=w(3?W}E zA>L0G!QGyyEI`PPaVix}mRoTK1sCHSfmUM)ye{UTGH2B;XojE)PNce|nt6$Ih{kHA z#b+RietL{oIsark84VD~7_`I7Kt0^4G%vwRmY^LfEW~Tka_!mz1r2D`PAAtvZO)>+ zYJHYqN(@2986xYX+pH2%x(TT&?bS$l1s{va6cjv^$f|;G%4#$-S+gEpU!;M)ZcF9K zJ-E|rwQtvfK+q2TUmAd(FZLoZbm8v*D#m~K5yp@C2tx-C!=P7Qw>5F>1bi@J42Hb+ z2Ht&ZD8`N+L4Z&@bvsNNKLMZ2{20@wOvTj6)9~)l5tuS@28O)xHhRC<4`W7+#i$V< z(9P(Fx85Gc_v(#Stu#Hm26fF_qPl4_l#}u37yt^*q)1V=qG86RdKfYk=O8E~_!4P2 zN%ngMhT;eu1el6x1Z3>hL!m%t96?hIAxOZ$qkusmAy5bniXb>U?a=}SpVw*Rl60HL zdKtn^sXN}Qxe}oU0N^G89T#i3}l&lrV{ZL}s+w zr&9#UPcth0P00N;1_Y$*`sRl}nd&xikVY$QIOe zlVBymiW4Z!7Za`QgLXL0LeS9GXr6c;o{T(k!c;luP<*)zqQ8pT|Cmx!S+2&!@q||= zWL2en@)`pL`OkX1ik&2(RzQlcR%4~+%1dg{x~`+OIk#=u*~Zpu5KK*VExLB@hJJne z;mtSRw4J_Nmu~3XsSA4cd=Xu{bw{`EJCJ_+E zkNOakd7SXUIK1`x>v;F=AsF@kFueW7>v)Z_Zd1b35)opT}jOpsCjJ;SLZOFmSWcWhpkr3m7~R#zdsWkU-$6 zYhH_^Cr;zRV{VT3B*yaZ4KBnp?CKY+02n;IQ3uwOkA3nnT0a{Up03KW%B%)(kQ*Vy zxpC_;?%d@Uzx@YXyY?OK-TO1y!VmUiJpBGIIDF_R-Wxm!Z7Pc#w;@$-gCCXY@dgaa zG8D$Soj{|{gFMpxg7|1;lR0F^&d0qL<0NJq6f*p zB-EP4qf(VN;~^pvnFNefUo28$;w`_f;v@$U673}PHjILo%b=4~fke^PV4i%TK_06h zRMU|ter6iMfV*1)U4~@o3Gjqgq&_c~K6j?1$;xQ>(oJY;yP?u>DsobVp{6;jkVrD^ z5`8VxZKTjoN~2d>%B~8v)itQCszz;7x&zJY(7Rg?^d=a*-2WvE88QTK41NQnMvcaM zZ@-7P2E9pEFcDKHOvNYDW?;_D8JIGDJZ4OsfF*P0VEMdx_;lJ-%$Yt7<42Cbycr*3 zD1qUX0WV?X@b?J@3o(c8#Hz*1@#)8(;^z7CZojbHgyVk7;Xhq0MR|Y|~4GE^ctn{UVjACYT*Hm92 zwWZ|5bc>f1=9Zhjf__IwmM#bJ0%n4Aq-H(PP4JQpxX2h@cs>}e5c zxx35Q{nk;bY=kqpn!Z2(TZzC*&P`ej3O2?>^5{~_bTFBNW=}c+#fVU`gy#_!^a8?z z8(Br8Vi9VcR@H1QHzvgtEC?xpAUG&?k?N1OnwbQGOHyRe5X1~5u%fYXQeVm;JCN%j|4%6-^~NU? zSke=c$P_f8NU5^;hLmEw0fVvt$`Ux41t+i-PL`l_q`*Rfz|539YcfjWbpVk-ZodJ7 zntC!bG{mLATB22ppdl{mX(*>*S666;WTg#d%}&p?O2gv9GJ--WDodK$gr#a}IhIP{ zn5?0w9CgiFp*e4F-Mj@lwQYwsHO?xD+@%V9=ND!Ds8GUpae4$Pt(G${yE<|Go6u8_3hG>F@n1ST{)DY!Y z6(rBeSOH8}s4)dKNXd1G_sW%U^SgQIQdOEX{Nr!`Y@L&4H_zC?fA0DzMi96!;3_=* zxWMI~4GK?JW!W;>=|9=X_?z^ZZV8bH3b$_m9#^kF=7&)l<_}i#dFt#rOeQt0t0+b( znL!0tIf0>Bb~2h~DnmfZ?|0MC)%@h8OAv$nggB%{X;QF^+-+`w0*7VEsVF8>P|P5k zE<(1%FC^1WKuFhQVKEAoD)Wz4#zAU10tB1g;Atphj#W${(F$x5d}Ik8sr$qt&X@^H5!cO2Mh@xsqW$7;UUn$H&7eRFwz(fqlXF?}k=kNc3W#`~Dg*HK@~4N zOHy*yQ31rL;2?7Wd@glYk|hK;rW?`-Ub++B5H(AMSv8}Wf`NjoAppte#1Rl;2?&V< zgLpCpe^dlFh%`){Ivv+<*5j^*AKm}GNfXL8evK-}CtZQ3?+e@?U<*&X8NV15p03QY zVV4?X_29Q$zr!u1UdRq^iYeUw-azBtw|~Huo40WMGBW|Jkvp&~=T5Jyl* zvYrQ(F*}+GQN|9`V4_MzMRrrAl|XkugJQ)Hj3r1o)Gs;rI7?3o3{v%2{;}pdXlSVP z7d7iqKtWoU?2Dow$v;bS?+Kdus1amQq=kO|p=t+CG@U^O0V8<=h00ImjwAn7sYJB^ ztKUIo&Jrxi=80==M1urb7I!!8YRWdKqA`O@Zb3zU8OpiJ$OuaFOHf93P*b64(^aTx zrU9+BbSbLPswrq{TFTaA3Zw&zUk23&;!>@bc$0=Jhn- zr8B4T`bU`k(T7+(eKJ1_d2`h*M36=4TS@V?41o$W{eZF%nMUxuWuBbwx@;AyL|l z+%&ZFD~K3J7r+}DOdx2&$2Btc;3d1zQYure(|U}bmo;-VN-0YfHW3u!eUV6rAwXy* zDL13RbelJQvBl<`+`cF2Hb=zx*Qjy~823K{g(m?)OZ5r?p!nIKpbs9X$_gyaveVLR zyTIk!1cmE&$rSEj&P&C&G# zN%s?c$Rtxx&?b?8TxK*`19x=|b_uu*0o{ariK6(N41-(-fr3WS3oMj|)Ss-{1dWhS z^hxEH2rpN{ALBIBxV(ttZjwYpPqC9Hnf@jj0#B-t#^KhTr$Csx4^>Gh?L+0!l{QrB zQKBGHP)<@OWvmL8(oA@?u&9K!tPIV{ zE6}28HD2t{2}54$kGIGIUZ!u}yHf}B@7e{!UVRnA-ylUND_A^r8kWtNi6zrN#&Xj5 z&t}ZPl3AZ%-Qq=N{LAOe#$p=nm#bG`{bvi!=vU481Y4Fa#^-b;*3+23T)GIWW`B&8 zv!-J{fnesC_c7s}H!x-7P|O@Z3KK@WgP9Y?^72^BrCYFI`XsDf^eMKjUW#2CR^xNN z?qTbxB$Dx}YBrji zTTM+c&RUE z=tq}9e$$P+k8z{^A#U7%gs%@A#HwYB(6O!pO|ufwEHfF+vQp8aAOlrdWD039C`uC5 zcOgHCz(A&uO5ZG=bWDN6{FrzvS+>$;)g4Gg#osW=#}|V%fdQ9*K>(rZM$=TFYmq5c zVwJg)F2;F9C!JXa+AX9t7!#>1gz6M{0@8(WH*q7BCsnk|Pbw%0^-L>aRu6#U3@Wfu z$6s2S%98VJfJsJjrgi^mKBC%`3ky_~luy^7nErG#R2CMac||4Kw`hSjH8li?a?}tA zYRbw{S6z)xty|OZyJ1N0{unWE5XKE1im`7G#SpTBA$?!u|3`2+m_D5`Z?3MreoW(dDy>U1)sYFTb3`tj&;khimYJ6;`!LKeiim@T#W;p z*5d1pYq5)fvwy={e970;^`qOiV%OT$eE*pkH|#AenlTkqKOBj1!-wImSNr3=H(s?h zk|6Qcs{_!xV@I?VODd%ck(q-^x)f#k#VFEj$vo*Vl-k@3HD#qHXIM5TS&CB1_>e&; zjuWf0abA{^cH>6j2o8!s6a9Zdkp>F?_J1|OZ@5&)5FH^w7)L_R@Aksom_QH~W@*ZJ zo*ODy@=J>`LK5%lXK*2bY(e!0iXC`^g5V-3xEo1`MS;gA1Q;ikDVM=v2Vx&ikWzID zN@J$P`RJk&fa1LvKH@!W*}jvT=C^+vDNlQO@W9br_(w+lOIH9C0lz-_S z=uh`I6tajR_z4o8aFxMIBOsadk}iQa4&lKXI1*tw^ms0fO1Be|_a|w9Yoe1nlQw`< zit{z|AUmIKLY6fdIoAag*(fb4;O<{aFepc-<}J{@bsKahD0HZ;LD!ab=+mhqULu`- zy2ZC~Kv=j*X+)iUhm zeLI#f#Lg9qxE5e9fk2?JeZ@j-T|OWCHm$;;Z5#2;_AhXB#}*vkx(P?NY{2pDn{jx{ zCIZ50eDlQy1Bv5&%<&xrg*7X%Xz~Qi7(E=D`M#fgG#bMPzhZ0LyKiFjTLUq2$UuyG za}ZwZ*&RLGw6WEhZc|xS4ob3ek)4uma~$&0bIBa|8)s?wt+XfA!Yz>*{#X7a`2C6S zsVg+j$@)n6)qo*5!s-^(K&5o$^Z(X_jG?imGCdmn;!^2zwDm@WOPeu7!9vv>L?T*x zj!G|Dg(86=TzVY}Oh>{O7K+Fq#TJ~vqcRi9N+^{Xt2jc#lGcLKoobwl=Xsn!phzHN z@JG8)RaJp`3l`zz*~_?f|F_O8NXf$Ar_nx;;mX!j*c4cQZ-+m3{UaCSXM=)8OrKhs z@N}*IJLd}$fux;q`<_7J2i)Knym;d-j-Nb%t#lm*59o!a`6(7uG^Rj6P|=d&3o3I~ zyR^htvq`m*BV0%(ZBvj?CCZk1Y$!w$2n0Yu*Hc{)c3BKTU?BRZKT>~etiZsfok0Sl zbR9Hs)hmrY7b%{utCKID=5%pLs!=8PVV4WG`!YVNpe=>MSGsq=tKB=Jce~bT zUsZ{=m6fO|C^BHkCnyx~H%;|vc(3I2b8Sv%c3LLETlfkuxs~z zZkD&mw6(GRz}@=?+`r$@7r}Mkn8UAI4*g_c@#*`1HYhy33={|k$~q`;5@@zi_2R8Z zf5tU_sSDTd;KZ>bSUh_g+BGYt@0f(DY=Q!rLUDr6%9~45oN;iuq+5AD4UmmSimDIk zi>V8;L=8<13_&0ea2<3#BhpI{@!9R_dr&+m^tItr!elbApT!S`01aAHGcUK)WRS1r=?d9&%?W|O{V)2+~y zq#DxT)|D0L(!2(J+P6lJHg)LVu`NcvG5}*=ABYJ<-oQI_1wP^~IqkinnEk;>%p{#$ zGWjDcpE}7|WCSyY^R`Ko@WsLfSW5udG;bD-YzB5NTa2xw@mm$_Hev6Y zm89~^jUDVKjhDd-G}h0ZWyjCw&%xG33$S~|GVG@Bzl+EHe9r!LD;-eSuoB;FUd`ij zY+W*kudxy*zS@k_d$-~AzU?@@dn*n8OPtvLInM6df^)mK;KKf$cH0@=cXkgSx9dxS z%WCYTtFmkPXW0GOLL6SV25UZ@jx}><(7l<5RXhh4O#Tp`eDEILd$||-wQY`GEo;!B zq7?1uTD2-EMO98N$}`Ba$hh zwxisLC^Ej#5EmM2;&kIs1BXU01mU^gG_pDdPlQBbxJh_5+1h2<2QOU*Q*aI=I4HeH z_8`~Q0S1>TS-6(Yk_`PkT4f!qeMghWm1=286*3OdGWs7zc8&mXN= zw3Eo|hR<>8+$FN?-_pJQ16lT8X;Xj2!$*H1ASjJbwrwEsAKM9@w!vRx{i2w{-xn?Y z5qHH7xNZv=GzRX@cf9Qn1_~GJe}}VIZsN@8V;C{y71R`F@J~-5Q;0`pTD;Q)m61Sj z(C#lO_b97Y%%Cff6XgU7izKzKpkf0M=xB{j35llEtj7oxbXz7tA(HdqQwO zT?&s!;vgzl_8UO>L>&n{nik{|g$#9;m&`#e$7=gkt-gdr61b#5lxhV|$w-S&Lr!Wg za>*7-((_PGw$LoEgziC0^lje(Z}xf-Z}#Ylp}l)x?2y;7V8U2TduJ#n40;_O4SEf; zM!t(B6USlRha)iegArIeV+y{Y@opjm*t%d27LFZltYFKMMKtP9c$|lg3qGOIFTnxs z@CVkd!tpOQ;^126c$mBKK7xSa1BX6ehrKlPZJ#Y7pv=QpE0%JRK`f?guxbS#|2d9q z;!eMrC*;;GIKA^r?)q{G)^n|+Yq5dG{RPhK-HP*H@5F_JyKt7qe|ncdfncy_D=svw zO9yu0GM57mTll`q$xv339T1cVUb_~|$5)HyGi}2N|&&XW#y*|c@nG-Q- z*kHWUu{FBaRG@234LVj;5g;m2o0HG;BMT*xbK`%X5i1ehIHVICvQsiqBHhP=5@hiH z#CT_7mVhBHCe12NwW7Szmhg}<5EvQ-^B9U3$QE9BJ_uoB;R38CEfp1*y5A(9qqk5V%5A@z}@!!C9)3jIbUFF$FOM2OOe#S@8vK zcJY3c7vy8+$20NOo&yAh-x(;}y8l;hs()?(gdeOm;NCsQrFb$Z__R&_lJ(^4{%lZq za_NNv%s}~8H9Fn8^R3->=l<_;jmNXM9^m@@2e^3Y42JaWiR#Q$(@m(#NJM3NtR2f! z6Rdn$Q=J0y8ZwCD+W7;iN2e&snc0w`xscI5r!$R@&y4XSL-h$Bm7cnh!ut}VRICzi zLY!W*27!W~pdq8z$a0^@N2GVz;86_%2@MJ}CLusDhCo)Jn*BJQj4I6)AZU8D+N*Pt zGEth9k1A5#=EcQi1Q!d^$G2|AN%{Ts@sDizoWA}>3nuC|8THAXTgV0o2;08I z`MrB_cK0scPRh^w&hFlZ%LjMk@}b=XgB`f|4GsSA9$Y*`P#{|n8#uRjJI?RhVIXkv z>#qzDv`+Ce9sXjSu?SmVZY1Dr!si6J-OCo?^w!VuH9_IPx@9=hz(&4WxtOlcC)hY| zCKgTl5F=mhi&r|eL-*#*(3!x{hK!+AQ88-s2o9Nb%=>aW}?Ju3C?5= z<>gf<$}cy~1@)#V%MjE!0&cZYlLe}^I_QN^t6NYRv}GrBne0Iuf&O>N%W2eHXrmyj ze2kM2t&8V_l%L5g;v=Gv7Oh})v}Lej=oWY)2nu11$r^&_j%etu1Yv{eibw)J!pTVZ zBApc#iBy$G$7*P=7kOzZnE&ZKY~QgLmu^0w`|uasc|cIO_ZI@e54P&-X=4J0pCJ$b zQ!5Zpc-Hz=KtW3{CQ#5amY{ih))o0ZuJL&B&O_Y1e;?a;VzjR;MR{^ODpKQ7ogt~T zSOS6?n4*#Imjs)SJARZIw*WyU%#u?pB`5@{6m3wfRF)&1>NAy_YMOK=Z%ejO@qSXx zSQEvFCc=B&Zk`w(0)iv*sThLBh-m<6c(7V!ovFl<@ltA1u7y;Dk{Tq%0xCk18j}cV zVMY!r^NUeeDi@&zUhdQhL;Lr^sDZCw99@HPWCpWo_^UsjgvH~>U>1R3(YOz>l;E&p z%0yEB=~yy#oC5|6XPa^Cq2X^|wg9_UEhd#$G=GI1&+L{#Z#Ms3$J4e`ZzFx*iW3SJ zk|7-3vKe2mUyn0iZO6sEU*Y_o9enH;IQbQgc;7Bt7rx%zaQQ1<-b=$jfUAd(;L11O z@OThck9>{G-|R6ExOQwmt{&a@4>UR*!CkKnT1087V<1Pgu6 zb7B<)g@a@)0)(TR*5VK^AErxkY|9#g+zNcXb~#;)Mc6?Wv~%eKteG(xQ{R0PBVOr` zfn7SHSBqM-Dl9-7;RvB~{o+o2aU4vMIHVe&x2;=4u@efh(AxpsGi*49U82Bl3hvp()IYmr{66CzB~8kuj)eA|pX#0%A>e`%-!mizG2pz!p1+BToH{!2kYl4}A4V+jNU z1skv3{T3H*-6KHU#e&%%qiL3QeZLb&NS3ma8keMf5NT7La-vnu=)?u&B8VZFIEu0h zngS%F=dPb8c{Cckz#xsNn;tDiXOAuICMrOQBJGo}D5l_#RynLhMx(5}RYOcd8;j~i zS`^x*4V$)Kl!=d~#3Y&kX}YEy$ER31ZXQp*^4xq>7Z#&)ZF3Ch)CB{(b;q!Nz3Czh zwAjF$4@Y6q#Ie{!-+#j=Q?c;Fkytr(BEjJUeE#W7>|C-C>t;^H*3ahBnAhXz7wd6= z#=n<4=#JF{2kxMUz9fa;Nor4@UCcl`unbMR@dfVEM%Q^fPGh@za6hgb*oRB|_Tj?b zUAT5=zqte2)h~U+U7v<~_3!~2|G@?zIEWj^j_`QcK;hQ$6S#fy1aJEWH&1Y#JcwJT z4&nA`=koRAU)vH;+&KC*ZXP>e$7=+G>qqw6=g?=ndT5V<)tOyi+HKbl?Zp`a(***P z)|qYm{9kUu(a%@o)OH0a*BMA0Tt}B@=|X(9WInbn{1nRwl;Z{sz=;06@pg|cc(Yp< zbgOELy4)NBL?K!b6q@H3pfWp`Zd;ZKvPxxJLEdzYu8&JG_2+yQk0nUH&1V5d39*C) zM_2)x7=lWmHL=_D83+oYbRi=N3LXIi&j+0w;f;gg3zs&6(%4=Dhd5n#N7`&j6~M)~ zd5+Oth-~POaD@h&Bz}~FkuKh6@@!tyVMvXUD!88vCmuN|NyZjhx2VI$OJn~{C&MFXaG7fSdG}%ckK}$6ViX+HfP^F>(A;~*+73ZRf40pj2;;8tf>KcY^TL#oAahWgE&upfR}^=F zSY*eiB8w}Ju0la-4oWif(W0y=I@H$U#kOtmN|!EpyLTV%Y6CFkowqQ5+$b!gOR!@4 zBz(DW4z}_HU;5D)te!RjYd@ZhEp!LITCxCJ7kr9c%NF9$hE>i^$;*3IEXF?i;(Ir) zz|rlSaB|lcoZYvLG=016zB0B;r0Ulic4qCy7x(QXAne6W8TsKuxOL=+fx;QO6gLik zO+zOGIKB^;xzk@iahL{vf<}J=w@y39Ye$dqcoKI`pT*s?XK?52N#1uHx6T~p@rZ$f zmH@)m$wLH=uL&Xt4HyInVhm>qK3B*Z^x5{#D2L;3uj^Toay*<#4vpH`T&f6sSm~s=#Tz&&C#{80`2Hh zv?(e<^SlBy<7%2$h@y;aDIs*l|4JttJ7kn0UBpZz>q>_TuPvuGj{=&t=WKc*YBZTJrM)~XEjs|Ys%Ea zzXyP4t$&3vJZ;0jDkz8*T)qC_Z?Obk*X_6Jzr*F51d2!BW6P$E=-8~>TBOTTeC9q> zrm5y278xF?H2Y}y(F6oBhGkps;${M64lLC|0mz@jRaRa||4Ia(6sVqd5N+{rlb9%}2j-a1U((*u=tmEd8E_{j3%GKCJGdPQ6h{u=j&}Vshj3HIedZYMoH>Qtr%uzj zPuaS2`iud>E#7wb>^a=Ka2E9!P7@p)UpSL9;s{|Z<4@3M8B?#q?EJ8+HP@$Amcba4oL+cy&!J~vP}_2ota#Cq&sxd3~Y z&&L-6hWFpbnEt)+@vtG7Fz8hbXw?#Z>sp{|)23)&R^enRa`RA@orj{#9OP$aqq11t zrNzij$wrohY~s|)o^O)y(sgX|BQQi*YVzOyS0n4D3=RoHxYV6P!x7GNC4#KLO(yCN z3xm4}T>&ynn=v zpwwa%GUX1?&^7W+wFb%{sL3f%u_%ThHlQLB&3nuw71Q{)WEy>vE1WKaqjr`DoRo?$tD`PHQXZtks%ryqIrPvrW=@*pn)*yq^G&4&MxA~ z*AzWlwMDPiZ7`r?N4g8WFo`GXjN$KK{`k>YKK&!Co;eAd=gqWW;mRrFv3$~4teQFj zpN$`bFXnxMBb(OX#Fh=VJD%n`M<6)6V+$_s*-k^*O@rQNMt_F}r(*yF0!usbE$-0u zrw-d=ZXG2E&@H(B%>jY{jrRC4+&y^|^{0=~IQQe~k$t#(;W+M`CnMm_e~Y)@K7G=F zL4a`g%voEvP7^53@pwUtY(bAd$H$-LYYGfbAL8SVpq@Z+|Liei3IeZ7U+=QZ*N-`_ zg1?`R6EJVo+nD~=K!WT@V-Bx(;D6AzC0^^;9^J{>n&stLO+%F=-inLJ z7ILkDtXXkWx)!-e@=G`;-83#Gj1v~>F~*>VDYd{`yY+7x^R_UPi&qLWj7u2{&4X|S zhng$l<|g9fCZbA5t6h+syWCf8RO+eWd1kHF5dsvtCc%vfGNA+pcZ1?ngr&xl{ZTf) zJ})g91)0*8NW@1ICSlW-9XNFI5^g^F6Yh{L-2U!QxW%QTDu(~r(q|d=vl^bBe%w#3 zUj-DjOpx{V-&jHb6s{8#uHAls9b0!|V4t3-&P_sTaW+q%_ptW9LV$t zWgH~67WX44h$(1hgWQI6H7^km)WfbnBQ7KaNm9>MLyFQp667S}ONm#Y5XC=DfZ*bv z6CSDxw{R!0*f8QOJV-6Gs$n1vjE=M6v03y_^HVcWOnd>ck2lQ{>0k3!K zhL7GJg1O^HV=;|>%_q~amAn1cC3CIl=g`Jg1ce0#2Ak*1#JZ2C(1%`&)AYG7lkN)u zE^)WKLW7c9Am2N1H1~izZD2PRJvS;&FmOP^*`Wh|z3#t#{2PMAG2A~xH{cXqgA+$+ zz{gQf zE*|G|#4rd#WC`_rJuwGmB5oW%KyW!^N5vh)CT^TKU_f+LLCO%KwJc~*EB2Z9q#e;``w!(^Aw|`65 z!O1$@y#4q`ws3*) zOi^T){%p4`HO0h9Pl4~1tvQk@DJaXyK}~TnIyb9D|F#5!uHEs@OD|#K(6_LNV6b%R zcx;$E!wQJDEnR>;YnEW&x~15?axs=q9*50yXPTQJ*FeFxbMm`s5ZB~;bH|j=t=(3; zzv#EtH5%0Qqug<6L^?W#=NMyP-8*}f#_SmBJsSQ!QvG{pPH>&%<P7oNb?Abxqv8wEIh3+9UR~ zp1`mM$2PCP!F7wVXT?0MBMbZF{Wr00_H@jnTQ>65ei*?u{FQ!qqel;Pt8Ibig+-|4 zf7ZOL9IdKqQBzut=1uERSyF?Hp=tums+n0 z8jhTsg3ok4d7fx+ZKT}JpeA%n!w?m!T81#5drld?gj7;gzYwWGGitnY=5KmjI^SF- z6e|})l5c4Q1;rF{QPjg)`%?nW!x( zLYL;XbQL<`jc(mBV!!~5f9nm*|7Z*u!iQKtdkS_eori5p7Bo}>uE0+E!mDOX#J11o zS~fwMh0_ECF#*K|wBrX-c(&6YbByLDjsFI(%b4z-rSa1c@0}ObcYJv*-TvUB4Bgqe zb=10l;RGI>laZg|V@{C~oWecs_V+KH!o$ln@Qb|fg4n@Xf{X%;C(UILDBM4P;YVPw z+s>b}$Jg^UZ}T(U78uZl5CafM-6Bi5quUx>i<@T@v-n$vB9OJvx+%9pz}E2nv;r}Q zs{%!fGY}LH?!dWSpX1EVjX1x1Gp-)kMvx$g?%Y6zwHyc5F2at_X5p)4^D+IsAqK{4 z2nZL(G zs&rJaQEr3+j&dQCm1z7zAWI=&@R%~Rrd&Iq5N=ZM>U{LL#YA~Z57jNP=4Ygjq z5Dl)w-`QWodKM@=YyA``1U7G74+t>-N}%u;8owjICZ?b_6Cfz)c=qg7?Ag5s<3_!U z>cR{YRH-B=RHQ0a;DZLds_Ri1hZI+c0YYJ%=03zAUt%edQM}!a%&2IqT$G54WY`jk zx{|(%AutG~V>RC|xCtWF4^LXBvE&}oKgAA$8;d278V1RQP~cJG8tQtCL7X?1{$?`L z;)L2sJel$dUKQxswhpg!?Sugx+G98w!u0pwwH;~^>FcI>A7kgzS=hR07IrLKguTij ztXhoC3qHYaG6oeHDUB#FkV~MTpeTP}2@r0dbfWpUXh?Uo>*}(O0s(K`NkBM_$5+o7C_KD;9*-_xB%oY05RmZ;7{nHI zbb#U%X+Igov17POR&k3=pvk zTy0YLC525*`%+9HHb&#^n34X|Vw^Jb!%(Olkw;V+0KG1dl9DIVV%dNz;_CG>jcL z9=rE_gQI7!{H;OiTL~A*-u|QgJz#9E~H0ztazz_hMTTnV^|oHA|#=$L7@`H)cjXp{6&O<>D>z5Pr+ZLutiS<)yL2A}5a#deC}X|ofPw(Q0fIC5_S$)_3;6cR z6+F6h84oXB`f=SmcYy$Lk(V#q<@&RZD?WM}a;-73z5H!wj2Mbd3uajr$35-lrhQBBvOnoOahtQO6iwMA7$a}$CMXfvqNQLzJ6I2v0}krNq&L}0}tB10k_ zUB_@SOO2sdzeA{H7edKQm1!{9cd~;-uZo=9HmN*?=dNlK1PXB~niC*c$$bo2MJ$g| zNG5pXq!18N6RkPwwbx(A*GEs`o3od3{lV`nu5eevM2~6gkN)FM|5FD3uYf{;83bNW zj6u);*`V<7kpl`39{v?~>oo!DPq;>=aD(jNHc{^O{qJ%9%59uIa|NHym_h$84+Tll zCcCCIWKOi#vJMI`s&X;St(2s68J+loz(TQv6#mGi$&LnN8Vy~#jFM+paL23GR=45^ z3IK)JG#|G>!R@m2W6%phTul%jB$;j(|I7&MpO4iT^=P^a1cc0Z`gAGjXjW8&_SIGB zT33UCJv!r!o?S6~KtIeGJqpVwO~lG6sxz2Nb}%0sKABERJ0E*jFTwtG+{ret#qq5h zv3JcgUA{j z2{(VJ)SM;9rUA1&pSjT`5hOIARvVXJ)2P&4>G2^sI-Y>w07H5#UDnK0WM!nHXYUtr z;Mhrm!WC-`xNY)=8W^L@!?Qu*r`&?40piKqp7q!#ANR9C;nCy2G<0$Og`n^k+`c1) zrQhKy-G>`Qxhr=cbGl?L%c|0?LxrxD;t1AW%@uAj>O&kWflI z8G$+_W38f5g-exb8J3n*sxrx?Oap0}OF$3?ZCEDOfReGS+=E6Q9pjQPKh&-bkN!%O-sD#X37-cM~9V3{)Ei0*DH-i46n{Bd`Pp z#twMAuT&qGKp_w(xKD%oF=%LkL+;*ZPvS8R^*b_v$Jc1|SG58LOCY%9T-W7Cx}Bi# z_|g@!g{%1P>NR|S?K-}_dX>QA82p2V<0C!pvhz3{A6>poAkp<}cKyKxF^q=Wu3sht z(fgex7|C5ZZlC#SPW(w!#6hej&tT_-o|NMM>95)@UM3)ySWFX1fe7rp0)P)=2wx*~U zGTj6MgXWARdVMwpJ0~p#xmoF`Yts^&w(rCtZlu@l{T_F@@zy_fgpU8&bmYIXo&Frq1dr^L>G2J0Rd^!yY$|B?>z~mL3$5qBmqRx z^}NUa6V5%(am_24M0C0IIrfKf+;{GTkldNqZ=R!L^~|Yq(S47d%NA(r@!YO0+DvqD z&n}%&z}Dg;n^qz7@E)!1D;AeQ1Bn@?-oOH(Hpd)#9u#X-2Lyux!Fi3+n}@7HAzxD}YC?ciUo4<1G)lp`4GIN$WIY4T zfq5)RM83={bt;JZTT9YN&CsAkP2L9A0Sb3Gyj3{j$1BgLIa;9k?z1Pp)T5vEEbtVwR7o|KFa+zianX5&tz5+U%~z73%7_%i*p-;jXw2KclFu zMILV=Bc}*gi^{QY=S~dkk%A#H;poq)O3?y>V3sro$zkVk6a$3rA>80(EH#Mp5h4@_ z{bS>e8zBlHTwDZpKL&O2L;#zYScXd1K*&xdz$}B8yw+K0-mzn6bYjs-7t`z%NR{9f zh)8aD5=wWAj>4dxsd!3U239DJ|6nxcO&pK!$9;mi<3AIHFddr(2HTg;$G+9eabnwA zT-d(_=l6;25g6>;j1x{-a*o{5Y!eZEXaxb401pIJ0ioa&{-_$T=0Ks<0J?1W^ku zr4<5&O8HonLoq+*;pZb^!PhJ0`<08e{W}|%P(>l|xltktqg0?(lA}K_e{P#Kq0(u% z#;nL?0bZs)*06Xg+oUnevjh$dVzQUQsfHvJ_{iD|re=%N1q|Y1Too{Acjj@ncJI#51-07$@aKoDv_Itkjr}e6!AyiPg-bQpZ{vC`7r^|2 zvT|`D${KMcy8wl95zm=E4gKQf0LF!3U`!CwVnWa@h;9MLsTP#3W zs@;occWlJj?dvsuka2J)4sTwC^Lw|VkaY)6s*PC%`qFyWF3?w$5@+-LH9)62>G4(GaDe7=lZi>v%jmOCafB=F$ShBo%f9$ zm$t!5Yc?)ASqd>KF!p|M)&n~+IS7VupbNncQwAUpu3fHmBkVt6e1XGiW_|t%J{t7` zK74%?UK#lmh7TBsp#vCOcuW`TaDx|TY%)E`_G-EkoCf`me{f#}vk@MeqU7*beoPm` ziyf3*O#qt;HBdkhEC2}T%wV_q*uOJfTlU^~=~y?aQjUR-5HN5OQ)DQ&Q$=VRoIr@+ zP#~ry2c==_u06OUH&j*vS2p~FDmha+TfrHy@<-M_KHv^2-roof1C9m)N7cwre;o=O z5oL|*=4o|}R1CL}lUs@ck(xO~`qhQPDbEkt^c~1T9h$AaLPEU}yF#c*C~~$E|znABw5QPBei43zV3Cr0dX`aRyc^ zdUCfz2;xJdwZW-RVk(|}WEkENY59>{FiWRS#LAhIG3}ENuyDc`ddJzbd;t!vUWQYf z*W$dWg7iJxkacK}b{?MFy#<#J?KIVU&V@@qnG%_4J05_lTvKDj^y&eJF0u~rx1K`X?a1wbCCo-{>ntMyaxS}u&NnD{9&fS{f=-I0WCQqAzi&^<7 ztGnrfg6)?4a|(c;ch?{MzF8>zBv2p_jQan}P|(FLbw9S+_1asgyKx8C8*bxLdNy)% zbFh8uS`6+Ht0m3-<4l^dm)xm)2qCRf;7E2tAvKW8V}rFtx_?}}u8QaSw=fS6@zc8M zc3>8W1hVVVLxAuw3z1l(?xX5}Kw!0E7fL(%gHbiGCV@jUg1rJX;+rIYaCSSg)A20< z!n@*ge)q*ESoQrhOd0z=CVudiMz#+M434i`fpgn7;L@(G$UVGQJO1*H?N=bAGoUCw zFa2J7Fn6~JFfiaFzt#;2x2mwujk{sKM*r^;x)S-VhrbngE-2jd z9^6MM*fwnUb3(xl1lD@66O(FzU?30(2F{?ME5RVI1p|k*tP>!tnm$$wUEl4pYhy;oupu(3lSuW(#~4oMCc9=oAzpsb7O{r zu>^Kec4d z7zGGrm5n%aIvqvV@-chbB=n07*QrYVbU&mVbU!)x5ggCX*g+7wg>mm=2$KB+b)n1P z#AI$2YFV*)8HK?AWaR z|7>8&IKFIc9+t=N|;A{3dKWAMHd(iFowIFGGZekCuFcPI;q2Pvr zHS0Fm-~IQXV2imd6x0+9CUt3{z^o3zzyULy-9R^jZXMkS!hw&+H?PqlHTzaA)t;11 zoOC^HDrSEADaOA1ipCh8f9z2VPwTG>d06e(zxO~S#iZ&G-ms7u_=}<>6xcMy-4UD! zO||gA10BtrN4lhPZTZNx#nx&L=?M{6R=aFPEf5lP9S8?zyZpu7@ZqdS&R&pfJu-l` z3oK;vK?Kw8Ny+G*oFY()L|l9<-gxIdoXgBZO|t-jos#wcR;8dfVOQF){dxRLMwi41 z1DMfogwHR39SW2kbq)LhS@gh?<`$IJG~-H6DXv~E$Hg-j@YYMuV^Dkm$3Qs0yw)Z3 zi;obuAY99qIYEi;1Dl?Dgqw{{EL5_AMQ)Gg1SJ+I5ei{GW;>4`M{0WtwL5jv0HRR^ z?2#u3==Yh*MShNdG#@Vx%M8>N^F3mdFr?Q2yzuzb`0$OlG3M3RF?0MlEES14>&s8E zW$`?m6lHL7+d7=vzEP(HvCseV-W~cpi&Ki(-h7IMHYd%{5ti{fZiB5sAQY?%VDF&z zfY3d-9~1}#2JN&8(e*Vq6cBVBOxjU14C*?R+^MQ=J^U5`x5fCmpK7k_19xO;KbnNH&|1#F#Rtn5SG%+<=eDBY06_I@}>IerXz-qG+e9tTp z+)%K+5*7?h@e&Mnt^^+mXVxtc1{^@cxB@?>av&V`uUaN9#0;#RH62U8{T5UIH5MPg z`WoJT?m0X?WC#ZL9Dtz$g+YCXpht3V#789|CMr=~2tVHtjWcu+75Wgzw{`Z>3DBIq z@X$jY(UGyME(~gWD-`H5Xl%iYosfYD7UkgEiCHmk%~be!nD%Q<4(B`t?w4eW(qFFc zc)0d*sE`d8^@Y{3wU)ViRU=Nc? z9Q+L^v{gj$2jzg3{E9|oD5Rx!!_$L@$wl!JK79E#O#A!`Ec$i|rhf7%*3FqMFxaH!%4fH) zLpogq0RhK|Q5|sBAn#B%Y!R+@YB{UG!zqZgx$T$fxk;bLDoH47`|VoxXGA8;fm7>;=qCLvnmB=^cyEd zbAAIG$+EZhq!xU6ZWrJrf~)~sWAx2*ut8b-(kMo06067V0eAR(|Bs= zaHMtbt!w+Z)00XeDl`sJVets?4HLx>DsDoc<3@B!)EqDu18S_+)NfE>Ni5pLeFlg=-s0yl0`8L7&HtAj-El? z&AU2Jp<1&KcUvbf*;qmQqNMg`^G}%rh4%KBq0qKr`bSh$H=($^9=WBp$SNqu(Gnk77rwX|t1$xDKLBl0~gNDR+lAMm~r4 zUU(T3#(adO-%Z29N#A1i_uu2-`n5PMt^!MG&+k}|OMA9xGgJ1FeaL06KgWxS%b?9n zEL(PfVAX-W(3yJsJt)*W<;QL?+zW%>LZP|zDsEQrPF$pwi5m6S)SyX2@{s;iEg-mF zi(hJL@sk{Q9{Sr=RRRM6LhW_j6ezR^9GV0wKN1M#Z7{LMk9o-5P_VCCvkJm0ht@1e z84IF{P*<4if`Gasp*1%A?xrSomR};IE|5ScgoByc`Xza2zi&)efTXDm|4hGV+pdc z{{#hoo+j=?v8g|>bpj^fSJHZj?-B~v>r@F458q%9Pv76=0x0YNAb26QDDTcEL?VblCsnx>pP{!5H|_dUEl@)^AQ z!b^DR$)_-wgIT)uLXY@V#0wCjLSl3QZAf5*HcWXtCD6=1ux{ZYj&S>DM+F04 zzIqd`H~;jfpzv4X3~d2RKELC@!P?(|0z0?Z?@eQCCx^3UUMoh9T(~(|Ss3%is~FZJ z34;jEw znFp6rXpJK{P+;m#%a2>3KtH@a5bjk9jZ8-tG1gG%$_AK#pOyYRu)oyS2^4De@ot^K zfDjNcGzk!z>uVJbH*4gO*BIBr9)7~W9(w*;Hw@h7Rtfw$T4M@MEQ0P0i?RV|z6#9eI6r7S*cPr?|3o8G-{s5a1_@p{ogo`*vkE z!c+c_AVdokLV^Mi9vY0~ghUJ;JQThA4#NB;t90S4LP30dg@QB5*+Sud_D5i_uD&}# zV9fqI$Np-a0$m5)kn6N|av(%OMLjaE7T{VjOPi14#Sz2sNRI>zj0+W25FjptpLR%g zmq)q}y(7ZaT^QcIhrl8MePUvfAaaVsFe4b9rAlC?p(A_OnRYZ)iYA!Insfr8le!On zBGFjP#0W5hK8yxOiBFgq9*bV7-7!pDhZjaZjj?aPjVYgfhII?(W6zouIKFiQ&hHbS z^zaT`Il4zw!9H9SC}bVoi|msJbgX%)Gv1tg5Ns^L0)a5F<-9bl;-j?-FxC}NDX`a| zt-+{K86nF=8Z!_fEq=Q44(p42co(h+w@!5Z4C=2^0b~ zCSV$-%m_EOSU>bY7qtifA?o#EaT~PS(aTAV(@dMb?hx?{QsXQSFWpzkdIewIKph?u ztXYVJxOj!apdpW8#+=1ElaZ61tN#KN+LXX=t-Zo9P%sGtwfetN(a7;oI%U?eqVhUq z=a(SAs1RF4hCe!>JNhTHJ|R?9LN8GWeIhwgDL~u_4qAzlKSTo35);rfDjIzS3h^xe zOCz4f zXCI8gG`SeoFIkL3TQ-V9SSxpyO*p@I3oacX6!xkdrST$jS1NK?2ro)rpw?|56gabx zcR*I|*%}15LTHV?Ut`I#$m@JLI0gCQ3a|mmxDdvT5Thbs%)trFHJ2Blsk~52ig`!2 zrONhzy5j-_!mo0`X}<^%erhlvxJ@`XfcVkAE?{V?s4!~cdX2gjcN7@48j!p#kRfR7 z!MFd1TS2r1Jc~`kRSEkq2!?C=`)CzryUI*jV}6zz;b~n7s~Fnq6kJ72PP&qhbRh@> zDg~<=v?oHK;C3ahh{Ch6hO@giD;N&0TaG;|7i0Ue^cQ zf^y_ty^1-rCgYL5$rz9vfj&_|suH*`wnwO+f`N4lgHvMATONDHMCrDtZXsd1#3eSs zU)O%K0<0_7eS4D5^ydf#v+}Fc!}5m_FtC7$Jr8!0QUKecy#saDA}bgBr}V*w;s8mVDDOPx4}TcG%qO_ zTq#I56g1ezc*3=-YJ>v&7}`UjiBKpsX-Q2(vJ;ZcPNnAXSumKGLa8234Kj)wU|j)? zD=>B-hy14-jkr~Bz;LIrLDd66pr4cHKk{=_4)uOO86QVBp96o4Acop!;B52r2~& z1!f`$1$8HOZo-Le>v2fjh`p6#ffcsaTX}M zb2;HFL?3|qQgRZ8J^DB%O`d_$@_NVLx65Geg+hCkz}MVL zq5VN`uPEBv{|*$Gd2ql$AQYC?;&M&_a`UcY+-DzRNUwNZ9!m&x3-(bU(C9*N%k+T6 zNc4)2MEB@Otz1mt6lXs_MERImi~by7(Ulcs#${kN8EI_&xsDHUcuH4z+k7x5D#;&# zu0xDSziv^9cxv#Yc=!32@wxbrb0$o{jup!U2qG!j%OH0VrnGdy3JYQc47|V?%xc|y zBp0LgYum>XtWt1SELu3YpNbG@6top-_3f z1v%HsQCL!qJb}WeAHIXZ-D5F0ITC%N*bT{S1Dl~jH5<{NYvKeBJ>$Z4|063JH3v66Skhv7jWbm**+UJlgcoBiNFL@~%8!GnDdVr)ub#FuJ;%Ur^ISiKm9{;7TN z+Q=6%=H*v0>&q{(dC`1T2gkQE{m7u=4!wZ1Le5bN?)`|i9-)24!Q|FWfRW8k>}#-- zhb$N@5bR;lDM|u`hJ4fQ=!Sxc8weZ$FjAkun7-)n-t6FfNU^5dIJk|&azGj1r?tmlHq|8K6F}Q(n zKc(PyA#C=+Cb|m@Ti$0$Az#;fiA=afxZO;m)Q~uhc~QHH-hm6 zF8uvw^!u0~uH|RqO1v`SDGcn<1IgSO8WD|fQ44`=mJ%5Fcm=>u6a!rZTLMiO5DE&2 ze{{qH|4#=|3aq8^)Eie9xtV!$_>Z{1oEb{v9uiJrvPwa>mvNqmsDvIpyP;>FUYId^ zo*d4bx?PRSWm)2E4(y*(1^2T*0|ooN|27opK9p8Aib^O(Sw$VroH~tnUwr|?`-l|k z7K84gzUUPZpqr%$1iKw7Ej|LNk-W6k%oaB%HPos@Kb*Ct%r zyIrNGm7X@;Wud^kkd?0%2IkSgz-AoS^=F%&Y$eZi zz?Nv&-Pk_b*n+)l69m83)|=pAeZ9s9m~n7J;U~w1XmuYPrNGw-1$~XFOu8SHZIzOQ zja3QOP2vA-0mYx?Mp0Yc0*AOT#$cgf;ON+Wl!8r9YD&_naI{Ly7BE>bTQ`o@KEA-e zp>+YN6lm!O4H)Q3P%UWMS;4RpJC`rSy4f?Zbn0YG`Q%^tX3S`OB8uUOL4(mdITeX9 z@rVqILU2Ghe7%F=C+6+JL9V_UY~(N&tzD4oQ?nARVIUa97JeAfLPxMZ~pwwvePY4${#9?S^EC$9$p+^`O z!9*f0AsW46A_WS8+7=zbyS&JMmWi?hk^@t81h;+DRi>FfcLs7am_y&KlTivR7zy_a zMtq3;pDBIu{9{ky-BGV#(Rbfr*Q#ZDF`N}3Ffy&VZ7v--aRAvTjLTpztfF%#w5Opo z$OU{^tvcs2s`5BPF-xr~S6<^q?!ckdu3piJ%4W(@zM9EGnCHe1 zcd`v-^07`<5-^lq$2K*|14~!o=Q=sq0)jid!^@$*d!tcR0b>e3HQm6S8)A*} zn%FPR&G@B-)}$^4T@1c{yWW9Eoe5a}RAoTG|HEb|`2X4zrW;E3XCe@&Zgk*FnVI*{ z`g@g{=T2|TJ|IJOt*hj-!ZzO4d< zZR#!_6{UD=^BR3TynZFNE}APhj2W2w^*Bub@-vM8@FP)|&xp$GfnJHJNQ#I>WN;(` z{6pa97ov+~{5^u?aEHLFlRr8=#GRBJ;pVM3E-Hn8{vQS%S--&PUvwurX?=~Cho>%< z3Gn2IcP@kp)JokDUr*gc%^@t2oF*~8!u3Y8>yAOj>Y8ShlwLD>5a#FBq1#+68#gy@rVFna8k66 zVe16ZhlbQDv$Ii?m8Djl!@UX|U`irmF(;XlX2nU)2 z1ciebLGg3*4SB7>WGCuPhT>*JovH*DH?d^e!oa!^_W!kjuyC@^wOOi^?t}S#*sEdT z$CPD*sGvWBf*TCK2L-KVxNO?3H5+l^xDH~`hN)9Ww3F4kgRC>jIIAe?6VOXEO3I_Je#D+%8-8>lna>EJm359=` z00Bd=+~q^z)rIv7UJ3=OgNK-n_(unY1G8jo+UwkjafgS+<>(|X2p7eOI`H&RAjF0R z2^{%DOu!t2WD(w9n{1hq%4{Cn)@0+~My-;l|IV zno<3p7L=CPB0nz=-%p%~k^TA^C#9|}xNRJfsj*UhHGYwq7 z;pjC?OwmT)p1+Hh0>RrYL6ltaDrxXgBJ8yLxGEjB-N-GfDP@vUZ z%|@j@It@T>bLtedKEVYA%_gwXN$;%8^UGbeJRi-K0*i71Lb=Ft_7)Vk%}=oDNFLl< z1cMypR_QNL_(cx=T`B}U^bJM@@Zi&}XaxjS1hW(7bA-pO#(Gs9+B;Ec(wqc?br)31H?G8iwM#S))d@Vbe{h)pEEL**eS7<_pB=fgVPA14Pb+2Sp*opS9!>@&}B?uzr2TZFmW@CXUC})k|=2>sp-J zy;*}#n$xyWIC&7cXAbLJ1iPq3;|NTNvHqS=X!UOyT$H;Q7pdF>1-cAvYJnS@-Acj6 z6dKqUV2hUAN`WKS^>xz;*(55VsX`7@6@lPDfj&BW3|x0-%?2>w=Rm<`4+sUi4~!Go zKx3OiXu{7;&H9{5fr^1@K|jyu^6wN10)e~LZ6(mwtU9q^uyAn4ISdq7TftFrgaeChZ1YoVZz1r9J>F$K4^PgS~OJZ>m(SEY7Pa`oJaL%N?c_o!*F=1fPriVP~-ibq0$ zRgWjOtkIz3zBNm+Nu_(${$ZrRPO$_P~JDWOR>?Lt;oIV*EnorWK-# zVf@5}@Dat}5r0JE&N3)QW+RyZ=q`0xWZ#>zP4mD9($oL1ZjE}G@0 zsDy|JfkFfl;#t`^9J6OHMrKx#s02<%zoSc*YOc4m9{TnN*=>IWg+EdV*8XcKSgYY$ zD}_R{NSY=TmsMiVj-7bp*=O-+w^R%fcVS>^G=_9zEP?e3!AKK@Ffd+TPmUAUAqr7G zUb@I7%!|AE{1E8nqqPSQKg( z_9(s{I|i#3%);JHD{*Sy7Mu~kl7S}<=+I#urw^;!kaza5$WPOOXM~#r3WWv=^tbiyDk{N3fi((*LUWlMHdm0*W&^ZGLGJ1dD!NmP z77ALM;J`pZAdgfIOjF*u(V#$3<7*5^%4j%u=NW3*v1rWy@bt*=+CDy z5CetAA`V|M?h@UHM)^Je4iwxeVSY@A=Zou5Bv8;!O1gk2xvW~iwPGf#G=1glE@`)dE$aj z&czQ9C~)>&#|~XoB~T$e@K3^lGZ(!?DbS@bla^R(&Q7*)fkKdf1j2$%UU{gvEu&u@ zg@xaJgDuPF;@Hl0xNv9((vR%IV_2$9*6e2n978Q&2F70$?10E(O2NNkRT3F!<$0 z6Ykc^>(}M$SSsBh|Aw%!>B&0N4?#u1W-E4BTBTr{p<0`woZzDYg(BUWiq)9#gvuyy%7%%A)fW_|q`K6>S4Jl3l>`inc7EZ0_IXbd6)A`v7& z2og91bGwvRfGPw71z))~eYGT-*Xx51@cL!Y@nOwEFg>Xw;^l_sB^Ib<`K-Y8MnsT5 z5@I9KU2dkSDc#Yp|4@wm^lMzcT7t{@WvG-Ja1}EVj#5w%Xh9S0C)C!mj)Cjo{LII?S_}1ehO2+`~)jzO~(FpOL1n;CR{wY9T@_JE5}Wd(w~9?gN%g2 zfWXHb0Rq90`&%fKwGC#q zP_TDvO%<}=q1Lz$twBZy3R;`M!!IzPa$tiLKd;#dtw*TCuXQ&R3crZ`Tw|c1H4Ah- zn3)h2(o$hu1;!evBCJZWflnLfpm6{T!C*_W`I=P{1`6C&nP*G@p=<8iLxBTtbJ~Dl z>q4yCU{!;e_$+GS!ZGBZJ*qB50kcRaxJ1T`gtq}@rY5ZmVauOYE$rN+K}S|Q?q9nM zyH_v9+WFJ5VA43u{Ni(r8a5nH4H$rK(XmJfi$XN7H~(-1aynB$DExhcv~rQF=GvfO zRKtVX8__{j!^7IV$Fw<1^4VhT(SbWkmfDAAbBL zvILhhDE$MnK%{;%?Twu75qWC@gLnv5f;Eq=EhhlKC zvW}6_7E}rp%Ebr0R#1Rd%jV&L7C27s0zB^0eLeYe2}Bn7+B;IP^tuP`1^&Kt-(ReA4eb}Gzwt?gv8i* zJn_g-eDu~ESTJQG*3Fx#6Mruq*d`Z*)9iH8G|n*X_@AM0N`P<@l~>M+QaG>j)(lZG zjZgQ3g1bh+xD4&)DclDN_OVSVi2G2+09^@gmbXRsZ7|OqJZHj^Jl>VNJ0(94IuA30 zj5G@c1%eZFRF&YU3AR=LTraN1b=nR5yS^E}*56PlsOn%QqJgQ-I_<1%u5e&f+15_& z4m$F={W%E(_n3L>Vrc7kp~*(nIZ)7LxYycdIJPd+bwlBb0fPmCx^q+x^1N6SUE#$O z>JD-u6I-Hnu>OV5=QD&h0Q2!H6#UDbUJwfb0*5$EkEhww56DZUmCod0^M0PzrxF3e5 z#9?4Uq(H&A5B=l9HS5qjl8azMksK0)2yq>P1qy7B<^UD?TWpJd_@Pet$N%$?#tr!9 zaugONny*hV!o%Z*(2)r64Mm9jVS)mJ<;@fY(yJ$4dU^!D`S>HunJ^B!RxHKEy<3qX zP{=&616hZ6;p&O~I>w9nZ8s1s6x{!EbJAb1?{oS_6e&1q%Of^DX=;?u34?rbYvWMiaoKVrX)}WDhoHQlD;D!Rh$}QV8x9gzqx2uMMM)!fE@CXG48>tis{CxTQ7K#g@ zN`a|K+6j}1a0eWz7U(`OMMi1SV&3@Abh`IzPdtv1g9dA(R00gpGb%+bn9Rd1Ie)jc(y=`h zehY_tp_I$T*r?xJ|A41`N6ro?kZPIif%+AOgxZZdJmohT&&g(DY=^+Az%>7h^v@bX00jvcf$x{IiVhaO~;2{%MDsK|CyN-}@= zF&!j-0I#lr;?spABq&VFLz5E|@Wimec<;5BFz4H^v1-nA9N)Yi8T$kVA{%p#>=qxB z)0p=o|FlsF?mBum6l_YeB)ts^)maxr8JIW%-3Nt34$GP^wJL@G2!+BcI-Hf(3I(bJ zjW4ih>8ijW-?$CVVKRq}hpw$)i3iRC;VylDf}!4V8K?|wyug8i1;S5_d~9%_(5L{p z%VMTFx(-eFpN1Cv+R%hwkXlAW$g0s&NHPPdZ?@pHk50@}Aq&q;(}YIG4+J=t3|pscqHlZWSmn#cb;p zs1z7i$UJOHpWR@vN`b1u)-UW^wFKLj%*E2l;{^<#;M3P$!As&s^i4=ad`P%JAqp{K zv7#ChA`=iHH!0sPf$;GRMAt5U=y80~2V>+5FJi~RgQyTtmkPiu8=D0R<_vPrO3ignP-hABe+LD(QfPn1 z{}dG5aImois)Q;5Lv_O~RLR9uQ{RNfmRmS=_8i`Ma}=H))*op}G3b{Ng}#YV`q(pq zyB)-53JpL?cpzf@eGt^Ov(|xgOg7blrgI)P%Qv*k%)?)eu7kH{5PZEu;Oh|xf6ri{ zv$zrBkHrXG2lnZXXP+31abw@biaFn5=ZXcmuxpDzVGA-3Z4=*8pm3b^3Hx+-3NM%f zxw9}Zr2|17bJx~$ZJA~tL?!UTtknP`fgmu*qfsf)g}4_8HWlf1A&lkdI5r*0#wx#J zM&+-eNe>7k^QIe;ioo27U1>g-`(WLMAH}rlkTC@wTE{(LCV_Pa3Ib*xH2cu3rkMyi z^grKdk=L8#-!wTNH;c=0Qy=d((VCpBL_KcG_ikaM6x|5tc~fPj0L*b^1PbmtingGm zDQ2=jxWUcWZ0=$QXz5h}f+z$wiWOv`ktNSg`Ll%rcV<+I@llN{@2{LkMV7Ozl|>_7`Cp(;Z0^*6V(FO+)w-D zBfR$Hqj-AI0CbCtL=yKk`1vDRR4RLH5+dT|nq*%@peWuzQ)Nqa+nHn7JakeRU5JMt z5*Okj&UiG#g*&k?g0nzG5wlS$EJ##uXc&@{lhL}nx zJ-6@LgMWSWE=G-f487t728jZO#Ax)3jYMBAx)3mQ3kyQBfFZ)iQx{{f4!pxdT<^v- zqmNRS@{0{9em)@z1&-Pd@C(!SRi=T012}XeTZT6j@b@yqT0MCUcVd>T zZ6}GD8paXvcAcSFb;v!GejeTk2?!LII7p{E4;eB9ufFyM4xf~3Q2suJ@^>u~SC0Y5 z3Q-Hy4R=s2hux?J1BH8yD#2zRem}tYmtqR-pk6N)W;DA>EiT+(lT#Ne1CwQ7 z1tZ5{k;49BL82CdgvwC?A?PJ?ZfKugc=P#@_+s>%m^N-Kc8E$iDYQ@Dvjtg)#eF!s z;~pqbDez80(}E>J!R0SG~%{SEug`cc7vVOsQohfcIV6ZLMKa0!3+6Ee-aFgGk=}@W~W+2L4 z)1B{eAKF@~Z*c!5dm%W&z956ND!zZ!@;+9``&yQB0p+;@0RcoYCp^ph zO{)+vb01{|OP!gP)YyU;0YNaFWx2Cyr_K{skx;mDa=*F|R11WH0^xwEZnVbu!tpI@ zaAfmJxv8wwH05ql3!4|t#?qd~gtAS$^nt&(0D+y5 zotT33RJXyCa~4@+F2;eE{se+|fV|Nhco~2oF6@+#6GXzX4E2S_9>v%{0WIh^SRzN@~2m~s!DQS5} zBNPM(Y>48(*8FVL)3hV7EuNqgh+I%`!@+EeZaYL43hrpW4KCVhLL2?3QlR_5Y=em( zn94;0;pb+$3{8&fU=F{6zyXAXf_;rj!N7u_H)YVa!_uZT`JC#Zxmtdf8UuwEf+vcuhFO$SiDpr^rA{AtGbRuM~-3QH{&t5 zPj3wBo{D}63Fs9UD`1F6_bB6w#WNKt(kmoT-Y_rvRu8FCu+2^!bV(R+3T}|70VB)& zbf$iQfFaZ;7>Qw`JdzW|#~Xp##~Tn zn`X?yCF3?&rO>uQzAbh@_rXHJ0>KRhzNXJBsb{>ChWcDEBd=Er7%D|+mFE!(a`@%_E|cfw@?KX6Fe(MaRoNM$7UZ=I z`MOMbJ@cZoIL0Yrq8lMf!9pQR)B@`j2!ykHw-|GvU}Fjv3P;3!U?yVU>SfrmR9uLe zlQI9B&+*mUui%}LkK^&)-H{p|j2@yGQX(Uf7!{59s2II*`G~8_Y(%iQyL`^x9JX51 zbT|-4w(0B-PJQ;!JtcG_d_1`{hFz522o2%-xJ118{43bDeLqTTZfNmRrJTbGIeV1~ zg&$j?z+j`bwlt+(@WO(_|6Ik`{(S!i3hpobJy>Y10=GgrCC){q71(#+Al`lZP4p8W z3`j{rKY>B-xH$9@wUEHs_ToB3hXm>#y+E<9T{>zlIES#Z$iq|K91gkkVO_Y7r~>~W zd84_zfvewq5h|ca430wIm_$6$w=dop`6NDg=?t1;cE^$5k3%~y3NBp-fLo z|Nom=1xkE{0?oPs1VOV29y%@qjfy~H2@En44pu4fd5e5rAaYB-2kpljCg4a&XeqSQ zP<2aOl$%blllvtI1?%cqccB#qZYyT(0%tn1=R@8XR(I-N2s8G+POPRd4^{I1Rp!&O zP;o6wfN)tXQy`HoP{@(T98nGOdbWK2ioA9uOa4ZeRWX#LpF3nROI}OVxkJoMFpS}JP zUU_sddPPSdRg`b9gm`p|jYDE|yxis4HOZMCVTkaLMwo9TygIQ_%7B3-&~%TxvVcjT z!0lz7IgGedXWcgC=Pf|tfLC8}k0YZo;>l;Qanml8U2oFC$Yzy-S*Jkba3hW_FzLx# zPVE8@znszfJ7!_5uCD*RDbBxdzk!1L@c-WH1_~_>6q<3ZpcscmDSYqIexc+*ew({)?X z!6MnhV=yQ&6;BUH!#mH9z$b6Nj5(9WW5c4^*tKdAPHta^3wyanY&|j#Y(%C&A^X@4 zRSR?@Smj12P%*fnKo`OmG-)41I%g>m3TIJvNyq2(=6% z78e*RVs^oRfb$lacC=7n#(@DhLg8N5;okWKRxMa{aF5%-c!DT|yW&3l%DZ}lGa!YB zmxtXs=)C(=9sKHwCD_=41&56@5DGsE5NIt8jbb-&Q_S62Y2pkG8dJE<;w7d^<>zF@ zqlKD zvgI`ab55REu7aT~^D;_AF_dJq6*Y0*Lhk7U$RQj~(uLTq#cUTDU=$$GxN5Ey3OZ+D zg)WgH7`S0-2gk!tpM=?8eT)gC-@=%eM&j|bUZN7@dW?-msz4zjCLUqI5uyNmHSQJa z6M?9JSozprp}?9On#RkxIL1zS4h%-uuAUlrp&{WIH0Tj5UA`9iW!FU+ z+%oe}2>}NP1`6`JlNGeFK5O?o*XX7hwZHgXo?Yb5Psv1Ta(_by^{s;af<;Bh zn<7vM)Hnh+Omh)u03w2e<==;(rx0{#x9%AA_#^o6m64e8@w-?yZ4x#voQ0h~%#%Ot z3S8K|4jFsa;o{zPxO8wME+5|N0Kq_kK(OwEf`M}Lj2Y|ITCZ^Flw4q^QOd<60)%n_ z0;OxkRZ81jy>Kh@c>&Y%alN<@b#mvY)$8z5x(SrZMlKuIfg{SC>C9rBXZU+iu!qb- z!3G?)U`e3B9S{E&_u(%0JTQiE!}$F?ymTMvHqeE*%b_Ya34|8hZJ~S6sE_Jiv=9pB zb=n>I+S@$*%vi8_ifX|LSXwZ&Xdu(XGS~}2INWlo8{IJl``ir#wIZ|WiX-Qm`5rl` z*?M_@t}6@(0%O;U3Pm{-2o&;Ael@QZ2o>_YOw>YoZmy`eJXFYId3H8RMJ*Ix7PXLZ z9!2SAag7UP&WTz$cNDp2kLbw$jKge>+9~d(S!GXS=GG*VgKr-*tKL1 z)_p$(^S=HJQ^$_Rm*PgeJYpDnB}OAIJP3*6MkL0^A}%UcuHR4uddW5H9g6UPD1-!r z>1K2qgN*FkVU~iW&Rsiu>Wl^-xz@XScnLf_5hB-qh=3tXT;zm=RDAlyH@K9QkE#X( z0rniVhr<2lE7&3>KDTGK{nz}F{nt>i|0$J1l|Z4gx*iwPuVCx8otQFp0)`Jw!+`Ed z=$#mgK1p%t85fJhuuzR9g!yyftCvDS!QkN`lFL(p5Ec-ktG2kv#g|!!E?nH{EuZt( z^;$8(fk;b?$4G(BJ1>sFxc6Sh{0X06?W`%-x>($YH4AWR>kqiNYc0|R443w;Ep$lXa`P*YH# zRxNg2q;Xw|K%s;q)OdF^jZ8O~inGSq35EB9z`70=0u~H5y=PqpJ`xai$O+>K|HGI9 zq0nGDBh`g)>J;oS6+3z9ZcDQ{{LOSHs1lm=wO?)$2>cx545s?gevTj8;VidQg*2kM zrUp$_qF9{l2V)8?l}6Fn-=jSY?C*I)fBymjLZM@P?qp*)b3xw2S}_ZQYJqK)fS2w9 zLBQvHjizA8HDKUlnJ9i@0{-9P%#;7%ra1Rr#Cd%HKEVV3TpU9q5U38Uas3>X0->;Z-gGRQ_$9vkcr?Bl{U*k|@(dpD-yPjz!jU3S zNR=C4YFwhY$58?W-qcu2!wpa|@bY9&1h=D$B49kh!%wG$brv^*>*g4U^cIys_nA8= zBZ9*b8XAtcxD>ql`g_=WL{v{jJ+3$05di#n4-_mY+8=7G9NgCaYyQZ7<39YJxmAL; zZ`M;SG@(MEke-=^A6Bfw)M*p&$dKMh>ydyS@exSt9xr}ZoGOK=06zqHa0!OMplfHL zWoMm%&#~;`fuZ6ago`g0qI(u#mi4Vf{)&K9<#q5 zgJn}c$NE{5uzk^V`6JH7nJvq4NuZFqM-;*V0%40>2wRbP&}1R9kD5WP%sx;pm_nxC zLZOJuUoy_1Bxaz7}zKX$7F>qZa=9C~8Xkwz)}Qg7OwS-51!ui0z?gM~cg{FsN|+W*|XrH7uM zzuRH}pwFAAFwEmGtbA;ZQy4JNbzr))xw=ZB!0d;{K&sl7)#~AI?=m>A)m_UsLuu98 z;Gmfa&Z8_X5hxT36p953g$i3*Rlb1(6`2JEp-`Gj7s5cHBqs~S*_Tn6l`deA$Mg*3 zU%G&+7tSK*>`COFVSmI4WFJ3<%ZCmjbN@bMsAAZ9&o(K7z(RpBh21|aR48m+I1|gJ zj2ETwJ|>KL8(+RP3a^bAib37t(IYlOuJITFM4a5X!Vw^coIMe~9Q+d!f#ARpjUR9m zdPh!QlIxr4b#HMsyye>GIynyyZ+Xs%&;AO9sK^+^$EV<_kuPD(?nB7PEk;R211hg` zRDp3N{s;yd7V)Qto%rEJyN10@I?PU zcz@J0nEcsjEcxy;teEj7Hq4obor|aA(5iVjy+xG5&Nax`y&jkMZ4?-6LdK!ZNI$d{ zmyhn$$xAGKb?+rq#lW}%C-_`Cfx?Vaa#4v|xO@)fIq7nzx-1Go+=gq}sN&27xpURz zT}3Uya8>Sb*GdEq1^~Ppauah)u^em`?-Z7(wXaZgLx5&gf`x%w9oST)jVGuJQGMOa zJzzzn98?|8&dx`t#98|v9Hqbm&&OW{2>-ryQ^D|)z~JXwEjnC`8{Yw79o`E*fuq8c&@hICO17epnTM$U=j$Ppmq9zQNOhNE&_91z8@%gk4BTnE-B z92A#tRU*k5ns`#&=z;8f8+1BZb8bToh ztJZG7v?-G@^6^K}zgIW(Nr^-MZiz^h!yFqFfDj)}N@9S816R2e_F+*Cen^f>Kuma~ z?usNWxpR-zUtGbZJ0bl85iRaV@1#V$F#HjG{@xpy_01<(K64z_&7FX43#VfDk{LL- zavn}?S}ssnD^S>Apm2at*n-R>+mU%}r`Eu;QN}&J*}4#nRu`n7L{X*yfvL#s3*tIl zK#54w(rmc^GcKT5q-o)W3&=lvPC#-2MHe$rnI(X@S|E2q@rwl(wEC+ei@6zE0a2=e z_#<^dvk2kPf`uylIoIP+57mgf8_L(!t zJ1y$qb6VWeO6hoZc-6J9pBFdIGx6lAl4Gf$I z1nafT6>{F9e!0b(^$VN`?JJ-66_=TFM7%idS%AU?GzqbZNJvUW@BV}E>9`3fD6d0t zRU`7N8c^PNTk94Gh4u&g??AzAe*+3PO;eOudS)J$|F9Oz7B9xA=bk}d)+uxoil!za zDLhn{yM%aoA=sODe_v4pU4@F?;&VkHCQ|$`Uqey#yE<~%rKi>_Fz6EOZ#u|&rzGOF zkx$~Q(Qo7XuRq4p@4mq11(UI7*(~f?ItxeEEEM-)MH>|M8z`h7+^kTza(uTy!7O~S zOG){>wUwG<*)E+#35T}onz&Gg+!4voyY|ScRG$Haymor0 ze0~p`pmvKwEueesv>?%+?heXw0Na*tbETuo0K&LLAd=+b z9LZ+pG-|1=JhI*T+ztky*6T%cX6F`NMtgM2dYs<1T|jdH7sW5VbYQ<+WQXMOfPmt# zJRX$S4~Xx2+))$fQ6v{RFZlAySM(6L@09i+Sy;3}L;UNO%3|Fod9$)e4r8%c32s-K zAfy%z)l|10TuORoB7Ui>H`xHD4V}IS9k#+l?(|6Txw;tyhkTvg8KJWo7_*Rn$N7(p z8)#P~hpbexCZSsE6Wq$6ecZsU5G)AXihwGrDm%xe0@CHZzkuu$C&k4RB_MbGywhjo zPJasL_U^;pH5`|=5eL`HdoNJf^}|XzoJ+84`3me^$pg$rrtLVsWvkrjH)GTMh1fWE zzFZfJeC`Yn`KH6!+5mw@ zq!a}P*blH|%6P1sJqg>zb=be`dmLG}2!}T=mOtzY@e|j|onZsg1qvBr z7ez5-9Nw-_$Ub3!;NDhz?d%b)k>{Y#OMABAL|GW+{%#$NN?;#s0NR zabUwT>|MQBFBn$9v5NB0`qgqVZ4_UW5aGqPPoZ&c&u+O>Z5LP^6rc8lxEd_Yz1(_N zBqVr1c*oScC)0KIkP!+Mg}7N&D0gT9LzRKU&2nd&QCS%p1rW{7VP$n82cWQ^=vGCQ zs)$=6^?8S^FyeFh8E$ealrt`l zMOX?mr-HFP6x=Z2@67ih_mq+MR0Fgt$BxS#eJ>7e+=P8=*Wtj%bvU+tGY)OufL(H^ z*Uz7aoy(SqtGE#BXV1pAMT@X`-aG-o9PD1c90ykOZ?pn?dzHtHS-&TQ1eC4qsKIWySkxDah%ScF{;6t*m$jdgRT>vGyj zW8T4-7oWyEFFb`;M?R)dNb8n@9tnwv4h?8Eo{>YF&LzTn29z z<^=>HHYx#0@!b#?l_cspP98-m#HFBb-$5Aj@fSFgnTx#2I+QfrLK%%E&vG-?@oawr z41Z)6683!atp8~!STNXcR^7l-OJ`+VUMWtU&cMP2i}9jBVMzZpr1k8M0Ro07@w5E> z0yN+fDE<{`$t}CFQ85Y!kFJD(<2G>oT1UQtjNZx{D{o+MU?5^*BJkv}LHO63qcHum zkFa9KL~Ncv6?>P?#F3SAacuo!9NoMWXLhX?2nYy7A*734+_w(r_pTGU$R)CSkaf(A z=*m83nxn3XWggs#FiS0{xvCYAbWeerw`PeIWv7L+MqMWxt?q-XyZSf-Pmy7lImd&`ZZ=c@zcmOUP zJfw%l=zW;kndwDhdD? zclfcwls>mrKgwez4|uI+D(psTZK86FBjR;(vt|LtYhI=fysg^ z`59WujWv~VlzTo}O89d*H3-EFCKd?@u4#NA|LjG9?^$`@#s8Pz{q(NgIJa-VLg47u zt=PYQo!pIoz`ixBMZ#~D!?zgAr%lEBd2_^%pNq9~zL)p>J6-X%b>UoWnll3{zWo{- zW=_SHc_QhjO~JaE{2R@|#@RFF^=Vi?^E<4XG6_FS`dS_*V%2wI(x7L446d)$G=)Gbxh1Yvnk1J_oB;uEp~Lg~4h4(7(4Rg}%KJ9vrGbhzzAN z5P1|DffVtVqQYa14<&8_bGF>(OVe$=-of&@5Pc(q0|Sr{7ljdz48+(sU&O4hKEmqn zC&>Yvi37`LK0?28uW=j1IDF;& z_O;k6cY!_PGwxn88yn=%Zx;yc5&-O&Cl}X@NmwHn)cToIu=tzLv3&A4xws}^wY*OI zVanH7I&mCUe)p|>-8A|4v#@K)GQF$q{$Yj4+~op~rSkC)0uCyS)goheii!|5v7bOX zjO?Swk$e0kuAMrAl1mp*%8_Hv1fsfp0Yu?t)E5gNN^(rUL@9lEqb6=Ti(=TO?9Ntj z?b}b)Jmj_aOlqsc*=c6Vk7scrrC>7W-=8|>fn5w9w;`8rXu>zY!-mjT62kRHi#|Am*Yv#-nX+8t% z=gh`NvE@@HW0~06Su?S9k*EU!!;(qk{(dUfO_PKE?F4L^HC-U_jr^^q zVzmHZy&T&0^0-+bvPo?1UadW;y6PXMcwsa?!1y`n7!i zTRG_8Dik(|ZTx{RZpAwu!9VCKv9W#VmGNiZ!!lW5u)?Sn}-@ zwZ)SrW5I+;qBMB%i~JRE9N)YZC%0~wJJdFu+qDN72M)>4DUje`+4Cn*l5twww5xM3 z;<~d#f_(zq=zL4=*taW+^|7U-7&leg^YE78wgZPd9RKdjRBVIAy=Ea{LBU7sGT8Wr zac9gbxaRUgogvv&%2MsCIz389%!#V0%1#%kG1z(*xhGF(fRHhkv+}#0+O`8nH;8Mn ziZOu&*dVfh^~@RazD&T}@n7P*u^(dk*pD#p%g?dkt1qxfUYqyD=a}~47&*)ziz_e< z+eHJbA`>EJ0VAwi$8h-ftORW6nE36eLteP+mOTPRRE606-HD8azhHnM%qA<44 z;NMpM2ID`+4926r`~*wCk^f`dCs;4QKO%0$87>~)WPo6yU|k48VdskZ*d|cm8hgeS z7JWM&pT0dxlUx}u)Nuig-RX_ zH2NVlC{)iyRCuJ$8jTPzL`TGp+ir3yBgF|O8nK_MYjM9`OiwBMU zC#B$40PU^)*ZyTFSmWLYLV-URp^%Yz4g2;U!0311!(eeA2K4DAZbUyF91$&&s#lNR zNJ>aXye{sHMOaXzuJ<-$vVFvLV5*X{^SyM)iVj-wrrQyM)Z_#__vA2)``}H?{pMd- zIddF#ES`>o%je;k`2NS%FTt@b%hY{1w}%R0y(orFxUgpvGPOowE3VKEZpWpao76Q} z^(`-`|Bt!1iq3M&vWENi>(N$q{2UGkcL^aO2{97fCBY%VAt4C~L`VpMIB|D(cXxLe z9FA4BeK-I3$M~mkBi zAHc`X9zOOCT9(QP~RbpeXFC%#DX4^);N9*K&SG&C0!@O2lW zqr6D<7947-Qtig121A&G^lcDu_+fI8jygogrE^XU6BNej$O{@@n!YY7!PYwYViutN zy4DsSg$fu*GLZxbwI>Uj)a->xR#TF?EiLN%@oKyWFGn?^<)U$y1u zHxPQy5s|Jgi1%_-v{abj|mKqk@m!sK<=fksXm?x93uGlq6iL2?hlYnP{?@v z2r&eVXm=Mxxj7@i?j{25Zz9t94kDdyA)Vk~MaQpBjZ#yi7RMD-u0mXhiYw&CJVQF= zWCFn`{D}u#ZeE1lWh=4;D>YW&ki{-ahgI0XO>iyO_NuQ}smw+dbo^YpGJUPa&q+){ zOxS`=?xqL_P>R=qSxZTTvz{y7W~bAXH|eH8%;x;em+ra74tt z8;EzaCjbx_9yue)!vQh(xLEF9L-Ipse$5rR+>z2AKU5Sn!|yS7tcMB|1Q4PK8u0{* z1b#2zt{rzedwA2~-HGgOS8w2ny%St6Ux72t)%F_hU$TYWsnc*gdjU`G+(m$s3&K1+ zNs}KdR*^&YP#m9tlB5L6plmvpbPFmZpi%17ma1$^Y5F3KWM7ayyrm<)Cw2dU?BPcR z3{rWiZT$MX2~pZ)D6QjbZSnVhlpv=3u7Jy&roFuOgh6UHr1V+lT}p$Lm!sXPbJD!@ zJm_myy%J^qLQjpv3bNJfFN%-ndy1e0@T0^cU_PMKa(loX+8IHPcj0&EHlEzN0naO! z;CAU8JjBkQg*zS0+x8;-ZeB&;otyB#bsd2OjTru3g1akn0-mBEERew9jZ`wWOgd>+ z@DpVvF(D|5c!o>@M;d8<5~V~Ur9=$bM1+eY!U-0U_nqK(>l%V@T}FhxEux)nAUnW= z%<&m2(qa`LXyXbh*cczCEI%O(8Bsw<2ziPqGDbH$TLlcaFIeHqsgtleco2qLbqETp z2nwsQnxL?Bq3Mc@n*03kW%Bel3J~Np;+wCQD7GLQ<`&B7RV&xw(@!*cI?E}w4GoO3 z!_XAoe!*z(9aSw5KV&j4^lDUNiF>LGvmk-;uTA1D@jo?h0f9p z^p)q(ve8?XrP7}z29f0Dj1b3bh;q4wNSB*PeCUXn2X_#7>k^{w-9h?eckVbY)T16K z3=1G_rA Asa~`5CtQKx;EaMdiIe!Vm#d7MQuJCt$ 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 Date: Tue, 3 Dec 2024 12:42:19 +0900 Subject: [PATCH 432/563] =?UTF-8?q?fix:=20=EA=B8=80=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=EC=9E=90=20=EC=88=98=EC=A0=95=20=EC=82=AC=ED=95=AD=20(#213)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/notices/query/service/NoticeServiceImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java index b1db0e8b..8e0366ab 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java @@ -45,6 +45,7 @@ public Page findNotices(Pageable pageable, SearchDTO searchDTO) { List notices = (List) redisTemplate.opsForValue().get(cacheKey); if (notices == null) { System.out.println("데이터베이스에서 데이터 조회 중..."); + System.out.println(searchDTO.getMemberId()); notices = noticeMapper.findNotices(offset, size, searchDTO); if (notices != null && !notices.isEmpty()) { // 데이터가 있을 때만 캐싱 redisService.setKeyWithTTL(cacheKey, notices, 30 * 60); // 캐싱 시 동일 키 사용 From b85c2f9f0942cad7751812318835fff6ecea7993 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Tue, 3 Dec 2024 14:42:18 +0900 Subject: [PATCH 433/563] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EB=B3=84=20?= =?UTF-8?q?=EA=B2=BD=EB=A0=A5,=20=EC=9E=90=EA=B2=A9=EC=A6=9D,=20=ED=95=99?= =?UTF-8?q?=EB=A0=A5,=20=EA=B0=80=EC=A1=B1=20=EB=8D=94=EB=AF=B8=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=83=9D=EC=84=B1(#216)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/dataloader/Initializer.java | 615 ++++++++++++++++++ .../global/dataloader/MemberInitializer.java | 301 --------- 2 files changed, 615 insertions(+), 301 deletions(-) create mode 100644 src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java delete mode 100644 src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java diff --git a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java new file mode 100644 index 00000000..109cfdf7 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java @@ -0,0 +1,615 @@ +package stanl_2.final_backend.global.dataloader; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Component; +import org.springframework.util.StreamUtils; +import org.springframework.web.multipart.MultipartFile; +import stanl_2.final_backend.domain.career.command.domain.aggregate.entity.Career; +import stanl_2.final_backend.domain.career.command.domain.repository.CareerRepository; +import stanl_2.final_backend.domain.certification.command.domain.aggregate.entity.Certification; +import stanl_2.final_backend.domain.certification.command.domain.repository.CertificationRepository; +import stanl_2.final_backend.domain.education.command.domain.aggregate.entity.Education; +import stanl_2.final_backend.domain.education.command.domain.repository.EducationRepository; +import stanl_2.final_backend.domain.family.command.domain.aggregate.entity.Family; +import stanl_2.final_backend.domain.family.command.domain.repository.FamilyRepository; +import stanl_2.final_backend.domain.member.command.application.dto.GrantDTO; +import stanl_2.final_backend.domain.member.command.application.dto.SignupDTO; +import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; +import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.Member; +import stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +@Slf4j +@Component +@RequiredArgsConstructor +public class Initializer implements ApplicationRunner { + + private final AuthCommandService authCommandService; + + private final MemberRepository memberRepository; + private final CareerRepository careerRepository; + private final CertificationRepository certificationRepository; + private final EducationRepository educationRepository; + private final FamilyRepository familyRepository; + + + @Override + public void run(ApplicationArguments args) throws Exception { + + // 우리 계정 + createOrUpdateMember( + "god", + "god", + "신하늘", + "god@stanl.com", + 0, + "MALE", + "000000-0000000", + "010-0000-0000", + "서울 동작구 보라매로 87", + "시스템 관리자", + "중졸", + "REGULAR", + "미필", + "한국은행", + "000-0000-000000-00000", + "CEN_000000000", + "ORG_000000000", + "GOD", + loadImage("god.png") + ); + + // 심사위원 1 계정 + createOrUpdateMember( + "god1", + "god1", + "이름1", + "god@stanl.com", + 0, + "MALE", + "000000-0000000", + "010-0000-0000", + "서울 동작구 보라매로 87", + "심사위원", + "중졸", + "TEMPORARY", + "미필", + "한국은행", + "000-0000-000000-00000", + "CEN_000000000", + "ORG_000000000", + "GOD", + loadImage("god.png") + ); + + // 심사위원 2 계정 + createOrUpdateMember( + "god2", + "god2", + "이름2", + "god@stanl.com", + 0, + "MALE", + "000000-0000000", + "010-0000-0000", + "서울 동작구 보라매로 87", + "심사위원", + "중졸", + "TEMPORARY", + "미필", + "한국은행", + "000-0000-000000-00000", + "CEN_000000000", + "ORG_000000000", + "GOD", + loadImage("god.png") + ); + + // 심사위원 3 계정 + createOrUpdateMember( + "god3", + "god3", + "이름3", + "god@stanl.com", + 0, + "MALE", + "000000-0000000", + "010-0000-0000", + "서울 동작구 보라매로 87", + "심사위원", + "중졸", + "TEMPORARY", + "미필", + "한국은행", + "000-0000-000000-00000", + "CEN_000000000", + "ORG_000000000", + "GOD", + loadImage("god.png") + ); + + Random random = new Random(); + String[] positions = {"Staff", "Manager", "Director", "Executive"}; + String[] grades = {"A", "B", "C", "D"}; + String[] jobTypes = {"REGULAR", "TEMPORARY"}; + String[] militaryStatus = {"fulfilled", "exemption", "unfulfilled"}; + String[] genders = {"MALE", "FEMALE"}; + String[] roles = {"EMPLOYEE", "ADMIN", "DIRECTOR"}; + String[] lastNames = {"김", "이", "박", "최", "정", "강", "조", "윤", "장", "임"}; + String[] firstNames = {"민수", "지훈", "서연", "예준", "하은", "도현", "지원", "유진", "현우", "수아"}; + String[] addresses = { + "서울특별시 강남구 테헤란로", + "부산광역시 해운대구 센텀중앙로", + "대구광역시 수성구 범어로", + "인천광역시 남동구 미래로", + "광주광역시 서구 상무대로", + "대전광역시 유성구 대덕대로", + "울산광역시 남구 번영로", + "경기도 성남시 분당구 판교로", + "강원도 춘천시 중앙로", + "충청북도 청주시 상당구 상당로" + }; + + + // 회원 및 역할 생성 로직 + for (int i = 1; i <= 94; i++) { + int centerId = random.nextInt(10) + 1; + int orgId = random.nextInt(10) + 1; + String sex = genders[i % 2]; + String name = lastNames[random.nextInt(lastNames.length)] + firstNames[random.nextInt(firstNames.length)]; + String address = addresses[random.nextInt(addresses.length)]; + + + createOrUpdateMember( + "user" + i, + "pass" + i, + name, + "user" + i + "@example.com", + 20 + random.nextInt(30), // Random age between 20-49 + sex, + String.format("123456-%07d", 1000000 + random.nextInt(9000000)), + "010-1234-567" + i, + address, + positions[random.nextInt(positions.length)], // Random position + grades[random.nextInt(grades.length)], // Random grade + jobTypes[random.nextInt(jobTypes.length)], // Random job type + militaryStatus[random.nextInt(militaryStatus.length)], // Random military status + "Bank" + centerId, + "123456789" + i, + String.format("CEN_%09d", centerId), + String.format("ORG_%09d", orgId), + roles[random.nextInt(roles.length)], // Random role + loadImage("default.png") + ); + } + + + // 영업 관련 경력 + String[] salesCareers = { + "영업 대표", + "지역 영업 관리자", + "계정 관리자", + "영업 컨설턴트", + "사업 개발 관리자", + "영업 코디네이터", + "영업 지역 관리자", + "영업 엔지니어", + "내부 영업 대표", + "주요 계정 관리자", + "전국 영업 관리자", + "영업 이사", + "소매 영업 사원", + "제약 영업 대표", + "자동차 영업 컨설턴트", + "영업 분석가", + "현장 영업 대표", + "선임 영업 임원", + "채널 영업 관리자", + "영업 운영 관리자", + "기술 영업 대표", + "영업 교육 담당자", + "디지털 영업 전문가", + "광고 영업 임원", + "미디어 영업 컨설턴트", + "B2B 영업 전문가", + "전자상거래 영업 관리자", + "기업 영업 관리자", + "리드 생성 전문가", + "수출 영업 관리자", + "프랜차이즈 영업 컨설턴트", + "보험 영업 대리인", + "금융 영업 상담사", + "소프트웨어 영업 대표", + "SaaS 영업 관리자", + "제품 영업 전문가", + "영업 계정 관리자", + "영업 개발 대표", + "부동산 영업 대리인", + "건설 영업 임원", + "헬스케어 영업 컨설턴트", + "산업 영업 대표", + "도매 영업 관리자", + "영업 관계 관리자", + "클라이언트 참여 전문가", + "영업 협상가", + "영업 리드 코디네이터", + "럭셔리 상품 영업 상담사", + "호스피탈리티 영업 임원", + "영업 지역 계정 임원" + }; + + // Career(경력) 저장 + for (int i = 1; i <= 100; i++) { + String memberId = String.format("M%09d", i); + for (int j = 0; j < 4; j++) { + Career newCareer = new Career(); + newCareer.setEmplDate(getRandomEmploymentDate()); + newCareer.setResignDate(getRandomResignationDate(LocalDate.parse(newCareer.getEmplDate(), DateTimeFormatter.ofPattern("yyyy-MM-dd")))); + newCareer.setName(salesCareers[random.nextInt(salesCareers.length)]); + newCareer.setMemberId(memberId); + careerRepository.save(newCareer); + } + } + + String[][] salesCertifications = { + {"영업관리사", "한국영업협회"}, + {"마케팅관리사", "대한마케팅협회"}, + {"국제판매전문가", "국제세일즈인증원"}, + {"고객관계관리(CRM) 자격증", "CRM연구소"}, + {"광고판매전문가", "한국광고협회"}, + {"유통관리사", "대한상공회의소"}, + {"물류관리사", "한국물류협회"}, + {"세일즈포스 인증 전문가", "Salesforce"}, + {"디지털마케팅 전문가 자격증", "한국디지털마케팅협회"}, + {"상담판매 자격증", "한국상담판매협회"}, + {"고객서비스전문가", "한국CS관리협회"}, + {"리테일 영업 자격증", "대한리테일협회"}, + {"보험판매 자격증", "대한보험협회"}, + {"제약영업 자격증", "한국제약협회"}, + {"B2B 영업전문가", "한국B2B영업협회"}, + {"자동차판매전문가", "한국자동차판매연합회"}, + {"부동산판매 자격증", "대한부동산협회"}, + {"프랜차이즈 관리 자격증", "프랜차이즈산업협회"}, + {"광고 및 프로모션 자격증", "한국프로모션협회"}, + {"국제 무역영업 자격증", "한국무역협회"}, + {"비즈니스 협상 자격증", "한국협상전문가협회"}, + {"제품관리 전문가", "한국제품관리연구소"}, + {"판매심리학 자격증", "한국심리학회"}, + {"금융상품판매 자격증", "한국금융협회"}, + {"의료기기 영업 자격증", "대한의료기기산업협회"}, + {"공급망 관리 자격증", "한국공급망관리협회"}, + {"소비자행동 분석 자격증", "대한소비자행동분석협회"}, + {"브랜드 관리 자격증", "한국브랜드관리협회"}, + {"전자상거래 마케팅 자격증", "대한전자상거래협회"}, + {"기업 대기업 영업 자격증", "대한기업영업협회"}, + {"퍼포먼스 마케팅 자격증", "한국퍼포먼스마케팅협회"}, + {"클라이언트 관계 관리 자격증", "한국클라이언트관리협회"}, + {"RFP(제안서 작성) 전문가", "한국RFP협회"}, + {"SaaS 판매 전문가", "한국SaaS협회"}, + {"클라우드 솔루션 판매 자격증", "한국클라우드산업협회"}, + {"헬스케어 판매 전문가", "대한헬스케어산업협회"}, + {"텔레마케팅 자격증", "한국텔레마케팅협회"}, + {"제품 발표 및 데모 자격증", "대한제품발표협회"}, + {"전시회 및 이벤트 영업 자격증", "한국전시산업협회"}, + {"기술 영업 전문가", "대한기술영업협회"}, + {"대리점 관리 자격증", "한국대리점관리협회"}, + {"국제 시장 개발 자격증", "국제시장개발연구소"}, + {"영업 전략 기획 자격증", "한국영업전략기획협회"}, + {"신사업 개발 자격증", "대한신사업개발협회"}, + {"리더십 및 팀 관리 자격증", "한국리더십센터"}, + {"상담 영업 전문가", "한국상담영업연구소"}, + {"호스피탈리티 및 관광 영업 자격증", "한국관광협회"}, + {"에너지 산업 영업 전문가", "대한에너지산업협회"}, + {"IT 영업 전문가", "한국IT영업협회"}, + {"소셜 미디어 마케팅 자격증", "대한소셜미디어마케팅협회"} + }; + + // Certification(자격증) 저장 + for (int i = 1; i <= 100; i++) { + String memberId = String.format("M%09d", i); + for (int j = 0; j < 4; j++) { + Certification newCertification = new Certification(); + newCertification.setAcquisitionDate(getRandomEmploymentDate()); + int n = random.nextInt(salesCareers.length); + newCertification.setAgency(salesCertifications[n][1]); + newCertification.setName(salesCertifications[n][0]); + newCertification.setScore(String.valueOf(random.nextInt(101))); + newCertification.setMemberId(memberId); + certificationRepository.save(newCertification); + } + } + + // 전공 + String[] salesMajors = { + "마케팅학", + "광고홍보학", + "경영학", + "국제경영학", + "판매관리학", + "고객관계관리학", + "유통물류학", + "상업교육", + "비즈니스 커뮤니케이션", + "디지털 마케팅", + "국제무역학", + "브랜드 관리학", + "소비자 행동 분석학", + "비즈니스 전략학", + "리테일 매니지먼트", + "판매 및 협상학", + "프랜차이즈 관리학", + "제품 관리학", + "광고 및 프로모션학", + "세일즈 엔지니어링", + "부동산 판매학", + "서비스 마케팅", + "헬스케어 영업학", + "전자상거래학", + "금융 영업학", + "기술 영업학", + "공급망 관리학", + "퍼포먼스 마케팅", + "B2B 영업학", + "에너지 산업 영업학", + "커뮤니케이션학", + "경제학", + "사회학", + "심리학", + "경영정보학", + "홍보학", + "미디어학", + "IT 비즈니스", + "국제관계학", + "고객 서비스 관리학", + "인적 자원 관리학", + "행동 과학", + "비즈니스 분석학", + "창업학", + "리더십 학", + "데이터 분석학", + "디자인 씽킹", + "공공 관계학", + "행동 경제학", + "프로젝트 관리학" + }; + + // 대학 + String[] universities = { + "서울대학교", "연세대학교", "고려대학교", "성균관대학교", "한양대학교", + "서강대학교", "중앙대학교", "경희대학교", "이화여자대학교", "한국외국어대학교", + "건국대학교", "동국대학교", "서울시립대학교", "숙명여자대학교", "숭실대학교", + "광운대학교", "명지대학교", "국민대학교", "세종대학교", "가톨릭대학교", + "홍익대학교", "단국대학교", "아주대학교", "인하대학교", "한국항공대학교", + "경기대학교", "가천대학교", "서울과학기술대학교", "한성대학교", "서울여자대학교", + "백석대학교", "상명대학교", "한남대학교", "동아대학교", "부산대학교", + "울산대학교", "부경대학교", "조선대학교", "전남대학교", "전북대학교", + "제주대학교", "포항공과대학교", "한동대학교", "울산과학기술원", "경북대학교", + "계명대학교", "영남대학교", "대구대학교", "대구가톨릭대학교", "안동대학교", + "청주대학교", "충북대학교", "충남대학교", "한서대학교", "공주대학교", + "강원대학교", "춘천교육대학교", "한국교통대학교", "목포대학교", "목포해양대학교", + "순천대학교", "순천향대학교", "원광대학교", "남서울대학교", "상지대학교", + "강릉원주대학교", "한국해양대학교", "창원대학교", "인제대학교", "동의대학교", + "부산외국어대학교", "신라대학교", "경남대학교", "창신대학교", "고신대학교", + "광주대학교", "호남대학교", "동신대학교", "전주대학교", "배재대학교", + "목원대학교", "백석문화대학교", "한밭대학교", "세명대학교", "중부대학교", + "한국산업기술대학교", "경운대학교", "대진대학교", "한라대학교", "제주국제대학교", + "협성대학교", "평택대학교", "가야대학교", "강남대학교", "건양대학교", + "경동대학교", "경민대학교", "경북과학대학교", "경인여자대학교", "경인교육대학교" + }; + + // Education(학력) 저장 + for (int i = 1; i < 100; i++) { + String memberId = String.format("M%09d", i); + Education newEducation = new Education(); + newEducation.setEntranceDate(getRandomEmploymentDate()); + newEducation.setGraduationDate(getRandomResignationDate(LocalDate.parse(newEducation.getEntranceDate(), DateTimeFormatter.ofPattern("yyyy-MM-dd")))); + newEducation.setMajor(salesMajors[random.nextInt(salesMajors.length)]); + newEducation.setName(universities[random.nextInt(universities.length)]); + newEducation.setScore(String.valueOf(2.0 + (Math.random() * (4.5 - 2.0)))); + newEducation.setMemId(memberId); + educationRepository.save(newEducation); + } + + // 관계 + String[] familyRelations = { + "아버지", "어머니", "형", "누나", "남동생", + "여동생", "할아버지", "할머니", "외할아버지", "외할머니", + "삼촌", "이모", "고모", "작은아버지", "작은어머니", + "큰아버지", "큰어머니", "사촌형", "사촌누나", "사촌동생", + "조카", "손자", "손녀", "시아버지", "시어머니", + "장인", "장모", "매형", "매제", "형부", + "올케", "처남", "처형", "형수", "제수", + "아들", "딸", "손자", "손녀" + }; + + + LocalDate randomBirthDate = LocalDate.of( + ThreadLocalRandom.current().nextInt(1980, 2024), // 1980년부터 2023년까지의 무작위 연도 + ThreadLocalRandom.current().nextInt(1, 13), // 1월부터 12월까지의 무작위 월 + ThreadLocalRandom.current().nextInt(1, 29) // 1일부터 28일까지의 무작위 일 (안전하게 28일까지 설정) + ); + + String randomPhone = String.format("010-%04d-%04d", + (int)(Math.random() * 10000), + (int)(Math.random() * 10000) + ); + + // Family(가족) 저장 + for (int i = 1; i <= 100; i++) { + String memberId = String.format("M%09d", i); + for (int j = 0; j < 4; j++) { + // 주민등록번호 생성 + String birthDateStr = randomBirthDate.format(DateTimeFormatter.ofPattern("yyMMdd")); // YYMMDD 형식 + String genderDigit = Math.random() < 0.5 ? "1" : "2"; // 성별은 1(남) 또는 2(여) + String randomNumbers = String.format("%04d", (int)(Math.random() * 10000)); // 임의의 4자리 번호 + String checkDigit = String.format("%d", (int)(Math.random() * 10)); // 검증용 마지막 자리는 0~9 + + // 주민등록번호 만들기 + String identNo = birthDateStr + genderDigit + randomNumbers + checkDigit; + + Family newFamily = new Family(); + newFamily.setName(lastNames[random.nextInt(lastNames.length)] + firstNames[random.nextInt(firstNames.length)]); + newFamily.setRelation(familyRelations[random.nextInt(familyRelations.length)]); + newFamily.setSex(Math.random() < 0.5 ? "MALE" : "FEMALE"); + newFamily.setBirth(birthDateStr); + newFamily.setPhone(randomPhone); + newFamily.setDie(Math.random() < 0.5 ? true : false); + newFamily.setDisability(Math.random() < 0.5 ? true : false); + newFamily.setIdentNo(identNo); + newFamily.setMemId(memberId); + + familyRepository.save(newFamily); + } + } + + + } + + private String getRandomEmploymentDate() { + Random random = new Random(); + + // Year range: 2015 - 2021 + int year = 2015 + random.nextInt(7); // Random year from 2015 to 2021 + int month = 1 + random.nextInt(12); // Random month from 1 to 12 + int day = 1 + random.nextInt(28); // Random day (safe max of 28) + + LocalDate employmentDate = LocalDate.of(year, month, day); + return employmentDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); + } + + private String getRandomResignationDate(LocalDate employmentDate) { + Random random = new Random(); + + // Maximum duration for resignation: 0 to 365 days after employment + int daysToAdd = random.nextInt(366); // Add between 0 and 365 days + + LocalDate resignationDate = employmentDate.plusDays(daysToAdd); + return resignationDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); + } + + private MultipartFile loadImage(String fileName) throws IOException { + // 리소스 디렉토리에서 파일 로드 + ClassPathResource resource = new ClassPathResource("image/" + fileName); + + // 파일 내용을 byte 배열로 읽기 + byte[] fileContent = StreamUtils.copyToByteArray(resource.getInputStream()); + + // MultipartFile 익명 구현체 생성 + return new MultipartFile() { + @Override + public String getName() { + return fileName; + } + + @Override + public String getOriginalFilename() { + return fileName; + } + + @Override + public String getContentType() { + try { + // 파일의 MIME 타입 결정 + return Files.probeContentType(resource.getFile().toPath()); + } catch (IOException e) { + return "application/octet-stream"; // 기본 MIME 타입 + } + } + + @Override + public boolean isEmpty() { + return fileContent.length == 0; + } + + @Override + public long getSize() { + return fileContent.length; + } + + @Override + public byte[] getBytes() throws IOException { + return fileContent; + } + + @Override + public InputStream getInputStream() throws IOException { + return resource.getInputStream(); + } + + @Override + public void transferTo(File dest) throws IOException, IllegalStateException { + Files.write(dest.toPath(), fileContent); + } + }; + } + + + private void createOrUpdateMember(String loginId, + String password, + String name, + String email, + int age, + String sex, + String identNo, + String phone, + String address, + String position, + String grade, + String jobType, + String military, + String bankName, + String account, + String centerId, + String organizationId, + String role, + MultipartFile imageUrl) throws Exception { + + // db에 정보가 있는지 확인 + Member existingMember = memberRepository.findByLoginId(loginId); + if (existingMember == null) { + // Create the user + SignupDTO signupDTO = new SignupDTO(); + signupDTO.setLoginId(loginId); + signupDTO.setPassword(password); + signupDTO.setName(name); + signupDTO.setEmail(email); + signupDTO.setAge(age); + signupDTO.setSex(sex); + signupDTO.setIdentNo(identNo); + signupDTO.setPhone(phone); + signupDTO.setAddress(address); + signupDTO.setPosition(position); + signupDTO.setGrade(grade); + signupDTO.setJobType(jobType); + signupDTO.setMilitary(military); + signupDTO.setBankName(bankName); + signupDTO.setAccount(account); + signupDTO.setCenterId(centerId); + signupDTO.setOrganizationId(organizationId); + + authCommandService.signup(signupDTO, imageUrl); + + // Grant role to the user + GrantDTO grantDTO = new GrantDTO(); + grantDTO.setLoginId(loginId); + grantDTO.setRole(role); + authCommandService.grantAuthority(grantDTO); + + log.info("{} 유저를 {} 역할로 생성합니다.", loginId, role); + } else { + log.info("{} 유저 정보가 이미 존재합니다.", loginId); + } + } +} diff --git a/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java b/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java deleted file mode 100644 index e733dd0d..00000000 --- a/src/main/java/stanl_2/final_backend/global/dataloader/MemberInitializer.java +++ /dev/null @@ -1,301 +0,0 @@ -package stanl_2.final_backend.global.dataloader; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.ApplicationArguments; -import org.springframework.boot.ApplicationRunner; -import org.springframework.core.io.ClassPathResource; -import org.springframework.stereotype.Component; -import org.springframework.util.StreamUtils; -import org.springframework.web.multipart.MultipartFile; -import stanl_2.final_backend.domain.member.command.application.dto.GrantDTO; -import stanl_2.final_backend.domain.member.command.application.dto.SignupDTO; -import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; -import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.Member; -import stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.util.Random; - -@Slf4j -@Component -public class MemberInitializer implements ApplicationRunner { - - private final AuthCommandService authCommandService; - private final MemberRepository memberRepository; - - @Autowired - public MemberInitializer(AuthCommandService authCommandService, - MemberRepository memberRepository) { - this.authCommandService = authCommandService; - this.memberRepository = memberRepository; - } - - @Override - public void run(ApplicationArguments args) throws Exception { - - // 우리 계정 - createOrUpdateMember( - "god", - "god", - "신하늘", - "god@stanl.com", - 0, - "MALE", - "000000-0000000", - "010-0000-0000", - "서울 동작구 보라매로 87", - "시스템 관리자", - "중졸", - "REGULAR", - "미필", - "한국은행", - "000-0000-000000-00000", - "CEN_000000000", - "ORG_000000000", - "GOD", - loadImage("god.png") - ); - - // 심사위원 1 계정 - createOrUpdateMember( - "god1", - "god1", - "이름1", - "god@stanl.com", - 0, - "MALE", - "000000-0000000", - "010-0000-0000", - "서울 동작구 보라매로 87", - "심사위원", - "중졸", - "TEMPORARY", - "미필", - "한국은행", - "000-0000-000000-00000", - "CEN_000000000", - "ORG_000000000", - "GOD", - loadImage("god.png") - ); - - // 심사위원 2 계정 - createOrUpdateMember( - "god2", - "god2", - "이름2", - "god@stanl.com", - 0, - "MALE", - "000000-0000000", - "010-0000-0000", - "서울 동작구 보라매로 87", - "심사위원", - "중졸", - "TEMPORARY", - "미필", - "한국은행", - "000-0000-000000-00000", - "CEN_000000000", - "ORG_000000000", - "GOD", - loadImage("god.png") - ); - - // 심사위원 3 계정 - createOrUpdateMember( - "god3", - "god3", - "이름3", - "god@stanl.com", - 0, - "MALE", - "000000-0000000", - "010-0000-0000", - "서울 동작구 보라매로 87", - "심사위원", - "중졸", - "TEMPORARY", - "미필", - "한국은행", - "000-0000-000000-00000", - "CEN_000000000", - "ORG_000000000", - "GOD", - loadImage("god.png") - ); - - Random random = new Random(); - String[] positions = {"Staff", "Manager", "Director", "Executive"}; - String[] grades = {"A", "B", "C", "D"}; - String[] jobTypes = {"REGULAR", "TEMPORARY"}; - String[] militaryStatus = {"fulfilled", "exemption", "unfulfilled"}; - String[] genders = {"MALE", "FEMALE"}; - String[] roles = {"EMPLOYEE", "ADMIN", "DIRECTOR"}; - String[] lastNames = {"김", "이", "박", "최", "정", "강", "조", "윤", "장", "임"}; - String[] firstNames = {"민수", "지훈", "서연", "예준", "하은", "도현", "지원", "유진", "현우", "수아"}; - String[] addresses = { - "서울특별시 강남구 테헤란로", - "부산광역시 해운대구 센텀중앙로", - "대구광역시 수성구 범어로", - "인천광역시 남동구 미래로", - "광주광역시 서구 상무대로", - "대전광역시 유성구 대덕대로", - "울산광역시 남구 번영로", - "경기도 성남시 분당구 판교로", - "강원도 춘천시 중앙로", - "충청북도 청주시 상당구 상당로" - }; - - - for (int i = 1; i <= 100; i++) { - int centerId = random.nextInt(10) + 1; // Center ID between 0-10 - int orgId = random.nextInt(10) + 1; // Org ID between 0-10 - String sex = genders[i % 2]; - String name = lastNames[random.nextInt(lastNames.length)] + firstNames[random.nextInt(firstNames.length)]; - String address = addresses[random.nextInt(addresses.length)]; - - - createOrUpdateMember( - "user" + i, - "pass" + i, - name, - "user" + i + "@example.com", - 20 + random.nextInt(30), // Random age between 20-49 - sex, - String.format("123456-%07d", 1000000 + random.nextInt(9000000)), - "010-1234-567" + i, - address, - positions[random.nextInt(positions.length)], // Random position - grades[random.nextInt(grades.length)], // Random grade - jobTypes[random.nextInt(jobTypes.length)], // Random job type - militaryStatus[random.nextInt(militaryStatus.length)], // Random military status - "Bank" + centerId, - "123456789" + i, - String.format("CEN_%09d", centerId), - String.format("ORG_%09d", orgId), - roles[random.nextInt(roles.length)], // Random role - loadImage("default.png") - ); - } - - } - - private MultipartFile loadImage(String fileName) throws IOException { - // 리소스 디렉토리에서 파일 로드 - ClassPathResource resource = new ClassPathResource("image/" + fileName); - - // 파일 내용을 byte 배열로 읽기 - byte[] fileContent = StreamUtils.copyToByteArray(resource.getInputStream()); - - // MultipartFile 익명 구현체 생성 - return new MultipartFile() { - @Override - public String getName() { - return fileName; - } - - @Override - public String getOriginalFilename() { - return fileName; - } - - @Override - public String getContentType() { - try { - // 파일의 MIME 타입 결정 - return Files.probeContentType(resource.getFile().toPath()); - } catch (IOException e) { - return "application/octet-stream"; // 기본 MIME 타입 - } - } - - @Override - public boolean isEmpty() { - return fileContent.length == 0; - } - - @Override - public long getSize() { - return fileContent.length; - } - - @Override - public byte[] getBytes() throws IOException { - return fileContent; - } - - @Override - public InputStream getInputStream() throws IOException { - return resource.getInputStream(); - } - - @Override - public void transferTo(File dest) throws IOException, IllegalStateException { - Files.write(dest.toPath(), fileContent); - } - }; - } - - - private void createOrUpdateMember(String loginId, - String password, - String name, - String email, - int age, - String sex, - String identNo, - String phone, - String address, - String position, - String grade, - String jobType, - String military, - String bankName, - String account, - String centerId, - String organizationId, - String role, - MultipartFile imageUrl) throws Exception { - - // db에 정보가 있는지 확인 - Member existingMember = memberRepository.findByLoginId(loginId); - if (existingMember == null) { - // Create the user - SignupDTO signupDTO = new SignupDTO(); - signupDTO.setLoginId(loginId); - signupDTO.setPassword(password); - signupDTO.setName(name); - signupDTO.setEmail(email); - signupDTO.setAge(age); - signupDTO.setSex(sex); - signupDTO.setIdentNo(identNo); - signupDTO.setPhone(phone); - signupDTO.setAddress(address); - signupDTO.setPosition(position); - signupDTO.setGrade(grade); - signupDTO.setJobType(jobType); - signupDTO.setMilitary(military); - signupDTO.setBankName(bankName); - signupDTO.setAccount(account); - signupDTO.setCenterId(centerId); - signupDTO.setOrganizationId(organizationId); - - authCommandService.signup(signupDTO, imageUrl); - - // Grant role to the user - GrantDTO grantDTO = new GrantDTO(); - grantDTO.setLoginId(loginId); - grantDTO.setRole(role); - authCommandService.grantAuthority(grantDTO); - - log.info("{} 유저를 {} 역할로 생성합니다.", loginId, role); - } else { - log.info("{} 유저 정보가 이미 존재합니다.", loginId); - } - } -} From 89b59d68aa7bbb1e0238db27448d0d27f7e0d150 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Tue, 3 Dec 2024 15:10:09 +0900 Subject: [PATCH 434/563] =?UTF-8?q?feat:=20=EC=A1=B0=EC=A7=81=EB=8F=84=20?= =?UTF-8?q?=EB=8D=94=EB=AF=B8=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1(#216)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/dataloader/Initializer.java | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java index 109cfdf7..ce59a208 100644 --- a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java +++ b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java @@ -21,6 +21,8 @@ import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.Member; import stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; +import stanl_2.final_backend.domain.organization.command.domain.aggregate.entity.Organization; +import stanl_2.final_backend.domain.organization.command.domain.repository.OrganizationRepository; import java.io.File; import java.io.IOException; @@ -43,6 +45,7 @@ public class Initializer implements ApplicationRunner { private final CertificationRepository certificationRepository; private final EducationRepository educationRepository; private final FamilyRepository familyRepository; + private final OrganizationRepository organizationRepository; @Override @@ -164,9 +167,9 @@ public void run(ApplicationArguments args) throws Exception { // 회원 및 역할 생성 로직 - for (int i = 1; i <= 94; i++) { + for (int i = 1; i <= 96; i++) { int centerId = random.nextInt(10) + 1; - int orgId = random.nextInt(10) + 1; + int orgId = random.nextInt(10) + 5; String sex = genders[i % 2]; String name = lastNames[random.nextInt(lastNames.length)] + firstNames[random.nextInt(firstNames.length)]; String address = addresses[random.nextInt(addresses.length)]; @@ -442,8 +445,8 @@ public void run(ApplicationArguments args) throws Exception { ); String randomPhone = String.format("010-%04d-%04d", - (int)(Math.random() * 10000), - (int)(Math.random() * 10000) + (int) (Math.random() * 10000), + (int) (Math.random() * 10000) ); // Family(가족) 저장 @@ -453,8 +456,8 @@ public void run(ApplicationArguments args) throws Exception { // 주민등록번호 생성 String birthDateStr = randomBirthDate.format(DateTimeFormatter.ofPattern("yyMMdd")); // YYMMDD 형식 String genderDigit = Math.random() < 0.5 ? "1" : "2"; // 성별은 1(남) 또는 2(여) - String randomNumbers = String.format("%04d", (int)(Math.random() * 10000)); // 임의의 4자리 번호 - String checkDigit = String.format("%d", (int)(Math.random() * 10)); // 검증용 마지막 자리는 0~9 + String randomNumbers = String.format("%04d", (int) (Math.random() * 10000)); // 임의의 4자리 번호 + String checkDigit = String.format("%d", (int) (Math.random() * 10)); // 검증용 마지막 자리는 0~9 // 주민등록번호 만들기 String identNo = birthDateStr + genderDigit + randomNumbers + checkDigit; @@ -475,6 +478,37 @@ public void run(ApplicationArguments args) throws Exception { } + // 부서 저장 + Organization org1 = new Organization("ORG_000000001", "서울 지사", null); + organizationRepository.save(org1); + Organization org2 = new Organization("ORG_000000002", "부산 지사", null); + organizationRepository.save(org2); + Organization org3 = new Organization("ORG_000000003", "인천 지사", null); + organizationRepository.save(org3); + Organization org4 = new Organization("ORG_000000004", "대전 지사", null); + organizationRepository.save(org4); + Organization org5 = new Organization("ORG_000000005", "영업부(서울 1팀)", "ORG_000000001"); + organizationRepository.save(org5); + Organization org6 = new Organization("ORG_000000006", "영업부(서울 2팀)", "ORG_000000001"); + organizationRepository.save(org6); + Organization org7 = new Organization("ORG_000000007", "영업부(부산 1팀)", "ORG_000000002"); + organizationRepository.save(org7); + Organization org8 = new Organization("ORG_000000008", "영업부(부산 2팀)", "ORG_000000002"); + organizationRepository.save(org8); + Organization org9 = new Organization("ORG_000000009", "영업부(인천 1팀)", "ORG_000000003"); + organizationRepository.save(org9); + Organization org10 = new Organization("ORG_000000010", "영업부(인천 2팀)", "ORG_000000003"); + organizationRepository.save(org10); + Organization org11 = new Organization("ORG_000000011", "영업부(대전 1팀)", "ORG_000000004"); + organizationRepository.save(org11); + Organization org12 = new Organization("ORG_000000012", "영업부(대전 2팀)", "ORG_000000004"); + organizationRepository.save(org12); + Organization org13 = new Organization("ORG_000000013", "마케팅부", "ORG_000000001"); + organizationRepository.save(org13); + Organization org14 = new Organization("ORG_000000014", "기술부", "ORG_000000001"); + organizationRepository.save(org14); + + } private String getRandomEmploymentDate() { From 7e2fafa66f3a0dd2773d8a9c1f85fe583c29770a Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Tue, 3 Dec 2024 15:30:54 +0900 Subject: [PATCH 435/563] =?UTF-8?q?feat:=20=EA=B3=A0=EA=B0=9D=20=EB=8D=94?= =?UTF-8?q?=EB=AF=B8=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=83=9D=EC=84=B1(#216)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/dataloader/Initializer.java | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java index ce59a208..e1990d26 100644 --- a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java +++ b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java @@ -12,6 +12,10 @@ import stanl_2.final_backend.domain.career.command.domain.repository.CareerRepository; import stanl_2.final_backend.domain.certification.command.domain.aggregate.entity.Certification; import stanl_2.final_backend.domain.certification.command.domain.repository.CertificationRepository; +import stanl_2.final_backend.domain.customer.command.application.dto.CustomerRegistDTO; +import stanl_2.final_backend.domain.customer.command.application.service.CustomerCommandService; +import stanl_2.final_backend.domain.customer.command.domain.aggregate.entity.Customer; +import stanl_2.final_backend.domain.customer.command.domain.repository.CustomerRepository; import stanl_2.final_backend.domain.education.command.domain.aggregate.entity.Education; import stanl_2.final_backend.domain.education.command.domain.repository.EducationRepository; import stanl_2.final_backend.domain.family.command.domain.aggregate.entity.Family; @@ -46,6 +50,7 @@ public class Initializer implements ApplicationRunner { private final EducationRepository educationRepository; private final FamilyRepository familyRepository; private final OrganizationRepository organizationRepository; + private final CustomerCommandService customerCommandService; @Override @@ -174,9 +179,8 @@ public void run(ApplicationArguments args) throws Exception { String name = lastNames[random.nextInt(lastNames.length)] + firstNames[random.nextInt(firstNames.length)]; String address = addresses[random.nextInt(addresses.length)]; - createOrUpdateMember( - "user" + i, + String.format("M%09d", i), "pass" + i, name, "user" + i + "@example.com", @@ -255,7 +259,7 @@ public void run(ApplicationArguments args) throws Exception { // Career(경력) 저장 for (int i = 1; i <= 100; i++) { - String memberId = String.format("M%09d", i); + String memberId = String.format("MEM_%09d", i); for (int j = 0; j < 4; j++) { Career newCareer = new Career(); newCareer.setEmplDate(getRandomEmploymentDate()); @@ -321,7 +325,7 @@ public void run(ApplicationArguments args) throws Exception { // Certification(자격증) 저장 for (int i = 1; i <= 100; i++) { - String memberId = String.format("M%09d", i); + String memberId = String.format("MEM_%09d", i); for (int j = 0; j < 4; j++) { Certification newCertification = new Certification(); newCertification.setAcquisitionDate(getRandomEmploymentDate()); @@ -414,7 +418,7 @@ public void run(ApplicationArguments args) throws Exception { // Education(학력) 저장 for (int i = 1; i < 100; i++) { - String memberId = String.format("M%09d", i); + String memberId = String.format("MEM_%09d", i); Education newEducation = new Education(); newEducation.setEntranceDate(getRandomEmploymentDate()); newEducation.setGraduationDate(getRandomResignationDate(LocalDate.parse(newEducation.getEntranceDate(), DateTimeFormatter.ofPattern("yyyy-MM-dd")))); @@ -451,7 +455,7 @@ public void run(ApplicationArguments args) throws Exception { // Family(가족) 저장 for (int i = 1; i <= 100; i++) { - String memberId = String.format("M%09d", i); + String memberId = String.format("MEM_%09d", i); for (int j = 0; j < 4; j++) { // 주민등록번호 생성 String birthDateStr = randomBirthDate.format(DateTimeFormatter.ofPattern("yyMMdd")); // YYMMDD 형식 @@ -509,6 +513,31 @@ public void run(ApplicationArguments args) throws Exception { organizationRepository.save(org14); + // 고객 인당 100명 씩 + for (int i = 1; i <= 100; i++) { + String memberId = String.format("M%09d", i); + for (int j = 0; j < 100; j++) { + String sex = genders[i % 2]; + String name = lastNames[random.nextInt(lastNames.length)] + firstNames[random.nextInt(firstNames.length)]; + int randomAge = ThreadLocalRandom.current().nextInt(20, 71); + String randomName = "user" + ThreadLocalRandom.current().nextInt(1000, 10000); + String randomEmail = randomName + "@example.com"; + + CustomerRegistDTO newCustomer = new CustomerRegistDTO(); + newCustomer.setName(name); + newCustomer.setAge(randomAge); + newCustomer.setPhone(randomPhone); + newCustomer.setEmergePhone(randomPhone); + newCustomer.setEmail(randomEmail); + newCustomer.setSex(sex); + newCustomer.setMemberId(memberId); + customerCommandService.registerCustomerInfo(newCustomer); + } + } + + + + } private String getRandomEmploymentDate() { From 7a82d9236335ec079a39accc801f1169018d2497 Mon Sep 17 00:00:00 2001 From: giuseog Date: Tue, 3 Dec 2024 15:51:16 +0900 Subject: [PATCH 436/563] =?UTF-8?q?feat:=20front=20=EC=9A=94=EA=B5=AC?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EB=94=B0=EB=9D=BC=20=EC=88=98=EC=A0=95(#1?= =?UTF-8?q?83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/dto/SalesHistorySearchDTO.java | 2 ++ .../query/repository/SalesHistoryMapper.java | 1 + .../query/service/SalesHistoryQueryService.java | 1 + .../service/SalesHistoryQueryServiceImpl.java | 13 +++++++++++++ .../query/repository/SalesHistoryMapper.xml | 16 +++++++++++++++- 5 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java index 6be458c8..02f9cfb9 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java @@ -20,4 +20,6 @@ public class SalesHistorySearchDTO { private String orderBy; private String customerName; private List customerList; + private List productList; + private String contractId; } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java index 71564fe0..a2edb7ac 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java @@ -110,6 +110,7 @@ List findAllStatisticsBySearch(@Param("size") int siz , @Param("offset") int offset, @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); + String findSalesHistoryIdByContractId(@Param("contractId") String contractId); } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java index db29832a..dd77fd31 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java @@ -51,4 +51,5 @@ public interface SalesHistoryQueryService { Page selectAllStatisticsBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); + String selectSalesHistoryIdByContractId(String contractId); } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java index 48cf8f38..b294303c 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java @@ -579,4 +579,17 @@ public Page selectAllStatisticsBySearch(SalesHistoryR return new PageImpl<>(salesHistoryList, pageable, total); } + + @Override + @Transactional + public String selectSalesHistoryIdByContractId(String contractId) { + + String salesHistoryId = salesHistoryMapper.findSalesHistoryIdByContractId(contractId); + + if(salesHistoryId == null){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } + + return salesHistoryId; + } } diff --git a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml index a14a34bd..6559af7d 100644 --- a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml @@ -251,6 +251,15 @@ #{cust_id}
      + + AND a.prod_id IN + + #{prod_id} + + + + AND a.conr_id = #{salesHistorySearchDTO.contractId} + AND a.created_at BETWEEN #{salesHistorySearchDTO.startDate} AND #{salesHistorySearchDTO.endDate} @@ -1086,6 +1095,11 @@ LIMIT #{size} OFFSET #{offset} - + From aa1a8859109b96218c28582e750c907b871b4258 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 3 Dec 2024 15:59:13 +0900 Subject: [PATCH 437/563] =?UTF-8?q?fix:=20=EA=B8=80=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=EC=9E=90=20=EC=88=98=EC=A0=95=20=EC=82=AC=ED=95=AD=20(#213)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/controller/NoticeController.java | 2 +- .../domain/service/NoticeCommandServiceImpl.java | 7 +++++-- .../query/controller/NoticeController.java | 9 ++++----- .../notices/query/repository/NoticeMapper.java | 2 +- .../notices/query/service/NoticeServiceImpl.java | 16 ++++++++++++++-- .../notices/query/repository/NoticeMapper.xml | 2 +- 6 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java index 656aff42..4737ca26 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java @@ -78,7 +78,7 @@ public ResponseEntity modifyNotice( @RequestPart(value = "file", required = false) MultipartFile file, Principal principal){ String memberLoginId = principal.getName(); - noticeModifyDTO.setMemberId(memberLoginId); + noticeModifyDTO.setMemberLoginId(memberLoginId); noticeModifyDTO.setContent(noticeModifyDTO.getContent()); if (file != null && !file.isEmpty()) { System.out.println("response:1"); diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java index 3633409c..eaa39aab 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java @@ -85,11 +85,13 @@ public void registerNotice(NoticeRegistDTO noticeRegistDTO, public NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO,Principal principal) { redisService.clearNoticeCache(); - String memberId= principal.getName(); + String memberId = authQueryService.selectMemberIdByLoginId(noticeModifyDTO.getMemberLoginId()); + System.out.println("memberId"+memberId); + Notice notice = noticeRepository.findById(id) .orElseThrow(() -> new NoticeCommonException(NoticeErrorCode.NOTICE_NOT_FOUND)); - + System.out.println("writer:"+notice.getMemberId()); if(!notice.getMemberId().equals(memberId)){ // 권한 오류 throw new NoticeCommonException(NoticeErrorCode.AUTHORIZATION_VIOLATION); @@ -121,6 +123,7 @@ public NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO,P public void deleteNotice(NoticeDeleteDTO noticeDeleteDTO, Principal principal) { redisService.clearNoticeCache(); String memberId = principal.getName(); + System.out.println(memberId); Notice notice = noticeRepository.findByNoticeId(noticeDeleteDTO.getNoticeId()) .orElseThrow(() -> new NoticeCommonException(NoticeErrorCode.NOTICE_NOT_FOUND)); diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java index c856ba5c..54f49913 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java @@ -12,17 +12,16 @@ import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import stanl_2.final_backend.domain.A_sample.common.response.SampleResponseMessage; import stanl_2.final_backend.domain.notices.common.response.NoticeResponseMessage; import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; import stanl_2.final_backend.domain.notices.query.dto.SearchDTO; import stanl_2.final_backend.domain.notices.query.service.NoticeService; + @RestController("queryNoticeController") @RequestMapping("/api/v1/notice") public class NoticeController { private final NoticeService noticeService; - @Autowired public NoticeController(NoticeService noticeService) { this.noticeService = noticeService; @@ -43,11 +42,11 @@ public ResponseEntity> getNotices( @RequestParam(required = false) String classification, @RequestParam(required = false) String startDate, @RequestParam(required = false) String endDate - ) { Pageable pageable = PageRequest.of(page, size); - SearchDTO searchDTO = new SearchDTO(title,tag,memberId,classification,startDate,endDate); - Page noticeDTOPage = noticeService.findNotices(pageable,searchDTO); + SearchDTO searchDTO = new SearchDTO(title, tag, memberId, classification, startDate, endDate); + + Page noticeDTOPage = noticeService.findNotices(pageable, searchDTO); return ResponseEntity.ok(noticeDTOPage); } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java b/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java index 2618f809..7da0713d 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.java @@ -22,7 +22,7 @@ List findNotices( @Param("searchDTO") SearchDTO searchDTO ); - int findNoticesCount(@Param("searchDTO") SearchDTO searchDTO); +// int findNoticesCount(@Param("searchDTO") SearchDTO searchDTO); NoticeDTO findNotice(@Param("noticeId") String noticeId); List findNoticesForExcel(); diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java index 8e0366ab..06374256 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java @@ -8,10 +8,13 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; import stanl_2.final_backend.domain.notices.query.dto.NoticeExcelDownload; import stanl_2.final_backend.domain.notices.query.dto.SearchDTO; import stanl_2.final_backend.domain.notices.query.repository.NoticeMapper; +import stanl_2.final_backend.domain.sales_history.common.exception.SalesHistoryCommonException; +import stanl_2.final_backend.domain.sales_history.common.exception.SalesHistoryErrorCode; import stanl_2.final_backend.global.excel.ExcelUtilsV1; import stanl_2.final_backend.global.redis.RedisService; @@ -24,12 +27,14 @@ public class NoticeServiceImpl implements NoticeService{ private final RedisTemplate redisTemplate; private final RedisService redisService; private final ExcelUtilsV1 excelUtilsV1; + private final MemberQueryService memberQueryService; @Autowired - public NoticeServiceImpl(NoticeMapper noticeMapper, RedisTemplate redisTemplate, RedisService redisService, ExcelUtilsV1 excelUtilsV1) { + public NoticeServiceImpl(NoticeMapper noticeMapper, RedisTemplate redisTemplate, RedisService redisService, ExcelUtilsV1 excelUtilsV1,MemberQueryService memberQueryService) { this.noticeMapper = noticeMapper; this.redisTemplate = redisTemplate; this.redisService =redisService; this.excelUtilsV1 =excelUtilsV1; + this.memberQueryService =memberQueryService; } @Transactional @@ -45,7 +50,6 @@ public Page findNotices(Pageable pageable, SearchDTO searchDTO) { List notices = (List) redisTemplate.opsForValue().get(cacheKey); if (notices == null) { System.out.println("데이터베이스에서 데이터 조회 중..."); - System.out.println(searchDTO.getMemberId()); notices = noticeMapper.findNotices(offset, size, searchDTO); if (notices != null && !notices.isEmpty()) { // 데이터가 있을 때만 캐싱 redisService.setKeyWithTTL(cacheKey, notices, 30 * 60); // 캐싱 시 동일 키 사용 @@ -53,6 +57,13 @@ public Page findNotices(Pageable pageable, SearchDTO searchDTO) { } else { System.out.println("캐시에서 데이터 조회 중..."); } + notices.forEach(notice -> { + try { + notice.setMemberId(memberQueryService.selectNameById(notice.getMemberId())); + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.MEMBER_NOT_FOUND); + } + }); int totalElements = noticeMapper.findNoticeCount(); // 총 개수 조회 return new PageImpl<>(notices, pageable, totalElements); } @@ -70,4 +81,5 @@ public void exportNoticesToExcel(HttpServletResponse response) { excelUtilsV1.download(NoticeExcelDownload.class, noticeList, "noticeExcel", response); } + } \ No newline at end of file diff --git a/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml b/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml index 96cf93c3..aaf081b3 100644 --- a/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/notices/query/repository/NoticeMapper.xml @@ -18,7 +18,7 @@ - + From 9f330a156c695cacfd93eade66926b7c9ee6e120 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Tue, 3 Dec 2024 16:17:35 +0900 Subject: [PATCH 438/563] =?UTF-8?q?feat:=20=EB=A7=A4=EC=9E=A5=20=EB=8D=94?= =?UTF-8?q?=EB=AF=B8=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=83=9D=EC=84=B1(#216)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/dataloader/Initializer.java | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java index e1990d26..b94cedaa 100644 --- a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java +++ b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java @@ -10,12 +10,12 @@ import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.career.command.domain.aggregate.entity.Career; import stanl_2.final_backend.domain.career.command.domain.repository.CareerRepository; +import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistDTO; +import stanl_2.final_backend.domain.center.command.application.service.CenterCommandService; import stanl_2.final_backend.domain.certification.command.domain.aggregate.entity.Certification; import stanl_2.final_backend.domain.certification.command.domain.repository.CertificationRepository; import stanl_2.final_backend.domain.customer.command.application.dto.CustomerRegistDTO; import stanl_2.final_backend.domain.customer.command.application.service.CustomerCommandService; -import stanl_2.final_backend.domain.customer.command.domain.aggregate.entity.Customer; -import stanl_2.final_backend.domain.customer.command.domain.repository.CustomerRepository; import stanl_2.final_backend.domain.education.command.domain.aggregate.entity.Education; import stanl_2.final_backend.domain.education.command.domain.repository.EducationRepository; import stanl_2.final_backend.domain.family.command.domain.aggregate.entity.Family; @@ -51,6 +51,7 @@ public class Initializer implements ApplicationRunner { private final FamilyRepository familyRepository; private final OrganizationRepository organizationRepository; private final CustomerCommandService customerCommandService; + private final CenterCommandService centerCommandService; @Override @@ -515,7 +516,7 @@ public void run(ApplicationArguments args) throws Exception { // 고객 인당 100명 씩 for (int i = 1; i <= 100; i++) { - String memberId = String.format("M%09d", i); + String memberId = String.format("MEM_%09d", i); for (int j = 0; j < 100; j++) { String sex = genders[i % 2]; String name = lastNames[random.nextInt(lastNames.length)] + firstNames[random.nextInt(firstNames.length)]; @@ -535,9 +536,42 @@ public void run(ApplicationArguments args) throws Exception { } } + String[][] carDealershipsInfo = { + {"서울 중앙 영업점", "서울특별시 중구 세종대로 123"}, + {"부산 서면 영업점", "부산광역시 부산진구 중앙대로 456"}, + {"인천 남동구 영업점", "인천광역시 남동구 인주대로 789"}, + {"대전 둔산 영업점", "대전광역시 서구 둔산로 101"}, + {"광주 광산구 영업점", "광주광역시 광산구 상무대로 202"}, + {"대구 동구 영업점", "대구광역시 동구 아양로 303"}, + {"울산 중구 영업점", "울산광역시 중구 태화로 404"}, + {"수원 영통구 영업점", "경기도 수원시 영통구 매탄로 505"}, + {"성남 분당 영업점", "경기도 성남시 분당구 정자일로 606"}, + {"제주 서귀포 영업점", "제주특별자치도 서귀포시 태평로 707"} + }; + + // 매장 등록 + CenterRegistDTO newCenter1 = new CenterRegistDTO("서울 중앙 영업점", "서울특별시 중구 세종대로 123", "02-123-4567", 25, "09:00 - 18:00", null); + centerCommandService.registCenter(newCenter1, loadImage("default.png")); + CenterRegistDTO newCenter2 = new CenterRegistDTO("부산 서면 영업점", "부산광역시 부산진구 중앙대로 456", "051-123-4567", 20, "09:00 - 18:00", null); + centerCommandService.registCenter(newCenter2, loadImage("default.png")); + CenterRegistDTO newCenter3 = new CenterRegistDTO("인천 남동구 영업점", "인천광역시 남동구 인주대로 789", "032-123-4567", 18, "09:00 - 18:00", null); + centerCommandService.registCenter(newCenter3, loadImage("default.png")); + CenterRegistDTO newCenter4 = new CenterRegistDTO("대전 둔산 영업점", "대전광역시 서구 둔산로 101", "042-123-4567", 15, "09:00 - 18:00", null); + centerCommandService.registCenter(newCenter4, loadImage("default.png")); + CenterRegistDTO newCenter5 = new CenterRegistDTO("광주 광산구 영업점", "광주광역시 광산구 상무대로 202", "062-123-4567", 17, "09:00 - 18:00", null); + centerCommandService.registCenter(newCenter5, loadImage("default.png")); + CenterRegistDTO newCenter6 = new CenterRegistDTO("대구 동구 영업점", "대구광역시 동구 아양로 303", "053-123-4567", 22, "09:00 - 18:00", null); + centerCommandService.registCenter(newCenter6, loadImage("default.png")); + CenterRegistDTO newCenter7 = new CenterRegistDTO("울산 중구 영업점", "울산광역시 중구 태화로 404", "052-123-4567", 13, "09:00 - 18:00", null); + centerCommandService.registCenter(newCenter7, loadImage("default.png")); + CenterRegistDTO newCenter8 = new CenterRegistDTO("수원 영통구 영업점", "경기도 수원시 영통구 매탄로 505", "031-123-4567", 19, "09:00 - 18:00", null); + centerCommandService.registCenter(newCenter8, loadImage("default.png")); + CenterRegistDTO newCenter9 = new CenterRegistDTO("성남 분당 영업점", "경기도 성남시 분당구 정자일로 606", "031-234-5678", 21, "09:00 - 18:00", null); + centerCommandService.registCenter(newCenter9, loadImage("default.png")); + CenterRegistDTO newCenter10 = new CenterRegistDTO("제주 서귀포 영업점", "제주특별자치도 서귀포시 태평로 707", "064-123-4567", 12, "09:00 - 18:00", null); + centerCommandService.registCenter(newCenter10, loadImage("default.png")); - } private String getRandomEmploymentDate() { From e3377a725ed7b5e60e1f34f75f043c7fb8a20465 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Tue, 3 Dec 2024 16:28:27 +0900 Subject: [PATCH 439/563] =?UTF-8?q?feat:=20frontend=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=EA=B6=8C=ED=95=9C=20=EC=A1=B0=EC=A0=95=20(#182)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../final_backend/domain/alarm/scheduler/AlarmScheduler.java | 2 +- .../global/security/config/RequestMatcherConfig.java | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java index 1192ff88..bfb69670 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java @@ -33,7 +33,7 @@ public AlarmScheduler(AlarmCommandService alarmCommandService, ScheduleQueryServ } // @Scheduled(cron = "0 0 2 * * *") // 매일 새벽 2시에 실행) - @Scheduled(cron = "0 31 20 * * *") + @Scheduled(cron = "0 2 14 * * *") @Transactional public void alarmTodaySchedule(){ diff --git a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java index 15e60ec4..e4a07905 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java @@ -48,7 +48,9 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception .requestMatchers(HttpMethod.POST, "/api/v1/contract").hasAnyRole("contract-post", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 등록 (전체) .requestMatchers(HttpMethod.GET, "/api/v1/contract/search").hasAnyRole("contract-search-get", "GOD", "DIRECTOR", "ADMIN") // 계약서 검색 조회 (영업관리자, 영업담당자) .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee").hasAnyRole("contract-employee-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 전체 조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee/{contractId}").hasAnyRole("contract-employee-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 전체 조회 (전체) .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee/**").hasAnyRole("contract-employee-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 상세조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/contract/center/{contractId}").hasAnyRole("contract-employee-id-get", "GOD", "DIRECTOR", "ADMIN") // 계약서 상세조회 (전체) .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee/search").hasAnyRole("contract-employee-search-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 검색 조회 (전체) // Customer API @@ -97,6 +99,7 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception .requestMatchers(HttpMethod.GET, "/api/v1/order").hasAnyRole("order-get", "GOD", "DIRECTOR", "ADMIN") // 수주서 전체 조회 (영업관리자, 영업담당자) .requestMatchers(HttpMethod.GET, "/api/v1/order/employee").hasAnyRole("order-employee-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 수주서 전체 조회 (전체) .requestMatchers(HttpMethod.GET, "/api/v1/order/{orderId}").hasAnyRole("order-id-get", "GOD", "DIRECTOR", "ADMIN") // 수주서 상세 조회 (영업관리자, 영업담당자) + .requestMatchers(HttpMethod.GET, "/api/v1/order/center/{orderId}").hasAnyRole("order-center-id-get", "GOD", "DIRECTOR", "ADMIN") // 수주서 상세 조회 (영업관리자, 영업담당자) .requestMatchers(HttpMethod.GET, "/api/v1/order/employee/{orderId}").hasAnyRole("order-employee-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 수주서 상세 조회 (전체) .requestMatchers(HttpMethod.GET, "/api/v1/order/search").hasAnyRole("order-search", "GOD", "DIRECTOR", "ADMIN") // 수주서 검색 조회 (영업관리자, 영업담당자) .requestMatchers(HttpMethod.GET, "/api/v1/order/employee/search").hasAnyRole("order-employee-search", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 수주서 검색 조회 (전체) @@ -142,7 +145,6 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception // Sample API .requestMatchers(HttpMethod.DELETE, "/api/v1/sample/{id}").hasAnyRole("sample-delete", "GOD") // 샘플 삭제 테스트 (시스템관리자) .requestMatchers(HttpMethod.GET, "/api/v1/sample/detail/{id}").hasAnyRole("sample-detail-get", "GOD") // 샘플 상세 조회 테스트 (시스템관리자) - .requestMatchers(HttpMethod.GET, "/api/v1/schedule/{scheduleId}").hasAnyRole("sample-schedule-get", "GOD") // 샘플 조회 테스트 (시스템관리자) .requestMatchers(HttpMethod.POST, "/api/v1/sample").hasAnyRole("sample-create", "GOD") // 샘플 요청 테스트 (시스템관리자) .requestMatchers(HttpMethod.PUT, "/api/v1/sample/{id}").hasAnyRole("sample-update", "GOD") // 샘플 수정 테스트 (시스템관리자) From d20fe81d6fbc0f03454dc2f1059ac2e0a4c9198f Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 3 Dec 2024 16:58:37 +0900 Subject: [PATCH 440/563] =?UTF-8?q?fix:=20=EA=B8=80=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=EC=9E=90=20=EC=88=98=EC=A0=95=20=EC=82=AC=ED=95=AD=20(#213)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ProblemController.java | 4 ++-- .../application/dto/ProblemRegistDTO.java | 2 ++ .../aggregate/service/ProblemServiceImpl.java | 14 +++++++++++--- .../query/service/ProblemServiceImpl.java | 16 ++++++++++++++-- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java index 4fd54af0..e2295abf 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java @@ -42,8 +42,8 @@ public ProblemController(ProblemCommandService problemCommandService, AuthQueryS public ResponseEntity postProblem(@RequestPart("dto") ProblemRegistDTO problemRegistDTO, // JSON 데이터 @RequestPart(value = "file", required = false) MultipartFile file, Principal principal){ - String memberId = authQueryService.selectMemberIdByLoginId(principal.getName()); - problemRegistDTO.setMemberId(memberId); + String memberLoginId = principal.getName(); + problemRegistDTO.setMemberLoginId(memberLoginId); if (file != null && !file.isEmpty()) { problemRegistDTO.setFileUrl(s3FileService.uploadOneFile(file)); System.out.println("response:1"); diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemRegistDTO.java index 7f7fc823..472ce950 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemRegistDTO.java @@ -19,6 +19,8 @@ public class ProblemRegistDTO { private String memberId; + private String memberLoginId; + private String productId; private String fileUrl; diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java index 03da74eb..0155585e 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java @@ -5,6 +5,7 @@ import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.notices.common.exception.NoticeCommonException; import stanl_2.final_backend.domain.notices.common.exception.NoticeErrorCode; import stanl_2.final_backend.domain.problem.command.application.dto.ProblemModifyDTO; @@ -29,14 +30,19 @@ public class ProblemServiceImpl implements ProblemCommandService { private final ProblemRepository problemRepository; + private final AuthQueryService authQueryService; private final RedisService redisService; private final ModelMapper modelMapper; @Autowired - public ProblemServiceImpl(ProblemRepository problemRepository, ModelMapper modelMapper, RedisService redisService) { + public ProblemServiceImpl(ProblemRepository problemRepository, + AuthQueryService authQueryService, + ModelMapper modelMapper, + RedisService redisService) { this.problemRepository = problemRepository; this.modelMapper = modelMapper; this.redisService = redisService; + this.authQueryService =authQueryService; } private String getCurrentTimestamp() { ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); @@ -45,10 +51,12 @@ private String getCurrentTimestamp() { @Transactional @Override - public void registerProblem(ProblemRegistDTO problemRegistDTO, Principal principal) { + public void registerProblem(ProblemRegistDTO problemRegistDTO, + Principal principal) { redisService.clearProblemCache(); - String memberId =principal.getName(); + String memberId = authQueryService.selectMemberIdByLoginId(problemRegistDTO.getMemberLoginId()); problemRegistDTO.setMemberId(memberId); + System.out.println("memberId"+memberId); try { Problem problem = modelMapper.map(problemRegistDTO, Problem.class); problemRepository.save(problem); diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java index 724d120c..01927dec 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java @@ -8,11 +8,15 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.Member; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import stanl_2.final_backend.domain.notices.query.dto.NoticeExcelDownload; import stanl_2.final_backend.domain.problem.query.dto.ProblemDTO; import stanl_2.final_backend.domain.problem.query.dto.ProblemExcelDownload; import stanl_2.final_backend.domain.problem.query.dto.ProblemSearchDTO; import stanl_2.final_backend.domain.problem.query.repository.ProblemMapper; +import stanl_2.final_backend.domain.sales_history.common.exception.SalesHistoryCommonException; +import stanl_2.final_backend.domain.sales_history.common.exception.SalesHistoryErrorCode; import stanl_2.final_backend.global.excel.ExcelUtilsV1; import stanl_2.final_backend.global.redis.RedisService; @@ -23,15 +27,16 @@ public class ProblemServiceImpl implements ProblemService { private final ProblemMapper problemMapper; private final RedisTemplate redisTemplate; private final RedisService redisService; - private final ExcelUtilsV1 excelUtilsV1; + private final MemberQueryService memberQueryService; @Autowired - public ProblemServiceImpl(ProblemMapper problemMapper, RedisTemplate redisTemplate, RedisService redisService, ExcelUtilsV1 excelUtilsV1) { + public ProblemServiceImpl(ProblemMapper problemMapper, RedisTemplate redisTemplate, RedisService redisService, ExcelUtilsV1 excelUtilsV1, MemberQueryService memberQueryService) { this.problemMapper = problemMapper; this.redisTemplate = redisTemplate; this.redisService = redisService; this.excelUtilsV1 =excelUtilsV1; + this.memberQueryService =memberQueryService; } @Transactional @@ -54,6 +59,13 @@ public Page findProblems(Pageable pageable, ProblemSearchDTO problem } else { System.out.println("캐시에서 문제 데이터 조회 중..."); } + problems.forEach(problem -> { + try { + problem.setMemberId(memberQueryService.selectNameById(problem.getMemberId())); + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.MEMBER_NOT_FOUND); + } + }); Integer totalElements = problemMapper.findProblemsCount(problemSearchDTO); return new PageImpl<>(problems, pageable, totalElements); } From f2727a243b0b7d2241dcfea1d529bd5073b0cf6a Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 3 Dec 2024 16:58:58 +0900 Subject: [PATCH 441/563] =?UTF-8?q?=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20?= =?UTF-8?q?=EA=B3=B5=EB=B0=B1=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/domain/service/NoticeCommandServiceImpl.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java index eaa39aab..fcfa59c9 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java @@ -58,7 +58,6 @@ private String getCurrentTimestamp() { @Transactional public void registerNotice(NoticeRegistDTO noticeRegistDTO, Principal principal) { - redisService.clearNoticeCache(); String memberId = authQueryService.selectMemberIdByLoginId(noticeRegistDTO.getMemberLoginId()); noticeRegistDTO.setMemberId(memberId); @@ -66,11 +65,8 @@ public void registerNotice(NoticeRegistDTO noticeRegistDTO, try { Notice notice = modelMapper.map(noticeRegistDTO, Notice.class); Notice newNotice = noticeRepository.save(notice); - NoticeAlarmDTO noticeAlarmDTO = modelMapper.map(newNotice, NoticeAlarmDTO.class); - alarmCommandService.sendNoticeAlarm(noticeAlarmDTO); - } catch (DataIntegrityViolationException e){ // DB 무결정 제약 조건 (NOT NULL, UNIQUE) 위반 throw new NoticeCommonException(NoticeErrorCode.DATA_INTEGRITY_VIOLATION); From e8b6e9410950ad92c0184aa9db0c326dcdcf2e1c Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 3 Dec 2024 17:12:48 +0900 Subject: [PATCH 442/563] =?UTF-8?q?fix:=20=EA=B8=80=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=EC=9E=90=20=EC=88=98=EC=A0=95=20=EC=82=AC=ED=95=AD=20(#215)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PromotionController.java | 4 ++-- .../application/dto/PromotionRegistDTO.java | 2 ++ .../aggregate/service/PromotionServiceImpl.java | 11 ++++++++--- .../query/service/PromotionServiceImpl.java | 14 +++++++++++++- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java index 951deaac..5bd5bdda 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java @@ -43,8 +43,8 @@ public PromotionController(PromotionCommandService promotionCommandService, Auth public ResponseEntity postNotice(@RequestPart("dto") PromotionRegistDTO promotionRegistDTO, // JSON 데이터 @RequestPart(value = "file", required = false) MultipartFile file, Principal principal){ - String memberId = authQueryService.selectMemberIdByLoginId(principal.getName()); - promotionRegistDTO.setMemberId(memberId); + String memberLoginId = principal.getName(); + promotionRegistDTO.setMemberLoginId(memberLoginId); if (file != null && !file.isEmpty()) { promotionRegistDTO.setFileUrl(s3FileService.uploadOneFile(file)); System.out.println("response:1"); diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionRegistDTO.java index b0870898..130db466 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionRegistDTO.java @@ -17,5 +17,7 @@ public class PromotionRegistDTO { private String memberId; + private String memberLoginId; + private String fileUrl; } diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java index bf46b68e..125b934c 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java @@ -5,6 +5,7 @@ import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.notices.common.exception.NoticeCommonException; import stanl_2.final_backend.domain.notices.common.exception.NoticeErrorCode; import stanl_2.final_backend.domain.problem.common.exception.ProblemCommonException; @@ -29,12 +30,14 @@ public class PromotionServiceImpl implements PromotionCommandService { private final RedisService redisService; private final PromotionRepository promotionRepository; + private final AuthQueryService authQueryService; private final ModelMapper modelMapper; @Autowired - public PromotionServiceImpl(PromotionRepository promotionRepository, ModelMapper modelMapper,RedisService redisService) { + public PromotionServiceImpl(PromotionRepository promotionRepository, ModelMapper modelMapper,RedisService redisService, AuthQueryService authQueryService) { this.redisService = redisService; this.promotionRepository = promotionRepository; + this.authQueryService = authQueryService; this.modelMapper = modelMapper; } private String getCurrentTimestamp() { @@ -46,7 +49,7 @@ private String getCurrentTimestamp() { @Override public void registerPromotion(PromotionRegistDTO promotionRegistDTO, Principal principal) { redisService.clearPromotionCache(); - String memberId =principal.getName(); + String memberId = authQueryService.selectMemberIdByLoginId(promotionRegistDTO.getMemberLoginId()); promotionRegistDTO.setMemberId(memberId); try { Promotion promotion =modelMapper.map(promotionRegistDTO,Promotion.class); @@ -95,10 +98,12 @@ public PromotionModifyDTO modifyPromotion(String promotionId, PromotionModifyDTO public void deletePromotion(String promotionId, Principal principal) { redisService.clearPromotionCache(); String memberId= principal.getName(); + System.out.println(memberId); Promotion promotion = promotionRepository.findById(promotionId) .orElseThrow(()-> new PromotionCommonException(PromotionErrorCode.PROMOTION_NOT_FOUND)); - if(!promotion.getMemberId().equals(memberId)){ + if(promotion.getMemberId().equals(memberId)){ + System.out.println(promotion.getMemberId()); // 권한 오류 throw new PromotionCommonException(PromotionErrorCode.AUTHORIZATION_VIOLATION); } diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java index a391b7cd..e1f23a73 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java @@ -8,12 +8,15 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import stanl_2.final_backend.domain.problem.query.dto.ProblemDTO; import stanl_2.final_backend.domain.problem.query.dto.ProblemExcelDownload; import stanl_2.final_backend.domain.promotion.query.dto.PromotionDTO; import stanl_2.final_backend.domain.promotion.query.dto.PromotionExcelDownload; import stanl_2.final_backend.domain.promotion.query.dto.PromotionSearchDTO; import stanl_2.final_backend.domain.promotion.query.repository.PromotionMapper; +import stanl_2.final_backend.domain.sales_history.common.exception.SalesHistoryCommonException; +import stanl_2.final_backend.domain.sales_history.common.exception.SalesHistoryErrorCode; import stanl_2.final_backend.global.excel.ExcelUtilsV1; import stanl_2.final_backend.global.redis.RedisService; @@ -25,10 +28,12 @@ public class PromotionServiceImpl implements PromotionService{ private final RedisTemplate redisTemplate; private final RedisService redisService; private final ExcelUtilsV1 excelUtilsV1; + private final MemberQueryService memberQueryService; @Autowired - public PromotionServiceImpl(PromotionMapper promotionMapper, RedisTemplate redisTemplate, RedisService redisService, ExcelUtilsV1 excelUtilsV1) { + public PromotionServiceImpl(PromotionMapper promotionMapper, RedisTemplate redisTemplate, RedisService redisService, ExcelUtilsV1 excelUtilsV1, MemberQueryService memberQueryService) { this.promotionMapper = promotionMapper; this.redisTemplate = redisTemplate; + this.memberQueryService = memberQueryService; this.redisService = redisService; this.excelUtilsV1 =excelUtilsV1; } @@ -48,6 +53,13 @@ public Page findPromotions(Pageable pageable, PromotionSearchDTO p } else { System.out.println("캐시에서 프로모션 데이터 조회 중..."); } + promotions.forEach(promotion -> { + try { + promotion.setMemberId(memberQueryService.selectNameById(promotion.getMemberId())); + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.MEMBER_NOT_FOUND); + } + }); Integer totalElements = promotionMapper.findPromotionsCount(promotionSearchDTO); return new PageImpl<>(promotions, pageable, totalElements); } From 8280b767d96de42663f37f2fe1f0d0d454999bf1 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 3 Dec 2024 17:17:06 +0900 Subject: [PATCH 443/563] =?UTF-8?q?fix:=20=EA=B8=80=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=EC=9E=90=20=EC=88=98=EC=A0=95=20=EC=82=AC=ED=95=AD=20(#215)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/application/controller/ProblemController.java | 6 ------ .../domain/aggregate/service/ProblemServiceImpl.java | 1 - .../command/application/controller/PromotionController.java | 6 ------ .../domain/aggregate/service/PromotionServiceImpl.java | 2 -- 4 files changed, 15 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java index e2295abf..37afaa35 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java @@ -46,12 +46,9 @@ public ResponseEntity postProblem(@RequestPart("dto") Pr problemRegistDTO.setMemberLoginId(memberLoginId); if (file != null && !file.isEmpty()) { problemRegistDTO.setFileUrl(s3FileService.uploadOneFile(file)); - System.out.println("response:1"); }else if(file==null || file.isEmpty()){ - System.out.println("response:2"); problemRegistDTO.setFileUrl(null); } else { - System.out.println("response:3"); problemRegistDTO.setFileUrl(null); } problemCommandService.registerProblem(problemRegistDTO,principal); @@ -76,13 +73,10 @@ public ResponseEntity modifyProblem(@PathVariable String problemModifyDTO.setMemberId(memberLoginId); problemModifyDTO.setContent(problemModifyDTO.getContent()); if (file != null && !file.isEmpty()) { - System.out.println("response:1"); problemModifyDTO.setFileUrl(s3FileService.uploadOneFile(file)); }else if(file==null || file.isEmpty()) { - System.out.println("response:2"); problemModifyDTO.setFileUrl(null); } else { - System.out.println("response:3"); problemModifyDTO.setFileUrl(s3FileService.uploadOneFile(file)); } problemCommandService.modifyProblem(problemId,problemModifyDTO,principal); diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java index 0155585e..68c6faaf 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java @@ -56,7 +56,6 @@ public void registerProblem(ProblemRegistDTO problemRegistDTO, redisService.clearProblemCache(); String memberId = authQueryService.selectMemberIdByLoginId(problemRegistDTO.getMemberLoginId()); problemRegistDTO.setMemberId(memberId); - System.out.println("memberId"+memberId); try { Problem problem = modelMapper.map(problemRegistDTO, Problem.class); problemRepository.save(problem); diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java index 5bd5bdda..a96a9553 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java @@ -47,12 +47,9 @@ public ResponseEntity postNotice(@RequestPart("dto") P promotionRegistDTO.setMemberLoginId(memberLoginId); if (file != null && !file.isEmpty()) { promotionRegistDTO.setFileUrl(s3FileService.uploadOneFile(file)); - System.out.println("response:1"); }else if(file==null || file.isEmpty()){ - System.out.println("response:2"); promotionRegistDTO.setFileUrl(null); } else { - System.out.println("response:3"); promotionRegistDTO.setFileUrl(null); } promotionCommandService.registerPromotion(promotionRegistDTO,principal); @@ -78,13 +75,10 @@ public ResponseEntity modifyNotice( promotionModifyDTO.setMemberId(memberLoginId); promotionModifyDTO.setContent(promotionModifyDTO.getContent()); if (file != null && !file.isEmpty()) { - System.out.println("response:1"); promotionModifyDTO.setFileUrl(s3FileService.uploadOneFile(file)); }else if(file==null || file.isEmpty()) { - System.out.println("response:2"); promotionModifyDTO.setFileUrl(null); } else { - System.out.println("response:3"); promotionModifyDTO.setFileUrl(s3FileService.uploadOneFile(file)); } promotionCommandService.modifyPromotion(promotionId,promotionModifyDTO,principal); diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java index 125b934c..30ea88c7 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java @@ -98,12 +98,10 @@ public PromotionModifyDTO modifyPromotion(String promotionId, PromotionModifyDTO public void deletePromotion(String promotionId, Principal principal) { redisService.clearPromotionCache(); String memberId= principal.getName(); - System.out.println(memberId); Promotion promotion = promotionRepository.findById(promotionId) .orElseThrow(()-> new PromotionCommonException(PromotionErrorCode.PROMOTION_NOT_FOUND)); if(promotion.getMemberId().equals(memberId)){ - System.out.println(promotion.getMemberId()); // 권한 오류 throw new PromotionCommonException(PromotionErrorCode.AUTHORIZATION_VIOLATION); } From 044c9d8ee18b76d5a25284ddb76345104b4b0558 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Tue, 3 Dec 2024 17:17:10 +0900 Subject: [PATCH 444/563] =?UTF-8?q?feat:=20dev=EC=97=90=EC=84=9C=20merge?= =?UTF-8?q?=ED=9B=84=EC=97=90=20=EC=88=98=EC=A0=95=EC=82=AC=ED=95=AD=20com?= =?UTF-8?q?mit=20(#182)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/query/repository/MemberMapper.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml index d8a8618a..7ecb0701 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml @@ -121,7 +121,7 @@ a.created_at, a.updated_at FROM tb_member a - WHERE a.mem_name = #{ name } + WHERE a.mem_id = #{ memberId } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SELECT COUNT(*) AS rank_count - FROM( + FROM ( SELECT - COUNT(*) + + + + LEFT(a.created_at, 10) AS period, + + + LEFT(a.created_at, 7) AS period, + + + LEFT(a.created_at, 4) AS period, + + + + + + a.mem_id, + + + a.cent_id, + + + SUM(a.sal_hist_ince) AS total_incentive, + SUM(a.sal_hist_no_of_veh) AS total_performance, + SUM(a.sal_hist_tota_sale) AS total_sales FROM tb_sales_history a + a.mem_id IN #{mem_id} + + a.cent_id IN + + #{cent_id} + + + AND a.created_at BETWEEN #{salesHistoryRankedDataDTO.startDate} AND #{salesHistoryRankedDataDTO.endDate} AND a.active = TRUE - GROUP BY a.mem_id) AS A + + + + GROUP BY + + + LEFT(a.created_at, 10), a.mem_id + + + LEFT(a.created_at, 7), a.mem_id + + + LEFT(a.created_at, 4), a.mem_id + + + + + GROUP BY + + + LEFT(a.created_at, 10), a.cent_id + + + LEFT(a.created_at, 7), a.cent_id + + + LEFT(a.created_at, 4), a.cent_id + + + + ) AS subquery SELECT a.mem_login_id, @@ -148,4 +157,100 @@ WHERE a.mem_name LIKE CONCAT('%', #{ memberName }, '%') + + + + \ No newline at end of file From be2231660eb324a351525a67aa388a9e01cf1683 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Wed, 4 Dec 2024 14:35:31 +0900 Subject: [PATCH 456/563] =?UTF-8?q?feat:=20=EC=9D=BC=EC=A0=95=20=EA=B6=8C?= =?UTF-8?q?=ED=95=9C=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=A0=84=EC=86=A1=20=EC=88=98=EC=A0=95=20(#216)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/domain/service/AlarmCommandServiceImpl.java | 7 +++++-- .../domain/alarm/scheduler/AlarmScheduler.java | 2 +- .../global/security/config/RequestMatcherConfig.java | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java index 975b49ab..12940f1b 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java @@ -161,8 +161,11 @@ public void sendNoticeAlarm(NoticeAlarmDTO noticeAlarmDTO){ memberIdList.addAll(memberQueryService.selectMemberByRole("god")); // 중복 제거 memberIdList = new ArrayList<>(new HashSet<>(memberIdList)); - } else { - memberQueryService.selectMemberByRole(noticeAlarmDTO.getTag()); + } else if (noticeAlarmDTO.getTag().equals("admin")){ + memberIdList.addAll(memberQueryService.selectMemberByRole("admin")); + memberIdList.addAll(memberQueryService.selectMemberByRole("god")); + }else { + memberIdList.addAll(memberQueryService.selectMemberByRole(noticeAlarmDTO.getTag())); } memberIdList.forEach(member -> { diff --git a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java index 52d580e3..fa7c4478 100644 --- a/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java +++ b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java @@ -33,7 +33,7 @@ public AlarmScheduler(AlarmCommandService alarmCommandService, ScheduleQueryServ } // @Scheduled(cron = "0 0 2 * * *") // 매일 새벽 2시에 실행) - @Scheduled(cron = "0 45 21 * * *") + @Scheduled(cron = "0 27 14 * * *") @Transactional public void alarmTodaySchedule(){ diff --git a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java index 38e3d409..7ea2824c 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java @@ -52,7 +52,7 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee").hasAnyRole("contract-employee-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 전체 조회 (전체) .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee/{contractId}").hasAnyRole("contract-employee-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 전체 조회 (전체) .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee/**").hasAnyRole("contract-employee-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 상세조회 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/contract/center/{contractId}").hasAnyRole("contract-employee-id-get", "GOD", "DIRECTOR", "ADMIN") // 계약서 상세조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/contract/center/{contractId}").hasAnyRole("contract-admin-id-get", "GOD", "DIRECTOR", "ADMIN") // 계약서 상세조회 (전체) .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee/search").hasAnyRole("contract-employee-search-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 검색 조회 (전체) // Customer API From 1eb3d6f0967b062191f657dfec08702977b8c156 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Wed, 4 Dec 2024 15:04:59 +0900 Subject: [PATCH 457/563] =?UTF-8?q?feat:=20=EC=82=AC=EC=9B=90=20=EC=97=91?= =?UTF-8?q?=EC=85=80=20=EB=8B=A4=EC=9A=B4=EB=A1=9C=EB=93=9C(#227)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/MemberController.java | 15 +++++ .../member/query/dto/MemberExcelDTO.java | 57 +++++++++++++++++++ .../member/query/repository/MemberMapper.java | 3 + .../query/service/MemberQueryService.java | 3 + .../query/service/MemberQueryServiceImpl.java | 28 ++++++++- .../member/query/repository/MemberMapper.xml | 39 +++++++++++++ 6 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberExcelDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java b/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java index 1cce3671..14442ac9 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/controller/MemberController.java @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; @@ -14,6 +15,7 @@ import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import stanl_2.final_backend.domain.customer.common.response.CustomerResponseMessage; import stanl_2.final_backend.domain.member.common.response.MemberResponseMessage; import stanl_2.final_backend.domain.member.query.dto.MemberCenterListDTO; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; @@ -209,5 +211,18 @@ public ResponseEntity getMemberByName( } + @Operation(summary = "엑셀 다운로드") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = CustomerResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("excel") + public void exportCustomer(HttpServletResponse response) throws GeneralSecurityException { + + memberQueryService.exportCustomerToExcel(response); + } + } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberExcelDTO.java b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberExcelDTO.java new file mode 100644 index 00000000..1b3bdcc0 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/member/query/dto/MemberExcelDTO.java @@ -0,0 +1,57 @@ +package stanl_2.final_backend.domain.member.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import stanl_2.final_backend.global.excel.ExcelColumnName; + +@Getter +@Setter +@AllArgsConstructor +public class MemberExcelDTO { + + @ExcelColumnName(name = "사원 번호") + private String loginId; + + @ExcelColumnName(name = "사원명") + private String name; + + @ExcelColumnName(name = "사원 이메일") + private String email; + + @ExcelColumnName(name = "사원 나이") + private Integer age; + + @ExcelColumnName(name = "사원 성별") + private String sex; + + @ExcelColumnName(name = "사원 연락처") + private String phone; + + @ExcelColumnName(name = "사원 비상연락처") + private String emergePhone; + + @ExcelColumnName(name = "사원 주소") + private String address; + + @ExcelColumnName(name = "비고") + private String note; + + @ExcelColumnName(name = "직급") + private String position; + + @ExcelColumnName(name = "학력") + private String grade; + + @ExcelColumnName(name = "고용형태") + private String jobType; + + @ExcelColumnName(name = "병역구분") + private String military; + + @ExcelColumnName(name = "은행명") + private String bankName; + + @ExcelColumnName(name = "계좌 번호") + private String account; +} diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java b/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java index 1c3a8702..a22d5c90 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/repository/MemberMapper.java @@ -4,6 +4,7 @@ import org.apache.ibatis.annotations.Param; import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; +import stanl_2.final_backend.domain.member.query.dto.MemberExcelDTO; import stanl_2.final_backend.domain.member.query.dto.MemberSearchDTO; import java.util.List; @@ -28,4 +29,6 @@ public interface MemberMapper { List findMemberByConditions(Map params); Integer findMemberCnt(Map params); + + List findMemberForExcel(); } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java index 0b96033f..bc55205b 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.member.query.service; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; @@ -26,5 +27,7 @@ public interface MemberQueryService { Page selectMemberBySearch(Pageable pageable, MemberSearchDTO memberSearchDTO) throws GeneralSecurityException; + void exportCustomerToExcel(HttpServletResponse response) throws GeneralSecurityException; + // MemberDetailDTO selectMemberDetail(String name) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index dce55397..8beeddbc 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.member.query.service; +import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; @@ -14,9 +15,11 @@ import stanl_2.final_backend.domain.member.common.exception.MemberCommonException; import stanl_2.final_backend.domain.member.common.exception.MemberErrorCode; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; +import stanl_2.final_backend.domain.member.query.dto.MemberExcelDTO; import stanl_2.final_backend.domain.member.query.dto.MemberSearchDTO; import stanl_2.final_backend.domain.member.query.repository.MemberMapper; import stanl_2.final_backend.domain.member.query.repository.MemberRoleMapper; +import stanl_2.final_backend.global.excel.ExcelUtilsV1; import stanl_2.final_backend.global.utils.AESUtils; import java.security.GeneralSecurityException; @@ -32,14 +35,16 @@ public class MemberQueryServiceImpl implements MemberQueryService { private final CenterQueryService centerQueryService; private final AESUtils aesUtils; private final MemberRoleMapper memberRoleMapper; + private final ExcelUtilsV1 excelUtilsV1; @Autowired public MemberQueryServiceImpl(MemberMapper memberMapper, CenterQueryService centerQueryService, AESUtils aesUtils, - MemberRoleMapper memberRoleMapper) { + MemberRoleMapper memberRoleMapper, ExcelUtilsV1 excelUtilsV1) { this.memberMapper = memberMapper; this.centerQueryService = centerQueryService; this.aesUtils = aesUtils; this.memberRoleMapper = memberRoleMapper; + this.excelUtilsV1 = excelUtilsV1; } @Override @@ -216,4 +221,25 @@ public Page selectMemberBySearch(Pageable pageable, MemberSearc return new PageImpl<>(memberList, pageable, count); } + + @Override + public void exportCustomerToExcel(HttpServletResponse response) throws GeneralSecurityException { + List memberExcels = memberMapper.findMemberForExcel(); + + if(memberExcels == null){ + throw new MemberCommonException(MemberErrorCode.MEMBER_NOT_FOUND); + } + + for(int i=0;i + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From e18490008638babc05af92ebb5e8d3c17739bfc1 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Wed, 4 Dec 2024 15:33:10 +0900 Subject: [PATCH 458/563] =?UTF-8?q?fix:=20=EC=82=AC=EC=9B=90=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EC=A1=B0=EA=B1=B4=EC=97=90=20=EB=94=B0=EB=A5=B8=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=88=98=EC=A0=95(#227)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/query/service/MemberQueryServiceImpl.java | 6 ++++++ .../domain/member/query/repository/MemberMapper.xml | 12 ++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index 8beeddbc..97664046 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -201,6 +201,12 @@ public Page selectMemberBySearch(Pageable pageable, MemberSearc params.put("sortField", sortField); params.put("sortOrder", sortOrder); + // 암호화 시켜서 검색 + memberSearchDTO.setLoginId(aesUtils.encrypt(memberSearchDTO.getLoginId())); + memberSearchDTO.setMemberName(aesUtils.encrypt(memberSearchDTO.getMemberName())); + memberSearchDTO.setPhone(aesUtils.encrypt(memberSearchDTO.getPhone())); + memberSearchDTO.setEmail(aesUtils.encrypt(memberSearchDTO.getEmail())); + params.put("loginId", memberSearchDTO.getLoginId()); params.put("memberName", memberSearchDTO.getMemberName()); params.put("phone", memberSearchDTO.getPhone()); diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml index 5151110c..42ba3cab 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml @@ -193,13 +193,13 @@ AND b.mem_login_id = #{ loginId } - AND b.mem_name LIKE CONCAT('%', #{ memberName }, '%') + AND b.mem_name = #{ memberName } - AND b.mem_pho LIKE CONCAT('%', #{ phone }, '%') + AND b.mem_pho = #{ phone } - AND b.mem_ema LIKE CONCAT('%', #{ email }, '%') + AND b.mem_ema = #{ email } AND a.cent_name LIKE CONCAT('%', #{ centerName }, '%') @@ -255,13 +255,13 @@ AND b.mem_login_id = #{ loginId } - AND b.mem_name LIKE CONCAT('%', #{ memberName }, '%') + AND b.mem_name = #{ memberName } - AND b.mem_pho LIKE CONCAT('%', #{ phone }, '%') + AND b.mem_pho = #{ phone } - AND b.mem_ema LIKE CONCAT('%', #{ email }, '%') + AND b.mem_ema = #{ email } AND a.cent_name LIKE CONCAT('%', #{ centerName }, '%') From 2010646374ec3d6b71a9fec5d80932f588392ca7 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Wed, 4 Dec 2024 17:25:51 +0900 Subject: [PATCH 459/563] =?UTF-8?q?fix:=20data=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=ED=99=94=20=EC=88=98=EC=A0=95(#216)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/dataloader/Initializer.java | 696 ++++++++++-------- 1 file changed, 370 insertions(+), 326 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java index f9c00c38..2d2e0074 100644 --- a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java +++ b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java @@ -12,10 +12,12 @@ import stanl_2.final_backend.domain.career.command.domain.repository.CareerRepository; import stanl_2.final_backend.domain.center.command.application.dto.request.CenterRegistDTO; import stanl_2.final_backend.domain.center.command.application.service.CenterCommandService; +import stanl_2.final_backend.domain.center.command.domain.repository.CenterRepository; import stanl_2.final_backend.domain.certification.command.domain.aggregate.entity.Certification; import stanl_2.final_backend.domain.certification.command.domain.repository.CertificationRepository; import stanl_2.final_backend.domain.customer.command.application.dto.CustomerRegistDTO; import stanl_2.final_backend.domain.customer.command.application.service.CustomerCommandService; +import stanl_2.final_backend.domain.customer.command.domain.repository.CustomerRepository; import stanl_2.final_backend.domain.education.command.domain.aggregate.entity.Education; import stanl_2.final_backend.domain.education.command.domain.repository.EducationRepository; import stanl_2.final_backend.domain.family.command.domain.aggregate.entity.Family; @@ -55,7 +57,9 @@ public class Initializer implements ApplicationRunner { private final FamilyRepository familyRepository; private final OrganizationRepository organizationRepository; private final CustomerCommandService customerCommandService; + private final CustomerRepository customerRepository; private final CenterCommandService centerCommandService; + private final CenterRepository centerRepository; private final ProductRepository productRepository; private final ProductOptionRepository productOptionRepository; @@ -156,7 +160,7 @@ public void run(ApplicationArguments args) throws Exception { ); Random random = new Random(); - String[] positions = {"EMPOLYEE", "ADMIN", "DIRECTOR"}; + String[] positions = {"영업 사원", "영업 관리자", "영업 담당자"}; String[] grades = {"A", "B", "C", "D"}; String[] jobTypes = {"REGULAR", "TEMPORARY"}; String[] militaryStatus = {"fulfilled", "exemption", "unfulfilled"}; @@ -185,7 +189,7 @@ public void run(ApplicationArguments args) throws Exception { String sex = genders[i % 2]; String name = lastNames[random.nextInt(lastNames.length)] + firstNames[random.nextInt(firstNames.length)]; String address = addresses[random.nextInt(addresses.length)]; - + int n = random.nextInt(positions.length); createOrUpdateMember( String.format("M%09d", i), "pass" + i, @@ -193,10 +197,10 @@ public void run(ApplicationArguments args) throws Exception { "user" + i + "@example.com", 20 + random.nextInt(30), // Random age between 20-49 sex, - String.format("123456-%07d", 1000000 + random.nextInt(9000000)), - "010-1234-567" + i, + String.format("123456-1%06d", 100000 + random.nextInt(900000)), + String.format("010-1234-%04d", i), address, - positions[random.nextInt(positions.length)], // Random position + positions[n], // Random position grades[random.nextInt(grades.length)], // Random grade jobTypes[random.nextInt(jobTypes.length)], // Random job type militaryStatus[random.nextInt(militaryStatus.length)], // Random military status @@ -204,7 +208,7 @@ public void run(ApplicationArguments args) throws Exception { "123456789" + i, String.format("CEN_%09d", centerId), String.format("ORG_%09d", orgId), - roles[random.nextInt(roles.length)], // Random role + roles[n], // Random role loadImage("default.png") ); } @@ -265,16 +269,20 @@ public void run(ApplicationArguments args) throws Exception { }; // Career(경력) 저장 - for (int i = 1; i <= 100; i++) { - String memberId = String.format("MEM_%09d", i); - for (int j = 0; j < 4; j++) { - Career newCareer = new Career(); - newCareer.setEmplDate(getRandomEmploymentDate()); - newCareer.setResignDate(getRandomResignationDate(LocalDate.parse(newCareer.getEmplDate(), DateTimeFormatter.ofPattern("yyyy-MM-dd")))); - newCareer.setName(salesCareers[random.nextInt(salesCareers.length)]); - newCareer.setMemberId(memberId); - careerRepository.save(newCareer); + if(careerRepository.count() == 0){ + for (int i = 1; i <= 100; i++) { + String memberId = String.format("MEM_%09d", i); + for (int j = 0; j < 4; j++) { + Career newCareer = new Career(); + newCareer.setEmplDate(getRandomEmploymentDate()); + newCareer.setResignDate(getRandomResignationDate(LocalDate.parse(newCareer.getEmplDate(), DateTimeFormatter.ofPattern("yyyy-MM-dd")))); + newCareer.setName(salesCareers[random.nextInt(salesCareers.length)]); + newCareer.setMemberId(memberId); + careerRepository.save(newCareer); + } } + }else{ + log.info("Career(경력) 테이블에 데이터가 존재합니다."); } String[][] salesCertifications = { @@ -331,18 +339,22 @@ public void run(ApplicationArguments args) throws Exception { }; // Certification(자격증) 저장 - for (int i = 1; i <= 100; i++) { - String memberId = String.format("MEM_%09d", i); - for (int j = 0; j < 4; j++) { - Certification newCertification = new Certification(); - newCertification.setAcquisitionDate(getRandomEmploymentDate()); - int n = random.nextInt(salesCareers.length); - newCertification.setAgency(salesCertifications[n][1]); - newCertification.setName(salesCertifications[n][0]); - newCertification.setScore(String.valueOf(random.nextInt(101))); - newCertification.setMemberId(memberId); - certificationRepository.save(newCertification); + if(certificationRepository.count() == 0){ + for (int i = 1; i <= 100; i++) { + String memberId = String.format("MEM_%09d", i); + for (int j = 0; j < 4; j++) { + Certification newCertification = new Certification(); + newCertification.setAcquisitionDate(getRandomEmploymentDate()); + int n = random.nextInt(salesCareers.length); + newCertification.setAgency(salesCertifications[n][1]); + newCertification.setName(salesCertifications[n][0]); + newCertification.setScore(String.valueOf(random.nextInt(101))); + newCertification.setMemberId(memberId); + certificationRepository.save(newCertification); + } } + } else { + log.info("Certification(자격증) 테이블에 데이터가 존재합니다."); } // 전공 @@ -424,16 +436,20 @@ public void run(ApplicationArguments args) throws Exception { }; // Education(학력) 저장 - for (int i = 1; i < 100; i++) { - String memberId = String.format("MEM_%09d", i); - Education newEducation = new Education(); - newEducation.setEntranceDate(getRandomEmploymentDate()); - newEducation.setGraduationDate(getRandomResignationDate(LocalDate.parse(newEducation.getEntranceDate(), DateTimeFormatter.ofPattern("yyyy-MM-dd")))); - newEducation.setMajor(salesMajors[random.nextInt(salesMajors.length)]); - newEducation.setName(universities[random.nextInt(universities.length)]); - newEducation.setScore(String.valueOf(2.0 + (Math.random() * (4.5 - 2.0)))); - newEducation.setMemId(memberId); - educationRepository.save(newEducation); + if(educationRepository.count()==0){ + for (int i = 1; i < 100; i++) { + String memberId = String.format("MEM_%09d", i); + Education newEducation = new Education(); + newEducation.setEntranceDate(getRandomEmploymentDate()); + newEducation.setGraduationDate(getRandomResignationDate(LocalDate.parse(newEducation.getEntranceDate(), DateTimeFormatter.ofPattern("yyyy-MM-dd")))); + newEducation.setMajor(salesMajors[random.nextInt(salesMajors.length)]); + newEducation.setName(universities[random.nextInt(universities.length)]); + newEducation.setScore(String.format("%.2f", 2.0 + (Math.random() * (4.5 - 2.0)))); + newEducation.setMemId(memberId); + educationRepository.save(newEducation); + } + } else{ + log.info("Education(학력) 테이블에 이미 값이 존재합니다."); } // 관계 @@ -461,87 +477,101 @@ public void run(ApplicationArguments args) throws Exception { ); // Family(가족) 저장 - for (int i = 1; i <= 100; i++) { - String memberId = String.format("MEM_%09d", i); - for (int j = 0; j < 4; j++) { - // 주민등록번호 생성 - String birthDateStr = randomBirthDate.format(DateTimeFormatter.ofPattern("yyMMdd")); // YYMMDD 형식 - String genderDigit = Math.random() < 0.5 ? "1" : "2"; // 성별은 1(남) 또는 2(여) - String randomNumbers = String.format("%04d", (int) (Math.random() * 10000)); // 임의의 4자리 번호 - String checkDigit = String.format("%d", (int) (Math.random() * 10)); // 검증용 마지막 자리는 0~9 - - // 주민등록번호 만들기 - String identNo = birthDateStr + genderDigit + randomNumbers + checkDigit; - - Family newFamily = new Family(); - newFamily.setName(lastNames[random.nextInt(lastNames.length)] + firstNames[random.nextInt(firstNames.length)]); - newFamily.setRelation(familyRelations[random.nextInt(familyRelations.length)]); - newFamily.setSex(Math.random() < 0.5 ? "MALE" : "FEMALE"); - newFamily.setBirth(birthDateStr); - newFamily.setPhone(randomPhone); - newFamily.setDie(Math.random() < 0.5 ? true : false); - newFamily.setDisability(Math.random() < 0.5 ? true : false); - newFamily.setIdentNo(identNo); - newFamily.setMemId(memberId); - - familyRepository.save(newFamily); + if(familyRepository.count() == 0){ + for (int i = 1; i <= 100; i++) { + String memberId = String.format("MEM_%09d", i); + for (int j = 0; j < 4; j++) { + // 주민등록번호 생성 + String birthDateStr = randomBirthDate.format(DateTimeFormatter.ofPattern("yyMMdd")); // YYMMDD 형식 + String birthDateStr2 = randomBirthDate.format(DateTimeFormatter.ofPattern("yyyy년 MM월 dd일")); // YYMMDD 형식 + String genderDigit = Math.random() < 0.5 ? "1" : "2"; // 성별은 1(남) 또는 2(여) + String randomNumbers = String.format("%04d", (int) (Math.random() * 10000)); // 임의의 4자리 번호 + String checkDigit = String.format("%d", (int) (Math.random() * 10)); // 검증용 마지막 자리는 0~9 + + // 주민등록번호 만들기 + String identNo = birthDateStr + genderDigit + "-" + randomNumbers + checkDigit; + + Family newFamily = new Family(); + newFamily.setName(lastNames[random.nextInt(lastNames.length)] + firstNames[random.nextInt(firstNames.length)]); + newFamily.setRelation(familyRelations[random.nextInt(familyRelations.length)]); + newFamily.setSex(Math.random() < 0.5 ? "MALE" : "FEMALE"); + newFamily.setBirth(birthDateStr2); + newFamily.setPhone(randomPhone); + newFamily.setDie(Math.random() < 0.5 ? true : false); + newFamily.setDisability(Math.random() < 0.5 ? true : false); + newFamily.setIdentNo(identNo); + newFamily.setMemId(memberId); + + familyRepository.save(newFamily); + } } + } else{ + log.info("family(가족) 테이블에 이미 데이터가 존재합니다."); } // 부서 저장 - Organization org1 = new Organization("ORG_000000001", "서울 지사", null); - organizationRepository.save(org1); - Organization org2 = new Organization("ORG_000000002", "부산 지사", null); - organizationRepository.save(org2); - Organization org3 = new Organization("ORG_000000003", "인천 지사", null); - organizationRepository.save(org3); - Organization org4 = new Organization("ORG_000000004", "대전 지사", null); - organizationRepository.save(org4); - Organization org5 = new Organization("ORG_000000005", "영업부(서울 1팀)", "ORG_000000001"); - organizationRepository.save(org5); - Organization org6 = new Organization("ORG_000000006", "영업부(서울 2팀)", "ORG_000000001"); - organizationRepository.save(org6); - Organization org7 = new Organization("ORG_000000007", "영업부(부산 1팀)", "ORG_000000002"); - organizationRepository.save(org7); - Organization org8 = new Organization("ORG_000000008", "영업부(부산 2팀)", "ORG_000000002"); - organizationRepository.save(org8); - Organization org9 = new Organization("ORG_000000009", "영업부(인천 1팀)", "ORG_000000003"); - organizationRepository.save(org9); - Organization org10 = new Organization("ORG_000000010", "영업부(인천 2팀)", "ORG_000000003"); - organizationRepository.save(org10); - Organization org11 = new Organization("ORG_000000011", "영업부(대전 1팀)", "ORG_000000004"); - organizationRepository.save(org11); - Organization org12 = new Organization("ORG_000000012", "영업부(대전 2팀)", "ORG_000000004"); - organizationRepository.save(org12); - Organization org13 = new Organization("ORG_000000013", "마케팅부", "ORG_000000001"); - organizationRepository.save(org13); - Organization org14 = new Organization("ORG_000000014", "기술부", "ORG_000000001"); - organizationRepository.save(org14); + if(organizationRepository.count() == 0){ + Organization org1 = new Organization("ORG_000000001", "서울 지사", null); + organizationRepository.save(org1); + Organization org2 = new Organization("ORG_000000002", "부산 지사", null); + organizationRepository.save(org2); + Organization org3 = new Organization("ORG_000000003", "인천 지사", null); + organizationRepository.save(org3); + Organization org4 = new Organization("ORG_000000004", "대전 지사", null); + organizationRepository.save(org4); + Organization org5 = new Organization("ORG_000000005", "영업부(서울 1팀)", "ORG_000000001"); + organizationRepository.save(org5); + Organization org6 = new Organization("ORG_000000006", "영업부(서울 2팀)", "ORG_000000001"); + organizationRepository.save(org6); + Organization org7 = new Organization("ORG_000000007", "영업부(부산 1팀)", "ORG_000000002"); + organizationRepository.save(org7); + Organization org8 = new Organization("ORG_000000008", "영업부(부산 2팀)", "ORG_000000002"); + organizationRepository.save(org8); + Organization org9 = new Organization("ORG_000000009", "영업부(인천 1팀)", "ORG_000000003"); + organizationRepository.save(org9); + Organization org10 = new Organization("ORG_000000010", "영업부(인천 2팀)", "ORG_000000003"); + organizationRepository.save(org10); + Organization org11 = new Organization("ORG_000000011", "영업부(대전 1팀)", "ORG_000000004"); + organizationRepository.save(org11); + Organization org12 = new Organization("ORG_000000012", "영업부(대전 2팀)", "ORG_000000004"); + organizationRepository.save(org12); + Organization org13 = new Organization("ORG_000000013", "마케팅부", "ORG_000000001"); + organizationRepository.save(org13); + Organization org14 = new Organization("ORG_000000014", "기술부", "ORG_000000001"); + organizationRepository.save(org14); + } else{ + log.info("organization(조직도) 테이블에 이미 데이터가 존재합니다."); + } // 고객 인당 100명 씩 - for (int i = 1; i <= 100; i++) { - String memberId = String.format("MEM_%09d", i); - for (int j = 0; j < 100; j++) { - String sex = genders[i % 2]; - String name = lastNames[random.nextInt(lastNames.length)] + firstNames[random.nextInt(firstNames.length)]; - int randomAge = ThreadLocalRandom.current().nextInt(20, 71); - String randomName = "user" + ThreadLocalRandom.current().nextInt(1000, 10000); - String randomEmail = randomName + "@example.com"; - - CustomerRegistDTO newCustomer = new CustomerRegistDTO(); - newCustomer.setName(name); - newCustomer.setAge(randomAge); - newCustomer.setPhone(randomPhone); - newCustomer.setEmergePhone(randomPhone); - newCustomer.setEmail(randomEmail); - newCustomer.setSex(sex); - newCustomer.setMemberId(memberId); - customerCommandService.registerCustomerInfo(newCustomer); + if(customerRepository.count() == 0){ + for (int i = 1; i <= 100; i++) { + String memberId = String.format("MEM_%09d", i); + for (int j = 0; j < 100; j++) { + String sex = genders[i % 2]; + String name = lastNames[random.nextInt(lastNames.length)] + firstNames[random.nextInt(firstNames.length)]; + int randomAge = ThreadLocalRandom.current().nextInt(20, 71); + String randomName = "user" + ThreadLocalRandom.current().nextInt(1000, 10000); + String randomEmail = randomName + "@example.com"; + + CustomerRegistDTO newCustomer = new CustomerRegistDTO(); + newCustomer.setName(name); + newCustomer.setAge(randomAge); + newCustomer.setPhone(randomPhone); + newCustomer.setEmergePhone(randomPhone); + newCustomer.setEmail(randomEmail); + newCustomer.setSex(sex); + newCustomer.setMemberId(memberId); + customerCommandService.registerCustomerInfo(newCustomer); + } } + } else { + log.info("customer(고객) 테이블에 이미 데이터가 존재합니다."); } + String[][] carDealershipsInfo = { {"서울 중앙 영업점", "서울특별시 중구 세종대로 123"}, {"부산 서면 영업점", "부산광역시 부산진구 중앙대로 456"}, @@ -556,330 +586,344 @@ public void run(ApplicationArguments args) throws Exception { }; // Center 매장 등록 - CenterRegistDTO newCenter1 = new CenterRegistDTO("서울 중앙 영업점", "서울특별시 중구 세종대로 123", "02-123-4567", 25, "09:00 - 18:00", null); - centerCommandService.registCenter(newCenter1, loadImage("default.png")); - CenterRegistDTO newCenter2 = new CenterRegistDTO("부산 서면 영업점", "부산광역시 부산진구 중앙대로 456", "051-123-4567", 20, "09:00 - 18:00", null); - centerCommandService.registCenter(newCenter2, loadImage("default.png")); - CenterRegistDTO newCenter3 = new CenterRegistDTO("인천 남동구 영업점", "인천광역시 남동구 인주대로 789", "032-123-4567", 18, "09:00 - 18:00", null); - centerCommandService.registCenter(newCenter3, loadImage("default.png")); - CenterRegistDTO newCenter4 = new CenterRegistDTO("대전 둔산 영업점", "대전광역시 서구 둔산로 101", "042-123-4567", 15, "09:00 - 18:00", null); - centerCommandService.registCenter(newCenter4, loadImage("default.png")); - CenterRegistDTO newCenter5 = new CenterRegistDTO("광주 광산구 영업점", "광주광역시 광산구 상무대로 202", "062-123-4567", 17, "09:00 - 18:00", null); - centerCommandService.registCenter(newCenter5, loadImage("default.png")); - CenterRegistDTO newCenter6 = new CenterRegistDTO("대구 동구 영업점", "대구광역시 동구 아양로 303", "053-123-4567", 22, "09:00 - 18:00", null); - centerCommandService.registCenter(newCenter6, loadImage("default.png")); - CenterRegistDTO newCenter7 = new CenterRegistDTO("울산 중구 영업점", "울산광역시 중구 태화로 404", "052-123-4567", 13, "09:00 - 18:00", null); - centerCommandService.registCenter(newCenter7, loadImage("default.png")); - CenterRegistDTO newCenter8 = new CenterRegistDTO("수원 영통구 영업점", "경기도 수원시 영통구 매탄로 505", "031-123-4567", 19, "09:00 - 18:00", null); - centerCommandService.registCenter(newCenter8, loadImage("default.png")); - CenterRegistDTO newCenter9 = new CenterRegistDTO("성남 분당 영업점", "경기도 성남시 분당구 정자일로 606", "031-234-5678", 21, "09:00 - 18:00", null); - centerCommandService.registCenter(newCenter9, loadImage("default.png")); - CenterRegistDTO newCenter10 = new CenterRegistDTO("제주 서귀포 영업점", "제주특별자치도 서귀포시 태평로 707", "064-123-4567", 12, "09:00 - 18:00", null); - centerCommandService.registCenter(newCenter10, loadImage("default.png")); + if(centerRepository.count() == 0){ + CenterRegistDTO newCenter1 = new CenterRegistDTO("서울 중앙 영업점", "서울특별시 중구 세종대로 123", "02-123-4567", 25, "09:00 - 18:00", null); + centerCommandService.registCenter(newCenter1, loadImage("default.png")); + CenterRegistDTO newCenter2 = new CenterRegistDTO("부산 서면 영업점", "부산광역시 부산진구 중앙대로 456", "051-123-4567", 20, "09:00 - 18:00", null); + centerCommandService.registCenter(newCenter2, loadImage("default.png")); + CenterRegistDTO newCenter3 = new CenterRegistDTO("인천 남동구 영업점", "인천광역시 남동구 인주대로 789", "032-123-4567", 18, "09:00 - 18:00", null); + centerCommandService.registCenter(newCenter3, loadImage("default.png")); + CenterRegistDTO newCenter4 = new CenterRegistDTO("대전 둔산 영업점", "대전광역시 서구 둔산로 101", "042-123-4567", 15, "09:00 - 18:00", null); + centerCommandService.registCenter(newCenter4, loadImage("default.png")); + CenterRegistDTO newCenter5 = new CenterRegistDTO("광주 광산구 영업점", "광주광역시 광산구 상무대로 202", "062-123-4567", 17, "09:00 - 18:00", null); + centerCommandService.registCenter(newCenter5, loadImage("default.png")); + CenterRegistDTO newCenter6 = new CenterRegistDTO("대구 동구 영업점", "대구광역시 동구 아양로 303", "053-123-4567", 22, "09:00 - 18:00", null); + centerCommandService.registCenter(newCenter6, loadImage("default.png")); + CenterRegistDTO newCenter7 = new CenterRegistDTO("울산 중구 영업점", "울산광역시 중구 태화로 404", "052-123-4567", 13, "09:00 - 18:00", null); + centerCommandService.registCenter(newCenter7, loadImage("default.png")); + CenterRegistDTO newCenter8 = new CenterRegistDTO("수원 영통구 영업점", "경기도 수원시 영통구 매탄로 505", "031-123-4567", 19, "09:00 - 18:00", null); + centerCommandService.registCenter(newCenter8, loadImage("default.png")); + CenterRegistDTO newCenter9 = new CenterRegistDTO("성남 분당 영업점", "경기도 성남시 분당구 정자일로 606", "031-234-5678", 21, "09:00 - 18:00", null); + centerCommandService.registCenter(newCenter9, loadImage("default.png")); + CenterRegistDTO newCenter10 = new CenterRegistDTO("제주 서귀포 영업점", "제주특별자치도 서귀포시 태평로 707", "064-123-4567", 12, "09:00 - 18:00", null); + centerCommandService.registCenter(newCenter10, loadImage("default.png")); + } else { + log.info("center(매장) 테이블에 데이터가 이미 존재합니다."); + } // Product 제품 등록 - Product newProduct1 = new Product("KIA15-S001", 20000000, "기아 소렌토 2015", 10); - productRepository.save(newProduct1); + if(productRepository.count() == 0){ + Product newProduct1 = new Product("KIA15-S001", 20000000, "기아 소렌토 2015", 10); + productRepository.save(newProduct1); - Product newProduct2 = new Product("KIA15-S002", 18000000, "기아 스포티지 2015", 8); - productRepository.save(newProduct2); + Product newProduct2 = new Product("KIA15-S002", 18000000, "기아 스포티지 2015", 8); + productRepository.save(newProduct2); - Product newProduct3 = new Product("KIA16-S003", 22000000, "기아 소렌토 2016", 12); - productRepository.save(newProduct3); + Product newProduct3 = new Product("KIA16-S003", 22000000, "기아 소렌토 2016", 12); + productRepository.save(newProduct3); - Product newProduct4 = new Product("KIA16-S004", 19500000, "기아 옵티마 2016", 5); - productRepository.save(newProduct4); + Product newProduct4 = new Product("KIA16-S004", 19500000, "기아 옵티마 2016", 5); + productRepository.save(newProduct4); - Product newProduct5 = new Product("KIA17-S005", 25000000, "기아 스팅어 2017", 7); - productRepository.save(newProduct5); + Product newProduct5 = new Product("KIA17-S005", 25000000, "기아 스팅어 2017", 7); + productRepository.save(newProduct5); - Product newProduct6 = new Product("KIA17-S006", 21000000, "기아 스포티지 2017", 9); - productRepository.save(newProduct6); + Product newProduct6 = new Product("KIA17-S006", 21000000, "기아 스포티지 2017", 9); + productRepository.save(newProduct6); - Product newProduct7 = new Product("KIA18-S007", 23000000, "기아 소렌토 2018", 6); - productRepository.save(newProduct7); + Product newProduct7 = new Product("KIA18-S007", 23000000, "기아 소렌토 2018", 6); + productRepository.save(newProduct7); - Product newProduct8 = new Product("KIA18-S008", 20000000, "기아 리오 2018", 11); - productRepository.save(newProduct8); + Product newProduct8 = new Product("KIA18-S008", 20000000, "기아 리오 2018", 11); + productRepository.save(newProduct8); - Product newProduct9 = new Product("KIA19-S009", 27000000, "기아 스팅어 2019", 4); - productRepository.save(newProduct9); + Product newProduct9 = new Product("KIA19-S009", 27000000, "기아 스팅어 2019", 4); + productRepository.save(newProduct9); - Product newProduct10 = new Product("KIA19-S010", 21500000, "기아 포르테 2019", 3); - productRepository.save(newProduct10); + Product newProduct10 = new Product("KIA19-S010", 21500000, "기아 포르테 2019", 3); + productRepository.save(newProduct10); - Product newProduct11 = new Product("KIA20-S011", 29000000, "기아 텔루라이드 2020", 10); - productRepository.save(newProduct11); + Product newProduct11 = new Product("KIA20-S011", 29000000, "기아 텔루라이드 2020", 10); + productRepository.save(newProduct11); - Product newProduct12 = new Product("KIA20-S012", 25000000, "기아 옵티마 2020", 7); - productRepository.save(newProduct12); + Product newProduct12 = new Product("KIA20-S012", 25000000, "기아 옵티마 2020", 7); + productRepository.save(newProduct12); - Product newProduct13 = new Product("KIA21-S013", 30000000, "기아 소렌토 2021", 12); - productRepository.save(newProduct13); + Product newProduct13 = new Product("KIA21-S013", 30000000, "기아 소렌토 2021", 12); + productRepository.save(newProduct13); - Product newProduct14 = new Product("KIA21-S014", 27000000, "기아 카니발 2021", 5); - productRepository.save(newProduct14); + Product newProduct14 = new Product("KIA21-S014", 27000000, "기아 카니발 2021", 5); + productRepository.save(newProduct14); - Product newProduct15 = new Product("KIA22-S015", 32000000, "기아 EV6 2022", 6); - productRepository.save(newProduct15); + Product newProduct15 = new Product("KIA22-S015", 32000000, "기아 EV6 2022", 6); + productRepository.save(newProduct15); - Product newProduct16 = new Product("KIA22-S016", 28000000, "기아 셀토스 2022", 8); - productRepository.save(newProduct16); + Product newProduct16 = new Product("KIA22-S016", 28000000, "기아 셀토스 2022", 8); + productRepository.save(newProduct16); - Product newProduct17 = new Product("KIA23-S017", 33000000, "기아 소렌토 2023", 9); - productRepository.save(newProduct17); + Product newProduct17 = new Product("KIA23-S017", 33000000, "기아 소렌토 2023", 9); + productRepository.save(newProduct17); - Product newProduct18 = new Product("KIA23-S018", 29000000, "기아 스포티지 2023", 4); - productRepository.save(newProduct18); + Product newProduct18 = new Product("KIA23-S018", 29000000, "기아 스포티지 2023", 4); + productRepository.save(newProduct18); - Product newProduct19 = new Product("KIA24-S019", 35000000, "기아 EV9 2024", 3); - productRepository.save(newProduct19); + Product newProduct19 = new Product("KIA24-S019", 35000000, "기아 EV9 2024", 3); + productRepository.save(newProduct19); - Product newProduct20 = new Product("KIA24-S020", 31000000, "기아 스팅어 2024", 5); - productRepository.save(newProduct20); + Product newProduct20 = new Product("KIA24-S020", 31000000, "기아 스팅어 2024", 5); + productRepository.save(newProduct20); - Product newProduct21 = new Product("KIA15-S021", 17000000, "기아 리오 2015", 6); - productRepository.save(newProduct21); + Product newProduct21 = new Product("KIA15-S021", 17000000, "기아 리오 2015", 6); + productRepository.save(newProduct21); - Product newProduct22 = new Product("KIA16-S022", 21000000, "기아 포르테 2016", 4); - productRepository.save(newProduct22); + Product newProduct22 = new Product("KIA16-S022", 21000000, "기아 포르테 2016", 4); + productRepository.save(newProduct22); - Product newProduct23 = new Product("KIA17-S023", 24000000, "기아 옵티마 2017", 8); - productRepository.save(newProduct23); + Product newProduct23 = new Product("KIA17-S023", 24000000, "기아 옵티마 2017", 8); + productRepository.save(newProduct23); - Product newProduct24 = new Product("KIA18-S024", 22500000, "기아 소울 2018", 7); - productRepository.save(newProduct24); + Product newProduct24 = new Product("KIA18-S024", 22500000, "기아 소울 2018", 7); + productRepository.save(newProduct24); - Product newProduct25 = new Product("KIA19-S025", 28000000, "기아 텔루라이드 2019", 5); - productRepository.save(newProduct25); + Product newProduct25 = new Product("KIA19-S025", 28000000, "기아 텔루라이드 2019", 5); + productRepository.save(newProduct25); - Product newProduct26 = new Product("KIA20-S026", 26000000, "기아 니로 2020", 9); - productRepository.save(newProduct26); + Product newProduct26 = new Product("KIA20-S026", 26000000, "기아 니로 2020", 9); + productRepository.save(newProduct26); - Product newProduct27 = new Product("KIA21-S027", 29500000, "기아 카니발 2021", 3); - productRepository.save(newProduct27); + Product newProduct27 = new Product("KIA21-S027", 29500000, "기아 카니발 2021", 3); + productRepository.save(newProduct27); - Product newProduct28 = new Product("KIA22-S028", 31000000, "기아 소렌토 2022", 10); - productRepository.save(newProduct28); + Product newProduct28 = new Product("KIA22-S028", 31000000, "기아 소렌토 2022", 10); + productRepository.save(newProduct28); - Product newProduct29 = new Product("KIA23-S029", 34000000, "기아 EV6 2023", 6); - productRepository.save(newProduct29); + Product newProduct29 = new Product("KIA23-S029", 34000000, "기아 EV6 2023", 6); + productRepository.save(newProduct29); - Product newProduct30 = new Product("KIA24-S030", 32000000, "기아 셀토스 2024", 4); - productRepository.save(newProduct30); + Product newProduct30 = new Product("KIA24-S030", 32000000, "기아 셀토스 2024", 4); + productRepository.save(newProduct30); - Product newProduct31 = new Product("KIA15-S031", 18500000, "기아 소울 2015", 7); - productRepository.save(newProduct31); + Product newProduct31 = new Product("KIA15-S031", 18500000, "기아 소울 2015", 7); + productRepository.save(newProduct31); - Product newProduct32 = new Product("KIA16-S032", 22000000, "기아 소렌토 2016", 6); - productRepository.save(newProduct32); + Product newProduct32 = new Product("KIA16-S032", 22000000, "기아 소렌토 2016", 6); + productRepository.save(newProduct32); - Product newProduct33 = new Product("KIA17-S033", 25000000, "기아 포르테 2017", 5); - productRepository.save(newProduct33); + Product newProduct33 = new Product("KIA17-S033", 25000000, "기아 포르테 2017", 5); + productRepository.save(newProduct33); - Product newProduct34 = new Product("KIA18-S034", 23000000, "기아 리오 2018", 8); - productRepository.save(newProduct34); + Product newProduct34 = new Product("KIA18-S034", 23000000, "기아 리오 2018", 8); + productRepository.save(newProduct34); - Product newProduct35 = new Product("KIA19-S035", 27000000, "기아 스포티지 2019", 4); - productRepository.save(newProduct35); + Product newProduct35 = new Product("KIA19-S035", 27000000, "기아 스포티지 2019", 4); + productRepository.save(newProduct35); - Product newProduct36 = new Product("KIA20-S036", 29500000, "기아 텔루라이드 2020", 9); - productRepository.save(newProduct36); + Product newProduct36 = new Product("KIA20-S036", 29500000, "기아 텔루라이드 2020", 9); + productRepository.save(newProduct36); - Product newProduct37 = new Product("KIA21-S037", 28000000, "기아 셀토스 2021", 6); - productRepository.save(newProduct37); + Product newProduct37 = new Product("KIA21-S037", 28000000, "기아 셀토스 2021", 6); + productRepository.save(newProduct37); - Product newProduct38 = new Product("KIA22-S038", 31500000, "기아 스포티지 2022", 10); - productRepository.save(newProduct38); + Product newProduct38 = new Product("KIA22-S038", 31500000, "기아 스포티지 2022", 10); + productRepository.save(newProduct38); - Product newProduct39 = new Product("KIA23-S039", 33000000, "기아 스팅어 2023", 7); - productRepository.save(newProduct39); + Product newProduct39 = new Product("KIA23-S039", 33000000, "기아 스팅어 2023", 7); + productRepository.save(newProduct39); - Product newProduct40 = new Product("KIA24-S040", 35500000, "기아 EV9 2024", 3); - productRepository.save(newProduct40); + Product newProduct40 = new Product("KIA24-S040", 35500000, "기아 EV9 2024", 3); + productRepository.save(newProduct40); - Product newProduct41 = new Product("KIA15-S041", 19500000, "기아 옵티마 2015", 6); - productRepository.save(newProduct41); + Product newProduct41 = new Product("KIA15-S041", 19500000, "기아 옵티마 2015", 6); + productRepository.save(newProduct41); - Product newProduct42 = new Product("KIA16-S042", 22500000, "기아 소울 2016", 5); - productRepository.save(newProduct42); + Product newProduct42 = new Product("KIA16-S042", 22500000, "기아 소울 2016", 5); + productRepository.save(newProduct42); - Product newProduct43 = new Product("KIA17-S043", 25500000, "기아 니로 2017", 9); - productRepository.save(newProduct43); + Product newProduct43 = new Product("KIA17-S043", 25500000, "기아 니로 2017", 9); + productRepository.save(newProduct43); - Product newProduct44 = new Product("KIA18-S044", 24000000, "기아 포르테 2018", 4); - productRepository.save(newProduct44); + Product newProduct44 = new Product("KIA18-S044", 24000000, "기아 포르테 2018", 4); + productRepository.save(newProduct44); - Product newProduct45 = new Product("KIA19-S045", 28500000, "기아 텔루라이드 2019", 8); - productRepository.save(newProduct45); + Product newProduct45 = new Product("KIA19-S045", 28500000, "기아 텔루라이드 2019", 8); + productRepository.save(newProduct45); - Product newProduct46 = new Product("KIA20-S046", 26500000, "기아 소렌토 2020", 7); - productRepository.save(newProduct46); + Product newProduct46 = new Product("KIA20-S046", 26500000, "기아 소렌토 2020", 7); + productRepository.save(newProduct46); - Product newProduct47 = new Product("KIA21-S047", 30000000, "기아 스팅어 2021", 3); - productRepository.save(newProduct47); + Product newProduct47 = new Product("KIA21-S047", 30000000, "기아 스팅어 2021", 3); + productRepository.save(newProduct47); - Product newProduct48 = new Product("KIA22-S048", 32500000, "기아 EV6 2022", 6); - productRepository.save(newProduct48); + Product newProduct48 = new Product("KIA22-S048", 32500000, "기아 EV6 2022", 6); + productRepository.save(newProduct48); - Product newProduct49 = new Product("KIA23-S049", 34000000, "기아 카니발 2023", 10); - productRepository.save(newProduct49); + Product newProduct49 = new Product("KIA23-S049", 34000000, "기아 카니발 2023", 10); + productRepository.save(newProduct49); + + Product newProduct50 = new Product("KIA24-S050", 36000000, "기아 EV9 2024", 5); + productRepository.save(newProduct50); + } else { + log.info("Product(제품) 테이블에 이미 데이터가 존재합니다."); + } - Product newProduct50 = new Product("KIA24-S050", 36000000, "기아 EV9 2024", 5); - productRepository.save(newProduct50); // 제품 옵션 등록 - ProductOption newProductOption1 = new ProductOption('K', 'N', 'A', 'A', 'L', '4', '2', 'A', 'P', 'A', 'U', '1', '0', '1', 'B', 'W', '1', '1', '1', '1', '1', '0', '1', '1', '1'); - productOptionRepository.save(newProductOption1); + if(productOptionRepository.count() == 0){ + ProductOption newProductOption1 = new ProductOption('K', 'N', 'A', 'A', 'L', '4', '2', 'A', 'P', 'A', 'U', '1', '0', '1', 'B', 'W', '1', '1', '1', '1', '1', '0', '1', '1', '1'); + productOptionRepository.save(newProductOption1); + + ProductOption newProductOption2 = new ProductOption('K', 'N', 'H', 'B', 'L', '4', '4', 'B', 'R', 'B', 'Z', '1', '1', '0', 'G', 'B', '0', '1', '0', '1', '0', '1', '0', '0', '1'); + productOptionRepository.save(newProductOption2); - ProductOption newProductOption2 = new ProductOption('K', 'N', 'H', 'B', 'L', '4', '4', 'B', 'R', 'B', 'Z', '1', '1', '0', 'G', 'B', '0', '1', '0', '1', '0', '1', '0', '0', '1'); - productOptionRepository.save(newProductOption2); + ProductOption newProductOption3 = new ProductOption('K', 'M', 'H', 'C', 'M', '6', '3', 'C', 'P', 'C', 'A', '0', '1', '1', 'R', 'G', '1', '1', '1', '0', '1', '1', '0', '1', '0'); + productOptionRepository.save(newProductOption3); - ProductOption newProductOption3 = new ProductOption('K', 'M', 'H', 'C', 'M', '6', '3', 'C', 'P', 'C', 'A', '0', '1', '1', 'R', 'G', '1', '1', '1', '0', '1', '1', '0', '1', '0'); - productOptionRepository.save(newProductOption3); + ProductOption newProductOption4 = new ProductOption('K', 'N', 'J', 'D', 'L', '2', '4', 'A', 'R', 'D', 'C', '1', '0', '0', 'B', 'R', '0', '0', '0', '1', '0', '0', '1', '0', '1'); + productOptionRepository.save(newProductOption4); - ProductOption newProductOption4 = new ProductOption('K', 'N', 'J', 'D', 'L', '2', '4', 'A', 'R', 'D', 'C', '1', '0', '0', 'B', 'R', '0', '0', '0', '1', '0', '0', '1', '0', '1'); - productOptionRepository.save(newProductOption4); + ProductOption newProductOption5 = new ProductOption('K', 'M', 'F', 'B', 'S', '5', '2', 'D', 'O', 'B', 'A', '1', '1', '1', 'Y', 'S', '0', '1', '1', '1', '0', '0', '0', '1', '0'); + productOptionRepository.save(newProductOption5); - ProductOption newProductOption5 = new ProductOption('K', 'M', 'F', 'B', 'S', '5', '2', 'D', 'O', 'B', 'A', '1', '1', '1', 'Y', 'S', '0', '1', '1', '1', '0', '0', '0', '1', '0'); - productOptionRepository.save(newProductOption5); + ProductOption newProductOption6 = new ProductOption('K', 'N', 'A', 'F', 'L', '3', '3', 'F', 'B', 'Y', 'S', '2', '0', '1', 'T', 'B', '1', '1', '0', '0', '0', '1', '1', '1', '1'); + productOptionRepository.save(newProductOption6); - ProductOption newProductOption6 = new ProductOption('K', 'N', 'A', 'F', 'L', '3', '3', 'F', 'B', 'Y', 'S', '2', '0', '1', 'T', 'B', '1', '1', '0', '0', '0', '1', '1', '1', '1'); - productOptionRepository.save(newProductOption6); + ProductOption newProductOption7 = new ProductOption('K', 'M', 'F', 'A', 'J', '4', '2', 'D', 'S', 'C', 'V', '1', '0', '0', 'A', 'Q', '1', '1', '1', '0', '0', '0', '1', '0', '1'); + productOptionRepository.save(newProductOption7); - ProductOption newProductOption7 = new ProductOption('K', 'M', 'F', 'A', 'J', '4', '2', 'D', 'S', 'C', 'V', '1', '0', '0', 'A', 'Q', '1', '1', '1', '0', '0', '0', '1', '0', '1'); - productOptionRepository.save(newProductOption7); + ProductOption newProductOption8 = new ProductOption('K', 'N', 'A', 'B', 'F', '5', '4', 'S', 'M', 'A', 'R', '0', '1', '0', 'T', 'S', '1', '1', '1', '0', '0', '1', '0', '1', '1'); + productOptionRepository.save(newProductOption8); - ProductOption newProductOption8 = new ProductOption('K', 'N', 'A', 'B', 'F', '5', '4', 'S', 'M', 'A', 'R', '0', '1', '0', 'T', 'S', '1', '1', '1', '0', '0', '1', '0', '1', '1'); - productOptionRepository.save(newProductOption8); + ProductOption newProductOption9 = new ProductOption('K', 'M', 'B', 'R', 'K', '2', '3', 'C', 'F', 'G', 'W', '1', '0', '0', 'A', 'F', '1', '1', '0', '1', '0', '0', '1', '0', '1'); + productOptionRepository.save(newProductOption9); - ProductOption newProductOption9 = new ProductOption('K', 'M', 'B', 'R', 'K', '2', '3', 'C', 'F', 'G', 'W', '1', '0', '0', 'A', 'F', '1', '1', '0', '1', '0', '0', '1', '0', '1'); - productOptionRepository.save(newProductOption9); + ProductOption newProductOption10 = new ProductOption('K', 'N', 'F', 'Y', 'L', '4', '3', 'B', 'Y', 'C', 'Z', '1', '1', '1', 'X', 'S', '0', '1', '0', '0', '1', '1', '1', '0', '1'); + productOptionRepository.save(newProductOption10); - ProductOption newProductOption10 = new ProductOption('K', 'N', 'F', 'Y', 'L', '4', '3', 'B', 'Y', 'C', 'Z', '1', '1', '1', 'X', 'S', '0', '1', '0', '0', '1', '1', '1', '0', '1'); - productOptionRepository.save(newProductOption10); + ProductOption newProductOption11 = new ProductOption('K', 'M', 'E', 'Z', 'S', '3', '2', 'F', 'M', 'A', 'B', '0', '1', '0', 'A', 'N', '1', '0', '1', '0', '0', '1', '1', '0', '1'); + productOptionRepository.save(newProductOption11); - ProductOption newProductOption11 = new ProductOption('K', 'M', 'E', 'Z', 'S', '3', '2', 'F', 'M', 'A', 'B', '0', '1', '0', 'A', 'N', '1', '0', '1', '0', '0', '1', '1', '0', '1'); - productOptionRepository.save(newProductOption11); + ProductOption newProductOption12 = new ProductOption('K', 'N', 'A', 'F', 'J', '2', '3', 'F', 'R', 'B', 'S', '1', '0', '1', 'Y', 'G', '1', '1', '0', '1', '1', '1', '0', '0', '0'); + productOptionRepository.save(newProductOption12); - ProductOption newProductOption12 = new ProductOption('K', 'N', 'A', 'F', 'J', '2', '3', 'F', 'R', 'B', 'S', '1', '0', '1', 'Y', 'G', '1', '1', '0', '1', '1', '1', '0', '0', '0'); - productOptionRepository.save(newProductOption12); + ProductOption newProductOption13 = new ProductOption('K', 'M', 'D', 'T', 'K', '5', '4', 'S', 'A', 'V', 'L', '0', '1', '1', 'B', 'M', '0', '1', '1', '0', '0', '0', '1', '1', '1'); + productOptionRepository.save(newProductOption13); - ProductOption newProductOption13 = new ProductOption('K', 'M', 'D', 'T', 'K', '5', '4', 'S', 'A', 'V', 'L', '0', '1', '1', 'B', 'M', '0', '1', '1', '0', '0', '0', '1', '1', '1'); - productOptionRepository.save(newProductOption13); + ProductOption newProductOption14 = new ProductOption('K', 'M', 'A', 'Z', 'P', '4', '2', 'D', 'P', 'S', 'B', '1', '1', '1', 'Y', 'Q', '1', '0', '0', '1', '1', '0', '0', '0', '1'); + productOptionRepository.save(newProductOption14); - ProductOption newProductOption14 = new ProductOption('K', 'M', 'A', 'Z', 'P', '4', '2', 'D', 'P', 'S', 'B', '1', '1', '1', 'Y', 'Q', '1', '0', '0', '1', '1', '0', '0', '0', '1'); - productOptionRepository.save(newProductOption14); + ProductOption newProductOption15 = new ProductOption('K', 'M', 'F', 'W', 'J', '6', '3', 'B', 'R', 'V', 'Z', '0', '0', '1', 'B', 'R', '1', '1', '0', '0', '1', '1', '0', '1', '0'); + productOptionRepository.save(newProductOption15); - ProductOption newProductOption15 = new ProductOption('K', 'M', 'F', 'W', 'J', '6', '3', 'B', 'R', 'V', 'Z', '0', '0', '1', 'B', 'R', '1', '1', '0', '0', '1', '1', '0', '1', '0'); - productOptionRepository.save(newProductOption15); + ProductOption newProductOption16 = new ProductOption('K', 'N', 'A', 'C', 'L', '3', '5', 'S', 'Q', 'M', 'R', '1', '1', '0', 'Y', 'B', '0', '1', '1', '0', '0', '0', '0', '0', '1'); + productOptionRepository.save(newProductOption16); - ProductOption newProductOption16 = new ProductOption('K', 'N', 'A', 'C', 'L', '3', '5', 'S', 'Q', 'M', 'R', '1', '1', '0', 'Y', 'B', '0', '1', '1', '0', '0', '0', '0', '0', '1'); - productOptionRepository.save(newProductOption16); + ProductOption newProductOption17 = new ProductOption('K', 'M', 'F', 'A', 'J', '4', '2', 'D', 'S', 'C', 'V', '1', '0', '1', 'A', 'Q', '1', '1', '0', '0', '1', '0', '1', '1', '0'); + productOptionRepository.save(newProductOption17); - ProductOption newProductOption17 = new ProductOption('K', 'M', 'F', 'A', 'J', '4', '2', 'D', 'S', 'C', 'V', '1', '0', '1', 'A', 'Q', '1', '1', '0', '0', '1', '0', '1', '1', '0'); - productOptionRepository.save(newProductOption17); + ProductOption newProductOption18 = new ProductOption('K', 'N', 'F', 'B', 'S', '3', '3', 'S', 'M', 'A', 'R', '0', '1', '0', 'T', 'S', '1', '1', '0', '1', '0', '1', '0', '1', '1'); + productOptionRepository.save(newProductOption18); - ProductOption newProductOption18 = new ProductOption('K', 'N', 'F', 'B', 'S', '3', '3', 'S', 'M', 'A', 'R', '0', '1', '0', 'T', 'S', '1', '1', '0', '1', '0', '1', '0', '1', '1'); - productOptionRepository.save(newProductOption18); + ProductOption newProductOption19 = new ProductOption('K', 'M', 'F', 'C', 'T', '5', '3', 'F', 'Q', 'C', 'Z', '0', '1', '1', 'B', 'R', '0', '1', '1', '1', '0', '1', '0', '0', '1'); + productOptionRepository.save(newProductOption19); - ProductOption newProductOption19 = new ProductOption('K', 'M', 'F', 'C', 'T', '5', '3', 'F', 'Q', 'C', 'Z', '0', '1', '1', 'B', 'R', '0', '1', '1', '1', '0', '1', '0', '0', '1'); - productOptionRepository.save(newProductOption19); + ProductOption newProductOption20 = new ProductOption('K', 'N', 'F', 'A', 'P', '4', '4', 'D', 'P', 'S', 'B', '1', '0', '0', 'Y', 'Q', '0', '1', '0', '1', '0', '0', '1', '1', '0'); + productOptionRepository.save(newProductOption20); - ProductOption newProductOption20 = new ProductOption('K', 'N', 'F', 'A', 'P', '4', '4', 'D', 'P', 'S', 'B', '1', '0', '0', 'Y', 'Q', '0', '1', '0', '1', '0', '0', '1', '1', '0'); - productOptionRepository.save(newProductOption20); + ProductOption newProductOption21 = new ProductOption('K', 'M', 'F', 'B', 'L', '3', '2', 'F', 'R', 'M', 'A', '1', '0', '1', 'G', 'Y', '0', '1', '0', '1', '1', '0', '0', '1', '1'); + productOptionRepository.save(newProductOption21); - ProductOption newProductOption21 = new ProductOption('K', 'M', 'F', 'B', 'L', '3', '2', 'F', 'R', 'M', 'A', '1', '0', '1', 'G', 'Y', '0', '1', '0', '1', '1', '0', '0', '1', '1'); - productOptionRepository.save(newProductOption21); + ProductOption newProductOption22 = new ProductOption('K', 'N', 'F', 'A', 'J', '5', '3', 'D', 'S', 'C', 'W', '0', '1', '1', 'A', 'F', '1', '0', '1', '1', '0', '0', '0', '0', '1'); + productOptionRepository.save(newProductOption22); - ProductOption newProductOption22 = new ProductOption('K', 'N', 'F', 'A', 'J', '5', '3', 'D', 'S', 'C', 'W', '0', '1', '1', 'A', 'F', '1', '0', '1', '1', '0', '0', '0', '0', '1'); - productOptionRepository.save(newProductOption22); + ProductOption newProductOption23 = new ProductOption('K', 'M', 'F', 'C', 'S', '4', '3', 'B', 'Q', 'A', 'Z', '1', '0', '0', 'Y', 'R', '1', '0', '1', '0', '1', '1', '1', '0', '0'); + productOptionRepository.save(newProductOption23); - ProductOption newProductOption23 = new ProductOption('K', 'M', 'F', 'C', 'S', '4', '3', 'B', 'Q', 'A', 'Z', '1', '0', '0', 'Y', 'R', '1', '0', '1', '0', '1', '1', '1', '0', '0'); - productOptionRepository.save(newProductOption23); + ProductOption newProductOption24 = new ProductOption('K', 'N', 'F', 'D', 'T', '2', '4', 'F', 'P', 'B', 'A', '1', '1', '0', 'Y', 'Q', '0', '1', '1', '1', '0', '1', '1', '0', '1'); + productOptionRepository.save(newProductOption24); - ProductOption newProductOption24 = new ProductOption('K', 'N', 'F', 'D', 'T', '2', '4', 'F', 'P', 'B', 'A', '1', '1', '0', 'Y', 'Q', '0', '1', '1', '1', '0', '1', '1', '0', '1'); - productOptionRepository.save(newProductOption24); + ProductOption newProductOption25 = new ProductOption('K', 'M', 'F', 'B', 'J', '3', '5', 'S', 'M', 'Z', 'Y', '0', '0', '1', 'A', 'T', '1', '1', '0', '1', '0', '0', '0', '0', '1'); + productOptionRepository.save(newProductOption25); - ProductOption newProductOption25 = new ProductOption('K', 'M', 'F', 'B', 'J', '3', '5', 'S', 'M', 'Z', 'Y', '0', '0', '1', 'A', 'T', '1', '1', '0', '1', '0', '0', '0', '0', '1'); - productOptionRepository.save(newProductOption25); + ProductOption newProductOption26 = new ProductOption('K', 'N', 'F', 'A', 'L', '4', '2', 'D', 'S', 'C', 'V', '1', '0', '1', 'G', 'Y', '0', '1', '1', '1', '1', '1', '0', '0', '1'); + productOptionRepository.save(newProductOption26); - ProductOption newProductOption26 = new ProductOption('K', 'N', 'F', 'A', 'L', '4', '2', 'D', 'S', 'C', 'V', '1', '0', '1', 'G', 'Y', '0', '1', '1', '1', '1', '1', '0', '0', '1'); - productOptionRepository.save(newProductOption26); + ProductOption newProductOption27 = new ProductOption('K', 'M', 'F', 'C', 'S', '2', '3', 'F', 'Q', 'B', 'A', '0', '1', '0', 'T', 'R', '0', '1', '1', '0', '0', '1', '1', '1', '0'); + productOptionRepository.save(newProductOption27); - ProductOption newProductOption27 = new ProductOption('K', 'M', 'F', 'C', 'S', '2', '3', 'F', 'Q', 'B', 'A', '0', '1', '0', 'T', 'R', '0', '1', '1', '0', '0', '1', '1', '1', '0'); - productOptionRepository.save(newProductOption27); + ProductOption newProductOption28 = new ProductOption('K', 'N', 'F', 'B', 'M', '5', '4', 'B', 'R', 'D', 'S', '1', '0', '1', 'X', 'Q', '1', '1', '0', '0', '1', '0', '0', '1', '1'); + productOptionRepository.save(newProductOption28); - ProductOption newProductOption28 = new ProductOption('K', 'N', 'F', 'B', 'M', '5', '4', 'B', 'R', 'D', 'S', '1', '0', '1', 'X', 'Q', '1', '1', '0', '0', '1', '0', '0', '1', '1'); - productOptionRepository.save(newProductOption28); + ProductOption newProductOption29 = new ProductOption('K', 'M', 'F', 'A', 'J', '3', '2', 'C', 'P', 'Z', 'V', '0', '1', '1', 'G', 'S', '1', '0', '0', '1', '0', '1', '0', '1', '0'); + productOptionRepository.save(newProductOption29); - ProductOption newProductOption29 = new ProductOption('K', 'M', 'F', 'A', 'J', '3', '2', 'C', 'P', 'Z', 'V', '0', '1', '1', 'G', 'S', '1', '0', '0', '1', '0', '1', '0', '1', '0'); - productOptionRepository.save(newProductOption29); + ProductOption newProductOption30 = new ProductOption('K', 'N', 'F', 'C', 'L', '6', '4', 'S', 'Q', 'M', 'Z', '1', '0', '0', 'A', 'T', '1', '1', '0', '1', '1', '0', '0', '1', '1'); + productOptionRepository.save(newProductOption30); - ProductOption newProductOption30 = new ProductOption('K', 'N', 'F', 'C', 'L', '6', '4', 'S', 'Q', 'M', 'Z', '1', '0', '0', 'A', 'T', '1', '1', '0', '1', '1', '0', '0', '1', '1'); - productOptionRepository.save(newProductOption30); + ProductOption newProductOption31 = new ProductOption('K', 'M', 'F', 'A', 'S', '2', '3', 'F', 'M', 'B', 'R', '0', '1', '1', 'Y', 'Q', '1', '1', '0', '0', '1', '0', '1', '1', '0'); + productOptionRepository.save(newProductOption31); - ProductOption newProductOption31 = new ProductOption('K', 'M', 'F', 'A', 'S', '2', '3', 'F', 'M', 'B', 'R', '0', '1', '1', 'Y', 'Q', '1', '1', '0', '0', '1', '0', '1', '1', '0'); - productOptionRepository.save(newProductOption31); + ProductOption newProductOption32 = new ProductOption('K', 'N', 'F', 'B', 'P', '4', '2', 'S', 'R', 'A', 'Q', '1', '0', '0', 'T', 'S', '0', '1', '1', '1', '0', '0', '1', '1', '0'); + productOptionRepository.save(newProductOption32); - ProductOption newProductOption32 = new ProductOption('K', 'N', 'F', 'B', 'P', '4', '2', 'S', 'R', 'A', 'Q', '1', '0', '0', 'T', 'S', '0', '1', '1', '1', '0', '0', '1', '1', '0'); - productOptionRepository.save(newProductOption32); + ProductOption newProductOption33 = new ProductOption('K', 'M', 'F', 'C', 'L', '3', '5', 'B', 'S', 'V', 'Z', '1', '0', '1', 'A', 'R', '1', '0', '0', '0', '1', '0', '0', '1', '0'); + productOptionRepository.save(newProductOption33); - ProductOption newProductOption33 = new ProductOption('K', 'M', 'F', 'C', 'L', '3', '5', 'B', 'S', 'V', 'Z', '1', '0', '1', 'A', 'R', '1', '0', '0', '0', '1', '0', '0', '1', '0'); - productOptionRepository.save(newProductOption33); + ProductOption newProductOption34 = new ProductOption('K', 'N', 'F', 'A', 'J', '2', '3', 'D', 'P', 'W', 'R', '0', '1', '0', 'G', 'Y', '1', '0', '1', '1', '0', '1', '0', '1', '0'); + productOptionRepository.save(newProductOption34); - ProductOption newProductOption34 = new ProductOption('K', 'N', 'F', 'A', 'J', '2', '3', 'D', 'P', 'W', 'R', '0', '1', '0', 'G', 'Y', '1', '0', '1', '1', '0', '1', '0', '1', '0'); - productOptionRepository.save(newProductOption34); + ProductOption newProductOption35 = new ProductOption('K', 'M', 'F', 'B', 'S', '4', '2', 'C', 'F', 'Z', 'V', '1', '0', '0', 'T', 'R', '0', '1', '0', '1', '0', '1', '0', '1', '1'); + productOptionRepository.save(newProductOption35); - ProductOption newProductOption35 = new ProductOption('K', 'M', 'F', 'B', 'S', '4', '2', 'C', 'F', 'Z', 'V', '1', '0', '0', 'T', 'R', '0', '1', '0', '1', '0', '1', '0', '1', '1'); - productOptionRepository.save(newProductOption35); + ProductOption newProductOption36 = new ProductOption('K', 'N', 'F', 'C', 'L', '3', '3', 'S', 'Q', 'M', 'Y', '1', '1', '0', 'A', 'T', '1', '0', '0', '1', '0', '1', '1', '1', '0'); + productOptionRepository.save(newProductOption36); - ProductOption newProductOption36 = new ProductOption('K', 'N', 'F', 'C', 'L', '3', '3', 'S', 'Q', 'M', 'Y', '1', '1', '0', 'A', 'T', '1', '0', '0', '1', '0', '1', '1', '1', '0'); - productOptionRepository.save(newProductOption36); + ProductOption newProductOption37 = new ProductOption('K', 'M', 'F', 'A', 'T', '5', '4', 'F', 'P', 'S', 'A', '0', '1', '1', 'G', 'R', '1', '1', '0', '1', '0', '1', '0', '0', '0'); + productOptionRepository.save(newProductOption37); - ProductOption newProductOption37 = new ProductOption('K', 'M', 'F', 'A', 'T', '5', '4', 'F', 'P', 'S', 'A', '0', '1', '1', 'G', 'R', '1', '1', '0', '1', '0', '1', '0', '0', '0'); - productOptionRepository.save(newProductOption37); + ProductOption newProductOption38 = new ProductOption('K', 'N', 'F', 'B', 'L', '2', '3', 'C', 'M', 'B', 'Z', '1', '1', '0', 'Y', 'P', '0', '0', '1', '1', '1', '1', '0', '0', '0'); + productOptionRepository.save(newProductOption38); - ProductOption newProductOption38 = new ProductOption('K', 'N', 'F', 'B', 'L', '2', '3', 'C', 'M', 'B', 'Z', '1', '1', '0', 'Y', 'P', '0', '0', '1', '1', '1', '1', '0', '0', '0'); - productOptionRepository.save(newProductOption38); + ProductOption newProductOption39 = new ProductOption('K', 'M', 'F', 'C', 'S', '4', '2', 'D', 'Q', 'A', 'R', '1', '0', '1', 'T', 'Y', '0', '1', '0', '1', '1', '0', '0', '0', '1'); + productOptionRepository.save(newProductOption39); - ProductOption newProductOption39 = new ProductOption('K', 'M', 'F', 'C', 'S', '4', '2', 'D', 'Q', 'A', 'R', '1', '0', '1', 'T', 'Y', '0', '1', '0', '1', '1', '0', '0', '0', '1'); - productOptionRepository.save(newProductOption39); + ProductOption newProductOption40 = new ProductOption('K', 'N', 'F', 'A', 'J', '3', '5', 'S', 'M', 'B', 'A', '0', '1', '0', 'Y', 'S', '1', '0', '1', '1', '1', '0', '0', '1', '0'); + productOptionRepository.save(newProductOption40); - ProductOption newProductOption40 = new ProductOption('K', 'N', 'F', 'A', 'J', '3', '5', 'S', 'M', 'B', 'A', '0', '1', '0', 'Y', 'S', '1', '0', '1', '1', '1', '0', '0', '1', '0'); - productOptionRepository.save(newProductOption40); + ProductOption newProductOption41 = new ProductOption('K', 'M', 'F', 'B', 'T', '5', '3', 'D', 'F', 'S', 'Q', '1', '1', '0', 'A', 'M', '0', '1', '0', '0', '1', '1', '1', '0', '1'); + productOptionRepository.save(newProductOption41); - ProductOption newProductOption41 = new ProductOption('K', 'M', 'F', 'B', 'T', '5', '3', 'D', 'F', 'S', 'Q', '1', '1', '0', 'A', 'M', '0', '1', '0', '0', '1', '1', '1', '0', '1'); - productOptionRepository.save(newProductOption41); + ProductOption newProductOption42 = new ProductOption('K', 'N', 'F', 'C', 'L', '4', '2', 'B', 'R', 'M', 'Z', '1', '0', '1', 'Y', 'S', '0', '1', '1', '1', '0', '1', '0', '1', '0'); + productOptionRepository.save(newProductOption42); - ProductOption newProductOption42 = new ProductOption('K', 'N', 'F', 'C', 'L', '4', '2', 'B', 'R', 'M', 'Z', '1', '0', '1', 'Y', 'S', '0', '1', '1', '1', '0', '1', '0', '1', '0'); - productOptionRepository.save(newProductOption42); + ProductOption newProductOption43 = new ProductOption('K', 'M', 'F', 'A', 'S', '3', '5', 'S', 'Q', 'B', 'C', '0', '1', '1', 'G', 'P', '1', '0', '0', '1', '1', '0', '0', '1', '0'); + productOptionRepository.save(newProductOption43); - ProductOption newProductOption43 = new ProductOption('K', 'M', 'F', 'A', 'S', '3', '5', 'S', 'Q', 'B', 'C', '0', '1', '1', 'G', 'P', '1', '0', '0', '1', '1', '0', '0', '1', '0'); - productOptionRepository.save(newProductOption43); + ProductOption newProductOption44 = new ProductOption('K', 'N', 'F', 'B', 'M', '6', '4', 'F', 'R', 'L', 'Y', '1', '0', '1', 'B', 'T', '0', '1', '1', '1', '0', '0', '1', '1', '0'); + productOptionRepository.save(newProductOption44); - ProductOption newProductOption44 = new ProductOption('K', 'N', 'F', 'B', 'M', '6', '4', 'F', 'R', 'L', 'Y', '1', '0', '1', 'B', 'T', '0', '1', '1', '1', '0', '0', '1', '1', '0'); - productOptionRepository.save(newProductOption44); + ProductOption newProductOption45 = new ProductOption('K', 'M', 'F', 'C', 'L', '5', '3', 'D', 'M', 'B', 'R', '0', '1', '0', 'Y', 'P', '1', '0', '1', '1', '1', '1', '0', '1', '0'); + productOptionRepository.save(newProductOption45); - ProductOption newProductOption45 = new ProductOption('K', 'M', 'F', 'C', 'L', '5', '3', 'D', 'M', 'B', 'R', '0', '1', '0', 'Y', 'P', '1', '0', '1', '1', '1', '1', '0', '1', '0'); - productOptionRepository.save(newProductOption45); + ProductOption newProductOption46 = new ProductOption('K', 'N', 'F', 'A', 'T', '4', '2', 'S', 'Q', 'P', 'W', '0', '1', '1', 'T', 'S', '1', '1', '0', '1', '0', '0', '0', '0', '1'); + productOptionRepository.save(newProductOption46); - ProductOption newProductOption46 = new ProductOption('K', 'N', 'F', 'A', 'T', '4', '2', 'S', 'Q', 'P', 'W', '0', '1', '1', 'T', 'S', '1', '1', '0', '1', '0', '0', '0', '0', '1'); - productOptionRepository.save(newProductOption46); + ProductOption newProductOption47 = new ProductOption('K', 'M', 'F', 'B', 'J', '3', '5', 'C', 'M', 'Z', 'R', '0', '1', '0', 'Y', 'Q', '1', '0', '1', '0', '0', '0', '1', '1', '1'); + productOptionRepository.save(newProductOption47); - ProductOption newProductOption47 = new ProductOption('K', 'M', 'F', 'B', 'J', '3', '5', 'C', 'M', 'Z', 'R', '0', '1', '0', 'Y', 'Q', '1', '0', '1', '0', '0', '0', '1', '1', '1'); - productOptionRepository.save(newProductOption47); + ProductOption newProductOption48 = new ProductOption('K', 'N', 'F', 'C', 'S', '5', '3', 'D', 'F', 'P', 'A', '1', '0', '1', 'A', 'T', '0', '1', '0', '1', '0', '1', '1', '0', '0'); + productOptionRepository.save(newProductOption48); - ProductOption newProductOption48 = new ProductOption('K', 'N', 'F', 'C', 'S', '5', '3', 'D', 'F', 'P', 'A', '1', '0', '1', 'A', 'T', '0', '1', '0', '1', '0', '1', '1', '0', '0'); - productOptionRepository.save(newProductOption48); + ProductOption newProductOption49 = new ProductOption('K', 'M', 'F', 'A', 'R', '2', '4', 'C', 'Q', 'B', 'M', '1', '0', '1', 'Y', 'P', '0', '1', '0', '0', '1', '1', '0', '0', '1'); + productOptionRepository.save(newProductOption49); - ProductOption newProductOption49 = new ProductOption('K', 'M', 'F', 'A', 'R', '2', '4', 'C', 'Q', 'B', 'M', '1', '0', '1', 'Y', 'P', '0', '1', '0', '0', '1', '1', '0', '0', '1'); - productOptionRepository.save(newProductOption49); + ProductOption newProductOption50 = new ProductOption('K', 'M', 'F', 'L', 'P', '3', '4', 'C', 'F', 'A', 'R', '1', '0', '1', 'G', 'Y', '0', '1', '0', '1', '1', '0', '0', '1', '0'); + productOptionRepository.save(newProductOption50); + } else { + log.info("ProductOption(제품 옵션) 테이블에 이미 데이터가 존재합니다."); + } - ProductOption newProductOption50 = new ProductOption('K', 'M', 'F', 'L', 'P', '3', '4', 'C', 'F', 'A', 'R', '1', '0', '1', 'G', 'Y', '0', '1', '0', '1', '1', '0', '0', '1', '0'); - productOptionRepository.save(newProductOption50); From b2fff4a530fac613e742879eea7dc7a581702fcd Mon Sep 17 00:00:00 2001 From: giuseog Date: Thu, 5 Dec 2024 02:29:17 +0900 Subject: [PATCH 460/563] =?UTF-8?q?feat:=20=EC=B5=9C=EA=B3=A0=EC=B9=98=20?= =?UTF-8?q?=EC=BF=BC=EB=A6=AC=20=EC=B6=94=EA=B0=80(#183)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SalesHistoryController.java | 23 +- .../dto/SalesHistoryStatisticsAverageDTO.java | 1 + .../query/repository/SalesHistoryMapper.java | 4 + .../service/SalesHistoryQueryService.java | 2 + .../service/SalesHistoryQueryServiceImpl.java | 35 +++ src/main/resources/application-prod.yml | 2 +- .../query/repository/SalesHistoryMapper.xml | 236 +++++++++++------- 7 files changed, 210 insertions(+), 93 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java index a6d09594..784b4d31 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java @@ -325,7 +325,7 @@ public ResponseEntity getStatisticsBySearch(@Reques @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", content = @Content(mediaType = "application/json")) }) - @PostMapping("statistics/average/employee") + @PostMapping("statistics/average") public ResponseEntity getStatisticsEmployeeAverageBySearch(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, @PageableDefault(size = 20) Pageable pageable){ @@ -533,4 +533,25 @@ public ResponseEntity getAllStatisticsBySearch(@Req .result(responseSalesHistory) .build()); } + + @Operation(summary = "통계(실적,수당,매출액) 최대값 조회기간 별 검색(관리자, 담당자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @PostMapping("statistics/best") + public ResponseEntity getStatisticsBestBySearch(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, + @PageableDefault(size = 20) Pageable pageable){ + + + + Page responseSalesHistory = salesHistoryQueryService.selectStatisticsBestBySearch(salesHistoryRankedDataDTO, pageable); + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("통계(실적,수당,매출액) 최대값 조회기간 별 검색(관리자, 담당자)") + .result(responseSalesHistory) + .build()); + } } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsAverageDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsAverageDTO.java index 22acf040..3887a904 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsAverageDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsAverageDTO.java @@ -17,4 +17,5 @@ public class SalesHistoryStatisticsAverageDTO { private BigDecimal averageTotalPerformance; private String month; private String year; + private String period; } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java index a2edb7ac..21e99b5a 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java @@ -111,6 +111,10 @@ List findAllStatisticsBySearch(@Param("size") int siz @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); String findSalesHistoryIdByContractId(@Param("contractId") String contractId); + + List findStatisticsBestBySearch(@Param("size") int size + , @Param("offset") int offset, + @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java index dd77fd31..8a3b599a 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java @@ -52,4 +52,6 @@ public interface SalesHistoryQueryService { Page selectAllStatisticsBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); String selectSalesHistoryIdByContractId(String contractId); + + Page selectStatisticsBestBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java index 715fb5b1..1702e979 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java @@ -371,6 +371,23 @@ public Page selectStatisticsBySearch(SalesHistoryRank throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); } + salesHistoryList.forEach(salesHistory -> { + try { + if(salesHistory.getMemberId() != null) { + salesHistory.setMemberId(memberQueryService.selectNameById(salesHistory.getMemberId())); + } + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.MEMBER_NOT_FOUND); + } + try { + if(salesHistory.getCenterId() != null) { + salesHistory.setCenterId(centerQueryService.selectNameById(salesHistory.getCenterId())); + } + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.CENTER_NOT_FOUND); + } + }); + return new PageImpl<>(salesHistoryList, pageable, total); } @@ -587,4 +604,22 @@ public String selectSalesHistoryIdByContractId(String contractId) { return salesHistoryId; } + + @Override + @Transactional + public Page selectStatisticsBestBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); + + List salesHistoryList = salesHistoryMapper.findStatisticsBestBySearch(size,offset, salesHistoryRankedDataDTO); + + int total = salesHistoryMapper.findStatisticsBySearchCountMonth(salesHistoryRankedDataDTO); + + if(salesHistoryList.isEmpty() || total == 0){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } + + return new PageImpl<>(salesHistoryList, pageable, total); + + } } diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index de12a564..8ab30fe5 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -11,7 +11,7 @@ spring: timeout: 5000ms jpa: hibernate: - ddl-auto: create + ddl-auto: update open-in-view: false logging: diff --git a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml index e5fe61a4..b90ab15f 100644 --- a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml @@ -46,6 +46,7 @@ + @@ -59,11 +60,12 @@ - - - - - + + + + + + @@ -548,43 +550,6 @@ GROUP BY MEM_ID) AS A - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SELECT - - SUMMARY.MONTH AS MONTH, - - - SUMMARY.YEAR AS YEAR, - - AVG(total_incentive) AS average_total_incentive, - AVG(total_performance) AS average_total_performance, - AVG(total_sales) AS average_total_sales + + + + COALESCE(LEFT(SUMMARY.created_at, 10), 'Unknown Period') AS period -- 일별 + + + COALESCE(LEFT(SUMMARY.created_at, 7), 'Unknown Period') AS period -- 월별 + + + COALESCE(LEFT(SUMMARY.created_at, 4), 'Unknown Period') AS period -- 연도별 + + , + COALESCE(AVG(SUMMARY.total_incentive), 0) AS average_total_incentive, + COALESCE(AVG(SUMMARY.total_performance), 0) AS average_total_performance, + COALESCE(AVG(SUMMARY.total_sales), 0) AS average_total_sales FROM ( SELECT - - LEFT(a.created_at, 7) AS MONTH, - - - LEFT(a.created_at, 4) AS YEAR, - - SUM(a.sal_hist_ince) AS total_incentive, - SUM(a.sal_hist_no_of_veh) AS total_performance, - SUM(a.sal_hist_tota_sale) AS total_sales, + a.created_at, - - LEFT(a.created_at, 7) + + + COALESCE(a.mem_id, 'Unknown Employee') AS group_key -- 사원별 - - LEFT(a.created_at, 4) + + COALESCE(a.cent_id, 'Unknown Center') AS group_key -- 매장별 - + + 'Unknown Group' AS group_key + + , + COALESCE(SUM(a.sal_hist_ince), 0) AS total_incentive, + COALESCE(SUM(a.sal_hist_no_of_veh), 0) AS total_performance, + COALESCE(SUM(a.sal_hist_tota_sale), 0) AS total_sales FROM tb_sales_history a + a.active = TRUE + AND a.created_at BETWEEN #{salesHistoryRankedDataDTO.startDate} AND #{salesHistoryRankedDataDTO.endDate} - AND a.active = TRUE + GROUP BY - - GROUP BY a.mem_id + + + LEFT(a.created_at, 10), group_key -- 일별 + 그룹별 + + + LEFT(a.created_at, 7), group_key -- 월별 + 그룹별 + + + LEFT(a.created_at, 4), group_key -- 연도별 + 그룹별 - - GROUP BY a.cent_id - - - , LEFT(a.created_at, 7) - - - , LEFT(a.created_at, 4) - ) AS SUMMARY - - GROUP BY SUMMARY.MONTH - - - GROUP BY SUMMARY.YEAR - + GROUP BY period LIMIT #{size} OFFSET #{offset} - + + From a6053b61cdec0d99c0a3f1a0006e205beec7e891 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 5 Dec 2024 02:36:21 +0900 Subject: [PATCH 461/563] =?UTF-8?q?cors=20error=20test=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../final_backend/global/config/WebConfig.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/main/java/stanl_2/final_backend/global/config/WebConfig.java diff --git a/src/main/java/stanl_2/final_backend/global/config/WebConfig.java b/src/main/java/stanl_2/final_backend/global/config/WebConfig.java new file mode 100644 index 00000000..2b0964b5 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/global/config/WebConfig.java @@ -0,0 +1,17 @@ +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/api/**") + .allowedOrigins("http://stanl2proj-env.eba-tupnyt2m.ap-northeast-2.elasticbeanstalk.com") + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .allowedHeaders("Content-Type", "Authorization") + .allowCredentials(true); + } +} From d0bc1d282483ea6d0dff139811912656085403d5 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 5 Dec 2024 02:38:48 +0900 Subject: [PATCH 462/563] =?UTF-8?q?cors=20error=20test=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/stanl_2/final_backend/global/config/WebConfig.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/global/config/WebConfig.java b/src/main/java/stanl_2/final_backend/global/config/WebConfig.java index 2b0964b5..3167a91a 100644 --- a/src/main/java/stanl_2/final_backend/global/config/WebConfig.java +++ b/src/main/java/stanl_2/final_backend/global/config/WebConfig.java @@ -1,4 +1,5 @@ -import org.springframework.context.annotation.Bean; +package stanl_2.final_backend.global.config; + import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; From 21d54b80c034e8b8be48bca2a8c004a6d43c1ad6 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 5 Dec 2024 02:56:07 +0900 Subject: [PATCH 463/563] =?UTF-8?q?cors=20error=20test=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../final_backend/global/config/WebConfig.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/global/config/WebConfig.java b/src/main/java/stanl_2/final_backend/global/config/WebConfig.java index 3167a91a..8203772f 100644 --- a/src/main/java/stanl_2/final_backend/global/config/WebConfig.java +++ b/src/main/java/stanl_2/final_backend/global/config/WebConfig.java @@ -1,5 +1,3 @@ -package stanl_2.final_backend.global.config; - import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -9,10 +7,12 @@ public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/api/**") - .allowedOrigins("http://stanl2proj-env.eba-tupnyt2m.ap-northeast-2.elasticbeanstalk.com") - .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") - .allowedHeaders("Content-Type", "Authorization") - .allowCredentials(true); + registry.addMapping("/api/**") // API 경로 매핑 + .allowedOrigins("http://stanl2proj-env.eba-tupnyt2m.ap-northeast-2.elasticbeanstalk.com") // 클라이언트 출처 + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 허용할 HTTP 메서드 + .allowedHeaders("Content-Type", "Authorization") // 허용할 요청 헤더 + .exposedHeaders("Authorization") // 응답 헤더 노출 + .allowCredentials(true) // 인증 정보 허용 + .maxAge(3600); // Preflight 요청 캐싱 시간 } } From c486d61e950fe3220d326b53825e86f6f17e77e3 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 5 Dec 2024 03:01:46 +0900 Subject: [PATCH 464/563] =?UTF-8?q?cors=20error=20test=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/stanl_2/final_backend/global/config/WebConfig.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/stanl_2/final_backend/global/config/WebConfig.java b/src/main/java/stanl_2/final_backend/global/config/WebConfig.java index 8203772f..447918f6 100644 --- a/src/main/java/stanl_2/final_backend/global/config/WebConfig.java +++ b/src/main/java/stanl_2/final_backend/global/config/WebConfig.java @@ -1,3 +1,5 @@ +package stanl_2.final_backend.global.config; + import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; From f86a5459d87ae70b53d71e5e5386462b3ce5e907 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Thu, 5 Dec 2024 15:45:15 +0900 Subject: [PATCH 465/563] =?UTF-8?q?fix:=20=EA=B6=8C=ED=95=9C=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20api=20=EC=88=98=EC=A0=95(#230)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/config/RequestMatcherConfig.java | 312 ++++++++++-------- 1 file changed, 179 insertions(+), 133 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java index 7ea2824c..9a3a570b 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java @@ -20,158 +20,204 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception "/api/v1/auth/checknum" ).permitAll() - // Alarm API - .requestMatchers(HttpMethod.GET, "/api/v1/alarm").hasAnyRole("alarm-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 회원 알림창 전체 조회 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/alarm/**").hasAnyRole("alarm-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 회원 태그별 알림창 전체 조회 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/alarm/read/**").hasAnyRole("alarm-read-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 회원 읽은 알림 상세 조회 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/alarm/unread/**").hasAnyRole("alarm-unread-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 회원 읽지 않은 알림 상세 조회 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/alarm/connect").hasAnyRole("alarm-connect-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // sse 연결 (전체) - .requestMatchers(HttpMethod.PUT, "/api/v1/alarm/**").hasAnyRole("alarm-*-put", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 회원 알림 읽음 처리 (전체) + // Organization API + .requestMatchers(HttpMethod.GET, "/api/v1/organization").hasAnyRole("organization-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // Auth API - .requestMatchers(HttpMethod.POST, "/api/v1/auth").hasAnyRole("auth-post", "GOD", "DIRECTOR", "ADMIN") // 권한 부여 (전체 except 영업사원) + .requestMatchers(HttpMethod.POST, "/api/v1/auth").hasAnyRole("grant-auth", "GOD", "DIRECTOR", "ADMIN") - // Career API - .requestMatchers(HttpMethod.GET, "/api/v1/career").hasAnyRole("career-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 경력 조회(접속중인 사용자) (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/career/other/**").hasAnyRole("career-other-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 경력 조회(with 사번) (전체) - .requestMatchers(HttpMethod.POST, "/api/v1/career").hasAnyRole("career-post", "GOD", "DIRECTOR", "ADMIN") // 경력 등록 (영업관리자, 영업담당자) + // Member API + .requestMatchers(HttpMethod.GET, "/api/v1/member").hasAnyRole("member-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/member/{centerId}").hasAnyRole("member-centerId-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/member/centerList").hasAnyRole("member-centerList-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/member/info/{loginId}").hasAnyRole("member-info-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/member/memberInfo/{memberId}").hasAnyRole("member-memberInfo-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/member/organization/{organizationId}").hasAnyRole("member-organization-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/member/search").hasAnyRole("member-search-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/member/search/list").hasAnyRole("member-search-list-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/member/excel").hasAnyRole("member-excel-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") - // Certification API - .requestMatchers(HttpMethod.GET, "/api/v1/certification").hasAnyRole("certification-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 자격증/외국어 조회(접속중인 사용자) (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/certification/other/**").hasAnyRole("certification-other-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 자격증/외국어 조회(with 사번) (전체) - .requestMatchers(HttpMethod.POST, "/api/v1/certification").hasAnyRole("certification-post", "GOD", "DIRECTOR", "ADMIN") // 자격증/외국어 등록 (영업 관리자, 영업담당자) - - // Contract API - .requestMatchers(HttpMethod.GET, "/api/v1/contract/{contractId}").hasAnyRole("contract-id-get", "GOD", "DIRECTOR", "ADMIN") // 계약서 상세조회 (영업관리자, 영업담당자) - .requestMatchers(HttpMethod.PUT, "/api/v1/contract/{contractId}").hasAnyRole("contract-id-put", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 수정 (전체) - .requestMatchers(HttpMethod.DELETE, "/api/v1/contract/{contractId}").hasAnyRole("contract-id-delete", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 삭제 (전체) - .requestMatchers(HttpMethod.PUT, "/api/v1/contract/status/**").hasAnyRole("contract-status-put", "GOD", "ADMIN") // 계약서 승인상태 수정 (관리자) - .requestMatchers(HttpMethod.GET, "/api/v1/contract").hasAnyRole("contract-get", "GOD", "DIRECTOR", "ADMIN") // 계약서 전체 조회 (영업관리자, 영업담당자) - .requestMatchers(HttpMethod.POST, "/api/v1/contract").hasAnyRole("contract-post", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 등록 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/contract/search").hasAnyRole("contract-search-get", "GOD", "DIRECTOR", "ADMIN") // 계약서 검색 조회 (영업관리자, 영업담당자) - .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee").hasAnyRole("contract-employee-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 전체 조회 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee/{contractId}").hasAnyRole("contract-employee-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 전체 조회 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee/**").hasAnyRole("contract-employee-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 상세조회 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/contract/center/{contractId}").hasAnyRole("contract-admin-id-get", "GOD", "DIRECTOR", "ADMIN") // 계약서 상세조회 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee/search").hasAnyRole("contract-employee-search-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 계약서 검색 조회 (전체) + // Education API + .requestMatchers(HttpMethod.GET, "/api/v1/education").hasAnyRole("education-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/education/other/{loginId}").hasAnyRole("education-other-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") - // Customer API - .requestMatchers(HttpMethod.DELETE, "/api/v1/customer/{customerId}").hasAnyRole("customer-id-delete", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 고객정보 삭제(자기 고객만) (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/customer/{customerId}").hasAnyRole("customer-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 고객정보 상세조회 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/customer/list").hasAnyRole("customer-list-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 고객번호로 전체 목록 조회 (전체) - .requestMatchers(HttpMethod.PUT, "/api/v1/customer/{customerId}").hasAnyRole("customer-id-put", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 고객정보 수정(자기 고객만) (전체) - .requestMatchers(HttpMethod.POST, "/api/v1/customer").hasAnyRole("customer-post", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 고객정보 등록 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/customer/search").hasAnyRole("customer-search-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 고객정보 조건별 조회 (전체) + // Family API + .requestMatchers(HttpMethod.GET, "/api/v1/family").hasAnyRole("family-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/family/other/{loginId}").hasAnyRole("family-other-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") - // Education API - .requestMatchers(HttpMethod.GET, "/api/v1/education").hasAnyRole("education-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 학력 조회(접속중인 사용자) (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/education/other/**").hasAnyRole("education-other-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 학력 조회(with 사번) (전체) + // Career API + .requestMatchers(HttpMethod.GET, "/api/v1/career").hasAnyRole("career-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/career/other/{loginId}").hasAnyRole("career-other-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/career").hasAnyRole("career-post", "GOD", "DIRECTOR", "ADMIN") - // Evaluation API - .requestMatchers(HttpMethod.DELETE, "/api/v1/evaluation/{id}").hasAnyRole("evaluation-id-delete", "GOD", "DIRECTOR", "ADMIN") // 평가서 삭제 (영업 관리자, 영업담당자) - .requestMatchers(HttpMethod.GET, "/api/v1/evaluation/manager").hasAnyRole("evaluation-manager-get", "GOD", "ADMIN") // 평가서 관리자 전체 조회 (영업 관리자) - .requestMatchers(HttpMethod.GET, "/api/v1/evaluation/representative").hasAnyRole("evaluation-representative-get", "GOD", "DIRECTOR") // 평가서 담당자 전체 조회 (영업담당자) - .requestMatchers(HttpMethod.GET, "/api/v1/evaluation/representative/search").hasAnyRole("evaluation-representative-search-get", "GOD", "DIRECTOR") // 평가서 담당자 검색 (영업담당자) - .requestMatchers(HttpMethod.GET, "/api/v1/evaluation/manager/search").hasAnyRole("evaluation-manager-search-get", "GOD", "ADMIN") // 평가서 관리자 검색 (영업 관리자) - .requestMatchers(HttpMethod.GET, "/api/v1/evaluation/{id}").hasAnyRole("evaluation-id-get", "GOD", "DIRECTOR", "ADMIN") // 평가서 상세 조회 (영업 관리자, 영업담당자) - .requestMatchers(HttpMethod.POST, "/api/v1/evaluation").hasAnyRole("evaluation-post", "GOD", "DIRECTOR", "ADMIN") // 평가서 등록 (영업 관리자, 영업담당자) - .requestMatchers(HttpMethod.PUT, "/api/v1/evaluation/{id}").hasAnyRole("evaluation-id-put", "GOD", "DIRECTOR", "ADMIN") // 평가서 수정 (영업 관리자, 영업담당자) + // Certification API + .requestMatchers(HttpMethod.GET, "/api/v1/certification").hasAnyRole("certification-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/certification/other/{loginId}").hasAnyRole("certification-other-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/certification").hasAnyRole("certification-post", "GOD", "DIRECTOR", "ADMIN") - // Family API - .requestMatchers(HttpMethod.GET, "/api/v1/family").hasAnyRole("family-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 가족 구성원 조회(접속중인 사용자) (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/family/other/{loginId}").hasAnyRole("family-other-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 가족 구성원 조회(with 사번) (전체) + // Customer API + .requestMatchers(HttpMethod.GET, "/api/v1/customer/list").hasAnyRole("customer-list-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/customer/search").hasAnyRole("customer-search-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/customer/{customerId}").hasAnyRole("customer-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/customer/contract/{customerId}").hasAnyRole("customer-contract-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/customer/excel").hasAnyRole("customer-excel-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/customer").hasAnyRole("customer-post", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.PUT, "/api/v1/customer/{customerId}").hasAnyRole("customer-put", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.DELETE, "/api/v1/customer/{customerId}").hasAnyRole("customer-delete", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + + // News API + .requestMatchers(HttpMethod.GET, "/api/v1/news/car").hasAnyRole("news-car-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + + // Claude API + .requestMatchers(HttpMethod.POST, "/api/v1/claude/summary").hasAnyRole("claude-summary-post", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + + // Contract API (Employee) + .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee").hasAnyRole("contract-employee-get", "GOD", "EMPLOYEE") + .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee/search").hasAnyRole("contract-employee-search-get", "GOD", "EMPLOYEE") + .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee/{contractId}").hasAnyRole("contract-employee-id-get", "GOD", "EMPLOYEE") + .requestMatchers(HttpMethod.POST, "/api/v1/contract").hasAnyRole("contract-post", "GOD", "EMPLOYEE", "ADMIN") + .requestMatchers(HttpMethod.PUT, "/api/v1/contract/{contractId}").hasAnyRole("contract-id-put", "GOD", "EMPLOYEE", "ADMIN") + .requestMatchers(HttpMethod.DELETE, "/api/v1/contract/{contractId}").hasAnyRole("contract-id-delete", "GOD", "EMPLOYEE", "ADMIN") + + // Contract API (Center) + .requestMatchers(HttpMethod.GET, "/api/v1/contract/center").hasAnyRole("contract-center-get", "GOD", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/contract/center/search").hasAnyRole("contract-center-search-get", "GOD", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/contract/center/{contractId}").hasAnyRole("contract-center-id-get", "GOD", "ADMIN") + + // Contract API (General) + .requestMatchers(HttpMethod.GET, "/api/v1/contract").hasAnyRole("contract-get", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.GET, "/api/v1/contract/search").hasAnyRole("contract-search-get", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.GET, "/api/v1/contract/{contractId}").hasAnyRole("contract-id-get", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.PUT, "/api/v1/contract/status/{contractId}").hasAnyRole("contract-status-id-put", "GOD", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/contract/excel").hasAnyRole("contract-excel-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + + + // Order API (Employee) + .requestMatchers(HttpMethod.GET, "/api/v1/order/employee").hasAnyRole("order-employee-get", "GOD", "EMPLOYEE") + .requestMatchers(HttpMethod.GET, "/api/v1/order/employee/search").hasAnyRole("order-employee-search-get", "GOD", "EMPLOYEE") + .requestMatchers(HttpMethod.GET, "/api/v1/order/employee/{orderId}").hasAnyRole("order-employee-id-get", "GOD", "EMPLOYEE") + + // Order API (General) + .requestMatchers(HttpMethod.POST, "/api/v1/order").hasAnyRole("order-post", "GOD", "EMPLOYEE", "ADMIN") + .requestMatchers(HttpMethod.PUT, "/api/v1/order/{orderId}").hasAnyRole("order-id-put", "GOD", "EMPLOYEE", "ADMIN") + .requestMatchers(HttpMethod.DELETE, "/api/v1/order/{orderId}").hasAnyRole("order-id-delete", "GOD", "EMPLOYEE", "ADMIN") + + // Order API (Search and Excel) + .requestMatchers(HttpMethod.GET, "/api/v1/order").hasAnyRole("order-get", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.GET, "/api/v1/order/{orderId}").hasAnyRole("order-id-get", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.GET, "/api/v1/order/search").hasAnyRole("order-search-get", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.GET, "/api/v1/order/excel").hasAnyRole("order-excel-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + + // Order API (Status) + .requestMatchers(HttpMethod.PUT, "/api/v1/order/status/{orderId}").hasAnyRole("order-status-id-put", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + + + // Purchase Order API + .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order/admin").hasAnyRole("purchase-order-admin-get", "GOD", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order/admin/search").hasAnyRole("purchase-order-admin-search-get", "GOD", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order/admin/{purchaseOrderId}").hasAnyRole("purchase-order-admin-id-get", "GOD", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/purchase-order").hasAnyRole("purchase-order-post", "GOD", "ADMIN") + .requestMatchers(HttpMethod.PUT, "/api/v1/purchase-order/{purchaseOrderId}").hasAnyRole("purchase-order-put", "GOD", "ADMIN") + .requestMatchers(HttpMethod.DELETE, "/api/v1/purchase-order/{purchaseOrderId}").hasAnyRole("purchase-order-delete", "GOD", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order").hasAnyRole("purchase-order-get", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order/search").hasAnyRole("purchase-order-search-get", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order/{purchaseOrderId}").hasAnyRole("purchase-order-id-get", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order/excel").hasAnyRole("purchase-order-excel-get", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.PUT, "/api/v1/purchase-order/status/{purchaseOrderId}").hasAnyRole("purchase-order-status-put", "GOD", "DIRECTOR") - // Member API - .requestMatchers(HttpMethod.GET, "/api/v1/member/authorities").hasAnyRole("GOD") // CHECK (시스템관리자) - .requestMatchers(HttpMethod.GET, "/api/v1/member/**").hasAnyRole("member-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 회원 정보 조회 (전체) + // Alarm API + .requestMatchers(HttpMethod.GET, "/api/v1/alarm").hasAnyRole("alarm-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/alarm/{type}").hasAnyRole("alarm-type-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/alarm/connect").hasAnyRole("alarm-connect-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.PUT, "/api/v1/alarm/{alarmId}").hasAnyRole("alarm-id-put", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") - // Log API - .requestMatchers(HttpMethod.GET, "/api/v1/log").hasAnyRole("log-get", "GOD") // 로그 조회 (시스템 관리자) + // Schedule API + .requestMatchers(HttpMethod.GET, "/api/v1/schedule").hasAnyRole("schedule-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/schedule/{scheduleId}").hasAnyRole("schedule-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/schedule/{year}/{month}").hasAnyRole("schedule-year-month-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/schedule").hasAnyRole("schedule-post", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.PUT, "/api/v1/schedule/{scheduleId}").hasAnyRole("schedule-id-put", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.DELETE, "/api/v1/schedule/{scheduleId}").hasAnyRole("schedule-id-delete", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // Notice API - .requestMatchers(HttpMethod.DELETE, "/api/v1/notice/{noticeId}").hasAnyRole("notice-delete", "GOD", "DIRECTOR") // 공지사항 삭제 (영업담당자) - .requestMatchers(HttpMethod.GET, "/api/v1/notice/all").hasAnyRole("notice-all-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 공지사항 전체 조회 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/notice/{noticeId}").hasAnyRole("notice-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 공지사항 Id로 조회 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/notice").hasAnyRole("notice-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 공지사항 조건별 조회 (전체) - .requestMatchers(HttpMethod.PUT, "/api/v1/notice/{noticeId}").hasAnyRole("notice-update", "GOD", "DIRECTOR") // 공지사항 수정 (영업담당자) - .requestMatchers(HttpMethod.POST, "/api/v1/notice").hasAnyRole("notice-create", "GOD", "DIRECTOR") // 공지사항 작성 (영업담당자) - - // Order API - .requestMatchers(HttpMethod.DELETE, "/api/v1/order/{orderId}").hasAnyRole("order-delete", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 수주서 삭제 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/order").hasAnyRole("order-get", "GOD", "DIRECTOR", "ADMIN") // 수주서 전체 조회 (영업관리자, 영업담당자) - .requestMatchers(HttpMethod.GET, "/api/v1/order/employee").hasAnyRole("order-employee-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 수주서 전체 조회 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/order/{orderId}").hasAnyRole("order-id-get", "GOD", "DIRECTOR", "ADMIN") // 수주서 상세 조회 (영업관리자, 영업담당자) - .requestMatchers(HttpMethod.GET, "/api/v1/order/center/{orderId}").hasAnyRole("order-center-id-get", "GOD", "DIRECTOR", "ADMIN") // 수주서 상세 조회 (영업관리자, 영업담당자) - .requestMatchers(HttpMethod.GET, "/api/v1/order/employee/{orderId}").hasAnyRole("order-employee-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 수주서 상세 조회 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/order/search").hasAnyRole("order-search", "GOD", "DIRECTOR", "ADMIN") // 수주서 검색 조회 (영업관리자, 영업담당자) - .requestMatchers(HttpMethod.GET, "/api/v1/order/employee/search").hasAnyRole("order-employee-search", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 수주서 검색 조회 (전체) - .requestMatchers(HttpMethod.POST, "/api/v1/order").hasAnyRole("order-create", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 수주서 등록 (전체) - .requestMatchers(HttpMethod.PUT, "/api/v1/order/{orderId}").hasAnyRole("order-update", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 수주서 수정 (전체) - .requestMatchers(HttpMethod.PUT, "/api/v1/order/status/{orderId}").hasAnyRole("order-status-update", "GOD", "ADMIN") // 수주서 승인상태 변경 (영업관리자) - - // Organization API - .requestMatchers(HttpMethod.GET, "/api/v1/organization").hasAnyRole("organization-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 사이드바 메뉴 조회 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/notice").hasAnyRole("notice-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/notice/{noticeId}").hasAnyRole("notice-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/notice/excel").hasAnyRole("notice-excel-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/notice").hasAnyRole("notice-post", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.PUT, "/api/v1/notice/{noticeId}").hasAnyRole("notice-id-put", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.DELETE, "/api/v1/notice/{noticeId}").hasAnyRole("notice-id-delete", "GOD", "DIRECTOR") // Problem API - .requestMatchers(HttpMethod.DELETE, "/api/v1/problem/{problemId}").hasAnyRole("problem-delete", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 문제사항 삭제 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/problem/{problemId}").hasAnyRole("problem-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 문제사항 Id로 조회 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/problem").hasAnyRole("problem-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 문제사항 조건별 조회 (전체) - .requestMatchers(HttpMethod.PUT, "/api/v1/problem/{problemId}").hasAnyRole("problem-update", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 문제사항 수정 (전체) - .requestMatchers(HttpMethod.PUT, "/api/v1/problem/status/{problemId}").hasAnyRole("problem-status-update", "GOD") // 문제사항 상태 수정 (전체) - .requestMatchers(HttpMethod.POST, "/api/v1/problem").hasAnyRole("problem-create", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 문제사항 작성 (전체) - - // Product API - .requestMatchers(HttpMethod.GET, "/api/v1/product").hasAnyRole("product-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 제품 조회 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/product/{id}").hasAnyRole("product-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 제품 상세 조회 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/product/search").hasAnyRole("product-search", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 제품 검색 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/problem").hasAnyRole("problem-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/problem/{problemId}").hasAnyRole("problem-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/problem/excel").hasAnyRole("problem-excel-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/problem").hasAnyRole("problem-post", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.PUT, "/api/v1/problem/{problemId}").hasAnyRole("problem-id-put", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.PUT, "/api/v1/problem/status/{problemId}").hasAnyRole("problem-status-put", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.DELETE, "/api/v1/problem/{problemId}").hasAnyRole("problem-id-delete", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // Promotion API - .requestMatchers(HttpMethod.DELETE, "/api/v1/promotion/{promotionId}").hasAnyRole("promotion-delete", "GOD", "DIRECTOR") // 프로모션 삭제 (영업담당자) - .requestMatchers(HttpMethod.GET, "/api/v1/promotion/{promotionId}").hasAnyRole("promotion-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 프로모션 Id로 조회 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/promotion").hasAnyRole("promotion-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 프로모션 조건별 조회 (전체) - .requestMatchers(HttpMethod.PUT, "/api/v1/promotion/{promotionId}").hasAnyRole("promotion-update", "GOD", "DIRECTOR") // 프로모션 수정 (영업담당자) - .requestMatchers(HttpMethod.POST, "/api/v1/promotion").hasAnyRole("promotion-create", "GOD", "DIRECTOR") // 프로모션 작성 (영업담당자) - - // Purchase-Order API - .requestMatchers(HttpMethod.DELETE, "/api/v1/purchase-order/{purchaseOrderId}").hasAnyRole("purchase-order-delete", "GOD", "ADMIN") // 발주서 삭제 (영업관리자) - .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order").hasAnyRole("purchase-order-get", "GOD", "DIRECTOR") // 발주서 전체 조회 (영업담당자) - .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order/admin").hasAnyRole("purchase-order-admin-get", "GOD", "ADMIN") // 발주서 전체 조회 (영업관리자) - .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order/{purchaseOrderId}").hasAnyRole("purchase-order-id-get", "GOD", "DIRECTOR") // 발주서 상세 조회 (영업담당자) - .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order/admin/{purchaseOrderId}").hasAnyRole("purchase-order-admin-id-get", "GOD", "ADMIN") // 발주서 상세 조회 (영업관리자) - .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order/search").hasAnyRole("purchase-order-search", "GOD", "DIRECTOR") // 발주서 검색 조회 (영업담당자) - .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order/admin/search").hasAnyRole("purchase-order-admin-search", "GOD", "ADMIN") // 발주서 검색 조회 (영업관리자) - .requestMatchers(HttpMethod.POST, "/api/v1/purchase-order").hasAnyRole("purchase-order-create", "GOD", "ADMIN") // 발주서 등록 (영업관리자) - .requestMatchers(HttpMethod.PUT, "/api/v1/purchase-order/{purchaseOrderId}").hasAnyRole("purchase-order-update", "GOD", "ADMIN") // 발주서 수정 (영업관리자) - .requestMatchers(HttpMethod.PUT, "/api/v1/purchase-order/stauts/{purchaseOrderId}").hasAnyRole("purchase-order-status-update", "GOD", "DIRECTOR") // 발주서 승인 상태 수정 (영업담당자) - - // Sample API - .requestMatchers(HttpMethod.DELETE, "/api/v1/sample/{id}").hasAnyRole("sample-delete", "GOD") // 샘플 삭제 테스트 (시스템관리자) - .requestMatchers(HttpMethod.GET, "/api/v1/sample/detail/{id}").hasAnyRole("sample-detail-get", "GOD") // 샘플 상세 조회 테스트 (시스템관리자) - .requestMatchers(HttpMethod.POST, "/api/v1/sample").hasAnyRole("sample-create", "GOD") // 샘플 요청 테스트 (시스템관리자) - .requestMatchers(HttpMethod.PUT, "/api/v1/sample/{id}").hasAnyRole("sample-update", "GOD") // 샘플 수정 테스트 (시스템관리자) + .requestMatchers(HttpMethod.GET, "/api/v1/promotion").hasAnyRole("promotion-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/promotion/{promotionId}").hasAnyRole("promotion-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/promotion/excel").hasAnyRole("promotion-excel-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/promotion").hasAnyRole("promotion-post", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.PUT, "/api/v1/promotion/{promotionId}").hasAnyRole("promotion-id-put", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.DELETE, "/api/v1/promotion/{promotionId}").hasAnyRole("promotion-id-delete", "GOD", "DIRECTOR") - // Schedule API - .requestMatchers(HttpMethod.DELETE, "/api/v1/schedule/{scheduleId}").hasAnyRole("schedule-delete", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 일정 삭제 api (전체) - .requestMatchers(HttpMethod.PUT, "/api/v1/schedule/{scheduleId}").hasAnyRole("schedule-update", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 일정 수정 api (전체) - .requestMatchers(HttpMethod.POST, "/api/v1/schedule").hasAnyRole("schedule-create", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 일정 등록 api (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/schedule").hasAnyRole("schedule-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 일정 전체 조회 api (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/schedule/{scheduleId}").hasAnyRole("schedule-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 일정 상세 조회 api (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/schedule/{year}/{month}").hasAnyRole("schedule-month-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 일정 조건별(년&일) 전체 조회 api (전체) + // File API + .requestMatchers(HttpMethod.POST, "/api/v1/file").hasAnyRole("file-post", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + + // Evaluation API + .requestMatchers(HttpMethod.GET, "/api/v1/evaluation/manager").hasAnyRole("evaluation-manager-get", "GOD", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/evaluation/manager/search").hasAnyRole("evaluation-manager-search-get", "GOD", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/evaluation/representative").hasAnyRole("evaluation-representative-get", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.GET, "/api/v1/evaluation/representative/search").hasAnyRole("evaluation-representative-search-get", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.GET, "/api/v1/evaluation/{id}").hasAnyRole("evaluation-id-get", "GOD", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/evaluation/excel").hasAnyRole("evaluation-excel-get", "GOD", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/evaluation").hasAnyRole("evaluation-post", "GOD", "ADMIN") + .requestMatchers(HttpMethod.PUT, "/api/v1/evaluation/{id}").hasAnyRole("evaluation-id-put", "GOD", "ADMIN") + .requestMatchers(HttpMethod.DELETE, "/api/v1/evaluation/{id}").hasAnyRole("evaluation-id-delete", "GOD", "ADMIN") + + // Product API + .requestMatchers(HttpMethod.GET, "/api/v1/product").hasAnyRole("product-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/product/excel").hasAnyRole("product-excel-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/product/{productId}").hasAnyRole("product-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/product/search").hasAnyRole("product-search-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // Center API - .requestMatchers(HttpMethod.GET, "/api/v1/center/{id}").hasAnyRole("center-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 영업매장 상세조회 (전체) - .requestMatchers(HttpMethod.PUT, "/api/v1/center/{id}").hasAnyRole("center-update", "GOD", "ADMIN") // 매장 수정 (영업 관리자) - .requestMatchers(HttpMethod.DELETE, "/api/v1/center/{id}").hasAnyRole("center-delete", "GOD", "ADMIN") // 매장 삭제 (영업 관리자) - .requestMatchers(HttpMethod.GET, "/api/v1/center").hasAnyRole("center-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 영업매장 조회 (전체) - .requestMatchers(HttpMethod.POST, "/api/v1/center").hasAnyRole("center-create", "GOD", "ADMIN") // 매장 등록 (영업 관리자) - .requestMatchers(HttpMethod.GET, "/api/v1/center/search").hasAnyRole("center-search", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 영업매장 검색 (전체) - - // SalesHistory API - .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory").hasAnyRole("salesHistory-get", "GOD", "DIRECTOR", "ADMIN") // 판매내역 조회 (영업관리자, 영업담당자) - .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee").hasAnyRole("salesHistory-employee-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 판매내역 조회 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/{salesHistoryId}").hasAnyRole("salesHistory-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 판매내역 상세조회 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search").hasAnyRole("salesHistory-statistics-search", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 사원 통계 조회기간별 검색 (전체) - .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search/month").hasAnyRole("salesHistory-statistics-month-search", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // 사원 통계 월별 검색 (전체) + .requestMatchers(HttpMethod.GET, "/api/v1/center").hasAnyRole("center-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/center/search").hasAnyRole("center-search-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/center/searchList").hasAnyRole("center-searchList-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/center/excel").hasAnyRole("center-excel-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/center/{centerId}").hasAnyRole("center-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/center").hasAnyRole("center-post", "GOD", "ADMIN") + .requestMatchers(HttpMethod.PUT, "/api/v1/center/{centerId}").hasAnyRole("center-id-put", "GOD", "ADMIN") + .requestMatchers(HttpMethod.DELETE, "/api/v1/center/{centerId}").hasAnyRole("center-id-delete", "GOD", "ADMIN") + + // Sales History API + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory").hasAnyRole("salesHistory-get", "GOD", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/{salesHistoryId}").hasAnyRole("salesHistory-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/excel").hasAnyRole("salesHistory-excel-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics").hasAnyRole("salesHistory-statistics-post", "GOD", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/search").hasAnyRole("salesHistory-statistics-search-post", "GOD", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/search/year").hasAnyRole("salesHistory-statistics-search-year-post", "GOD", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/search/month").hasAnyRole("salesHistory-statistics-search-month-post", "GOD", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/center/search").hasAnyRole("salesHistory-statistics-center-search-post", "GOD", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/center/search/year").hasAnyRole("salesHistory-statistics-center-search-year-post", "GOD", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/center/search/month").hasAnyRole("salesHistory-statistics-center-search-month-post", "GOD", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/average/employee").hasAnyRole("salesHistory-statistics-average-employee-post", "GOD", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/average/center").hasAnyRole("salesHistory-statistics-average-center-post", "GOD", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/all").hasAnyRole("salesHistory-statistics-all-post", "GOD", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/all/year").hasAnyRole("salesHistory-statistics-all-year-post", "GOD", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/all/month").hasAnyRole("salesHistory-statistics-all-month-post", "GOD", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/search").hasAnyRole("salesHistory-search-post", "GOD", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee").hasAnyRole("salesHistory-employee-get", "GOD", "EMPLOYEE") + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics").hasAnyRole("salesHistory-employee-statistics-get", "GOD", "EMPLOYEE") + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search").hasAnyRole("salesHistory-employee-statistics-search-get", "GOD", "EMPLOYEE") + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search/year").hasAnyRole("salesHistory-employee-statistics-search-year-get", "GOD", "EMPLOYEE") + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search/month").hasAnyRole("salesHistory-employee-statistics-search-month-get", "GOD", "EMPLOYEE") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/employee/search").hasAnyRole("salesHistory-employee-search-post", "GOD", "EMPLOYEE") + // 그 외 나머지 시스템 관리자만 접근 가능 .anyRequest().hasRole("GOD") From c0f4b21bf63d8737ffa7952782a46871752f93cc Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Thu, 5 Dec 2024 15:50:23 +0900 Subject: [PATCH 466/563] =?UTF-8?q?fix:=20=EC=82=AC=EC=9B=90,=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=EC=9E=90,=20=EB=8B=B4=EB=8B=B9=EC=9E=90=20=EA=B3=A0?= =?UTF-8?q?=EC=A0=95=EC=A0=81=EC=9D=B8=20dummy=20data=20=EC=88=98=EC=A0=95?= =?UTF-8?q?(#231)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/dataloader/Initializer.java | 70 ++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java index 1eadc586..c2365bc9 100644 --- a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java +++ b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java @@ -162,6 +162,74 @@ public void run(ApplicationArguments args) throws Exception { loadImage("god.png") ); + + createOrUpdateMember( + "eee", + "eee", + "사원1", + "employee@stanl.com", + 0, + "MALE", + "000000-0000000", + "010-0000-0000", + "서울 동작구 보라매로 87", + "사원", + "중졸", + "TEMPORARY", + "미필", + "한국은행", + "000-0000-000000-00000", + "CEN_000000000", + "ORG_000000000", + "EMPLOYEE", + loadImage("default.png") + ); + + createOrUpdateMember( + "aaa", + "aaa", + "관리자1", + "admin@stanl.com", + 0, + "MALE", + "000000-0000000", + "010-0000-0000", + "서울 동작구 보라매로 87", + "영업 관리자", + "중졸", + "TEMPORARY", + "미필", + "한국은행", + "000-0000-000000-00000", + "CEN_000000000", + "ORG_000000000", + "ADMIN", + loadImage("default.png") + ); + + createOrUpdateMember( + "ddd", + "ddd", + "담당자1", + "director@stanl.com", + 0, + "MALE", + "000000-0000000", + "010-0000-0000", + "서울 동작구 보라매로 87", + "영업 담당자", + "중졸", + "TEMPORARY", + "미필", + "한국은행", + "000-0000-000000-00000", + "CEN_000000000", + "ORG_000000000", + "DIRECTOR", + loadImage("default.png") + ); + + Random random = new Random(); String[] positions = {"영업 사원", "영업 관리자", "영업 담당자"}; String[] grades = {"A", "B", "C", "D"}; @@ -186,7 +254,7 @@ public void run(ApplicationArguments args) throws Exception { // 회원 및 역할 생성 로직 - for (int i = 1; i <= 96; i++) { + for (int i = 1; i <= 93; i++) { int centerId = random.nextInt(10) + 1; int orgId = random.nextInt(10) + 5; String sex = genders[i % 2]; From 20e75325d9b6ed68f17fb16ea2a0bc8675cf9b97 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Thu, 5 Dec 2024 15:52:25 +0900 Subject: [PATCH 467/563] =?UTF-8?q?fix:=20=EB=B3=91=EC=97=AD=20=EC=86=8C?= =?UTF-8?q?=EB=AC=B8=EC=9E=90=20->=20=EB=8C=80=EB=AC=B8=EC=9E=90(#231)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stanl_2/final_backend/global/dataloader/Initializer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java index c2365bc9..60b382f6 100644 --- a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java +++ b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java @@ -234,7 +234,7 @@ public void run(ApplicationArguments args) throws Exception { String[] positions = {"영업 사원", "영업 관리자", "영업 담당자"}; String[] grades = {"A", "B", "C", "D"}; String[] jobTypes = {"REGULAR", "TEMPORARY"}; - String[] militaryStatus = {"fulfilled", "exemption", "unfulfilled"}; + String[] militaryStatus = {"FULFILLED", "EXEMPTION", "UNFULFILLED"}; String[] genders = {"MALE", "FEMALE"}; String[] roles = {"EMPLOYEE", "ADMIN", "DIRECTOR"}; String[] lastNames = {"김", "이", "박", "최", "정", "강", "조", "유", "윤", "장", "임", "기", "방", "하", "도", "한", "손", "송", "오", "조", "서", "배", "홍", "류", "신", "권", "곽"}; From d30fa31226cd3cbec5eb90624451ebc0a1759433 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 5 Dec 2024 21:07:28 +0900 Subject: [PATCH 468/563] =?UTF-8?q?=EC=9E=84=EC=8B=9C=EB=A1=9C=20eb?= =?UTF-8?q?=EC=9D=98stanl2proj=EC=97=90=20=EB=B0=B0=ED=8F=AC=20=ED=95=B4?= =?UTF-8?q?=EB=86=93=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 3be38658..e96da0b6 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -39,6 +39,7 @@ jwt: secret-key: s4G7tL9qH8vI2eP0j5W1uF3bM6nK8rXc header: Authorization + encryption: algorithm: AES transformation: AES/ECB/PKCS5Padding From bbd144c3846d6eb1ceeec1433437c67f4a204abb Mon Sep 17 00:00:00 2001 From: giuseog Date: Thu, 5 Dec 2024 21:27:12 +0900 Subject: [PATCH 469/563] =?UTF-8?q?feat:=20front=20=EA=B5=AC=EC=A1=B0?= =?UTF-8?q?=EC=97=90=20=EB=A7=9E=EC=B6=98=20=ED=8C=90=EB=A7=A4=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=EC=88=98=EC=A0=95(#183)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SalesHistoryController.java | 4 ++-- .../query/dto/SalesHistoryStatisticsDTO.java | 2 ++ .../query/repository/SalesHistoryMapper.java | 2 +- .../query/service/SalesHistoryQueryService.java | 2 +- .../service/SalesHistoryQueryServiceImpl.java | 14 +++++++++----- .../query/repository/SalesHistoryMapper.xml | 17 ++++++++++------- 6 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java index 784b4d31..35fdc71c 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java @@ -268,11 +268,11 @@ public ResponseEntity getStatisticsSearchYearByEmpl salesHistorySearchDTO.setStartDate(params.get("startDate")); salesHistorySearchDTO.setEndDate(params.get("endDate")); - SalesHistoryStatisticsDTO responseSalesHistory = salesHistoryQueryService.selectStatisticsSearchYearByEmployee(salesHistorySearchDTO); + List responseSalesHistory = salesHistoryQueryService.selectStatisticsSearchYearByEmployee(salesHistorySearchDTO); return ResponseEntity.ok(SalesHistoryResponseMessage.builder() .httpStatus(200) - .msg("사원 통계(실적,수당,매출액) 월 별 검색 성공") + .msg("사원 통계(실적,수당,매출액) 연도 별 검색 성공") .result(responseSalesHistory) .build()); } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsDTO.java index 50a244fa..4c307fb1 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistoryStatisticsDTO.java @@ -12,4 +12,6 @@ public class SalesHistoryStatisticsDTO { private int totalSales; private String orderBy; private String month; + private String year; + private String createdAt; } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java index 21e99b5a..95704e1f 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java @@ -32,7 +32,7 @@ List findAllSalesHistory(@Param("size") int size List findStatisticsSearchMonthByEmployee(@Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); - SalesHistoryStatisticsDTO findStatisticsSearchYearByEmployee(@Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); + List findStatisticsSearchYearByEmployee(@Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); List findAllRank(@Param("salesHistoryRankedDataDTO")SalesHistoryRankedDataDTO salesHistoryRankedDataDTO , @Param("size") int size diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java index 8a3b599a..15ba8e5f 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java @@ -21,7 +21,7 @@ public interface SalesHistoryQueryService { List selectStatisticsSearchMonthByEmployee(SalesHistorySearchDTO salesHistorySearchDTO); - SalesHistoryStatisticsDTO selectStatisticsSearchYearByEmployee(SalesHistorySearchDTO salesHistorySearchDTO); + List selectStatisticsSearchYearByEmployee(SalesHistorySearchDTO salesHistorySearchDTO); Page selectStatistics(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java index 1702e979..a82f0764 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java @@ -280,7 +280,6 @@ public SalesHistoryStatisticsDTO selectStatisticsSearchByEmployee(SalesHistorySe public List selectStatisticsSearchMonthByEmployee(SalesHistorySearchDTO salesHistorySearchDTO) { salesHistorySearchDTO.setSearcherName(authQueryService.selectMemberIdByLoginId(salesHistorySearchDTO.getSearcherName())); - System.out.println(salesHistorySearchDTO.getSearcherName()); String parseStartDate = salesHistorySearchDTO.getStartDate(); String parseEndDate = salesHistorySearchDTO.getEndDate(); @@ -298,7 +297,7 @@ public List selectStatisticsSearchMonthByEmployee(Sal @Override @Transactional(readOnly = true) - public SalesHistoryStatisticsDTO selectStatisticsSearchYearByEmployee(SalesHistorySearchDTO salesHistorySearchDTO) { + public List selectStatisticsSearchYearByEmployee(SalesHistorySearchDTO salesHistorySearchDTO) { salesHistorySearchDTO.setSearcherName(authQueryService.selectMemberIdByLoginId(salesHistorySearchDTO.getSearcherName())); String parseStartDate = salesHistorySearchDTO.getStartDate(); @@ -307,12 +306,17 @@ public SalesHistoryStatisticsDTO selectStatisticsSearchYearByEmployee(SalesHisto parseStartDate = parseStartDate.substring(0,4); parseEndDate = parseEndDate.substring(0,4); - SalesHistoryStatisticsDTO salesHistoryStatisticsDTO = salesHistoryMapper.findStatisticsSearchYearByEmployee(salesHistorySearchDTO); + salesHistorySearchDTO.setStartDate(parseStartDate); + salesHistorySearchDTO.setEndDate(parseEndDate); - if(salesHistoryStatisticsDTO == null){ + List salesHistoryStatisticsDTOList = salesHistoryMapper.findStatisticsSearchYearByEmployee(salesHistorySearchDTO); + + System.out.println("check: " + salesHistoryStatisticsDTOList); + + if(salesHistoryStatisticsDTOList == null){ throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); } - return salesHistoryStatisticsDTO; + return salesHistoryStatisticsDTOList; } @Override diff --git a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml index b90ab15f..bd17f786 100644 --- a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml @@ -35,6 +35,8 @@ + + @@ -425,6 +427,7 @@ - SELECT LEFT(a.created_at, 4) AS YEAR, - SUM(a.sal_hist_ince) AS INCENTIVE, - SUM(a.sal_hist_no_of_veh) AS PERFORMANCE, - SUM(a.sal_hist_tota_sale) AS TOTAL_SALES + COALESCE(SUM(a.sal_hist_ince), 0) AS INCENTIVE, + COALESCE(SUM(a.sal_hist_no_of_veh), 0) AS PERFORMANCE, + COALESCE(SUM(a.sal_hist_tota_sale), 0) AS TOTAL_SALES FROM tb_sales_history a a.mem_id = #{salesHistorySearchDTO.searcherName} AND a.active = TRUE @@ -498,13 +501,13 @@ GROUP BY LEFT(a.created_at, 4) - ORDER BY total_incentive DESC + ORDER BY INCENTIVE DESC - ORDER BY total_performance DESC + ORDER BY PERFORMANCE DESC - ORDER BY total_sales DESC + ORDER BY TOTAL_SALES DESC ORDER BY YEAR ASC From 2f46b012f04874fe38999016ac06e96afb023f3e Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 5 Dec 2024 21:42:18 +0900 Subject: [PATCH 470/563] =?UTF-8?q?=EC=9E=84=EC=8B=9C=EB=A1=9C=20eb?= =?UTF-8?q?=EC=9D=98stanl2proj=EC=97=90=20=EB=B0=B0=ED=8F=AC=20=ED=95=B4?= =?UTF-8?q?=EB=86=93=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index a92599d2..8254f10b 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -12,8 +12,8 @@ jobs: env: AWS_REGION: ap-northeast-2 - APPLICATION_NAME: "motive-backend" - ENVIRONMENT_NAME: "Motive-backend-env" + APPLICATION_NAME: "motive-frontend" + ENVIRONMENT_NAME: "Motive-frontend-env" steps: - name: Checkout code From 4d2ca9ed5adf6f5e60d7b08ef04797907b0fe9d6 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 5 Dec 2024 22:09:59 +0900 Subject: [PATCH 471/563] =?UTF-8?q?=EC=9E=84=EC=8B=9C=EB=A1=9C=20eb?= =?UTF-8?q?=EC=9D=98stanl2proj=EC=97=90=20=EB=B0=B0=ED=8F=AC=20=ED=95=B4?= =?UTF-8?q?=EB=86=93=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 8254f10b..2d08a4f4 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -15,6 +15,7 @@ jobs: APPLICATION_NAME: "motive-frontend" ENVIRONMENT_NAME: "Motive-frontend-env" + steps: - name: Checkout code uses: actions/checkout@v3 From 61595db65e9f618073be8d8473935dbdfc6e4e74 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Thu, 5 Dec 2024 22:46:31 +0900 Subject: [PATCH 472/563] =?UTF-8?q?=EC=9E=84=EC=8B=9C=EB=A1=9C=20eb?= =?UTF-8?q?=EC=9D=98stanl2proj=EC=97=90=20=EB=B0=B0=ED=8F=AC=20=ED=95=B4?= =?UTF-8?q?=EB=86=93=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 2d08a4f4..2c37d650 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -12,8 +12,8 @@ jobs: env: AWS_REGION: ap-northeast-2 - APPLICATION_NAME: "motive-frontend" - ENVIRONMENT_NAME: "Motive-frontend-env" + APPLICATION_NAME: "motive-back" + ENVIRONMENT_NAME: "Motive-back-env" steps: From f5f1a445ac5202b458238957a077fe9f5cb1a37a Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Thu, 5 Dec 2024 23:27:09 +0900 Subject: [PATCH 473/563] =?UTF-8?q?fix:=20=EB=8D=94=EB=AF=B8=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=88=98=EC=A0=95(#231)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../final_backend/global/dataloader/Initializer.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java index 60b382f6..f00a42b2 100644 --- a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java +++ b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java @@ -232,7 +232,6 @@ public void run(ApplicationArguments args) throws Exception { Random random = new Random(); String[] positions = {"영업 사원", "영업 관리자", "영업 담당자"}; - String[] grades = {"A", "B", "C", "D"}; String[] jobTypes = {"REGULAR", "TEMPORARY"}; String[] militaryStatus = {"FULFILLED", "EXEMPTION", "UNFULFILLED"}; String[] genders = {"MALE", "FEMALE"}; @@ -272,7 +271,7 @@ public void run(ApplicationArguments args) throws Exception { String.format("010-1234-%04d", i), address, positions[n], // Random position - grades[random.nextInt(grades.length)], // Random grade + "대학 졸업", jobTypes[random.nextInt(jobTypes.length)], // Random job type militaryStatus[random.nextInt(militaryStatus.length)], // Random military status "Bank" + centerId, @@ -557,10 +556,10 @@ public void run(ApplicationArguments args) throws Exception { String birthDateStr2 = randomBirthDate.format(DateTimeFormatter.ofPattern("yyyy년 MM월 dd일")); // YYMMDD 형식 String genderDigit = Math.random() < 0.5 ? "1" : "2"; // 성별은 1(남) 또는 2(여) String randomNumbers = String.format("%04d", (int) (Math.random() * 10000)); // 임의의 4자리 번호 - String checkDigit = String.format("%d", (int) (Math.random() * 10)); // 검증용 마지막 자리는 0~9 + String checkDigit = String.format("%02d", (int) (Math.random() * 10)); // 검증용 마지막 자리는 0~9 // 주민등록번호 만들기 - String identNo = birthDateStr + genderDigit + "-" + randomNumbers + checkDigit; + String identNo = birthDateStr + "-" + genderDigit + randomNumbers + checkDigit; Family newFamily = new Family(); newFamily.setName(lastNames[random.nextInt(lastNames.length)] + firstNames[random.nextInt(firstNames.length)]); @@ -568,8 +567,8 @@ public void run(ApplicationArguments args) throws Exception { newFamily.setSex(Math.random() < 0.5 ? "MALE" : "FEMALE"); newFamily.setBirth(birthDateStr2); newFamily.setPhone(randomPhone); - newFamily.setDie(Math.random() < 0.5 ? true : false); - newFamily.setDisability(Math.random() < 0.5 ? true : false); + newFamily.setDie(Math.random() < 0.1 ? true : false); + newFamily.setDisability(Math.random() < 0.1 ? true : false); newFamily.setIdentNo(identNo); newFamily.setMemId(memberId); From d2c6d550e3c41a4f42e39b8ddb2cf90d44bd3054 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Fri, 6 Dec 2024 01:27:55 +0900 Subject: [PATCH 474/563] =?UTF-8?q?fix:=20feat/integration=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/config/RequestMatcherConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java index 9a3a570b..dfcdeb19 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java @@ -211,6 +211,7 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/all/year").hasAnyRole("salesHistory-statistics-all-year-post", "GOD", "DIRECTOR", "ADMIN") .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/all/month").hasAnyRole("salesHistory-statistics-all-month-post", "GOD", "DIRECTOR", "ADMIN") .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/search").hasAnyRole("salesHistory-search-post", "GOD", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/best").hasAnyRole("salesHistory-best", "GOD", "DIRECTOR", "ADMIN") .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee").hasAnyRole("salesHistory-employee-get", "GOD", "EMPLOYEE") .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics").hasAnyRole("salesHistory-employee-statistics-get", "GOD", "EMPLOYEE") .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search").hasAnyRole("salesHistory-employee-statistics-search-get", "GOD", "EMPLOYEE") From e7f6cc315d6b3984df925d5b0f4dc895e54cf397 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Fri, 6 Dec 2024 01:29:53 +0900 Subject: [PATCH 475/563] =?UTF-8?q?feat:=20https=20=EC=9A=94=EC=B2=AD=20se?= =?UTF-8?q?curity=20config=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/config/ProdSecurityConfig.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index 3ad2d625..bb45945e 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -53,7 +53,8 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication // 필터 순서: JWT 검증 -> CSRF .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, logRepository), UsernamePasswordAuthenticationFilter.class) - .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()) +// .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()) + .requiresChannel(rcc -> rcc.anyRequest().requiresSecure()) // Only HTTPS // 인증 및 권한 예외를 처리 .exceptionHandling(ex -> ex .authenticationEntryPoint((request, response, authException) -> { From 6006477f93e973aab1ae1575c341a19c1efc561e Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 6 Dec 2024 01:56:42 +0900 Subject: [PATCH 476/563] =?UTF-8?q?=EC=9E=84=EC=8B=9C=EB=A1=9C=20eb?= =?UTF-8?q?=EC=9D=98stanl2proj=EC=97=90=20=EB=B0=B0=ED=8F=AC=20=ED=95=B4?= =?UTF-8?q?=EB=86=93=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../final_backend/global/security/config/ProdSecurityConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index 3ad2d625..5900de9b 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -54,6 +54,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication // 필터 순서: JWT 검증 -> CSRF .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, logRepository), UsernamePasswordAuthenticationFilter.class) .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()) + // 인증 및 권한 예외를 처리 .exceptionHandling(ex -> ex .authenticationEntryPoint((request, response, authException) -> { From e79b27b0e381a528836ad90d53c129f001e3b556 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 6 Dec 2024 02:05:01 +0900 Subject: [PATCH 477/563] =?UTF-8?q?cors=20=EC=97=90=EB=9F=AC=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=20=EB=8F=84=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/stanl_2/final_backend/global/config/WebConfig.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/global/config/WebConfig.java b/src/main/java/stanl_2/final_backend/global/config/WebConfig.java index 447918f6..b8508aef 100644 --- a/src/main/java/stanl_2/final_backend/global/config/WebConfig.java +++ b/src/main/java/stanl_2/final_backend/global/config/WebConfig.java @@ -9,8 +9,8 @@ public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/api/**") // API 경로 매핑 - .allowedOrigins("http://stanl2proj-env.eba-tupnyt2m.ap-northeast-2.elasticbeanstalk.com") // 클라이언트 출처 + registry.addMapping("/**") // API 경로 매핑 + .allowedOrigins("https://stanl2-final-frontend.vercel.app") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 허용할 HTTP 메서드 .allowedHeaders("Content-Type", "Authorization") // 허용할 요청 헤더 .exposedHeaders("Authorization") // 응답 헤더 노출 From 08b3a9fa9e325ae47d5cf9648357e249755fe89d Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 6 Dec 2024 02:12:14 +0900 Subject: [PATCH 478/563] =?UTF-8?q?cors=20=EC=97=90=EB=9F=AC=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=20=EB=8F=84=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../final_backend/global/security/config/ProdSecurityConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index bb45945e..b48fe896 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -1,3 +1,4 @@ + package stanl_2.final_backend.global.security.config; import lombok.extern.slf4j.Slf4j; From ea675cf4be08ec93a0f3c8df0f2d2ca3b596df3d Mon Sep 17 00:00:00 2001 From: giuseog Date: Fri, 6 Dec 2024 09:24:50 +0900 Subject: [PATCH 479/563] =?UTF-8?q?fix:=20ddl=20=EA=B5=AC=EC=A1=B0?= =?UTF-8?q?=EC=97=90=20=EB=A7=9E=EC=B6=98=20=EC=BF=BC=EB=A6=AC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95(#235)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/product/query/repository/ProductMapper.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml b/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml index 7ec87675..f6120300 100644 --- a/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml @@ -88,7 +88,7 @@ b.opt_sun_roof, b.opt_sond FROM tb_product a - JOIN tb_product_option b ON a.prod_id = b.prod_id + JOIN tb_product_option b ON a.prod_id = b.product_id WHERE a.prod_id = #{productId} AND a.active = TRUE From 3a1bcda3382a1435a159c0993c621e8467044a23 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Fri, 6 Dec 2024 09:30:04 +0900 Subject: [PATCH 480/563] =?UTF-8?q?refactor:=20=EA=B3=84=EC=95=BD=EC=84=9C?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/contract/query/dto/ContractExcelDTO.java | 6 +++--- .../contract/query/repository/ContractMapper.xml | 11 ++++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractExcelDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractExcelDTO.java index 325afd57..693c9d83 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractExcelDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractExcelDTO.java @@ -28,9 +28,9 @@ public class ContractExcelDTO { @ExcelColumnName(name = "제품명") private String carName; - @ExcelColumnName(name = "고객 구분") - private String customerClassifcation; - @ExcelColumnName(name = "고객 조건") private String customerPurchaseCondition; + + @ExcelColumnName(name = "계약일자") + private String createdAt; } diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index c0f6d761..62fab251 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -73,6 +73,7 @@ + @@ -91,6 +92,7 @@ + From b6320d10d37aa2f1d0e2c68aec84940983f7469e Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 6 Dec 2024 09:33:47 +0900 Subject: [PATCH 481/563] =?UTF-8?q?fix:=20DB=EA=B4=80=EB=A0=A8=20=EC=9D=B4?= =?UTF-8?q?=EC=8A=88=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 7e5cf1a7..407da6de 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -3,13 +3,23 @@ server: spring: application: - name: ${SPRING_APP_NAME} + name: finalbackend datasource: - driver-class-name: org.mariadb.jdbc.Driver - url: jdbc:mariadb://${DATABASE_HOST}:${MARIA_DATABASE_PORT}/${MARIA_DATABASE_NAME} - username: ${DB_USERNAME} - password: ${DB_PASSWORD} + writer: + hikari: + jdbc-url: jdbc:mariadb://motive.cdw8kw8go27z.ap-northeast-2.rds.amazonaws.com:3306/motive + username: admin + password: motivepassword + driver-class-name: org.mariadb.jdbc.Driver + + reader: + hikari: + jdbc-url: jdbc:mariadb://readonlymotive.cdw8kw8go27z.ap-northeast-2.rds.amazonaws.com:3306/motive + username: admin + password: motivepassword + driver-class-name: org.mariadb.jdbc.Driver + profiles: active: ${SPRING_PROFILES_ACTIVE} From 332da4b864e296f06dbfcb1d460f3c310e4fcde5 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 6 Dec 2024 10:25:46 +0900 Subject: [PATCH 482/563] =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/config/ProdSecurityConfig.java | 4 +--- src/main/resources/application-prod.yml | 2 +- src/main/resources/application.yml | 7 ++++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index b48fe896..d51d7c21 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -54,9 +54,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication // 필터 순서: JWT 검증 -> CSRF .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, logRepository), UsernamePasswordAuthenticationFilter.class) -// .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()) - .requiresChannel(rcc -> rcc.anyRequest().requiresSecure()) // Only HTTPS - // 인증 및 권한 예외를 처리 + .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()) // Only HTTPS .exceptionHandling(ex -> ex .authenticationEntryPoint((request, response, authException) -> { log.error("Authentication error: {}", authException.getMessage()); diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index b783f8a4..049ddb22 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -17,7 +17,7 @@ spring: logging: level: - org.springframework.security: WARN + org.springframework.security: debug org.mybatis: debug mybatis: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 407da6de..25a23af9 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -3,7 +3,7 @@ server: spring: application: - name: finalbackend + name: ${SPRING_APP_NAME} datasource: writer: @@ -20,7 +20,6 @@ spring: password: motivepassword driver-class-name: org.mariadb.jdbc.Driver - profiles: active: ${SPRING_PROFILES_ACTIVE} @@ -92,4 +91,6 @@ claude: naver: client-id: CdxW72qcvZLVTHrViB20 - client-secret: ZjP2cbXlfU \ No newline at end of file + client-secret: ZjP2cbXlfU + + From 8f6004bd1811b3d47617d756407d8b425f21ee3e Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 6 Dec 2024 10:37:58 +0900 Subject: [PATCH 483/563] =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 25a23af9..cd3bb913 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -3,7 +3,7 @@ server: spring: application: - name: ${SPRING_APP_NAME} + name: finalbackend datasource: writer: From 7bf1da4b1bb9b9efa09aad2eaa839ea5a6821e63 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 6 Dec 2024 11:07:15 +0900 Subject: [PATCH 484/563] =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index cd3bb913..53ff7a91 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -71,12 +71,13 @@ logging: cloud: aws: credentials: - access-key: ${S3_ACCESSKEY} - secret-key: ${S3_SECRETKEY} + access-key: AKIAQE3ROMWSWEBT7F7O + secret-key: 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 s3: - bucket: ${S3_BUCKETNAME} + bucket: motivebk region: - static: ${S3_REGION} + static: ap-northeast-2 + stack: auto: false From fa146a9c40bb714438e634f9b6f076a89604242c Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 6 Dec 2024 11:23:55 +0900 Subject: [PATCH 485/563] =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nginx/default.conf | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/nginx/default.conf b/nginx/default.conf index 50fa47fc..ef84e516 100644 --- a/nginx/default.conf +++ b/nginx/default.conf @@ -1,9 +1,23 @@ upstream backend { - server backend:8080; # 백엔드 컨테이너의 포트를 8080으로 수정 + server backend:8080; # backend 컨테이너가 8080번 포트에서 실행되고 있는지 확인 } server { listen 80; + server_name devms.site; + + # HTTP 요청을 HTTPS로 리디렉션하는 설정 (선택 사항) + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 443 ssl; + server_name devms.site; + + ssl_certificate /path/to/ssl/certificate.crt; + ssl_certificate_key /path/to/ssl/private.key; location / { proxy_pass http://backend; @@ -15,4 +29,4 @@ server { proxy_set_header Connection "keep-alive"; proxy_cache_bypass $http_upgrade; } -} \ No newline at end of file +} From c4408797504b40eb59874357c4bedc79d754c9a2 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 6 Dec 2024 11:42:09 +0900 Subject: [PATCH 486/563] =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 94 +++++++++++++++--------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 53ff7a91..b22b70c0 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,6 +1,7 @@ server: forward-headers-strategy: native + spring: application: name: finalbackend @@ -20,6 +21,7 @@ spring: password: motivepassword driver-class-name: org.mariadb.jdbc.Driver + profiles: active: ${SPRING_PROFILES_ACTIVE} @@ -28,6 +30,7 @@ spring: enabled: false restart: enabled: false + servlet: multipart: enabled: true @@ -46,52 +49,49 @@ spring: mail.smtp.ssl.protocols: TLSv1.2 mail.debug: true -jwt: - secret-key: ${JWT_SECRET_KEY} - header: ${JWT_HEADER} - -encryption: - algorithm: ${ALGORITHM} - transformation: ${TRANSFORMATION} - secret-key: ${SECRET_DEFAULT_KEY} - - -logging: - pattern: - console: ${LOGPATTERN_CONSOLE:%green(%d{HH:mm:ss.SSS}) %blue(%-5level) %red([%thread]) %yellow(%logger{15}) - %msg%n} - level: - org: - springframework: - security: ${SPRING_SECURITY_LOG_LEVEL:TRACE} - apache.ibatis: DEBUG - mybatis: DEBUG - jdbc.sqltiming: DEBUG - jdbc.resultsettable: TRACE - -cloud: - aws: - credentials: - access-key: AKIAQE3ROMWSWEBT7F7O - secret-key: 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 - s3: - bucket: motivebk - region: - static: ap-northeast-2 - - stack: - auto: false - -mybatis: - configuration: - log-impl: org.apache.ibatis.logging.stdout.StdOutImpl - -claude: - api: - key: ${CLAUDE_API_KEY} - url: https://api.claude.ai/v1/summary - -naver: - client-id: CdxW72qcvZLVTHrViB20 - client-secret: ZjP2cbXlfU + wt: + secret-key: s4G7tL9qH8vI2eP0j5W1uF3bM6nK8rXc + header: Authorization + + + encryption: + algorithm: AES + transformation: AES/ECB/PKCS5Padding + secret-key: haWh*9teA@2sT!nLaO$i0lEcj3cU282d + + + + logging: + pattern: + console: ${LOGPATTERN_CONSOLE:%green(%d{HH:mm:ss.SSS}) %blue(%-5level) %red([%thread]) %yellow(%logger{15}) - %msg%n} + level: + org: + springframework: + security: ${SPRING_SECURITY_LOG_LEVEL:TRACE} + + cloud: + aws: + credentials: + access-key: AKIAQE3ROMWSWEBT7F7O + secret-key: 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 + s3: + bucket: motivebk + region: + static: ap-northeast-2 + stack: + auto: false + + mybatis: + configuration: + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + + claude: + api: + key: ${CLAUDE_API_KEY} + url: https://api.claude.ai/v1/summary + + naver: + client-id: CdxW72qcvZLVTHrViB20 + client-secret: ZjP2cbXlfU From 87dedcc5bbc914e85749f4b97b9e816782c6395c Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 6 Dec 2024 15:31:34 +0900 Subject: [PATCH 487/563] =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nginx/default.conf | 18 ++---------------- src/main/resources/application.yml | 10 +++++----- 2 files changed, 7 insertions(+), 21 deletions(-) diff --git a/nginx/default.conf b/nginx/default.conf index ef84e516..50fa47fc 100644 --- a/nginx/default.conf +++ b/nginx/default.conf @@ -1,23 +1,9 @@ upstream backend { - server backend:8080; # backend 컨테이너가 8080번 포트에서 실행되고 있는지 확인 + server backend:8080; # 백엔드 컨테이너의 포트를 8080으로 수정 } server { listen 80; - server_name devms.site; - - # HTTP 요청을 HTTPS로 리디렉션하는 설정 (선택 사항) - location / { - return 301 https://$host$request_uri; - } -} - -server { - listen 443 ssl; - server_name devms.site; - - ssl_certificate /path/to/ssl/certificate.crt; - ssl_certificate_key /path/to/ssl/private.key; location / { proxy_pass http://backend; @@ -29,4 +15,4 @@ server { proxy_set_header Connection "keep-alive"; proxy_cache_bypass $http_upgrade; } -} +} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index b22b70c0..e70bb685 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -49,7 +49,7 @@ spring: mail.smtp.ssl.protocols: TLSv1.2 mail.debug: true - wt: + jwt: secret-key: s4G7tL9qH8vI2eP0j5W1uF3bM6nK8rXc header: Authorization @@ -85,10 +85,10 @@ spring: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl - claude: - api: - key: ${CLAUDE_API_KEY} - url: https://api.claude.ai/v1/summary +# claude: +# api: +# key: ${CLAUDE_API_KEY} +# url: https://api.claude.ai/v1/summary naver: client-id: CdxW72qcvZLVTHrViB20 From 7a3e09a2fbc8e6849b3e82475302bfc938bdbd60 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 6 Dec 2024 15:46:21 +0900 Subject: [PATCH 488/563] =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index e70bb685..0c47e5ad 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -40,12 +40,12 @@ spring: mail: host: port: - username: ${EMAIL_ID} - password: ${EMAIL_PWD} + username: stanl2e2@naver.com + password: "Stanl222" properties: mail.smtp.auth: true mail.smtp.ssl.enable: true - mail.smtp.ssl.trust: ${EMAIL_HOST} + mail.smtp.ssl.trust: smtp.naver.com mail.smtp.ssl.protocols: TLSv1.2 mail.debug: true @@ -72,8 +72,8 @@ spring: cloud: aws: credentials: - access-key: AKIAQE3ROMWSWEBT7F7O - secret-key: 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 + access-key: AKIAQE3ROMWSU6NF677B + secret-key: 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 s3: bucket: motivebk region: From de475aa0ba31dae54ebd930c716e9e1b83dc2f2b Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 6 Dec 2024 16:06:05 +0900 Subject: [PATCH 489/563] =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 0c47e5ad..c39238ce 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -72,8 +72,8 @@ spring: cloud: aws: credentials: - access-key: AKIAQE3ROMWSU6NF677B - secret-key: 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 + access-key: AKIAQE3ROMWSWEBT7F7O + secret-key: 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 s3: bucket: motivebk region: From 6b65ff027cbce1a32eceafaa6f587f42c66abda4 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 6 Dec 2024 16:40:58 +0900 Subject: [PATCH 490/563] =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index c39238ce..cf3de28d 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -40,7 +40,7 @@ spring: mail: host: port: - username: stanl2e2@naver.com + username: "stanl2e2@naver.com" password: "Stanl222" properties: mail.smtp.auth: true @@ -69,17 +69,17 @@ spring: springframework: security: ${SPRING_SECURITY_LOG_LEVEL:TRACE} - cloud: - aws: - credentials: - access-key: AKIAQE3ROMWSWEBT7F7O - secret-key: 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 - s3: - bucket: motivebk - region: - static: ap-northeast-2 - stack: - auto: false +cloud: + aws: + credentials: + access-key: "AKIAQE3ROMWSWEBT7F7O" + secret-key: "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" + s3: + bucket: "motivebk" + region: + static: "ap-northeast-2" + stack: + auto: false mybatis: configuration: From b2e5382dc80e623af0852a262dfa2bd83422c869 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 6 Dec 2024 16:42:11 +0900 Subject: [PATCH 491/563] =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 2c37d650..66db22d7 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -52,7 +52,7 @@ jobs: with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }} - aws-region: ${{ env.AWS_REGION }} + aws-region: ${{ secrets.AWS_REGION }} - name: Set Environment Variables run: | From c7357a34c579b0dda3efef93ef86a62f0f4ad964 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 6 Dec 2024 16:55:27 +0900 Subject: [PATCH 492/563] =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 66db22d7..2c37d650 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -52,7 +52,7 @@ jobs: with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }} - aws-region: ${{ secrets.AWS_REGION }} + aws-region: ${{ env.AWS_REGION }} - name: Set Environment Variables run: | From deee535eedc687a8e89be087159e4233f5b37eb2 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 6 Dec 2024 17:19:02 +0900 Subject: [PATCH 493/563] =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 40 +++++++++++++++--------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index cf3de28d..b7f2dda8 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -40,8 +40,8 @@ spring: mail: host: port: - username: "stanl2e2@naver.com" - password: "Stanl222" + username: stanl2e2@naver.com + password: Stanl222 properties: mail.smtp.auth: true mail.smtp.ssl.enable: true @@ -49,15 +49,15 @@ spring: mail.smtp.ssl.protocols: TLSv1.2 mail.debug: true - jwt: - secret-key: s4G7tL9qH8vI2eP0j5W1uF3bM6nK8rXc - header: Authorization +jwt: + secret-key: s4G7tL9qH8vI2eP0j5W1uF3bM6nK8rXc + header: Authorization - encryption: - algorithm: AES - transformation: AES/ECB/PKCS5Padding - secret-key: haWh*9teA@2sT!nLaO$i0lEcj3cU282d +encryption: + algorithm: AES + transformation: AES/ECB/PKCS5Padding + secret-key: "haWh*9teA@2sT!nLaO$i0lEcj3cU282d" @@ -72,12 +72,12 @@ spring: cloud: aws: credentials: - access-key: "AKIAQE3ROMWSWEBT7F7O" - secret-key: "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" + access-key: AKIAQE3ROMWSWEBT7F7O + secret-key: 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 s3: - bucket: "motivebk" + bucket: motivebk region: - static: "ap-northeast-2" + static: ap-northeast-2 stack: auto: false @@ -85,13 +85,13 @@ cloud: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl -# claude: -# api: -# key: ${CLAUDE_API_KEY} -# url: https://api.claude.ai/v1/summary +claude: + api: + key: ${CLAUDE_API_KEY} + url: https://api.claude.ai/v1/summary - naver: - client-id: CdxW72qcvZLVTHrViB20 - client-secret: ZjP2cbXlfU +naver: + client-id: CdxW72qcvZLVTHrViB20 + client-secret: ZjP2cbXlfU From 4374d528ca76f83072751e2a8b9826875d37fa3e Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 6 Dec 2024 17:34:48 +0900 Subject: [PATCH 494/563] =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index b7f2dda8..c8a94394 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -87,7 +87,7 @@ cloud: claude: api: - key: ${CLAUDE_API_KEY} + key: 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 url: https://api.claude.ai/v1/summary naver: From e40cd637dc1432efc9025c056a890f7e4cd5b36f Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 6 Dec 2024 17:51:36 +0900 Subject: [PATCH 495/563] =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/config/ProdSecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index d51d7c21..9e250cd1 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -76,7 +76,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); - config.setAllowedOrigins(Collections.singletonList("http://localhost:5173")); + config.setAllowedOrigins(Collections.singletonList("https://final-frontend-nine.vercel.app/")); config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); config.setAllowCredentials(true); config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "X-XSRF-TOKEN")); From 220fb4c2a371350771f2d6478158ba9b667aabec Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Fri, 6 Dec 2024 18:02:13 +0900 Subject: [PATCH 496/563] =?UTF-8?q?refactor:=20=EA=B3=84=EC=95=BD=EC=84=9C?= =?UTF-8?q?=20=EC=B0=A8=EB=9F=89=EA=B8=88=EC=95=A1=20=EC=BB=AC=EB=9F=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../contract/command/application/dto/ContractModifyDTO.java | 1 + .../contract/command/application/dto/ContractRegistDTO.java | 1 + .../contract/command/domain/aggregate/entity/Contract.java | 3 +++ .../domain/contract/query/dto/ContractSelectAllDTO.java | 1 + .../domain/contract/query/repository/ContractMapper.xml | 5 ++++- 5 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java index 06acab5b..4bc04bb6 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java @@ -28,6 +28,7 @@ public class ContractModifyDTO { private String selectOption; private Integer downPayment; private Integer intermediatePayment; + private Integer vehiclePrice; private Integer remainderPayment; private Integer consignmentPayment; private Integer totalSales; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java index c87c4280..31c8d9fa 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java @@ -25,6 +25,7 @@ public class ContractRegistDTO { private String serialNum; private String selectOption; private Integer downPayment; + private Integer vehiclePrice; private Integer intermediatePayment; private Integer remainderPayment; private Integer consignmentPayment; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java index 47b96f79..3df5315b 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java @@ -99,6 +99,9 @@ public class Contract { @Column(name = "CONR_TOTA_SALE", nullable = false) private Integer totalSales = 0; + @Column(name = "CONR_VEHI_PRIC", nullable = false) + private Integer vehiclePrice = 0; + @Lob @Column(name = "CREATED_URL", nullable = false, columnDefinition = "TEXT") private String createdUrl; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java index 762bc9d5..42345692 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java @@ -18,6 +18,7 @@ public class ContractSelectAllDTO { private String customerId; private String productId; private String carName; + private String createdAt; private String customerClassifcation; private String customerPurchaseCondition; } diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index 62fab251..7e88d578 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -28,6 +28,7 @@ + @@ -71,6 +72,7 @@ + @@ -185,7 +187,8 @@ a.created_url, a.active, a.conr_cust_cla, - a.conr_cust_pur_cond + a.conr_cust_pur_cond, + a.created_at, FROM tb_contract a WHERE a.mem_id = #{ memberId } AND a.active = TRUE From b82e03ce243f50b6ad38e4d94351604884e7a0de Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 6 Dec 2024 19:32:19 +0900 Subject: [PATCH 497/563] =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/notices/query/controller/NoticeController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java index 54f49913..383dd42e 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java @@ -32,7 +32,7 @@ public NoticeController(NoticeService noticeService) { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = NoticeResponseMessage.class))}) }) - @GetMapping + @GetMapping("search") public ResponseEntity> getNotices( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, From 52bd98880c32967465286e425cf16c1406910307 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Fri, 6 Dec 2024 20:17:43 +0900 Subject: [PATCH 498/563] =?UTF-8?q?fix:=20=EB=8D=94=EB=AF=B8=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=88=98=EC=A0=95(#240)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/dataloader/Initializer.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java index f00a42b2..94d2426c 100644 --- a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java +++ b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java @@ -179,7 +179,7 @@ public void run(ApplicationArguments args) throws Exception { "미필", "한국은행", "000-0000-000000-00000", - "CEN_000000000", + "CEN_000000001", "ORG_000000000", "EMPLOYEE", loadImage("default.png") @@ -201,7 +201,7 @@ public void run(ApplicationArguments args) throws Exception { "미필", "한국은행", "000-0000-000000-00000", - "CEN_000000000", + "CEN_000000001", "ORG_000000000", "ADMIN", loadImage("default.png") @@ -223,7 +223,7 @@ public void run(ApplicationArguments args) throws Exception { "미필", "한국은행", "000-0000-000000-00000", - "CEN_000000000", + "CEN_000000001", "ORG_000000000", "DIRECTOR", loadImage("default.png") @@ -231,7 +231,8 @@ public void run(ApplicationArguments args) throws Exception { Random random = new Random(); - String[] positions = {"영업 사원", "영업 관리자", "영업 담당자"}; + String[] positions = {"INTERN", "STAFF", "ASSISTANT", "MANAGER", "SENIOR", "EXECUTIVE", "DIRECTO", "CEO"}; + String[] grade = {"High School", "Associate", "Bachelor", "Master", "Doctoral"}; String[] jobTypes = {"REGULAR", "TEMPORARY"}; String[] militaryStatus = {"FULFILLED", "EXEMPTION", "UNFULFILLED"}; String[] genders = {"MALE", "FEMALE"}; @@ -256,10 +257,9 @@ public void run(ApplicationArguments args) throws Exception { for (int i = 1; i <= 93; i++) { int centerId = random.nextInt(10) + 1; int orgId = random.nextInt(10) + 5; - String sex = genders[i % 2]; + String sex = genders[(i+1) % 2]; String name = lastNames[random.nextInt(lastNames.length)] + firstNames[random.nextInt(firstNames.length)]; String address = addresses[random.nextInt(addresses.length)]; - int n = random.nextInt(positions.length); createOrUpdateMember( String.format("M%09d", i), "pass" + i, @@ -270,15 +270,15 @@ public void run(ApplicationArguments args) throws Exception { String.format("123456-1%06d", 100000 + random.nextInt(900000)), String.format("010-1234-%04d", i), address, - positions[n], // Random position - "대학 졸업", + positions[random.nextInt(positions.length)], // Random position + grade[random.nextInt(grade.length)], jobTypes[random.nextInt(jobTypes.length)], // Random job type militaryStatus[random.nextInt(militaryStatus.length)], // Random military status "Bank" + centerId, "123456789" + i, String.format("CEN_%09d", centerId), String.format("ORG_%09d", orgId), - roles[n], // Random role + roles[random.nextInt(roles.length)], // Random role loadImage("default.png") ); } From ecca8ba1a35f1a62da56f834bcf92961fdca9113 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 6 Dec 2024 20:31:21 +0900 Subject: [PATCH 499/563] =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/notices/query/controller/NoticeController.java | 2 +- .../global/security/config/ProdSecurityConfig.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java index 383dd42e..ae609bc1 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java @@ -32,7 +32,7 @@ public NoticeController(NoticeService noticeService) { @ApiResponse(responseCode = "200", description = "성공", content = {@Content(schema = @Schema(implementation = NoticeResponseMessage.class))}) }) - @GetMapping("search") + @GetMapping() public ResponseEntity> getNotices( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index 9e250cd1..ee698d12 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -76,7 +76,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); - config.setAllowedOrigins(Collections.singletonList("https://final-frontend-nine.vercel.app/")); + config.setAllowedOrigins(Collections.singletonList("http://localhost:5173/")); config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); config.setAllowCredentials(true); config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "X-XSRF-TOKEN")); From 7f0ae99764f01823b4e10d9963a1feca4db22fda Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 6 Dec 2024 21:02:18 +0900 Subject: [PATCH 500/563] production: change corsconfig cod --- .../global/security/config/ProdSecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index ee698d12..9e250cd1 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -76,7 +76,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); - config.setAllowedOrigins(Collections.singletonList("http://localhost:5173/")); + config.setAllowedOrigins(Collections.singletonList("https://final-frontend-nine.vercel.app/")); config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); config.setAllowCredentials(true); config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "X-XSRF-TOKEN")); From c0bf05c1ec9ffeef961085fdda5fdd442536c398 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Fri, 6 Dec 2024 21:54:36 +0900 Subject: [PATCH 501/563] =?UTF-8?q?feat:=20LogginAspect=20Success=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=82=AC=EC=9B=90=EB=B2=88=ED=98=B8=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=20=EB=82=A8=EA=B9=80(#242)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/log/command/aggregate/Log.java | 3 +++ .../domain/log/command/aop/LoggingAspect.java | 12 ++++++++++++ .../global/exception/GlobalCommonException.java | 1 + .../final_backend/global/mail/MailServiceImpl.java | 1 + 4 files changed, 17 insertions(+) diff --git a/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java b/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java index 9e19bf82..f426fc4c 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java +++ b/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java @@ -64,6 +64,9 @@ public class Log { @Column(name = "ERROR_MESSAGE", columnDefinition = "TEXT") private String errorMessage; + @Column(name = "LOGIN_ID") + private String loginId; + @PrePersist private void prePersist() { diff --git a/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java b/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java index e1bee917..4aca835c 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java +++ b/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java @@ -6,6 +6,8 @@ import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -73,6 +75,16 @@ public void logRequestSuccess(JoinPoint joinPoint) { logEntry.setSessionId(safeValue(request.getRequestedSessionId())); logEntry.setUserAgent(safeValue(request.getHeader("User-Agent"))); + String loginId = "anonymousUser"; + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + + if (authentication != null && authentication.isAuthenticated() && !"anonymousUser".equals(authentication.getPrincipal())) { + loginId = authentication.getPrincipal().toString(); + } + + logEntry.setLoginId(loginId); + // 네트워크 정보 logEntry.setIpAddress(safeValue(getClientIp(request))); logEntry.setHostName(safeValue(request.getRemoteHost())); diff --git a/src/main/java/stanl_2/final_backend/global/exception/GlobalCommonException.java b/src/main/java/stanl_2/final_backend/global/exception/GlobalCommonException.java index 74d94f8a..352f04e8 100644 --- a/src/main/java/stanl_2/final_backend/global/exception/GlobalCommonException.java +++ b/src/main/java/stanl_2/final_backend/global/exception/GlobalCommonException.java @@ -6,6 +6,7 @@ @Getter @RequiredArgsConstructor public class GlobalCommonException extends RuntimeException { + private final GlobalErrorCode errorCode; // 에러 발생시 ErroCode 별 메시지 diff --git a/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java b/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java index 84e5e076..23d318ec 100644 --- a/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/global/mail/MailServiceImpl.java @@ -48,6 +48,7 @@ private String setContext(String code) { // thymeleaf 기반의 html 파일에 값을 넣고 연결 Context context = new Context(); + // templateengine과 classloadertemplateresolver를 활용하여 resource/template에 위치한 mail.html 연결 TemplateEngine templateEngine = new TemplateEngine(); ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver(); From 95633a314b9998c61f42bc5103c2a8080f6dbfce Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Sat, 7 Dec 2024 11:49:53 +0900 Subject: [PATCH 502/563] =?UTF-8?q?fix:=20globalException=20LOG=EC=97=90?= =?UTF-8?q?=20user=20=EC=A0=95=EB=B3=B4=20=EC=B6=94=EA=B0=80(#242)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/log/command/aop/LoggingAspect.java | 5 ++++- .../global/exception/GlobalExceptionHandler.java | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java b/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java index 4aca835c..c7a7c738 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java +++ b/src/main/java/stanl_2/final_backend/domain/log/command/aop/LoggingAspect.java @@ -79,7 +79,10 @@ public void logRequestSuccess(JoinPoint joinPoint) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication != null && authentication.isAuthenticated() && !"anonymousUser".equals(authentication.getPrincipal())) { + if (authentication != null && authentication.isAuthenticated() && + !"anonymousUser".equals(authentication.getPrincipal()) && + !authentication.getPrincipal().toString().startsWith("stanl_2") + ) { loginId = authentication.getPrincipal().toString(); } diff --git a/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java b/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java index fc09ed0b..cb1674b9 100644 --- a/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/stanl_2/final_backend/global/exception/GlobalExceptionHandler.java @@ -7,6 +7,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingServletRequestParameterException; @@ -113,6 +115,19 @@ private void saveErrorLog(String message, Exception e, HttpServletRequest reques logEntry.setSessionId(safeValue(request.getRequestedSessionId())); logEntry.setUserAgent(safeValue(request.getHeader("User-Agent"))); + String loginId = "anonymousUser"; + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + + if (authentication != null && authentication.isAuthenticated() && + !"anonymousUser".equals(authentication.getPrincipal()) && + !authentication.getPrincipal().toString().startsWith("stanl_2") + ) { + loginId = authentication.getPrincipal().toString(); + } + + logEntry.setLoginId(loginId); + // 네트워크 정보 logEntry.setIpAddress(safeValue(getClientIp(request))); logEntry.setHostName(safeValue(request.getRemoteHost())); From cddf34776ec996fde04d965e2a233b4401325c8e Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Sat, 7 Dec 2024 12:10:11 +0900 Subject: [PATCH 503/563] =?UTF-8?q?fix:=20JWTValidation=20LOG=EC=97=90=20U?= =?UTF-8?q?ser=20=EC=A0=95=EB=B3=B4=20=EC=B6=94=EA=B0=80(#242)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/filter/JWTTokenValidatorFilter.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java index 8e43735f..a4968bb0 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java @@ -125,6 +125,19 @@ private void saveErrorLog(String message, Exception e, HttpServletRequest reques logEntry.setSessionId(safeValue(request.getRequestedSessionId())); logEntry.setUserAgent(safeValue(request.getHeader("User-Agent"))); + String loginId = "anonymousUser"; + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + + if (authentication != null && authentication.isAuthenticated() && + !"anonymousUser".equals(authentication.getPrincipal()) && + !authentication.getPrincipal().toString().startsWith("stanl_2") + ) { + loginId = authentication.getPrincipal().toString(); + } + + logEntry.setLoginId(loginId); + // 네트워크 정보 logEntry.setIpAddress(safeValue(getClientIp(request))); logEntry.setHostName(safeValue(request.getRemoteHost())); From c2dcceecdc49fb7a80fc5be4db311fb2fd7f34d7 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Sat, 7 Dec 2024 12:28:56 +0900 Subject: [PATCH 504/563] =?UTF-8?q?fix:=20=EB=A1=9C=EA=B7=B8=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EB=B0=8F=20=EC=97=91=EC=85=80=20=EB=8B=A4=EC=9A=B4?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20=EB=A1=9C=EC=A7=81=20=EC=82=AC=EB=B2=88=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80(#243)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/log/command/aggregate/Log.java | 6 +-- .../log/query/controller/LogController.java | 5 ++- .../domain/log/query/dto/LogDTO.java | 1 + .../domain/log/query/dto/LogExcelDTO.java | 3 ++ .../domain/log/query/dto/LogSearchDTO.java | 3 ++ .../domain/log/query/repository/LogMapper.xml | 39 ++++++++++++------- 6 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java b/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java index f426fc4c..170f8717 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java +++ b/src/main/java/stanl_2/final_backend/domain/log/command/aggregate/Log.java @@ -28,6 +28,9 @@ public class Log { @Column(name = "LOG_ID", nullable = false) private String logId; + @Column(name = "LOGIN_ID") + private String loginId; + @Column(name = "SESSION_ID", nullable = false) private String sessionId; @@ -64,9 +67,6 @@ public class Log { @Column(name = "ERROR_MESSAGE", columnDefinition = "TEXT") private String errorMessage; - @Column(name = "LOGIN_ID") - private String loginId; - @PrePersist private void prePersist() { diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java b/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java index 00f16bdf..107443c6 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java @@ -46,6 +46,7 @@ public LogController(LogQueryService logQueryService) { @GetMapping("") public ResponseEntity getLogs( @RequestParam(required = false) String logId, + @RequestParam(required = false) String loginId, @RequestParam(required = false) String ipAddress, @RequestParam(required = false) String requestTime_start, @RequestParam(required = false) String requestTime_end, @@ -64,9 +65,9 @@ public ResponseEntity getLogs( } - LogSearchDTO searchLogDTO = new LogSearchDTO(logId, ipAddress, requestTime_start, requestTime_end, status, method, uri); + LogSearchDTO searchLogDTO = new LogSearchDTO(logId, loginId, ipAddress, requestTime_start, requestTime_end, status, method, uri); Page logDTOPage = logQueryService.selectLogs(pageable, searchLogDTO); - + return ResponseEntity.ok(LogResponseMessage.builder() .httpStatus(200) .result(logDTOPage) diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogDTO.java b/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogDTO.java index 42724178..8ae932bd 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogDTO.java @@ -11,6 +11,7 @@ @Getter public class LogDTO { private String logId; + private String loginId; private String sessionId; private String userAgent; private String ipAddress; diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogExcelDTO.java b/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogExcelDTO.java index 9d59e519..75ccc114 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogExcelDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogExcelDTO.java @@ -10,6 +10,9 @@ public class LogExcelDTO { @ExcelColumnName(name = "로그 번호") private String logId; + @ExcelColumnName(name = "접근한 유저") + private String loginId; + @ExcelColumnName(name = "트랜잭션 번호") private String transactionId; diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogSearchDTO.java index 4c6cf4f8..7f2c67ad 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/dto/LogSearchDTO.java @@ -11,6 +11,7 @@ @Setter public class LogSearchDTO { private String logId; + private String loginId; private String sessionId; private String userAgent; private String ipAddress; @@ -26,6 +27,7 @@ public class LogSearchDTO { private String errorMessage; public LogSearchDTO(String logId, + String loginId, String ipAddress, String requestTime_start, String requestTime_end, @@ -33,6 +35,7 @@ public LogSearchDTO(String logId, String method, String uri){ this.logId = logId; + this.loginId = loginId; this.ipAddress = ipAddress; this.requestTime_start = requestTime_start; this.requestTime_end = requestTime_end; diff --git a/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml b/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml index f14fbbed..17c77076 100644 --- a/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/log/query/repository/LogMapper.xml @@ -5,6 +5,7 @@ + @@ -21,6 +22,7 @@ + @@ -41,6 +43,9 @@ AND a.log_id LIKE CONCAT('%', #{ searchLogDTO.logId }, '%') + + AND a.login_id LIKE CONCAT('%', #{ searchLogDTO.loginId }, '%') + AND a.ip_address LIKE CONCAT('%', #{ searchLogDTO.ipAddress }, '%') @@ -65,6 +70,7 @@ SELECT - a.log_id, - a.transaction_id, - a.request_time, - a.method, - a.uri, - a.query_string, - a.user_agent, - a.ip_address, - a.host_name, - a.remote_port, - a.status, - a.error_message - FROM tb_log a + a.log_id, + a.login_id, + a.transaction_id, + a.request_time, + a.method, + a.uri, + a.query_string, + a.user_agent, + a.ip_address, + a.host_name, + a.remote_port, + a.status, + a.error_message + FROM tb_log a \ No newline at end of file From f2f256929910edc404d0f305ecbb7a8f2650e539 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Sat, 7 Dec 2024 16:44:10 +0900 Subject: [PATCH 505/563] =?UTF-8?q?feat:=20=EC=9E=84=EC=9B=90=20=EB=98=90?= =?UTF-8?q?=EB=8A=94=20=EB=8C=80=ED=91=9C=EC=9D=B4=EC=82=AC=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EB=B0=9C=EC=83=9D=EC=8B=9C=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EB=A9=94=EC=9D=BC=20=EC=A0=84=EC=86=A1(#242)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../log/query/controller/LogController.java | 2 +- .../query/service/MemberQueryService.java | 5 ++ .../query/service/MemberQueryServiceImpl.java | 25 +++++- .../global/dataloader/Initializer.java | 2 +- .../exception/GlobalExceptionHandler.java | 16 +++- .../global/mail/MailService.java | 3 + .../global/mail/MailServiceImpl.java | 62 +++++++++++++ .../security/config/DevSecurityConfig.java | 8 +- .../security/config/ProdSecurityConfig.java | 10 ++- .../filter/JWTTokenValidatorFilter.java | 14 ++- src/main/resources/templates/errorMail.html | 89 +++++++++++++++++++ 11 files changed, 223 insertions(+), 13 deletions(-) create mode 100644 src/main/resources/templates/errorMail.html diff --git a/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java b/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java index 107443c6..c749ee8f 100644 --- a/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java +++ b/src/main/java/stanl_2/final_backend/domain/log/query/controller/LogController.java @@ -67,7 +67,7 @@ public ResponseEntity getLogs( LogSearchDTO searchLogDTO = new LogSearchDTO(logId, loginId, ipAddress, requestTime_start, requestTime_end, status, method, uri); Page logDTOPage = logQueryService.selectLogs(pageable, searchLogDTO); - + return ResponseEntity.ok(LogResponseMessage.builder() .httpStatus(200) .result(logDTOPage) diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java index bc55205b..05e03b6f 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryService.java @@ -1,8 +1,10 @@ package stanl_2.final_backend.domain.member.query.service; +import jakarta.mail.MessagingException; import jakarta.servlet.http.HttpServletResponse; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import stanl_2.final_backend.domain.log.command.aggregate.Log; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; import stanl_2.final_backend.domain.member.query.dto.MemberSearchDTO; @@ -15,6 +17,7 @@ public interface MemberQueryService { List selectMemberByRole(String role); List selectMemberByCenterId(String centerId); + List selectMemberByCenterList(List centerList); String selectNameById(String memberId) throws GeneralSecurityException; @@ -29,5 +32,7 @@ public interface MemberQueryService { void exportCustomerToExcel(HttpServletResponse response) throws GeneralSecurityException; + void sendErrorMail(String loginId, Log logEntry) throws GeneralSecurityException, MessagingException; + // MemberDetailDTO selectMemberDetail(String name) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index 97664046..d6424afb 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -1,5 +1,6 @@ package stanl_2.final_backend.domain.member.query.service; +import jakarta.mail.MessagingException; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -12,6 +13,7 @@ import org.springframework.web.bind.annotation.RequestParam; import stanl_2.final_backend.domain.center.query.dto.CenterSelectAllDTO; import stanl_2.final_backend.domain.center.query.service.CenterQueryService; +import stanl_2.final_backend.domain.log.command.aggregate.Log; import stanl_2.final_backend.domain.member.common.exception.MemberCommonException; import stanl_2.final_backend.domain.member.common.exception.MemberErrorCode; import stanl_2.final_backend.domain.member.query.dto.MemberDTO; @@ -20,6 +22,7 @@ import stanl_2.final_backend.domain.member.query.repository.MemberMapper; import stanl_2.final_backend.domain.member.query.repository.MemberRoleMapper; import stanl_2.final_backend.global.excel.ExcelUtilsV1; +import stanl_2.final_backend.global.mail.MailService; import stanl_2.final_backend.global.utils.AESUtils; import java.security.GeneralSecurityException; @@ -36,15 +39,21 @@ public class MemberQueryServiceImpl implements MemberQueryService { private final AESUtils aesUtils; private final MemberRoleMapper memberRoleMapper; private final ExcelUtilsV1 excelUtilsV1; + private final MailService mailService; @Autowired - public MemberQueryServiceImpl(MemberMapper memberMapper, CenterQueryService centerQueryService, AESUtils aesUtils, - MemberRoleMapper memberRoleMapper, ExcelUtilsV1 excelUtilsV1) { + public MemberQueryServiceImpl(MemberMapper memberMapper, + CenterQueryService centerQueryService, + AESUtils aesUtils, + MemberRoleMapper memberRoleMapper, + ExcelUtilsV1 excelUtilsV1, + MailService mailService) { this.memberMapper = memberMapper; this.centerQueryService = centerQueryService; this.aesUtils = aesUtils; this.memberRoleMapper = memberRoleMapper; this.excelUtilsV1 = excelUtilsV1; + this.mailService = mailService; } @Override @@ -134,7 +143,6 @@ public List selectMemberByOrganizationId(String organizationId) throw for(int i=0;i auth // 인증 없이 접근 가능한 API 설정 .anyRequest().permitAll()) - .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, logRepository), UsernamePasswordAuthenticationFilter.class) + .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, logRepository, memberQueryService), UsernamePasswordAuthenticationFilter.class) .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()) .exceptionHandling(ex -> ex .authenticationEntryPoint((request, response, authException) -> { diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index 3ad2d625..11a27de5 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -20,6 +20,7 @@ import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import stanl_2.final_backend.domain.log.command.repository.LogRepository; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import stanl_2.final_backend.global.exception.GlobalCommonException; import stanl_2.final_backend.global.exception.GlobalErrorCode; import stanl_2.final_backend.global.security.filter.JWTTokenValidatorFilter; @@ -37,11 +38,14 @@ public class ProdSecurityConfig { @Value("${jwt.secret-key}") private String jwtSecretKey; - private LogRepository logRepository; + private final LogRepository logRepository; + private final MemberQueryService memberQueryService; @Autowired - public ProdSecurityConfig(LogRepository logRepository) { + public ProdSecurityConfig(LogRepository logRepository, + MemberQueryService memberQueryService) { this.logRepository = logRepository; + this.memberQueryService = memberQueryService; } @Bean @@ -52,7 +56,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication .csrf(csrf -> csrf.disable()) // 필터 순서: JWT 검증 -> CSRF - .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, logRepository), UsernamePasswordAuthenticationFilter.class) + .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, logRepository, memberQueryService), UsernamePasswordAuthenticationFilter.class) .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()) // 인증 및 권한 예외를 처리 .exceptionHandling(ex -> ex diff --git a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java index a4968bb0..1f554b5d 100644 --- a/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java +++ b/src/main/java/stanl_2/final_backend/global/security/filter/JWTTokenValidatorFilter.java @@ -18,6 +18,7 @@ import org.springframework.web.filter.OncePerRequestFilter; import stanl_2.final_backend.domain.log.command.aggregate.Log; import stanl_2.final_backend.domain.log.command.repository.LogRepository; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import stanl_2.final_backend.global.exception.GlobalCommonException; import stanl_2.final_backend.global.exception.GlobalErrorCode; @@ -34,10 +35,14 @@ public class JWTTokenValidatorFilter extends OncePerRequestFilter { private final String jwtSecretKey; private final LogRepository logRepository; // 로그 저장소 + private final MemberQueryService memberQueryService; - public JWTTokenValidatorFilter(String jwtSecretKey, LogRepository logRepository) { + public JWTTokenValidatorFilter(String jwtSecretKey, + LogRepository logRepository, + MemberQueryService memberQueryService) { this.jwtSecretKey = jwtSecretKey; this.logRepository = logRepository; // DI 주입 + this.memberQueryService = memberQueryService; } @Override @@ -149,6 +154,13 @@ private void saveErrorLog(String message, Exception e, HttpServletRequest reques logRepository.save(logEntry); + // 임원 일시 메일 전송 + String pos = memberQueryService.selectMemberInfo(loginId).getPosition(); + + if("DIRECTOR".equals(pos) || "CEO".equals(pos)){ + memberQueryService.sendErrorMail(loginId, logEntry); + } + } catch (Exception ex) { log.error("로그 저장 실패: {}", ex.getMessage()); throw new GlobalCommonException(GlobalErrorCode.FAIL_LOG_SAVE); diff --git a/src/main/resources/templates/errorMail.html b/src/main/resources/templates/errorMail.html new file mode 100644 index 00000000..60f39997 --- /dev/null +++ b/src/main/resources/templates/errorMail.html @@ -0,0 +1,89 @@ + + + + + + + Error Notification + + + +
      +

      🚨 시스템 에러 알림

      +

      안녕하세요,

      +

      다음과 같은 에러가 발생했습니다. 빠른 확인 및 조치를 부탁드립니다:

      + +

      유저 정보

      +
      +

      Login ID: [Login ID]

      +

      Name: [Name]

      +

      Position: [Position]

      +

      User Agent: [User Agent]

      +
      + +

      에러 정보

      +
      +

      상태: ERROR

      +

      에러 메시지: [에러 메시지]

      +
      + +

      요청 정보

      +
      +

      URI: [URI]

      +

      Method: [Method]

      +

      Query String: [Query String]

      +
      + +

      네트워크 정보

      +
      +

      IP 주소: [IP Address]

      +

      호스트 이름: [Host Name]

      +

      포트 번호: [Port]

      +
      + +

      감사합니다.

      +
      + + + From df63701019221747147fb4d8cc70c59deb890a8c Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Sat, 7 Dec 2024 16:53:11 +0900 Subject: [PATCH 506/563] =?UTF-8?q?fix:=20=EB=A7=A4=ED=8D=BC=20=EB=8C=80?= =?UTF-8?q?=EC=86=8C=EB=AC=B8=EC=9E=90=20=ED=9C=B4=EB=A8=BC=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EC=88=98=EC=A0=95(#242)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/query/repository/MemberMapper.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml index 42ba3cab..da914a7e 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml @@ -106,7 +106,7 @@ From cacbb564b183dce0d1942284d8bab6f811c94000 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Sat, 7 Dec 2024 17:12:22 +0900 Subject: [PATCH 507/563] =?UTF-8?q?production:=20MemberMapper.xml=20=20fin?= =?UTF-8?q?dNameById=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/query/repository/MemberMapper.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml index 42ba3cab..da914a7e 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml @@ -106,7 +106,7 @@ From c9724782be4cade0ef3fc9c06c6b22018f8ef01e Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Sat, 7 Dec 2024 17:57:20 +0900 Subject: [PATCH 508/563] =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notices/query/controller/NoticeController.java | 14 +++++++------- ...{NoticeService.java => NoticeQueryService.java} | 2 +- ...erviceImpl.java => NoticeQueryServiceImpl.java} | 4 ++-- .../global/security/config/ProdSecurityConfig.java | 2 +- src/main/resources/application.yml | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) rename src/main/java/stanl_2/final_backend/domain/notices/query/service/{NoticeService.java => NoticeQueryService.java} (93%) rename src/main/java/stanl_2/final_backend/domain/notices/query/service/{NoticeServiceImpl.java => NoticeQueryServiceImpl.java} (95%) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java index ae609bc1..2f6d6c55 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/controller/NoticeController.java @@ -15,16 +15,16 @@ import stanl_2.final_backend.domain.notices.common.response.NoticeResponseMessage; import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; import stanl_2.final_backend.domain.notices.query.dto.SearchDTO; -import stanl_2.final_backend.domain.notices.query.service.NoticeService; +import stanl_2.final_backend.domain.notices.query.service.NoticeQueryService; @RestController("queryNoticeController") @RequestMapping("/api/v1/notice") public class NoticeController { - private final NoticeService noticeService; + private final NoticeQueryService noticeQueryService; @Autowired - public NoticeController(NoticeService noticeService) { - this.noticeService = noticeService; + public NoticeController(NoticeQueryService noticeQueryService) { + this.noticeQueryService = noticeQueryService; } @Operation(summary = "공지사항 조건별 조회") @@ -46,7 +46,7 @@ public ResponseEntity> getNotices( Pageable pageable = PageRequest.of(page, size); SearchDTO searchDTO = new SearchDTO(title, tag, memberId, classification, startDate, endDate); - Page noticeDTOPage = noticeService.findNotices(pageable, searchDTO); + Page noticeDTOPage = noticeQueryService.findNotices(pageable, searchDTO); return ResponseEntity.ok(noticeDTOPage); } @@ -58,7 +58,7 @@ public ResponseEntity> getNotices( }) @GetMapping("{noticeId}") public ResponseEntity getNotice(@PathVariable String noticeId){ - NoticeDTO noticeDTO = noticeService.findNotice(noticeId); + NoticeDTO noticeDTO = noticeQueryService.findNotice(noticeId); return ResponseEntity.ok(noticeDTO); } @Operation(summary = "공지사항 엑셀 다운 테스트") @@ -69,7 +69,7 @@ public ResponseEntity getNotice(@PathVariable String noticeId){ @GetMapping("/excel") public void exportNotice(HttpServletResponse response){ - noticeService.exportNoticesToExcel(response); + noticeQueryService.exportNoticesToExcel(response); } } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeQueryService.java similarity index 93% rename from src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java rename to src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeQueryService.java index b07a800f..85a7357d 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeService.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeQueryService.java @@ -7,7 +7,7 @@ import stanl_2.final_backend.domain.notices.query.dto.SearchDTO; -public interface NoticeService { +public interface NoticeQueryService { Page findNotices(Pageable pageable, SearchDTO searchDTO); NoticeDTO findNotice(String noticeId); diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeQueryServiceImpl.java similarity index 95% rename from src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java rename to src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeQueryServiceImpl.java index 0f56708b..4f650323 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeQueryServiceImpl.java @@ -30,14 +30,14 @@ @Transactional(readOnly = true) @Service("queryNoticeServiceImpl") -public class NoticeServiceImpl implements NoticeService{ +public class NoticeQueryServiceImpl implements NoticeQueryService { private final NoticeMapper noticeMapper; private final RedisTemplate redisTemplate; private final RedisService redisService; private final ExcelUtilsV1 excelUtilsV1; private final MemberQueryService memberQueryService; @Autowired - public NoticeServiceImpl(NoticeMapper noticeMapper, RedisTemplate redisTemplate, RedisService redisService, ExcelUtilsV1 excelUtilsV1,MemberQueryService memberQueryService) { + public NoticeQueryServiceImpl(NoticeMapper noticeMapper, RedisTemplate redisTemplate, RedisService redisService, ExcelUtilsV1 excelUtilsV1, MemberQueryService memberQueryService) { this.noticeMapper = noticeMapper; this.redisTemplate = redisTemplate; this.redisService =redisService; diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index 9e250cd1..d51d7c21 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -76,7 +76,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); - config.setAllowedOrigins(Collections.singletonList("https://final-frontend-nine.vercel.app/")); + config.setAllowedOrigins(Collections.singletonList("http://localhost:5173")); config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); config.setAllowCredentials(true); config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "X-XSRF-TOKEN")); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index c8a94394..feb23a5e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -34,8 +34,8 @@ spring: servlet: multipart: enabled: true - max-file-size: 10MB - max-request-size: 10MB + max-file-size: 100MB + max-request-size: 100MB mail: host: From b7cc7a8f2e02f129dee5ec57d50ef753aca546a2 Mon Sep 17 00:00:00 2001 From: giuseog Date: Sat, 7 Dec 2024 18:04:39 +0900 Subject: [PATCH 509/563] =?UTF-8?q?feat:=20=EA=B2=80=EC=83=89=20=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=20=EC=9C=84=ED=95=B4=20=EC=88=98=EC=A0=95(#183)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/dto/SalesHistorySearchDTO.java | 2 + .../service/SalesHistoryQueryServiceImpl.java | 4 ++ .../query/repository/SalesHistoryMapper.xml | 50 ++++++++++++++++++- 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java index 02f9cfb9..f388a289 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java @@ -21,5 +21,7 @@ public class SalesHistorySearchDTO { private String customerName; private List customerList; private List productList; + private List contractList; private String contractId; + private String productId; } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java index a82f0764..a1293d81 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java @@ -120,8 +120,12 @@ public Page selectSalesHistorySearchByEmployee(SalesHisto salesHistorySearchDTO.setSearcherName(authQueryService.selectMemberIdByLoginId(salesHistorySearchDTO.getSearcherName())); + System.out.println("=============================================================================="); + List salesHistoryList = salesHistoryMapper.findSalesHistorySearchByEmployee(size,offset, salesHistorySearchDTO, sortField, sortOrder); + System.out.println("salesHistoryList = " + salesHistoryList); + int total = salesHistoryMapper.findSalesHistorySearchCountByEmployee(salesHistorySearchDTO); if(salesHistoryList.isEmpty() || total == 0){ diff --git a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml index bd17f786..ad9005a7 100644 --- a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml @@ -164,6 +164,21 @@ AND a.created_at BETWEEN #{salesHistorySearchDTO.startDate} AND #{salesHistorySearchDTO.endDate} + + AND a.conr_id = #{salesHistorySearchDTO.contractId} + + + AND a.prod_id IN + + #{prod_id} + + + + AND a.cust_id IN + + #{cust_id} + +
      @@ -444,8 +459,39 @@ SELECT COUNT(*) AS cnt FROM tb_sales_history a - WHERE a.active = TRUE AND a.mem_id = #{searcherId} - ORDER BY a.created_at DESC + + a.active = TRUE + + AND a.mem_id IN + + #{mem_id} + + + + AND a.cent_id IN + + #{cent_id} + + + + AND a.cust_id IN + + #{cust_id} + + + + AND a.prod_id IN + + #{prod_id} + + + + AND a.conr_id = #{salesHistorySearchDTO.contractId} + + + AND a.created_at BETWEEN #{salesHistorySearchDTO.startDate} AND #{salesHistorySearchDTO.endDate} + + + SELECT + a.sch_id, + a.sch_name, + a.sch_cont, + a.sch_tag, + a.sch_srt_at, + a.sch_end_at, + a.mem_id + FROM tb_schedule a + WHERE mem_id = #{ memberId } + AND sch_srt_at LIKE CONCAT(#{ month },'%') + AND active = true + + + \ No newline at end of file From 53554f124ada3d86bfc8196a5961a083dec5c039 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Sat, 7 Dec 2024 21:19:35 +0900 Subject: [PATCH 513/563] =?UTF-8?q?feat:=20=EB=8C=80=EC=8B=9C=EB=B3=B4?= =?UTF-8?q?=EB=93=9C=20=EA=B6=8C=ED=95=9C=EB=B3=84=20=EB=A9=94=EC=86=8C?= =?UTF-8?q?=EB=93=9C=20=EA=B5=AC=ED=98=84=20=EC=A4=91=20(#239)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/DashBoardController.java | 49 ++++++++++-- ...shBoardDTO.java => DashBoardAdminDTO.java} | 2 +- .../query/dto/DashBoardDirectorDTO.java | 16 ++++ .../query/dto/DashBoardEmployeeDTO.java | 22 ++++++ .../query/repository/DashBoardMapper.java | 4 +- .../query/service/DashBoardQueryService.java | 6 +- .../service/DashBoardQueryServiceImpl.java | 76 ++++++++++++++++--- 7 files changed, 152 insertions(+), 23 deletions(-) rename src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/{DashBoardDTO.java => DashBoardAdminDTO.java} (92%) create mode 100644 src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardDirectorDTO.java create mode 100644 src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardEmployeeDTO.java diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/controller/DashBoardController.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/controller/DashBoardController.java index 1d005c26..664ad74b 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/controller/DashBoardController.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/controller/DashBoardController.java @@ -9,12 +9,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import stanl_2.final_backend.domain.customer.common.response.CustomerResponseMessage; import stanl_2.final_backend.domain.dashBoard.common.response.DashBoardResponseMessage; -import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardDTO; +import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardAdminDTO; import stanl_2.final_backend.domain.dashBoard.query.service.DashBoardQueryService; import java.security.Principal; @@ -31,17 +30,55 @@ public DashBoardController(DashBoardQueryService dashBoardQueryService) { this.dashBoardQueryService = dashBoardQueryService; } - @Operation(summary = "대시보드 정보 조회") + @Operation(summary = "대시보드 정보 조회 (사원)") @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "대시보드 조회 성공", + @ApiResponse(responseCode = "200", description = "대시보드 조회 성공(사원)", content = {@Content(schema = @Schema(implementation = CustomerResponseMessage.class))}) }) @GetMapping("") - public ResponseEntity selectDashBoard(Principal principal){ + public ResponseEntity selectDashBoardForEmployee(Principal principal){ String memberLoginId = principal.getName(); - DashBoardDTO boardResponseDTO = dashBoardQueryService.selectAllInfo(memberLoginId); + DashBoardAdminDTO boardResponseDTO = dashBoardQueryService.selectInfoForEmployee(memberLoginId); + + return ResponseEntity.ok(DashBoardResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(boardResponseDTO) + .build()); + } + + @Operation(summary = "대시보드 정보 조회 (관리자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "대시보드 조회 성공(관리자)", + content = {@Content(schema = @Schema(implementation = CustomerResponseMessage.class))}) + }) + @GetMapping("") + public ResponseEntity selectDashBoardForAdmin(Principal principal){ + + String memberLoginId = principal.getName(); + + DashBoardAdminDTO boardResponseDTO = dashBoardQueryService.selectInfoForAdmin(memberLoginId); + + return ResponseEntity.ok(DashBoardResponseMessage.builder() + .httpStatus(200) + .msg("성공") + .result(boardResponseDTO) + .build()); + } + + @Operation(summary = "대시보드 정보 조회 (담당자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "대시보드 조회 성공(담당자)", + content = {@Content(schema = @Schema(implementation = CustomerResponseMessage.class))}) + }) + @GetMapping("/director") + public ResponseEntity selectDashBoardForDirector(Principal principal){ + + String memberLoginId = principal.getName(); + + DashBoardAdminDTO boardResponseDTO = dashBoardQueryService.selectInfoForEmployeeAndAdmin(memberLoginId); return ResponseEntity.ok(DashBoardResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardDTO.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardAdminDTO.java similarity index 92% rename from src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardDTO.java rename to src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardAdminDTO.java index 5917d8fb..89b18ef8 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardAdminDTO.java @@ -9,7 +9,7 @@ @AllArgsConstructor @Getter @Setter -public class DashBoardDTO { +public class DashBoardAdminDTO { private String unreadContract; private String unreadPurchase; diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardDirectorDTO.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardDirectorDTO.java new file mode 100644 index 00000000..2ebc0e58 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardDirectorDTO.java @@ -0,0 +1,16 @@ +package stanl_2.final_backend.domain.dashBoard.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +public class DashBoardDirectorDTO { + + private String memberLoginId; + private String memberId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardEmployeeDTO.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardEmployeeDTO.java new file mode 100644 index 00000000..35505110 --- /dev/null +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardEmployeeDTO.java @@ -0,0 +1,22 @@ +package stanl_2.final_backend.domain.dashBoard.query.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +public class DashBoardEmployeeDTO { + + private String unreadContract; + private String unreadPurchase; + private String unreadPurchaseOrder; + + private String noticeTitle; + + private String memberLoginId; + private String memberId; +} diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/repository/DashBoardMapper.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/repository/DashBoardMapper.java index cd5f039e..6764a34a 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/repository/DashBoardMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/repository/DashBoardMapper.java @@ -1,7 +1,7 @@ package stanl_2.final_backend.domain.dashBoard.query.repository; -import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardDTO; +import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardAdminDTO; public interface DashBoardMapper { - DashBoardDTO findDashBoardInfoByMemberId(String memberId); + DashBoardAdminDTO findDashBoardInfoByMemberId(String memberId); } diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryService.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryService.java index f2fdc559..70a34c74 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryService.java @@ -1,7 +1,9 @@ package stanl_2.final_backend.domain.dashBoard.query.service; -import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardDTO; +import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardAdminDTO; public interface DashBoardQueryService { - DashBoardDTO selectAllInfo(String memberLoginId); + DashBoardAdminDTO selectInfoForEmployee(String memberLoginId); + + DashBoardAdminDTO selectInfoForAdmin(String memberLoginId); } diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java index 8d03c8b6..d126cce3 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java @@ -1,21 +1,26 @@ package stanl_2.final_backend.domain.dashBoard.query.service; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import stanl_2.final_backend.domain.contract.query.dto.ContractSearchDTO; import stanl_2.final_backend.domain.contract.query.service.ContractQueryService; import stanl_2.final_backend.domain.customer.query.service.CustomerQueryService; import stanl_2.final_backend.domain.dashBoard.common.exception.DashBoardCommonException; import stanl_2.final_backend.domain.dashBoard.common.exception.DashBoardErrorCode; -import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardDTO; +import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardAdminDTO; import stanl_2.final_backend.domain.dashBoard.query.repository.DashBoardMapper; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.notices.query.service.NoticeService; +import stanl_2.final_backend.domain.order.query.dto.OrderSelectSearchDTO; import stanl_2.final_backend.domain.order.query.service.OrderQueryService; import stanl_2.final_backend.domain.purchase_order.query.service.PurchaseOrderQueryService; import stanl_2.final_backend.domain.sales_history.query.service.SalesHistoryQueryService; -import stanl_2.final_backend.domain.schedule.common.exception.ScheduleCommonException; -import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; @Slf4j @Service("queryDashBoardService") @@ -25,28 +30,75 @@ public class DashBoardQueryServiceImpl implements DashBoardQueryService { private final AuthQueryService authQueryService; private final ContractQueryService contractQueryService; private final OrderQueryService orderQueryService; - private final PurchaseOrderQueryService queryService; + private final PurchaseOrderQueryService purchaseOrderQueryService; private final SalesHistoryQueryService salesHistoryQueryService; private final CustomerQueryService customerQueryService; private final NoticeService noticeService; + private String getCurrentTime() { + ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); + return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } - - @Autowired - public DashBoardQueryServiceImpl(DashBoardMapper dashBoardMapper, AuthQueryService authQueryService) { + public DashBoardQueryServiceImpl(DashBoardMapper dashBoardMapper, AuthQueryService authQueryService, + ContractQueryService contractQueryService, OrderQueryService orderQueryService, + PurchaseOrderQueryService purchaseOrderQueryService, SalesHistoryQueryService salesHistoryQueryService, + CustomerQueryService customerQueryService, NoticeService noticeService) { this.dashBoardMapper = dashBoardMapper; this.authQueryService = authQueryService; + this.contractQueryService = contractQueryService; + this.orderQueryService = orderQueryService; + this.purchaseOrderQueryService = purchaseOrderQueryService; + this.salesHistoryQueryService = salesHistoryQueryService; + this.customerQueryService = customerQueryService; + this.noticeService = noticeService; } + @Override - public DashBoardDTO selectAllInfo(String memberLoginId) { + @Transactional(readOnly = true) + public DashBoardAdminDTO selectInfoForEmployee(String memberLoginId) { + String memberId = authQueryService.selectMemberIdByLoginId(memberLoginId); + // Pageable을 null로 전달하거나 유효한 Pageable 사용 + Pageable pageable = Pageable.unpaged(); + // 이번달 조회를 위한 날짜 지정 + String startAt = getCurrentTime().substring(0,7) + "-01"; + String endAt = getCurrentTime().substring(0,9); + + + // 이번달 Contract 받아오기 + ContractSearchDTO contractSearchDTO = new ContractSearchDTO(); + contractSearchDTO.setMemberId(memberId); + contractSearchDTO.setStartAt(startAt); + contractSearchDTO.setEndAt(endAt); + Integer unreadContract = contractQueryService.selectBySearchEmployee(contractSearchDTO, pageable).getNumberOfElements(); + + // 이번달 Order 받아오기 + OrderSelectSearchDTO orderSelectSearchDTO = new OrderSelectSearchDTO(); + orderSelectSearchDTO.setMemberId(memberId); + orderSelectSearchDTO.setStartDate(startAt); + orderSelectSearchDTO.setEndDate(endAt); + Integer unreadOrder = orderQueryService.selectSearchOrdersEmployee(orderSelectSearchDTO, pageable).getNumberOfElements(); - DashBoardDTO responseDashBoardDTO = dashBoardMapper.findDashBoardInfoByMemberId(memberId); + // 이번달 판매내역 받아오기 - if(responseDashBoardDTO == null){ + // 이번달 내 고객 순위 조회 + + // 이번달 판매사원 순위 + + // 공지사항 + + DashBoardAdminDTO responseDashBoardAdminDTO = dashBoardMapper.findDashBoardInfoByMemberId(memberId); + + if(responseDashBoardAdminDTO == null){ throw new DashBoardCommonException(DashBoardErrorCode.DATA_NOT_FOUND); } - return responseDashBoardDTO; + return responseDashBoardAdminDTO; + } + + @Override + public DashBoardAdminDTO selectInfoForAdmin(String memberLoginId) { + return null; } } From d3786fc6200479bfa4510f8b86bc35b40580ad28 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Sat, 7 Dec 2024 23:21:43 +0900 Subject: [PATCH 514/563] =?UTF-8?q?fix:=20=EA=B6=8C=ED=95=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/config/RequestMatcherConfig.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java index dfcdeb19..823f6ca9 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java @@ -87,7 +87,7 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception // Contract API (General) .requestMatchers(HttpMethod.GET, "/api/v1/contract").hasAnyRole("contract-get", "GOD", "DIRECTOR") .requestMatchers(HttpMethod.GET, "/api/v1/contract/search").hasAnyRole("contract-search-get", "GOD", "DIRECTOR") - .requestMatchers(HttpMethod.GET, "/api/v1/contract/{contractId}").hasAnyRole("contract-id-get", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.GET, "/api/v1/contract/{contractId}").hasAnyRole("contract-id-get", "GOD", "DIRECTOR", "ADMIN", "EMPLOYEE") .requestMatchers(HttpMethod.PUT, "/api/v1/contract/status/{contractId}").hasAnyRole("contract-status-id-put", "GOD", "ADMIN") .requestMatchers(HttpMethod.GET, "/api/v1/contract/excel").hasAnyRole("contract-excel-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") @@ -104,7 +104,7 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception // Order API (Search and Excel) .requestMatchers(HttpMethod.GET, "/api/v1/order").hasAnyRole("order-get", "GOD", "DIRECTOR") - .requestMatchers(HttpMethod.GET, "/api/v1/order/{orderId}").hasAnyRole("order-id-get", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.GET, "/api/v1/order/{orderId}").hasAnyRole("order-id-get", "GOD", "DIRECTOR", "EMPLOYEE", "EMPLOYEE") .requestMatchers(HttpMethod.GET, "/api/v1/order/search").hasAnyRole("order-search-get", "GOD", "DIRECTOR") .requestMatchers(HttpMethod.GET, "/api/v1/order/excel").hasAnyRole("order-excel-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") @@ -121,7 +121,7 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception .requestMatchers(HttpMethod.DELETE, "/api/v1/purchase-order/{purchaseOrderId}").hasAnyRole("purchase-order-delete", "GOD", "ADMIN") .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order").hasAnyRole("purchase-order-get", "GOD", "DIRECTOR") .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order/search").hasAnyRole("purchase-order-search-get", "GOD", "DIRECTOR") - .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order/{purchaseOrderId}").hasAnyRole("purchase-order-id-get", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order/{purchaseOrderId}").hasAnyRole("purchase-order-id-get", "GOD", "DIRECTOR", "ADMIN", "EMPLOYEE") .requestMatchers(HttpMethod.GET, "/api/v1/purchase-order/excel").hasAnyRole("purchase-order-excel-get", "GOD", "DIRECTOR") .requestMatchers(HttpMethod.PUT, "/api/v1/purchase-order/status/{purchaseOrderId}").hasAnyRole("purchase-order-status-put", "GOD", "DIRECTOR") From 84fd4c493c3ad9ca66903946abdd06a2d2cb9d09 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Sun, 8 Dec 2024 00:58:59 +0900 Subject: [PATCH 515/563] =?UTF-8?q?feat:=20employee=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EC=99=84=EC=84=B1=20(#239)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/service/DashBoardQueryServiceImpl.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java index d126cce3..86af0900 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java @@ -1,6 +1,7 @@ package stanl_2.final_backend.domain.dashBoard.query.service; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -16,11 +17,15 @@ import stanl_2.final_backend.domain.order.query.dto.OrderSelectSearchDTO; import stanl_2.final_backend.domain.order.query.service.OrderQueryService; import stanl_2.final_backend.domain.purchase_order.query.service.PurchaseOrderQueryService; +import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySearchDTO; +import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectDTO; import stanl_2.final_backend.domain.sales_history.query.service.SalesHistoryQueryService; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; @Slf4j @Service("queryDashBoardService") @@ -82,11 +87,22 @@ public DashBoardAdminDTO selectInfoForEmployee(String memberLoginId) { Integer unreadOrder = orderQueryService.selectSearchOrdersEmployee(orderSelectSearchDTO, pageable).getNumberOfElements(); // 이번달 판매내역 받아오기 + ArrayList memberList = new ArrayList(); + memberList.add(memberId); + + SalesHistorySearchDTO salesHistorySearchDTO = new SalesHistorySearchDTO(); + salesHistorySearchDTO.setMemberList(memberList); + salesHistorySearchDTO.setStartDate(startAt); + salesHistorySearchDTO.setEndDate(endAt); + Page resultPage = salesHistoryQueryService.selectSalesHistorySearchByEmployee(salesHistorySearchDTO, pageable); + Integer totalPrice = resultPage.getContent().isEmpty() ? null : resultPage.getContent().get(0).getSalesHistoryTotalSales(); // 이번달 내 고객 순위 조회 + // 이번달 판매사원 순위 + // 공지사항 DashBoardAdminDTO responseDashBoardAdminDTO = dashBoardMapper.findDashBoardInfoByMemberId(memberId); From 528e8304d0c29b276f89da1cc9be618cb7cb97ff Mon Sep 17 00:00:00 2001 From: giuseog Date: Sun, 8 Dec 2024 01:12:00 +0900 Subject: [PATCH 516/563] =?UTF-8?q?fix:=20=ED=8C=90=EB=A7=A4=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=EC=95=88=EC=93=B0=EB=8A=94=20api=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0(#183)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SalesHistoryController.java | 183 -------- .../query/repository/SalesHistoryMapper.java | 40 -- .../service/SalesHistoryQueryService.java | 16 - .../service/SalesHistoryQueryServiceImpl.java | 178 +------- .../query/repository/SalesHistoryMapper.xml | 389 +----------------- 5 files changed, 4 insertions(+), 802 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java index 35fdc71c..3e919fad 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java @@ -338,126 +338,6 @@ public ResponseEntity getStatisticsEmployeeAverageB .build()); } - @Operation(summary = "매장 별 통계(실적,수당,매출액) 조회기간별 평균 조회") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), - @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", - content = @Content(mediaType = "application/json")) - }) - @PostMapping("statistics/average/center") - public ResponseEntity getStatisticsCenterAverageBySearch(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, - @PageableDefault(size = 20) Pageable pageable){ - - Page responseSalesHistory = salesHistoryQueryService.selectStatisticsAverageBySearch(salesHistoryRankedDataDTO,pageable); - - return ResponseEntity.ok(SalesHistoryResponseMessage.builder() - .httpStatus(200) - .msg("매장 별 통계(실적,수당,매출액) 조회기간별 평균 조회 성공") - .result(responseSalesHistory) - .build()); - } - - @Operation(summary = "사원 별 통계(실적,수당,매출액) 월별 검색") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), - @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", - content = @Content(mediaType = "application/json")) - }) - @PostMapping("statistics/search/month") - public ResponseEntity getStatisticsBySearchMonth(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, - @PageableDefault(size = 20) Pageable pageable){ - - Page responseSalesHistory = salesHistoryQueryService.selectStatisticsBySearchMonth(salesHistoryRankedDataDTO,pageable); - - return ResponseEntity.ok(SalesHistoryResponseMessage.builder() - .httpStatus(200) - .msg("사원 별 통계(실적,수당,매출액) 월별 검색 성공") - .result(responseSalesHistory) - .build()); - } - - @Operation(summary = "사원 별 통계(실적,수당,매출액) 연도별 검색") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), - @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", - content = @Content(mediaType = "application/json")) - }) - @PostMapping("statistics/search/year") - public ResponseEntity getStatisticsBySearchYear(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, - @PageableDefault(size = 20) Pageable pageable){ - - Page responseSalesHistory = salesHistoryQueryService.selectStatisticsBySearchYear(salesHistoryRankedDataDTO,pageable); - - return ResponseEntity.ok(SalesHistoryResponseMessage.builder() - .httpStatus(200) - .msg("사원 별 통계(실적,수당,매출액) 연도별 검색 성공") - .result(responseSalesHistory) - .build()); - } - - @Operation(summary = "매장 별 통계(실적,수당,매출액) 조회기간별 검색") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), - @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", - content = @Content(mediaType = "application/json")) - }) - @PostMapping("statistics/center/search") - public ResponseEntity getStatisticsCenterBySearch(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, - @PageableDefault(size = 20) Pageable pageable){ - - Page responseSalesHistory = salesHistoryQueryService.selectStatisticsCenterBySearch(salesHistoryRankedDataDTO,pageable); - - return ResponseEntity.ok(SalesHistoryResponseMessage.builder() - .httpStatus(200) - .msg("매장 별 통계(실적,수당,매출액) 검색 성공") - .result(responseSalesHistory) - .build()); - } - - @Operation(summary = "매장 별 통계(실적,수당,매출액) 월별 검색") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), - @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", - content = @Content(mediaType = "application/json")) - }) - @PostMapping("statistics/center/search/month") - public ResponseEntity getStatisticsCenterBySearchMonth(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, - @PageableDefault(size = 20) Pageable pageable){ - - Page responseSalesHistory = salesHistoryQueryService.selectStatisticsCenterBySearchMonth(salesHistoryRankedDataDTO,pageable); - - return ResponseEntity.ok(SalesHistoryResponseMessage.builder() - .httpStatus(200) - .msg("매장 별 통계(실적,수당,매출액) 월별 검색 성공") - .result(responseSalesHistory) - .build()); - } - -@Operation(summary = "매장 별 통계(실적,수당,매출액) 연도별 검색") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), - @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", - content = @Content(mediaType = "application/json")) - }) - @PostMapping("statistics/center/search/year") - public ResponseEntity getStatisticsCenterBySearchYear(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, - @PageableDefault(size = 20) Pageable pageable){ - - Page responseSalesHistory = salesHistoryQueryService.selectStatisticsCenterBySearchYear(salesHistoryRankedDataDTO,pageable); - - return ResponseEntity.ok(SalesHistoryResponseMessage.builder() - .httpStatus(200) - .msg("매장 별 통계(실적,수당,매출액) 연도별 검색 성공") - .result(responseSalesHistory) - .build()); - } - @Operation(summary = "판매내역 엑셀 다운") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "판매내역 엑셀 다운 성공", @@ -471,69 +351,6 @@ public void exporSalesHistory(HttpServletResponse response){ salesHistoryQueryService.exportSalesHistoryToExcel(response); } - @Operation(summary = "전체 통계(실적,수당,매출액) 월별 검색(관리자, 담당자)") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), - @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", - content = @Content(mediaType = "application/json")) - }) - @PostMapping("statistics/all/month") - public ResponseEntity getAllStatisticsByMonth(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, - @PageableDefault(size = 20) Pageable pageable){ - - - Page responseSalesHistory = salesHistoryQueryService.selectAllStatisticsByMonth(salesHistoryRankedDataDTO, pageable); - - return ResponseEntity.ok(SalesHistoryResponseMessage.builder() - .httpStatus(200) - .msg("전체 통계(실적,수당,매출액) 월별 검색(관리자, 담당자) 성공") - .result(responseSalesHistory) - .build()); - } - - @Operation(summary = "전체 통계(실적,수당,매출액) 연도 별 검색(관리자, 담당자)") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), - @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", - content = @Content(mediaType = "application/json")) - }) - @PostMapping("statistics/all/year") - public ResponseEntity getAllStatisticsByYear(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, - @PageableDefault(size = 20) Pageable pageable){ - - - - Page responseSalesHistory = salesHistoryQueryService.selectAllStatisticsByYear(salesHistoryRankedDataDTO, pageable); - return ResponseEntity.ok(SalesHistoryResponseMessage.builder() - .httpStatus(200) - .msg("전체 통계(실적,수당,매출액) 연도 별 검색(관리자, 담당자)") - .result(responseSalesHistory) - .build()); - } - - @Operation(summary = "전체 통계(실적,수당,매출액) 조회기간 별 검색(관리자, 담당자)") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "성공", - content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), - @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", - content = @Content(mediaType = "application/json")) - }) - @PostMapping("statistics/all") - public ResponseEntity getAllStatisticsBySearch(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, - @PageableDefault(size = 20) Pageable pageable){ - - - - Page responseSalesHistory = salesHistoryQueryService.selectAllStatisticsBySearch(salesHistoryRankedDataDTO, pageable); - return ResponseEntity.ok(SalesHistoryResponseMessage.builder() - .httpStatus(200) - .msg("전체 통계(실적,수당,매출액) 조회기간 별 검색(관리자, 담당자)") - .result(responseSalesHistory) - .build()); - } - @Operation(summary = "통계(실적,수당,매출액) 최대값 조회기간 별 검색(관리자, 담당자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java index 95704e1f..30d8312f 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java @@ -46,36 +46,8 @@ List findStatisticsBySearch(@Param("size") int size int findStatisticsBySearchCount(@Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); - List findStatisticsCenterBySearch(@Param("size") int size - , @Param("offset") int offset, - @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); - - int findStatisticsCenterBySearchCount(@Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); - - List findStatisticsBySearchMonth(@Param("size") int size - , @Param("offset") int offset, - @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); - int findStatisticsBySearchCountMonth(@Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); - List findStatisticsBySearchYear(@Param("size") int size - , @Param("offset") int offset, - @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); - - int findStatisticsBySearchCountYear(@Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); - - List findStatisticsCenterBySearchMonth(@Param("size") int size - , @Param("offset") int offset, - @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); - - int findStatisticsCenterBySearchCountMonth(@Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); - - List findStatisticsCenterBySearchYear(@Param("size") int size - , @Param("offset") int offset, - @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); - - int findStatisticsCenterBySearchCountYear(@Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); - List findSalesHistorySearchByEmployee(@Param("size") int size , @Param("offset") int offset, @Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO, @@ -98,18 +70,6 @@ List findStatisticsAverageBySearch(@Param("siz List findSalesHistoryForExcel(); - List findAllStatisticsByMonth(@Param("size") int size - , @Param("offset") int offset, - @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); - - List findAllStatisticsByYear(@Param("size") int size - , @Param("offset") int offset, - @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); - - List findAllStatisticsBySearch(@Param("size") int size - , @Param("offset") int offset, - @Param("salesHistoryRankedDataDTO") SalesHistoryRankedDataDTO salesHistoryRankedDataDTO); - String findSalesHistoryIdByContractId(@Param("contractId") String contractId); List findStatisticsBestBySearch(@Param("size") int size diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java index 15ba8e5f..181c66b0 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java @@ -27,16 +27,6 @@ public interface SalesHistoryQueryService { Page selectStatisticsBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); - Page selectStatisticsCenterBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); - - Page selectStatisticsBySearchMonth(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); - - Page selectStatisticsBySearchYear(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); - - Page selectStatisticsCenterBySearchMonth(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); - - Page selectStatisticsCenterBySearchYear(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); - Page selectSalesHistorySearchByEmployee(SalesHistorySearchDTO salesHistorySearchDTO, Pageable pageable); Page selectSalesHistoryBySearch(SalesHistorySearchDTO salesHistorySearchDTO, Pageable pageable) throws GeneralSecurityException; @@ -45,12 +35,6 @@ public interface SalesHistoryQueryService { void exportSalesHistoryToExcel(HttpServletResponse response); - Page selectAllStatisticsByMonth(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); - - Page selectAllStatisticsByYear(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); - - Page selectAllStatisticsBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); - String selectSalesHistoryIdByContractId(String contractId); Page selectStatisticsBestBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java index a1293d81..21451c0d 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java @@ -120,12 +120,8 @@ public Page selectSalesHistorySearchByEmployee(SalesHisto salesHistorySearchDTO.setSearcherName(authQueryService.selectMemberIdByLoginId(salesHistorySearchDTO.getSearcherName())); - System.out.println("=============================================================================="); - List salesHistoryList = salesHistoryMapper.findSalesHistorySearchByEmployee(size,offset, salesHistorySearchDTO, sortField, sortOrder); - System.out.println("salesHistoryList = " + salesHistoryList); - int total = salesHistoryMapper.findSalesHistorySearchCountByEmployee(salesHistorySearchDTO); if(salesHistoryList.isEmpty() || total == 0){ @@ -315,8 +311,6 @@ public List selectStatisticsSearchYearByEmployee(Sale List salesHistoryStatisticsDTOList = salesHistoryMapper.findStatisticsSearchYearByEmployee(salesHistorySearchDTO); - System.out.println("check: " + salesHistoryStatisticsDTOList); - if(salesHistoryStatisticsDTOList == null){ throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); } @@ -399,113 +393,6 @@ public Page selectStatisticsBySearch(SalesHistoryRank return new PageImpl<>(salesHistoryList, pageable, total); } - @Override - @Transactional(readOnly = true) - public Page selectStatisticsBySearchMonth(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { - int offset = Math.toIntExact(pageable.getOffset()); - int size = pageable.getPageSize(); - - String parseStartDate = salesHistoryRankedDataDTO.getStartDate(); - String parseEndDate = salesHistoryRankedDataDTO.getEndDate(); - - parseStartDate = parseStartDate.substring(0,7); - parseEndDate = parseEndDate.substring(0,7); - - List salesHistoryList = salesHistoryMapper.findStatisticsBySearchMonth(size,offset, salesHistoryRankedDataDTO); - - int total = salesHistoryMapper.findStatisticsBySearchCountMonth(salesHistoryRankedDataDTO); - - if(salesHistoryList.isEmpty() || total == 0){ - throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); - } - - return new PageImpl<>(salesHistoryList, pageable, total); - } - - @Override - @Transactional(readOnly = true) - public Page selectStatisticsBySearchYear(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { - int offset = Math.toIntExact(pageable.getOffset()); - int size = pageable.getPageSize(); - - String parseStartDate = salesHistoryRankedDataDTO.getStartDate(); - String parseEndDate = salesHistoryRankedDataDTO.getEndDate(); - - parseStartDate = parseStartDate.substring(0,4); - parseEndDate = parseEndDate.substring(0,4); - - List salesHistoryList = salesHistoryMapper.findStatisticsBySearchYear(size,offset, salesHistoryRankedDataDTO); - - int total = salesHistoryMapper.findStatisticsBySearchCountYear(salesHistoryRankedDataDTO); - - if(salesHistoryList.isEmpty() || total == 0){ - throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); - } - - return new PageImpl<>(salesHistoryList, pageable, total); - } - - @Override - @Transactional(readOnly = true) - public Page selectStatisticsCenterBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { - int offset = Math.toIntExact(pageable.getOffset()); - int size = pageable.getPageSize(); - - List salesHistoryList = salesHistoryMapper.findStatisticsCenterBySearch(size,offset, salesHistoryRankedDataDTO); - - int total = salesHistoryMapper.findStatisticsCenterBySearchCount(salesHistoryRankedDataDTO); - - if(salesHistoryList.isEmpty() || total == 0){ - throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); - } - - return new PageImpl<>(salesHistoryList, pageable, total); - } - - @Override - public Page selectStatisticsCenterBySearchMonth(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { - int offset = Math.toIntExact(pageable.getOffset()); - int size = pageable.getPageSize(); - - String parseStartDate = salesHistoryRankedDataDTO.getStartDate(); - String parseEndDate = salesHistoryRankedDataDTO.getEndDate(); - - parseStartDate = parseStartDate.substring(0,7); - parseEndDate = parseEndDate.substring(0,7); - - List salesHistoryList = salesHistoryMapper.findStatisticsCenterBySearchMonth(size,offset, salesHistoryRankedDataDTO); - - int total = salesHistoryMapper.findStatisticsCenterBySearchCountMonth(salesHistoryRankedDataDTO); - - if(salesHistoryList.isEmpty() || total == 0){ - throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); - } - - return new PageImpl<>(salesHistoryList, pageable, total); - } - - @Override - public Page selectStatisticsCenterBySearchYear(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { - int offset = Math.toIntExact(pageable.getOffset()); - int size = pageable.getPageSize(); - - String parseStartDate = salesHistoryRankedDataDTO.getStartDate(); - String parseEndDate = salesHistoryRankedDataDTO.getEndDate(); - - parseStartDate = parseStartDate.substring(0,4); - parseEndDate = parseEndDate.substring(0,4); - - List salesHistoryList = salesHistoryMapper.findStatisticsCenterBySearchYear(size,offset, salesHistoryRankedDataDTO); - - int total = salesHistoryMapper.findStatisticsCenterBySearchCountYear(salesHistoryRankedDataDTO); - - if(salesHistoryList.isEmpty() || total == 0){ - throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); - } - - return new PageImpl<>(salesHistoryList, pageable, total); - } - @Override @Transactional public void exportSalesHistoryToExcel(HttpServletResponse response) { @@ -537,69 +424,6 @@ public void exportSalesHistoryToExcel(HttpServletResponse response) { } - @Override - @Transactional - public Page selectAllStatisticsByMonth(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { - int offset = Math.toIntExact(pageable.getOffset()); - int size = pageable.getPageSize(); - - String parseStartDate = salesHistoryRankedDataDTO.getStartDate(); - String parseEndDate = salesHistoryRankedDataDTO.getEndDate(); - - parseStartDate = parseStartDate.substring(0,7); - parseEndDate = parseEndDate.substring(0,7); - - List salesHistoryList = salesHistoryMapper.findAllStatisticsByMonth(size,offset, salesHistoryRankedDataDTO); - - int total = salesHistoryMapper.findStatisticsBySearchCountMonth(salesHistoryRankedDataDTO); - - if(salesHistoryList.isEmpty() || total == 0){ - throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); - } - - return new PageImpl<>(salesHistoryList, pageable, total); - } - - @Override - @Transactional - public Page selectAllStatisticsByYear(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { - int offset = Math.toIntExact(pageable.getOffset()); - int size = pageable.getPageSize(); - - String parseStartDate = salesHistoryRankedDataDTO.getStartDate(); - String parseEndDate = salesHistoryRankedDataDTO.getEndDate(); - - parseStartDate = parseStartDate.substring(0,4); - parseEndDate = parseEndDate.substring(0,4); - - List salesHistoryList = salesHistoryMapper.findAllStatisticsByYear(size,offset, salesHistoryRankedDataDTO); - - int total = salesHistoryMapper.findStatisticsBySearchCountMonth(salesHistoryRankedDataDTO); - - if(salesHistoryList.isEmpty() || total == 0){ - throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); - } - - return new PageImpl<>(salesHistoryList, pageable, total); - } - - @Override - @Transactional - public Page selectAllStatisticsBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { - int offset = Math.toIntExact(pageable.getOffset()); - int size = pageable.getPageSize(); - - List salesHistoryList = salesHistoryMapper.findAllStatisticsBySearch(size,offset, salesHistoryRankedDataDTO); - - int total = salesHistoryMapper.findStatisticsBySearchCountMonth(salesHistoryRankedDataDTO); - - if(salesHistoryList.isEmpty() || total == 0){ - throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); - } - - return new PageImpl<>(salesHistoryList, pageable, total); - } - @Override @Transactional public String selectSalesHistoryIdByContractId(String contractId) { @@ -621,7 +445,7 @@ public Page selectStatisticsBestBySearch(SalesHistory List salesHistoryList = salesHistoryMapper.findStatisticsBestBySearch(size,offset, salesHistoryRankedDataDTO); - int total = salesHistoryMapper.findStatisticsBySearchCountMonth(salesHistoryRankedDataDTO); + int total = salesHistoryMapper.findStatisticsBySearchCount(salesHistoryRankedDataDTO); if(salesHistoryList.isEmpty() || total == 0){ throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); diff --git a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml index ad9005a7..82e37e0d 100644 --- a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml @@ -706,7 +706,7 @@ ORDER BY period ASC - LIMIT #{size} OFFSET #{offset} + @@ -768,7 +768,7 @@ ) AS SUMMARY GROUP BY period - LIMIT #{size} OFFSET #{offset} + - - - - - - - - - - - - - - - - - - - - - - - - - From 70948414bb9f1bda0e562cd5697bdba3bf75b730 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Sun, 8 Dec 2024 01:16:26 +0900 Subject: [PATCH 517/563] =?UTF-8?q?feat:=20dev=EC=9D=98=20=EB=82=B4?= =?UTF-8?q?=EC=9A=A9=20commit=EC=A0=84=20=EC=A0=81=EC=9A=A9=20(#239)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/dashBoard/query/dto/DashBoardAdminDTO.java | 7 ++++--- .../domain/dashBoard/query/dto/DashBoardEmployeeDTO.java | 6 +++--- .../dashBoard/query/service/DashBoardQueryServiceImpl.java | 6 +++++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardAdminDTO.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardAdminDTO.java index 89b18ef8..1b5070a6 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardAdminDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardAdminDTO.java @@ -11,11 +11,12 @@ @Setter public class DashBoardAdminDTO { - private String unreadContract; - private String unreadPurchase; - private String unreadPurchaseOrder; + private Integer unreadContract; + private Integer unreadOrder; + private Integer unreadPurchaseOrder; private String noticeTitle; + private Integer totalPrice; private String memberLoginId; private String memberId; diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardEmployeeDTO.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardEmployeeDTO.java index 35505110..e1458230 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardEmployeeDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardEmployeeDTO.java @@ -11,9 +11,9 @@ @Setter public class DashBoardEmployeeDTO { - private String unreadContract; - private String unreadPurchase; - private String unreadPurchaseOrder; + private Integer unreadContract; + private Integer unreadOrder; + private Integer unreadPurchaseOrder; private String noticeTitle; diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java index 86af0900..9d0ee5fc 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java @@ -63,7 +63,7 @@ public DashBoardQueryServiceImpl(DashBoardMapper dashBoardMapper, AuthQueryServi @Transactional(readOnly = true) public DashBoardAdminDTO selectInfoForEmployee(String memberLoginId) { - + DashBoardAdminDTO dashBoardAdminDTO = new DashBoardAdminDTO(); String memberId = authQueryService.selectMemberIdByLoginId(memberLoginId); // Pageable을 null로 전달하거나 유효한 Pageable 사용 Pageable pageable = Pageable.unpaged(); @@ -78,6 +78,8 @@ public DashBoardAdminDTO selectInfoForEmployee(String memberLoginId) { contractSearchDTO.setStartAt(startAt); contractSearchDTO.setEndAt(endAt); Integer unreadContract = contractQueryService.selectBySearchEmployee(contractSearchDTO, pageable).getNumberOfElements(); + dashBoardAdminDTO.setUnreadContract(unreadContract); + // 이번달 Order 받아오기 OrderSelectSearchDTO orderSelectSearchDTO = new OrderSelectSearchDTO(); @@ -85,6 +87,7 @@ public DashBoardAdminDTO selectInfoForEmployee(String memberLoginId) { orderSelectSearchDTO.setStartDate(startAt); orderSelectSearchDTO.setEndDate(endAt); Integer unreadOrder = orderQueryService.selectSearchOrdersEmployee(orderSelectSearchDTO, pageable).getNumberOfElements(); + dashBoardAdminDTO.setUnreadOrder(unreadOrder); // 이번달 판매내역 받아오기 ArrayList memberList = new ArrayList(); @@ -96,6 +99,7 @@ public DashBoardAdminDTO selectInfoForEmployee(String memberLoginId) { salesHistorySearchDTO.setEndDate(endAt); Page resultPage = salesHistoryQueryService.selectSalesHistorySearchByEmployee(salesHistorySearchDTO, pageable); Integer totalPrice = resultPage.getContent().isEmpty() ? null : resultPage.getContent().get(0).getSalesHistoryTotalSales(); + dashBoardAdminDTO.setTotalPrice(totalPrice); // 이번달 내 고객 순위 조회 From f2dee7008414cf4251f4ea3b972a87c541c6a5ef Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Sun, 8 Dec 2024 01:59:16 +0900 Subject: [PATCH 518/563] =?UTF-8?q?fix:=20=EC=97=90=EB=9F=AC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/config/RequestMatcherConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java index 823f6ca9..f83d7621 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java @@ -104,7 +104,7 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception // Order API (Search and Excel) .requestMatchers(HttpMethod.GET, "/api/v1/order").hasAnyRole("order-get", "GOD", "DIRECTOR") - .requestMatchers(HttpMethod.GET, "/api/v1/order/{orderId}").hasAnyRole("order-id-get", "GOD", "DIRECTOR", "EMPLOYEE", "EMPLOYEE") + .requestMatchers(HttpMethod.GET, "/api/v1/order/{orderId}").hasAnyRole("order-id-get", "GOD", "DIRECTOR", "EMPLOYEE", "ADMIN") .requestMatchers(HttpMethod.GET, "/api/v1/order/search").hasAnyRole("order-search-get", "GOD", "DIRECTOR") .requestMatchers(HttpMethod.GET, "/api/v1/order/excel").hasAnyRole("order-excel-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") From 27a35b3dcadb96dd513341b6d4a7622c31d9fe68 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Sun, 8 Dec 2024 03:43:48 +0900 Subject: [PATCH 519/563] =?UTF-8?q?fix:=20notice=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=88=98=EC=A0=95=EC=82=AC=ED=95=AD=20#24?= =?UTF-8?q?8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/NoticeController.java | 13 ++++++++--- .../service/NoticeCommandService.java | 5 +++-- .../service/NoticeCommandServiceImpl.java | 17 +++++++++----- .../query/service/NoticeQueryServiceImpl.java | 1 - .../global/dataloader/Initializer.java | 22 +++++++++++++++++++ .../security/config/ProdSecurityConfig.java | 4 ++-- 6 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java index f9e85b7f..8273e05b 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java @@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeDeleteDTO; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeModifyDTO; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; @@ -17,6 +18,7 @@ import stanl_2.final_backend.domain.notices.common.response.NoticeResponseMessage; import stanl_2.final_backend.domain.s3.command.domain.service.S3FileServiceImpl; +import java.security.GeneralSecurityException; import java.security.Principal; @RestController("commandNoticeController") @@ -28,13 +30,15 @@ public class NoticeController { private final S3FileServiceImpl s3FileService; private final NoticeModifyDTO noticeModifyDTO; + private final MemberQueryService memberQueryService; @Autowired - public NoticeController(NoticeCommandService noticeCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService, NoticeModifyDTO noticeModifyDTO){ + public NoticeController(NoticeCommandService noticeCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService, NoticeModifyDTO noticeModifyDTO,MemberQueryService memberQueryService){ this.noticeModifyDTO = noticeModifyDTO; this.noticeCommandService = noticeCommandService; this.authQueryService =authQueryService; this.s3FileService = s3FileService; + this.memberQueryService = memberQueryService; } @Operation(summary = "공지사항 작성") @@ -45,7 +49,7 @@ public NoticeController(NoticeCommandService noticeCommandService, AuthQueryServ @PostMapping(value = "") public ResponseEntity postNotice(@RequestPart("dto") NoticeRegistDTO noticeRegistDTO, // JSON 데이터 @RequestPart(value = "file", required = false) MultipartFile file, - Principal principal){ + Principal principal) throws GeneralSecurityException { String memberLoginId = principal.getName(); noticeRegistDTO.setMemberLoginId(memberLoginId); if (file != null && !file.isEmpty()) { @@ -73,8 +77,11 @@ public ResponseEntity modifyNotice( @PathVariable String noticeId, @RequestPart("dto") NoticeModifyDTO noticeModifyDTO, // JSON 데이터 @RequestPart(value = "file", required = false) MultipartFile file, - Principal principal){ + Principal principal) throws GeneralSecurityException { String memberLoginId = principal.getName(); + String memberId = authQueryService.selectMemberIdByLoginId(memberLoginId); + memberId=memberQueryService.selectNameById(memberId); + noticeModifyDTO.setMemberId(memberId); noticeModifyDTO.setMemberLoginId(memberLoginId); noticeModifyDTO.setContent(noticeModifyDTO.getContent()); if (file != null && !file.isEmpty()) { diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/service/NoticeCommandService.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/service/NoticeCommandService.java index 4f3754c3..7024d114 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/service/NoticeCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/service/NoticeCommandService.java @@ -4,12 +4,13 @@ import stanl_2.final_backend.domain.notices.command.application.dto.NoticeModifyDTO; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; +import java.security.GeneralSecurityException; import java.security.Principal; public interface NoticeCommandService { - void registerNotice(NoticeRegistDTO noticeRegistDTO, Principal principal); + void registerNotice(NoticeRegistDTO noticeRegistDTO, Principal principal) throws GeneralSecurityException; - NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO,Principal principal); + NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO,Principal principal) throws GeneralSecurityException; void deleteNotice(NoticeDeleteDTO noticeDeleteDTO,Principal principal); } diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java index 297350e0..2c6e461c 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java @@ -8,6 +8,7 @@ import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.alarm.command.application.service.AlarmCommandService; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeAlarmDTO; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeDeleteDTO; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeModifyDTO; @@ -17,10 +18,9 @@ import stanl_2.final_backend.domain.notices.command.domain.repository.NoticeRepository; import stanl_2.final_backend.domain.notices.common.exception.NoticeCommonException; import stanl_2.final_backend.domain.notices.common.exception.NoticeErrorCode; -import stanl_2.final_backend.domain.schedule.common.exception.ScheduleCommonException; -import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; import stanl_2.final_backend.global.redis.RedisService; +import java.security.GeneralSecurityException; import java.security.Principal; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -38,16 +38,18 @@ public class NoticeCommandServiceImpl implements NoticeCommandService { private final ModelMapper modelMapper; private final AlarmCommandService alarmCommandService; + private final MemberQueryService memberQueryService; @Autowired public NoticeCommandServiceImpl(NoticeRepository noticeRepository, ModelMapper modelMapper, AuthQueryService authQueryService, AlarmCommandService alarmCommandService, - RedisService redisService) { + RedisService redisService, MemberQueryService memberQueryService) { this.noticeRepository = noticeRepository; this.modelMapper = modelMapper; this.authQueryService =authQueryService; this.alarmCommandService = alarmCommandService; this.redisService = redisService; + this.memberQueryService = memberQueryService; } private String getCurrentTimestamp() { @@ -58,10 +60,11 @@ private String getCurrentTimestamp() { @Override @Transactional(readOnly = false) public void registerNotice(NoticeRegistDTO noticeRegistDTO, - Principal principal) { + Principal principal) throws GeneralSecurityException { System.out.println("[Before Cache Clear] Transaction ReadOnly: " + isCurrentTransactionReadOnly()); redisService.clearNoticeCache(); String memberId = authQueryService.selectMemberIdByLoginId(noticeRegistDTO.getMemberLoginId()); + memberId=memberQueryService.selectNameById(memberId); noticeRegistDTO.setMemberId(memberId); try { Notice notice = modelMapper.map(noticeRegistDTO, Notice.class); @@ -77,11 +80,11 @@ public void registerNotice(NoticeRegistDTO noticeRegistDTO, } } @Override - public NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO,Principal principal) { + public NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO,Principal principal) throws GeneralSecurityException { redisService.clearNoticeCache(); String memberId = authQueryService.selectMemberIdByLoginId(noticeModifyDTO.getMemberLoginId()); - + memberId=memberQueryService.selectNameById(memberId); Notice notice = noticeRepository.findById(id) .orElseThrow(() -> new NoticeCommonException(NoticeErrorCode.NOTICE_NOT_FOUND)); @@ -91,6 +94,8 @@ public NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO,P } try { Notice updateNotice = modelMapper.map(noticeModifyDTO, Notice.class); + updateNotice.setTag(notice.getTag());//수정예정 + updateNotice.setClassification(notice.getClassification());//수정예정 updateNotice.setNoticeId(notice.getNoticeId()); updateNotice.setMemberId(notice.getMemberId()); updateNotice.setCreatedAt(notice.getCreatedAt()); diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeQueryServiceImpl.java index 4f650323..a257e22f 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeQueryServiceImpl.java @@ -71,7 +71,6 @@ public Page findNotices(Pageable pageable, SearchDTO searchDTO) { } notices.forEach(notice -> { try { - notice.setMemberId(memberQueryService.selectNameById(notice.getMemberId())); } catch (Exception e) { throw new SalesHistoryCommonException(SalesHistoryErrorCode.MEMBER_NOT_FOUND); } diff --git a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java index 94d2426c..c8ea3b62 100644 --- a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java +++ b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java @@ -70,6 +70,28 @@ public class Initializer implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { + createOrUpdateMember( + "godgod", + "godgod", + "신하늘", + "godgod@stanl.com", + 0, + "MALE", + "000000-0000000", + "010-0000-0000", + "서울 동작구 보라매로 87", + "시스템 관리자", + "중졸", + "REGULAR", + "미필", + "한국은행", + "000-0000-000000-00000", + "CEN_000000000", + "ORG_000000000", + "GOD", + loadImage("god.png") + ); + // 우리 계정 createOrUpdateMember( "god", diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index 9e250cd1..0cd47100 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -54,7 +54,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication // 필터 순서: JWT 검증 -> CSRF .addFilterBefore(new JWTTokenValidatorFilter(jwtSecretKey, logRepository), UsernamePasswordAuthenticationFilter.class) - .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()) // Only HTTPS + .requiresChannel(rcc -> rcc.anyRequest().requiresInsecure()) // Only HTTPS .exceptionHandling(ex -> ex .authenticationEntryPoint((request, response, authException) -> { log.error("Authentication error: {}", authException.getMessage()); @@ -76,7 +76,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); - config.setAllowedOrigins(Collections.singletonList("https://final-frontend-nine.vercel.app/")); + config.setAllowedOrigins(Collections.singletonList("http://localhost:5173")); config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); config.setAllowCredentials(true); config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "X-XSRF-TOKEN")); From 41e6f537016dd3ea14b18b6e480a4352ca36987c Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Sun, 8 Dec 2024 03:44:33 +0900 Subject: [PATCH 520/563] =?UTF-8?q?fix:=20promotion=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20=EC=88=98=EC=A0=95=EC=82=AC=ED=95=AD=20?= =?UTF-8?q?#249?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PromotionController.java | 15 +++++++++++---- .../application/dto/PromotionModifyDTO.java | 1 + .../service/PromotionCommandService.java | 5 +++-- .../aggregate/service/PromotionServiceImpl.java | 17 ++++++++++++----- .../query/service/PromotionServiceImpl.java | 1 - 5 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java index d476b4ee..6868aa7c 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java @@ -10,12 +10,14 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionModifyDTO; import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionRegistDTO; import stanl_2.final_backend.domain.promotion.command.application.service.PromotionCommandService; import stanl_2.final_backend.domain.promotion.common.response.PromotionResponseMessage; import stanl_2.final_backend.domain.s3.command.domain.service.S3FileServiceImpl; +import java.security.GeneralSecurityException; import java.security.Principal; @RestController("commandPromotionController") @@ -24,12 +26,14 @@ public class PromotionController { private final PromotionCommandService promotionCommandService; private final AuthQueryService authQueryService; private final S3FileServiceImpl s3FileService; + private final MemberQueryService memberQueryService; @Autowired - public PromotionController(PromotionCommandService promotionCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService) { + public PromotionController(PromotionCommandService promotionCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService,MemberQueryService memberQueryService) { this.promotionCommandService = promotionCommandService; this.authQueryService =authQueryService; this.s3FileService = s3FileService; + this.memberQueryService =memberQueryService; } @Operation(summary = "프로모션 작성") @@ -40,7 +44,7 @@ public PromotionController(PromotionCommandService promotionCommandService, Auth @PostMapping("") public ResponseEntity postNotice(@RequestPart("dto") PromotionRegistDTO promotionRegistDTO, // JSON 데이터 @RequestPart(value = "file", required = false) MultipartFile file, - Principal principal){ + Principal principal) throws GeneralSecurityException { String memberLoginId = principal.getName(); promotionRegistDTO.setMemberLoginId(memberLoginId); if (file != null && !file.isEmpty()) { @@ -67,9 +71,12 @@ public ResponseEntity modifyNotice( @PathVariable String promotionId, @RequestPart("dto") PromotionModifyDTO promotionModifyDTO, @RequestPart(value = "file", required = false) MultipartFile file, - Principal principal){ + Principal principal) throws GeneralSecurityException { String memberLoginId = principal.getName(); - promotionModifyDTO.setMemberId(memberLoginId); + String memberId = authQueryService.selectMemberIdByLoginId(memberLoginId); + memberId=memberQueryService.selectNameById(memberId); + promotionModifyDTO.setMemberId(memberId); + promotionModifyDTO.setMemberLoginId(memberLoginId); promotionModifyDTO.setContent(promotionModifyDTO.getContent()); if (file != null && !file.isEmpty()) { promotionModifyDTO.setFileUrl(s3FileService.uploadOneFile(file)); diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java index bea0c722..9fd998d8 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/dto/PromotionModifyDTO.java @@ -16,5 +16,6 @@ public class PromotionModifyDTO { private String memberId; private String content; private String fileUrl; + private String memberLoginId; } diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/service/PromotionCommandService.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/service/PromotionCommandService.java index a8abdc19..a69c2dc0 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/service/PromotionCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/service/PromotionCommandService.java @@ -3,13 +3,14 @@ import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionModifyDTO; import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionRegistDTO; +import java.security.GeneralSecurityException; import java.security.Principal; public interface PromotionCommandService { - void registerPromotion(PromotionRegistDTO promotionRegistDTO, Principal principal); + void registerPromotion(PromotionRegistDTO promotionRegistDTO, Principal principal) throws GeneralSecurityException; - PromotionModifyDTO modifyPromotion(String promotionId, PromotionModifyDTO promotionModifyDTO, Principal principal); + PromotionModifyDTO modifyPromotion(String promotionId, PromotionModifyDTO promotionModifyDTO, Principal principal) throws GeneralSecurityException; void deletePromotion(String promotionId, Principal principal); } diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java index 558008fb..5a9f6ba1 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java @@ -6,6 +6,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import stanl_2.final_backend.domain.notices.common.exception.NoticeCommonException; import stanl_2.final_backend.domain.notices.common.exception.NoticeErrorCode; import stanl_2.final_backend.domain.problem.common.exception.ProblemCommonException; @@ -19,6 +20,7 @@ import stanl_2.final_backend.domain.promotion.common.exception.PromotionErrorCode; import stanl_2.final_backend.global.redis.RedisService; +import java.security.GeneralSecurityException; import java.security.Principal; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -32,13 +34,15 @@ public class PromotionServiceImpl implements PromotionCommandService { private final AuthQueryService authQueryService; private final ModelMapper modelMapper; + private final MemberQueryService memberQueryService; @Autowired - public PromotionServiceImpl(PromotionRepository promotionRepository, ModelMapper modelMapper,RedisService redisService, AuthQueryService authQueryService) { + public PromotionServiceImpl(PromotionRepository promotionRepository, ModelMapper modelMapper,RedisService redisService, AuthQueryService authQueryService, MemberQueryService memberQueryService) { this.redisService = redisService; this.promotionRepository = promotionRepository; this.authQueryService = authQueryService; this.modelMapper = modelMapper; + this.memberQueryService =memberQueryService; } private String getCurrentTimestamp() { ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); @@ -47,9 +51,10 @@ private String getCurrentTimestamp() { @Transactional @Override - public void registerPromotion(PromotionRegistDTO promotionRegistDTO, Principal principal) { + public void registerPromotion(PromotionRegistDTO promotionRegistDTO, Principal principal) throws GeneralSecurityException { redisService.clearPromotionCache(); String memberId = authQueryService.selectMemberIdByLoginId(promotionRegistDTO.getMemberLoginId()); + memberId=memberQueryService.selectNameById(memberId); promotionRegistDTO.setMemberId(memberId); try { Promotion promotion =modelMapper.map(promotionRegistDTO,Promotion.class); @@ -64,12 +69,14 @@ public void registerPromotion(PromotionRegistDTO promotionRegistDTO, Principal p @Transactional @Override - public PromotionModifyDTO modifyPromotion(String promotionId, PromotionModifyDTO promotionModifyDTO, Principal principal) { + public PromotionModifyDTO modifyPromotion(String promotionId, PromotionModifyDTO promotionModifyDTO, Principal principal) throws GeneralSecurityException { redisService.clearPromotionCache(); - String memberId= principal.getName(); + String memberId=authQueryService.selectMemberIdByLoginId(promotionModifyDTO.getMemberLoginId()); + memberId=memberQueryService.selectNameById(memberId); + Promotion promotion = promotionRepository.findById(promotionId) .orElseThrow(() -> new PromotionCommonException(PromotionErrorCode.PROMOTION_NOT_FOUND)); - if(promotion.getMemberId().equals(memberId)){ + if(!promotion.getMemberId().equals(memberId)){ throw new ProblemCommonException(ProblemErrorCode.AUTHORIZATION_VIOLATION); } try { diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java index e1f23a73..28145e40 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/query/service/PromotionServiceImpl.java @@ -55,7 +55,6 @@ public Page findPromotions(Pageable pageable, PromotionSearchDTO p } promotions.forEach(promotion -> { try { - promotion.setMemberId(memberQueryService.selectNameById(promotion.getMemberId())); } catch (Exception e) { throw new SalesHistoryCommonException(SalesHistoryErrorCode.MEMBER_NOT_FOUND); } From 20d14579c8d62a5420f6a1206c7617a8ed4ae9e7 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Sun, 8 Dec 2024 03:44:52 +0900 Subject: [PATCH 521/563] =?UTF-8?q?fix:=20problem=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=88=98=EC=A0=95=EC=82=AC=ED=95=AD=20#25?= =?UTF-8?q?0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ProblemController.java | 18 ++++++++---- .../application/dto/ProblemModifyDTO.java | 1 + .../service/ProblemCommandService.java | 5 ++-- .../aggregate/service/ProblemServiceImpl.java | 29 +++++++++++-------- .../query/service/ProblemServiceImpl.java | 1 - 5 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java index 7e46b819..7db71694 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java @@ -10,12 +10,14 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import stanl_2.final_backend.domain.problem.command.application.dto.ProblemModifyDTO; import stanl_2.final_backend.domain.problem.command.application.dto.ProblemRegistDTO; import stanl_2.final_backend.domain.problem.command.application.service.ProblemCommandService; import stanl_2.final_backend.domain.problem.common.response.ProblemResponseMessage; import stanl_2.final_backend.domain.s3.command.domain.service.S3FileServiceImpl; +import java.security.GeneralSecurityException; import java.security.Principal; @RestController("commandProblemController") @@ -24,11 +26,13 @@ public class ProblemController { private final ProblemCommandService problemCommandService; private final AuthQueryService authQueryService; private final S3FileServiceImpl s3FileService; + private final MemberQueryService memberQueryService; @Autowired - public ProblemController(ProblemCommandService problemCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService) { + public ProblemController(ProblemCommandService problemCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService,MemberQueryService memberQueryService) { this.problemCommandService = problemCommandService; this.authQueryService = authQueryService; this.s3FileService = s3FileService; + this.memberQueryService =memberQueryService; } @Operation(summary = "문제사항 작성") @@ -39,7 +43,7 @@ public ProblemController(ProblemCommandService problemCommandService, AuthQueryS @PostMapping("") public ResponseEntity postProblem(@RequestPart("dto") ProblemRegistDTO problemRegistDTO, // JSON 데이터 @RequestPart(value = "file", required = false) MultipartFile file, - Principal principal){ + Principal principal) throws GeneralSecurityException { String memberLoginId = principal.getName(); problemRegistDTO.setMemberLoginId(memberLoginId); if (file != null && !file.isEmpty()) { @@ -66,9 +70,12 @@ public ResponseEntity postProblem(@RequestPart("dto") Pr public ResponseEntity modifyProblem(@PathVariable String problemId, @RequestPart("dto") ProblemModifyDTO problemModifyDTO, @RequestPart(value = "file", required = false) MultipartFile file, - Principal principal){ + Principal principal) throws GeneralSecurityException { String memberLoginId = principal.getName(); - problemModifyDTO.setMemberId(memberLoginId); + String memberId = authQueryService.selectMemberIdByLoginId(memberLoginId); + memberId=memberQueryService.selectNameById(memberId); + problemModifyDTO.setMemberId(memberId); + problemModifyDTO.setMemberLoginId(memberLoginId); problemModifyDTO.setContent(problemModifyDTO.getContent()); if (file != null && !file.isEmpty()) { problemModifyDTO.setFileUrl(s3FileService.uploadOneFile(file)); @@ -77,8 +84,7 @@ public ResponseEntity modifyProblem(@PathVariable String } else { problemModifyDTO.setFileUrl(s3FileService.uploadOneFile(file)); } - problemCommandService.modifyProblem(problemId,problemModifyDTO,principal); - + problemCommandService.modifyProblem(problemId,problemModifyDTO,principal); return ResponseEntity.ok(ProblemResponseMessage.builder() .httpStatus(200) .msg("성공") diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java index df260153..206bad9c 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/dto/ProblemModifyDTO.java @@ -15,6 +15,7 @@ public class ProblemModifyDTO { private String title; private String content; private String memberId; + private String memberLoginId; private String fileUrl; } diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/service/ProblemCommandService.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/service/ProblemCommandService.java index ad2ffbf4..7c576f45 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/application/service/ProblemCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/service/ProblemCommandService.java @@ -3,13 +3,14 @@ import stanl_2.final_backend.domain.problem.command.application.dto.ProblemModifyDTO; import stanl_2.final_backend.domain.problem.command.application.dto.ProblemRegistDTO; +import java.security.GeneralSecurityException; import java.security.Principal; public interface ProblemCommandService { - void registerProblem(ProblemRegistDTO problemRegistDTO, Principal principal); + void registerProblem(ProblemRegistDTO problemRegistDTO, Principal principal) throws GeneralSecurityException; - ProblemModifyDTO modifyProblem(String problemId, ProblemModifyDTO problemModifyDTO, Principal principal); + ProblemModifyDTO modifyProblem(String problemId, ProblemModifyDTO problemModifyDTO, Principal principal) throws GeneralSecurityException; void modifyStatus(String problemId, Principal principal); void deleteProblem(String problemId, Principal principal); diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java index 7126741c..10e79b41 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java @@ -6,6 +6,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import stanl_2.final_backend.domain.notices.common.exception.NoticeCommonException; import stanl_2.final_backend.domain.notices.common.exception.NoticeErrorCode; import stanl_2.final_backend.domain.problem.command.application.dto.ProblemModifyDTO; @@ -15,12 +16,9 @@ import stanl_2.final_backend.domain.problem.command.domain.aggregate.repository.ProblemRepository; import stanl_2.final_backend.domain.problem.common.exception.ProblemCommonException; import stanl_2.final_backend.domain.problem.common.exception.ProblemErrorCode; -import stanl_2.final_backend.domain.promotion.common.exception.PromotionCommonException; -import stanl_2.final_backend.domain.promotion.common.exception.PromotionErrorCode; -import stanl_2.final_backend.domain.schedule.common.exception.ScheduleCommonException; -import stanl_2.final_backend.domain.schedule.common.exception.ScheduleErrorCode; import stanl_2.final_backend.global.redis.RedisService; +import java.security.GeneralSecurityException; import java.security.Principal; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -33,16 +31,20 @@ public class ProblemServiceImpl implements ProblemCommandService { private final AuthQueryService authQueryService; private final RedisService redisService; private final ModelMapper modelMapper; + private final MemberQueryService memberQueryService; + @Autowired public ProblemServiceImpl(ProblemRepository problemRepository, AuthQueryService authQueryService, ModelMapper modelMapper, - RedisService redisService) { + RedisService redisService, + MemberQueryService memberQueryService) { this.problemRepository = problemRepository; this.modelMapper = modelMapper; this.redisService = redisService; this.authQueryService =authQueryService; + this.memberQueryService = memberQueryService; } private String getCurrentTimestamp() { ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); @@ -52,9 +54,10 @@ private String getCurrentTimestamp() { @Transactional @Override public void registerProblem(ProblemRegistDTO problemRegistDTO, - Principal principal) { + Principal principal) throws GeneralSecurityException { redisService.clearProblemCache(); String memberId = authQueryService.selectMemberIdByLoginId(problemRegistDTO.getMemberLoginId()); + memberId=memberQueryService.selectNameById(memberId); problemRegistDTO.setMemberId(memberId); try { Problem problem = modelMapper.map(problemRegistDTO, Problem.class); @@ -69,21 +72,23 @@ public void registerProblem(ProblemRegistDTO problemRegistDTO, @Transactional @Override - public ProblemModifyDTO modifyProblem(String problemId, ProblemModifyDTO problemModifyDTO,Principal principal) { + public ProblemModifyDTO modifyProblem(String problemId, ProblemModifyDTO problemModifyDTO,Principal principal) throws GeneralSecurityException { redisService.clearProblemCache(); - String memberId= principal.getName(); + String memberId = authQueryService.selectMemberIdByLoginId(problemModifyDTO.getMemberLoginId()); + memberId=memberQueryService.selectNameById(memberId); + Problem problem = problemRepository.findById(problemId) .orElseThrow(() -> new ProblemCommonException(ProblemErrorCode.PROBLEM_NOT_FOUND)); - if(problem.getMemberId().equals(memberId)){ + if(!problem.getMemberId().equals(memberId)){ throw new ProblemCommonException(ProblemErrorCode.AUTHORIZATION_VIOLATION); + } try { + System.out.println("test1"); Problem updateProblem = modelMapper.map(problemModifyDTO, Problem.class); updateProblem.setProblemId(problem.getProblemId()); - updateProblem.setProblemId(problem.getTitle()); - updateProblem.setProblemId(problem.getContent()); updateProblem.setMemberId(problem.getMemberId()); - updateProblem.setStatus("DONE"); + updateProblem.setStatus(problem.getStatus()); updateProblem.setCreatedAt(problem.getCreatedAt()); updateProblem.setActive(problem.getActive()); updateProblem.setCustomerId(problem.getCustomerId()); diff --git a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java index 01927dec..394cff5e 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/query/service/ProblemServiceImpl.java @@ -61,7 +61,6 @@ public Page findProblems(Pageable pageable, ProblemSearchDTO problem } problems.forEach(problem -> { try { - problem.setMemberId(memberQueryService.selectNameById(problem.getMemberId())); } catch (Exception e) { throw new SalesHistoryCommonException(SalesHistoryErrorCode.MEMBER_NOT_FOUND); } From 5a498b58ce8f9000d47a9b1b555142d481cce4a9 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Sun, 8 Dec 2024 04:10:08 +0900 Subject: [PATCH 522/563] =?UTF-8?q?refactor:=20=EA=B3=84=EC=95=BD=EC=84=9C?= =?UTF-8?q?,=20=EB=B0=9C=EC=A3=BC=EC=84=9C,=20=EC=88=98=EC=A3=BC=EC=84=9C?= =?UTF-8?q?=20=ED=94=BC=EB=93=9C=EB=B0=B1=20=EC=88=98=EC=A0=95=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20(#251)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/ContractController.java | 37 +- .../query/dto/ContractSelectAllDTO.java | 22 ++ .../query/dto/ContractSeletIdDTO.java | 3 +- .../query/repository/ContractMapper.java | 22 +- .../service/ContractQueryServiceImpl.java | 146 ++++---- .../application/dto/OrderModifyDTO.java | 2 + .../application/dto/OrderRegistDTO.java | 1 + .../service/OrderCommandService.java | 6 +- .../domain/aggregate/entity/Order.java | 3 + .../service/OrderCommandServiceImpl.java | 16 +- .../query/controller/OrderController.java | 91 ++++- .../order/query/dto/OrderSelectSearchDTO.java | 1 + .../order/query/repository/OrderMapper.java | 30 +- .../query/service/OrderQueryService.java | 6 +- .../query/service/OrderQueryServiceImpl.java | 170 ++++++++-- .../query/repository/ContractMapper.xml | 315 ++++++++---------- .../order/query/repository/OrderMapper.xml | 204 +++++++++++- 17 files changed, 731 insertions(+), 344 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java index bfdd10e9..a39ba5aa 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java @@ -42,20 +42,11 @@ public ContractController(ContractQueryService contractQueryService) { }) @GetMapping("employee") public ResponseEntity getAllContractEmployee(@PageableDefault(size = 10) Pageable pageable, - Principal principal, - @RequestParam(required = false) String sortField, - @RequestParam(required = false) String sortOrder) { + Principal principal) { ContractSelectAllDTO contractSelectAllDTO = new ContractSelectAllDTO(); contractSelectAllDTO.setMemberId(principal.getName()); - // 정렬 추가 - if (sortField != null && sortOrder != null) { - Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; - pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); - } - - Page responseContracts = contractQueryService.selectAllContractEmployee(contractSelectAllDTO, pageable); return ResponseEntity.ok(ContractResponseMessage.builder() @@ -105,6 +96,8 @@ public ResponseEntity getContractBySearchEmployee(Princ @RequestParam(required = false) String status, @RequestParam(required = false) String companyName, @RequestParam(required = false) String customerPurchaseCondition, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder, @PageableDefault(size = 10) Pageable pageable) { ContractSearchDTO contractSearchDTO = new ContractSearchDTO(); @@ -121,6 +114,12 @@ public ResponseEntity getContractBySearchEmployee(Princ contractSearchDTO.setCompanyName(companyName); contractSearchDTO.setCustomerPurchaseCondition(customerPurchaseCondition); + // 정렬 추가 + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } + Page responseContracts = contractQueryService.selectBySearchEmployee(contractSearchDTO, pageable); return ResponseEntity.ok(ContractResponseMessage.builder() @@ -138,19 +137,11 @@ public ResponseEntity getContractBySearchEmployee(Princ }) @GetMapping("center") public ResponseEntity getAllContractAdmin(@PageableDefault(size = 10) Pageable pageable, - Principal principal, - @RequestParam(required = false) String sortField, - @RequestParam(required = false) String sortOrder) throws GeneralSecurityException { + Principal principal) throws GeneralSecurityException { ContractSelectAllDTO contractSelectAllDTO = new ContractSelectAllDTO(); contractSelectAllDTO.setMemberId(principal.getName()); - // 정렬 추가 - if (sortField != null && sortOrder != null) { - Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; - pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); - } - Page responseContracts = contractQueryService.selectAllContractAdmin(contractSelectAllDTO, pageable); return ResponseEntity.ok(ContractResponseMessage.builder() @@ -200,6 +191,8 @@ public ResponseEntity getContractBySearchAdmin(Principa @RequestParam(required = false) String status, @RequestParam(required = false) String companyName, @RequestParam(required = false) String customerPurchaseCondition, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder, @PageableDefault(size = 10) Pageable pageable) throws GeneralSecurityException { ContractSearchDTO contractSearchDTO = new ContractSearchDTO(); @@ -216,6 +209,12 @@ public ResponseEntity getContractBySearchAdmin(Principa contractSearchDTO.setCompanyName(companyName); contractSearchDTO.setCustomerPurchaseCondition(customerPurchaseCondition); + // 정렬 추가 + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } + Page responseContracts = contractQueryService.selectBySearchAdmin(contractSearchDTO, pageable); return ResponseEntity.ok(ContractResponseMessage.builder() diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java index 42345692..d0c5452c 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java @@ -21,4 +21,26 @@ public class ContractSelectAllDTO { private String createdAt; private String customerClassifcation; private String customerPurchaseCondition; + private String customerIdentifiNo; + private String customerAddress; + private String customerEmail; + private String customerPhone; + private String serialNum; + private String selectOption; + private Integer downPayment; + private Integer intermediatePayment; + private Integer remainderPayment; + private Integer consignmentPayment; + private String deliveryDate; + private String deliveryLocationLoc; + private String numberOfVehicles; + private String totalSales; + private String createdUrl; + private String updatedUrl; + private boolean active; + private String updatedAt; + private String deletedAt; + private String searchMemberId; + private String startAt; + private String endAt; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java index 9e4e8b8f..d8436007 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java @@ -29,11 +29,12 @@ public class ContractSeletIdDTO { private String deliveryDate; private String deliveryLocation; private String status; - private Integer NumberOfVehicles; + private Integer numberOfVehicles; private Integer totalSales; private String carName; private String createdUrl; private String updatedUrl; + private String vehiclePrice; private boolean active; private String createdAt; private String updatedAt; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java index 127a6f70..5bf2667b 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.java @@ -11,22 +11,22 @@ @Mapper public interface ContractMapper { - ContractSeletIdDTO findContractByIdAndMemId(@Param("purchaseOrderId") String purchaseOrderId, + ContractSeletIdDTO findContractByIdAndMemId(@Param("contractId") String contractId, @Param("memberId") String memberId); List findContractBySearchAndMemberId(@Param("offset") int offset, @Param("pageSize") int pageSize, - @Param("contractSearchDTO") ContractSearchDTO contractSearchDTO); + @Param("contractSearchDTO") ContractSearchDTO contractSearchDTO, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); - int findContractBySearchAndMemberIdCount(ContractSearchDTO contractSearchDTO); + int findContractBySearchAndMemberIdCount(@Param("contractSearchDTO") ContractSearchDTO contractSearchDTO); List findContractAllByMemId(@Param("offset") int offset, @Param("pageSize") int pageSize, - @Param("memberId") String memberId, - @Param("sortField") String sortField, - @Param("sortOrder") String sortOrder); + @Param("memberId") String memberId); - int findContractCountByMemId(String memId); + int findContractCountByMemId(String memberId); List findContractAll(@Param("offset") int offset, @Param("pageSize") int pageSize, @@ -47,9 +47,7 @@ List findContractBySearch(@Param("offset") int offset, List findContractAllByCenterId(@Param("offset") int offset, @Param("pageSize") int pageSize, - @Param("centerId") String centerId, - @Param("sortField") String sortField, - @Param("sortOrder") String sortOrder); + @Param("centerId") String centerId); Integer findContractCountByCenterId(@Param("centerId") String centerId); @@ -59,7 +57,9 @@ ContractSeletIdDTO findContractByIdAndCenterId(@Param("contractId")String contra List findContractBySearchAndCenterId(@Param("offset") int offset, @Param("pageSize") int pageSize, @Param("contractSearchDTO") ContractSearchDTO contractSearchDTO, - @Param("centerId") String centerId); + @Param("centerId") String centerId, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); Integer findContractBySearchAndCenterCount(@Param("contractSearchDTO") ContractSearchDTO contractSearchDTO, @Param("centerId") String centerId); diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java index 4c2210ad..a9e7c84b 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/service/ContractQueryServiceImpl.java @@ -58,33 +58,19 @@ public Page selectAllContractEmployee(ContractSelectAllDTO int offset = Math.toIntExact(pageable.getOffset()); int pageSize = pageable.getPageSize(); - // 정렬 정보 가져오기 - Sort sort = pageable.getSort(); - String sortField = null; - String sortOrder = null; - if (sort.isSorted()) { - sortField = sort.iterator().next().getProperty(); - sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; - } - - String caschKey = "myCache::contracts::offset=" + offset + "::pageSize=" + pageSize; - - List contracts = (List) redisTemplate.opsForValue().get(caschKey); + List contracts = contractMapper.findContractAllByMemId(offset, pageSize, memberId); if (contracts == null) { - contracts = contractMapper.findContractAllByMemId(offset, pageSize, memberId, sortField, sortOrder); + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); + } - if (contracts == null) { - throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); - } + int count = contractMapper.findContractCountByMemId(memberId); - redisTemplate.opsForValue().set(caschKey, contracts); + if (count == 0) { + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); } - Integer count = contractMapper.findContractCountByMemId(memberId); - int totalContract = (count != null) ? count : 0; - - return new PageImpl<>(contracts, pageable, totalContract); + return new PageImpl<>(contracts, pageable, count); } // 계약서 상세조회 @@ -105,6 +91,7 @@ public ContractSeletIdDTO selectDetailContractEmployee(ContractSeletIdDTO contra if (content == null) { String unescapedHtml = StringEscapeUtils.unescapeJson(responseContract.getCreatedUrl()); responseContract.setCreatedUrl(unescapedHtml); + return responseContract; } responseContract.setCreatedUrl(content); @@ -119,58 +106,67 @@ public Page selectBySearchEmployee(ContractSearchDTO contract if ("대기".equals(contractSearchDTO.getStatus())) { contractSearchDTO.setStatus("WAIT"); - } - if ("승인".equals(contractSearchDTO.getStatus())) { + } else if ("승인".equals(contractSearchDTO.getStatus())) { contractSearchDTO.setStatus("APPROVED"); + } else if ("취소".equals(contractSearchDTO.getStatus())) { + contractSearchDTO.setStatus("LEASE"); } - if ("취소".equals(contractSearchDTO.getStatus())) { - contractSearchDTO.setStatus("CANCEL"); + + if ("현금".equals(contractSearchDTO.getCustomerPurchaseCondition())) { + contractSearchDTO.setCustomerPurchaseCondition("CASH"); + } else if ("할부".equals(contractSearchDTO.getCustomerPurchaseCondition())) { + contractSearchDTO.setCustomerPurchaseCondition("INSTALLMENT"); + } else if ("리스".equals(contractSearchDTO.getCustomerPurchaseCondition())) { + contractSearchDTO.setCustomerPurchaseCondition("CANCEL"); + } + + if ("개인".equals(contractSearchDTO.getCustomerClassifcation())) { + contractSearchDTO.setCustomerClassifcation("PERSONAL"); + } else if ("법인".equals(contractSearchDTO.getCustomerClassifcation())) { + contractSearchDTO.setCustomerClassifcation("BUSINESS"); } int offset = Math.toIntExact(pageable.getOffset()); int pageSize = pageable.getPageSize(); - List contracts = contractMapper.findContractBySearchAndMemberId(offset, pageSize, contractSearchDTO); + + // 정렬 정보 가져오기 + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } + + List contracts = contractMapper.findContractBySearchAndMemberId(offset, pageSize, contractSearchDTO, sortField, sortOrder); if (contracts == null) { throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); } - Integer count = contractMapper.findContractBySearchAndMemberIdCount(contractSearchDTO); - int totalContract = (count != null) ? count : 0; + int count = contractMapper.findContractBySearchAndMemberIdCount(contractSearchDTO); + if (count == 0) { + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); + } - return new PageImpl<>(contracts, pageable, totalContract); + return new PageImpl<>(contracts, pageable, count); } // 영업 관리자 조회 @Override @Transactional(readOnly = true) public Page selectAllContractAdmin(ContractSelectAllDTO contractSelectAllDTO, Pageable pageable) throws GeneralSecurityException { + String memberId = authQueryService.selectMemberIdByLoginId(contractSelectAllDTO.getMemberId()); String centerId = memberQueryService.selectMemberInfo(contractSelectAllDTO.getMemberId()).getCenterId(); + contractSelectAllDTO.setMemberId(memberId); int offset = Math.toIntExact(pageable.getOffset()); int pageSize = pageable.getPageSize(); - // 정렬 정보 가져오기 - Sort sort = pageable.getSort(); - String sortField = null; - String sortOrder = null; - if (sort.isSorted()) { - sortField = sort.iterator().next().getProperty(); - sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; - } - - String caschKey = "myCache::contracts::offset=" + offset + "::pageSize=" + pageSize; - - List contracts = (List) redisTemplate.opsForValue().get(caschKey); + List contracts = contractMapper.findContractAllByCenterId(offset, pageSize, centerId); if (contracts == null) { - contracts = contractMapper.findContractAllByCenterId(offset, pageSize, centerId, sortField, sortOrder); - - if (contracts == null) { - throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); - } - - redisTemplate.opsForValue().set(caschKey, contracts); + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); } Integer count = contractMapper.findContractCountByCenterId(centerId); @@ -182,13 +178,25 @@ public Page selectAllContractAdmin(ContractSelectAllDTO co @Override @Transactional(readOnly = true) public ContractSeletIdDTO selectDetailContractAdmin(ContractSeletIdDTO contractSeletIdDTO) throws GeneralSecurityException { + String memberId = authQueryService.selectMemberIdByLoginId(contractSeletIdDTO.getMemberId()); String centerId = memberQueryService.selectMemberInfo(contractSeletIdDTO.getMemberId()).getCenterId(); + contractSeletIdDTO.setMemberId(memberId); + ContractSeletIdDTO responseContract = contractMapper.findContractByIdAndCenterId(contractSeletIdDTO.getContractId(), centerId); - // 이스케이프된 HTML 제거 - String unescapedHtml = StringEscapeUtils.unescapeJson(responseContract.getCreatedUrl()); - responseContract.setCreatedUrl(unescapedHtml); + if (responseContract == null) { + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); + } + + String content = updateHistoryQueryService.selectUpdateHistoryByContractId(responseContract.getContractId()); + + if (content == null) { + String unescapedHtml = StringEscapeUtils.unescapeJson(responseContract.getCreatedUrl()); + responseContract.setCreatedUrl(unescapedHtml); + return responseContract; + } + responseContract.setCreatedUrl(content); return responseContract; } @@ -196,16 +204,18 @@ public ContractSeletIdDTO selectDetailContractAdmin(ContractSeletIdDTO contractS @Override @Transactional(readOnly = true) public Page selectBySearchAdmin(ContractSearchDTO contractSearchDTO, Pageable pageable) throws GeneralSecurityException { + + String memberId = authQueryService.selectMemberIdByLoginId(contractSearchDTO.getMemberId()); String centerId = memberQueryService.selectMemberInfo(contractSearchDTO.getMemberId()).getCenterId(); + contractSearchDTO.setMemberId(memberId); + if ("대기".equals(contractSearchDTO.getStatus())) { contractSearchDTO.setStatus("WAIT"); - } - if ("승인".equals(contractSearchDTO.getStatus())) { + } else if ("승인".equals(contractSearchDTO.getStatus())) { contractSearchDTO.setStatus("APPROVED"); - } - if ("취소".equals(contractSearchDTO.getStatus())) { - contractSearchDTO.setStatus("CANCEL"); + } else if ("취소".equals(contractSearchDTO.getStatus())) { + contractSearchDTO.setStatus("LEASE"); } if ("현금".equals(contractSearchDTO.getCustomerPurchaseCondition())) { @@ -216,24 +226,36 @@ public Page selectBySearchAdmin(ContractSearchDTO contractSea contractSearchDTO.setCustomerPurchaseCondition("CANCEL"); } - if (contractSearchDTO.getCustomerClassifcation().equals("개인")) { + if ("개인".equals(contractSearchDTO.getCustomerClassifcation())) { contractSearchDTO.setCustomerClassifcation("PERSONAL"); - } else if (contractSearchDTO.getCustomerClassifcation().equals("법인")) { + } else if ("법인".equals(contractSearchDTO.getCustomerClassifcation())) { contractSearchDTO.setCustomerClassifcation("BUSINESS"); } int offset = Math.toIntExact(pageable.getOffset()); int pageSize = pageable.getPageSize(); - List contracts = contractMapper.findContractBySearchAndCenterId(offset, pageSize, contractSearchDTO, centerId); + + // 정렬 정보 가져오기 + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } + + List contracts = contractMapper.findContractBySearchAndCenterId(offset, pageSize, contractSearchDTO, centerId, sortField, sortOrder); if (contracts == null) { throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); } - Integer count = contractMapper.findContractBySearchAndCenterCount(contractSearchDTO, centerId); - int totalContract = (count != null) ? count : 0; + int count = contractMapper.findContractBySearchAndCenterCount(contractSearchDTO, centerId); + if (count == 0) { + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); + } - return new PageImpl<>(contracts, pageable, totalContract); + return new PageImpl<>(contracts, pageable, count); } // 영업담당자 조회 diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderModifyDTO.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderModifyDTO.java index 8d278610..038cdd82 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderModifyDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderModifyDTO.java @@ -10,5 +10,7 @@ public class OrderModifyDTO { private String orderId; private String title; private String content; + private String contractId; private String memberId; + private String centerId; } diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderRegistDTO.java index f9dc54e9..986ea09d 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderRegistDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/dto/OrderRegistDTO.java @@ -11,4 +11,5 @@ public class OrderRegistDTO { private String content; private String contractId; private String memberId; + private String centerId; } diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/service/OrderCommandService.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/service/OrderCommandService.java index b570cb6a..b1a1cf9f 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/application/service/OrderCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/service/OrderCommandService.java @@ -4,10 +4,12 @@ import stanl_2.final_backend.domain.order.command.application.dto.OrderRegistDTO; import stanl_2.final_backend.domain.order.command.application.dto.OrderStatusModifyDTO; +import java.security.GeneralSecurityException; + public interface OrderCommandService { - void registerOrder(OrderRegistDTO orderRegistDTO); + void registerOrder(OrderRegistDTO orderRegistDTO) throws GeneralSecurityException; - OrderModifyDTO modifyOrder(OrderModifyDTO orderModifyDTO); + OrderModifyDTO modifyOrder(OrderModifyDTO orderModifyDTO) throws GeneralSecurityException; void deleteOrder(String orderId, String loginId); diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/domain/aggregate/entity/Order.java b/src/main/java/stanl_2/final_backend/domain/order/command/domain/aggregate/entity/Order.java index 012fd0be..089fab83 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/domain/aggregate/entity/Order.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/domain/aggregate/entity/Order.java @@ -60,6 +60,9 @@ public class Order { @Column(name = "MEM_ID", nullable = false) private String memberId; + @Column(name = "CENT_ID", nullable = false) + private String centerId; + // Insert 되기 전에 실행 @PrePersist private void prePersist() { diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java index 806d0af0..1e454e0f 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/domain/service/OrderCommandServiceImpl.java @@ -6,6 +6,7 @@ import org.springframework.transaction.annotation.Transactional; import stanl_2.final_backend.domain.alarm.command.application.service.AlarmCommandService; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; import stanl_2.final_backend.domain.order.command.application.dto.OrderAlarmDTO; import stanl_2.final_backend.domain.order.command.application.dto.OrderModifyDTO; import stanl_2.final_backend.domain.order.command.application.dto.OrderRegistDTO; @@ -17,6 +18,7 @@ import stanl_2.final_backend.domain.order.common.exception.OrderErrorCode; import stanl_2.final_backend.domain.s3.command.application.service.S3FileService; +import java.security.GeneralSecurityException; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -29,14 +31,16 @@ public class OrderCommandServiceImpl implements OrderCommandService { private final ModelMapper modelMapper; private final S3FileService s3FileService; private final AlarmCommandService alarmCommandService; + private final MemberQueryService memberQueryService; public OrderCommandServiceImpl(OrderRepository orderRepository, AuthQueryService authQueryService, ModelMapper modelMapper, - S3FileService s3FileService ,AlarmCommandService alarmCommandService) { + S3FileService s3FileService , AlarmCommandService alarmCommandService, MemberQueryService memberQueryService) { this.orderRepository = orderRepository; this.authQueryService = authQueryService; this.modelMapper = modelMapper; this.s3FileService = s3FileService; this.alarmCommandService = alarmCommandService; + this.memberQueryService = memberQueryService; } private String getCurrentTime() { @@ -46,15 +50,17 @@ private String getCurrentTime() { @Override @Transactional - public void registerOrder(OrderRegistDTO orderRegistDTO) { + public void registerOrder(OrderRegistDTO orderRegistDTO) throws GeneralSecurityException { String memberId = authQueryService.selectMemberIdByLoginId(orderRegistDTO.getMemberId()); + String centerId = memberQueryService.selectMemberInfo(orderRegistDTO.getMemberId()).getCenterId(); String unescapedHtml = StringEscapeUtils.unescapeJson(orderRegistDTO.getContent()); String updatedS3Url = s3FileService.uploadHtml(unescapedHtml, orderRegistDTO.getTitle()); orderRegistDTO.setMemberId(memberId); orderRegistDTO.setContent(updatedS3Url); + orderRegistDTO.setCenterId(centerId); Order order = modelMapper.map(orderRegistDTO, Order.class); @@ -63,9 +69,12 @@ public void registerOrder(OrderRegistDTO orderRegistDTO) { @Override @Transactional - public OrderModifyDTO modifyOrder(OrderModifyDTO orderModifyDTO) { + public OrderModifyDTO modifyOrder(OrderModifyDTO orderModifyDTO) throws GeneralSecurityException { String memberId = authQueryService.selectMemberIdByLoginId(orderModifyDTO.getMemberId()); + String centerId = memberQueryService.selectMemberInfo(orderModifyDTO.getMemberId()).getCenterId(); + orderModifyDTO.setMemberId(memberId); + orderModifyDTO.setCenterId(centerId); Order order = orderRepository.findByOrderIdAndMemberId(orderModifyDTO.getOrderId(), memberId); @@ -83,7 +92,6 @@ public OrderModifyDTO modifyOrder(OrderModifyDTO orderModifyDTO) { updateOrder.setUpdatedAt(order.getUpdatedAt()); updateOrder.setStatus(order.getStatus()); updateOrder.setActive(order.getActive()); - updateOrder.setContractId(order.getContractId()); orderRepository.save(updateOrder); diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java b/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java index 7cedae20..9780832c 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java @@ -41,10 +41,17 @@ public OrderController(OrderQueryService orderQueryService) { }) @GetMapping("employee") public ResponseEntity getAllOrderEmployee(Principal principal, - @PageableDefault(size = 10)Pageable pageable) { + @PageableDefault(size = 10)Pageable pageable, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder) { String loginId = principal.getName(); + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } + Page responseOrders = orderQueryService.selectAllEmployee(loginId, pageable); return ResponseEntity.ok(OrderResponseMessage.builder() @@ -89,8 +96,10 @@ public ResponseEntity getSearchOrderEmployee(@RequestParam @RequestParam(required = false) String searchMemberId, @RequestParam(required = false) String startDate, @RequestParam(required = false) String endDate, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder, Principal principal, - @PageableDefault(size = 10) Pageable pageable) { + @PageableDefault(size = 10) Pageable pageable) throws GeneralSecurityException { OrderSelectSearchDTO orderSelectSearchDTO = new OrderSelectSearchDTO(); orderSelectSearchDTO.setTitle(title); @@ -101,6 +110,11 @@ public ResponseEntity getSearchOrderEmployee(@RequestParam orderSelectSearchDTO.setEndDate(endDate); orderSelectSearchDTO.setMemberId(principal.getName()); + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } + Page responseOrders = orderQueryService.selectSearchOrdersEmployee(orderSelectSearchDTO, pageable); return ResponseEntity.ok(OrderResponseMessage.builder() @@ -110,8 +124,8 @@ public ResponseEntity getSearchOrderEmployee(@RequestParam .build()); } - // 영업관리자, 영업담당자 조회 - @Operation(summary = "수주서 전체 조회(영업관리자, 영업담당자)") + // 영업담당자 조회 + @Operation(summary = "수주서 전체 조회(영업담당자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "수주서 전체 조회 성공", content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) @@ -128,7 +142,7 @@ public ResponseEntity getAllOrder(@PageableDefault(size = .build()); } - @Operation(summary = "수주서 상세 조회(영업관리자, 영업담당자)") + @Operation(summary = "수주서 상세 조회(영업담당자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "수주서 상세 조회 성공", content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) @@ -148,7 +162,7 @@ public ResponseEntity getDetailOrder(@PathVariable("orderI .build()); } - @Operation(summary = "수주서 검색 조회(영업관리자, 영업담당자)") + @Operation(summary = "수주서 검색 조회(영업담당자)") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "수주서 검색 조회 성공", content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) @@ -190,6 +204,71 @@ public ResponseEntity getSearchOrder(@RequestParam(require .build()); } + // 영업관리자 + @Operation(summary = "수주서 전체 조회(영업담당자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "수주서 전체 조회 성공", + content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) + }) + @GetMapping("center") + public ResponseEntity getAllOrderCenter(@PageableDefault(size = 10)Pageable pageable, + Principal principal) { + + String memberId = principal.getName(); + Page responseOrders = orderQueryService.selectAllCenter(pageable, memberId); + + return ResponseEntity.ok(OrderResponseMessage.builder() + .httpStatus(200) + .msg("수주서 전체 조회 성공") + .result(responseOrders) + .build()); + } + + @Operation(summary = "수주서 검색 조회(영업담당자)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "수주서 검색 조회 성공", + content = {@Content(schema = @Schema(implementation = OrderResponseMessage.class))}) + }) + @GetMapping("center/search") + public ResponseEntity getSearchOrderCenter( + Principal principal, + @RequestParam(required = false) String title, + @RequestParam(required = false) String orderId, + @RequestParam(required = false) String status, + @RequestParam(required = false) String adminId, + @RequestParam(required = false) String searchMemberId, + @RequestParam(required = false) String startDate, + @RequestParam(required = false) String endDate, + @RequestParam(required = false) String sortField, + @RequestParam(required = false) String sortOrder, + @RequestParam(required = false) String productName, + @PageableDefault(size = 10) Pageable pageable) throws GeneralSecurityException { + + OrderSelectSearchDTO orderSelectSearchDTO = new OrderSelectSearchDTO(); + orderSelectSearchDTO.setTitle(title); + orderSelectSearchDTO.setStatus(status); + orderSelectSearchDTO.setAdminId(adminId); + orderSelectSearchDTO.setSearchMemberId(searchMemberId); + orderSelectSearchDTO.setStartDate(startDate); + orderSelectSearchDTO.setEndDate(endDate); + orderSelectSearchDTO.setProductName(productName); + orderSelectSearchDTO.setOrderId(orderId); + orderSelectSearchDTO.setMemberId(principal.getName()); + + if (sortField != null && sortOrder != null) { + Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); + } + + Page responseOrders = orderQueryService.selectSearchOrdersCenter(orderSelectSearchDTO, pageable); + + return ResponseEntity.ok(OrderResponseMessage.builder() + .httpStatus(200) + .msg("수주서 검색 조회 성공") + .result(responseOrders) + .build()); + } + @Operation(summary = "수주서 엑셀 다운로드") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "수주서 엑셀 다운로드 성공", diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectSearchDTO.java index 35f285c7..979d63e5 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderSelectSearchDTO.java @@ -20,4 +20,5 @@ public class OrderSelectSearchDTO { private String startDate; private String endDate; private String createdAt; + private String centerId; } diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java b/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java index bf2b6bcb..f09d6b48 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/repository/OrderMapper.java @@ -13,17 +13,24 @@ public interface OrderMapper { List findSearchOrderByMemberId(@Param("offset") int offset, @Param("pageSize") int pageSize, - @Param("orderSelectSearchDTO") OrderSelectSearchDTO orderSelectSearchDTO); + @Param("orderSelectSearchDTO") OrderSelectSearchDTO orderSelectSearchDTO, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); - int findOrderCountByMemberId(String memberId); + int findOrderCountByMemberId(@Param("memberId") String memberId); OrderSelectIdDTO findOrderByIdAndMemberId(String orderId, String memberId); - List findAllOrderByMemberId(int offset, int pageSize, String memberId); + List findAllOrderByMemberId(@Param("offset") int offset, + @Param("pageSize") int pageSize, + @Param("memberId") String memberId, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); int findOrderSearchCountByMemberId(OrderSelectSearchDTO orderSelectSearchDTO); - List findAllOrder(int offset, int pageSize); + List findAllOrder(@Param("offset") int offset, + @Param("pageSize") int pageSize); int findOrderCount(); @@ -40,4 +47,19 @@ List findSearchOrder(@Param("offset") int offset, List findOrderForExcel(); String selectByContractId(String orderId); + + List findAllOrderCenter(@Param("offset") int offset, + @Param("pageSize") int pageSize, + @Param("memberId") String memberId1, + @Param("centerId") String centerId); + + Integer findOrderCountCenter(); + + List findSearchOrderCenter(@Param("offset") int offset, + @Param("pageSize") int pageSize, + @Param("orderSelectSearchDTO") OrderSelectSearchDTO orderSelectSearchDTO, + @Param("sortField") String sortField, + @Param("sortOrder") String sortOrder); + + Integer findOrderSearchCountCenter(OrderSelectSearchDTO orderSelectSearchDTO); } diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java index 010008f5..8a0d332f 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryService.java @@ -14,7 +14,7 @@ public interface OrderQueryService { OrderSelectIdDTO selectDetailOrderEmployee(OrderSelectIdDTO orderSelectIdDTO); - Page selectSearchOrdersEmployee(OrderSelectSearchDTO orderSelectSearchDTO, Pageable pageable); + Page selectSearchOrdersEmployee(OrderSelectSearchDTO orderSelectSearchDTO, Pageable pageable) throws GeneralSecurityException; Page selectAll(Pageable pageable); @@ -25,4 +25,8 @@ public interface OrderQueryService { String selectByContractId(String orderId); void exportOrder(HttpServletResponse response); + + Page selectAllCenter(Pageable pageable, String memberId) throws GeneralSecurityException; + + Page selectSearchOrdersCenter(OrderSelectSearchDTO orderSelectSearchDTO, Pageable pageable) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java index 35cdfb9d..6eba05a1 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/service/OrderQueryServiceImpl.java @@ -52,21 +52,20 @@ public Page selectAllEmployee(String loginId, Pageable pageab int offset = Math.toIntExact(pageable.getOffset()); int pageSize = pageable.getPageSize(); - String cacheKey = "myCache::orders::offset=" + offset + "::size=" + pageSize; - - // 캐시 조회 - List orders = (List) redisTemplate.opsForValue().get(cacheKey); - - if (orders == null) { - orders = orderMapper.findAllOrderByMemberId(offset, pageSize, memberId); + // 정렬 정보 가져오기 + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } - // 결과가 null이거나 빈 리스트인지 확인 - if (orders == null || orders.isEmpty()) { - throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); - } + List orders = orderMapper.findAllOrderByMemberId(offset, pageSize, memberId, sortField, sortOrder); - // 캐시에 데이터 저장 (빈 리스트는 저장하지 않음) - redisTemplate.opsForValue().set(cacheKey, orders); + // 결과가 null이거나 빈 리스트인지 확인 + if (orders == null || orders.isEmpty()) { + throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); } // 전체 개수 조회 @@ -93,23 +92,58 @@ public OrderSelectIdDTO selectDetailOrderEmployee(OrderSelectIdDTO orderSelectId @Override @Transactional(readOnly = true) - public Page selectSearchOrdersEmployee(OrderSelectSearchDTO orderSelectSearchDTO, Pageable pageable) { + public Page selectSearchOrdersEmployee(OrderSelectSearchDTO orderSelectSearchDTO, Pageable pageable) throws GeneralSecurityException { String memberId = authQueryService.selectMemberIdByLoginId(orderSelectSearchDTO.getMemberId()); orderSelectSearchDTO.setMemberId(memberId); + if ("대기".equals(orderSelectSearchDTO.getStatus())) { + orderSelectSearchDTO.setStatus("WAIT"); + } + if ("승인".equals(orderSelectSearchDTO.getStatus())) { + orderSelectSearchDTO.setStatus("APPROVED"); + } + if ("취소".equals(orderSelectSearchDTO.getStatus())) { + orderSelectSearchDTO.setStatus("CANCEL"); + } + int offset = Math.toIntExact(pageable.getOffset()); int pageSize = pageable.getPageSize(); - List orders = orderMapper.findSearchOrderByMemberId(offset, pageSize, orderSelectSearchDTO); + // 정렬 정보 가져오기 + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } - if (orders == null || orders.isEmpty()) { + List orders = orderMapper.findSearchOrderByMemberId(offset, pageSize, orderSelectSearchDTO, sortField, sortOrder); + + if (orders == null) { throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); } - Integer count = orderMapper.findOrderSearchCountByMemberId(orderSelectSearchDTO); - int totalOrder = (count != null) ? count : 0; + for (OrderSelectSearchDTO order : orders) { + if (order.getMemberId() != null) { + String memberName = memberQueryService.selectNameById(order.getMemberId()); + order.setMemberName(memberName); + } - return new PageImpl<>(orders, pageable, totalOrder); + if (order.getAdminId() != null) { + String adminName = memberQueryService.selectNameById(order.getAdminId()); + order.setAdminName(adminName); + } else { + order.setAdminName("-"); + } + } + + int count = orderMapper.findOrderSearchCountByMemberId(orderSelectSearchDTO); + if (count == 0) { + throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); + } + + return new PageImpl<>(orders, pageable, count); } // 영업담당자, 영업관리자 조회 @@ -120,21 +154,12 @@ public Page selectAll(Pageable pageable) { int offset = Math.toIntExact(pageable.getOffset()); int pageSize = pageable.getPageSize(); - String cacheKey = "myCache::orders::offset=" + offset + "::size=" + pageSize; - // 캐시 조회 - List orders = (List) redisTemplate.opsForValue().get(cacheKey); - - if (orders == null) { - orders = orderMapper.findAllOrder(offset, pageSize); + List orders = orderMapper.findAllOrder(offset, pageSize); - // 결과가 null이거나 빈 리스트인지 확인 - if (orders == null || orders.isEmpty()) { - throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); - } - - // 캐시에 데이터 저장 (빈 리스트는 저장하지 않음) - redisTemplate.opsForValue().set(cacheKey, orders); + // 결과가 null이거나 빈 리스트인지 확인 + if (orders == null || orders.isEmpty()) { + throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); } // 전체 개수 조회 @@ -190,7 +215,6 @@ public Page selectSearchOrders(OrderSelectSearchDTO orderS } for (OrderSelectSearchDTO order : orders) { - log.info("dasdas: " + order.getProductName()); if (order.getMemberId() != null) { String memberName = memberQueryService.selectNameById(order.getMemberId()); order.setMemberName(memberName); @@ -230,4 +254,84 @@ public void exportOrder(HttpServletResponse response) { excelUtilsV1.download(OrderExcelDTO.class, orderExcels, "orderExcel", response); } + + @Override + public Page selectAllCenter(Pageable pageable, String memberId) throws GeneralSecurityException { + + String memberId1 = authQueryService.selectMemberIdByLoginId(memberId); + String centerId = memberQueryService.selectMemberInfo(memberId).getCenterId(); + + int offset = Math.toIntExact(pageable.getOffset()); + int pageSize = pageable.getPageSize(); + + // 캐시 조회 + List orders = orderMapper.findAllOrderCenter(offset, pageSize, memberId1, centerId); + + // 결과가 null이거나 빈 리스트인지 확인 + if (orders == null || orders.isEmpty()) { + throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); + } + + // 전체 개수 조회 + Integer count = orderMapper.findOrderCountCenter(); + int totalOrder = (count != null) ? count : 0; + + return new PageImpl<>(orders, pageable, totalOrder); + } + + @Override + public Page selectSearchOrdersCenter(OrderSelectSearchDTO orderSelectSearchDTO, Pageable pageable) throws GeneralSecurityException { + + String memberId = authQueryService.selectMemberIdByLoginId(orderSelectSearchDTO.getMemberId()); + String centerId = memberQueryService.selectMemberInfo(orderSelectSearchDTO.getMemberId()).getCenterId(); + orderSelectSearchDTO.setMemberId(memberId); + orderSelectSearchDTO.setCenterId(centerId); + + if ("대기".equals(orderSelectSearchDTO.getStatus())) { + orderSelectSearchDTO.setStatus("WAIT"); + } + if ("승인".equals(orderSelectSearchDTO.getStatus())) { + orderSelectSearchDTO.setStatus("APPROVED"); + } + if ("취소".equals(orderSelectSearchDTO.getStatus())) { + orderSelectSearchDTO.setStatus("CANCEL"); + } + + int offset = Math.toIntExact(pageable.getOffset()); + int pageSize = pageable.getPageSize(); + + // 정렬 정보 가져오기 + Sort sort = pageable.getSort(); + String sortField = null; + String sortOrder = null; + if (sort.isSorted()) { + sortField = sort.iterator().next().getProperty(); + sortOrder = sort.iterator().next().isAscending() ? "ASC" : "DESC"; + } + + List orders = orderMapper.findSearchOrderCenter(offset, pageSize, orderSelectSearchDTO, sortField, sortOrder); + + if (orders == null || orders.isEmpty()) { + throw new OrderCommonException(OrderErrorCode.ORDER_NOT_FOUND); + } + + for (OrderSelectSearchDTO order : orders) { + if (order.getMemberId() != null) { + String memberName = memberQueryService.selectNameById(order.getMemberId()); + order.setMemberName(memberName); + } + + if (order.getAdminId() != null) { + String adminName = memberQueryService.selectNameById(order.getAdminId()); + order.setAdminName(adminName); + } else { + order.setAdminName("-"); + } + } + + Integer count = orderMapper.findOrderSearchCountCenter(orderSelectSearchDTO); + int totalOrder = (count != null) ? count : 0; + + return new PageImpl<>(orders, pageable, totalOrder); + } } diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index 7e88d578..08131b07 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -68,15 +68,16 @@ + + - - - - + + + @@ -130,7 +131,8 @@ a.cent_id, a.cust_id, a.prod_id, - a.mem_id + a.mem_id, + a.conr_vehi_pric FROM tb_contract a WHERE a.conr_id = #{ contractId } AND a.mem_id = #{ memberId } @@ -170,7 +172,8 @@ a.cent_id, a.cust_id, a.prod_id, - a.mem_id + a.mem_id, + a.conr_vehi_pric FROM tb_contract a WHERE a.conr_id = #{ contractId } AND a.active = TRUE @@ -179,19 +182,19 @@ @@ -210,6 +213,7 @@ a.created_at FROM tb_contract a WHERE a.active = TRUE + AND a.conr_stat = 'APPROVED' @@ -251,50 +255,49 @@ LIMIT #{pageSize} OFFSET #{offset} - SELECT a.conr_id, a.conr_cust_name, a.conr_comp_name, + a.conr_car_name, a.conr_ttl, a.conr_stat, - a.created_url, a.conr_cust_pur_cond, - a.conr_car_name, - a.created_at, - a.updated_at, - a.deleted_at, - a.active + a.conr_cust_cla, + a.created_at FROM tb_contract a - a.mem_id = #{contractSearchDTO.memberId} - AND a.active = TRUE - + a.active = TRUE + + AND a.mem_id = #{contractSearchDTO.searchMemberId} + + AND a.cent_id = #{contractSearchDTO.centerId} - - AND a.conr_name LIKE CONCAT('%', #{contractSearchDTO.title}, '%') + + AND a.conr_ttl LIKE CONCAT('%', #{contractSearchDTO.title}, '%') - + AND a.conr_cust_name LIKE CONCAT('%', #{contractSearchDTO.customerName}, '%') - + AND a.conr_cust_cla LIKE CONCAT('%', #{contractSearchDTO.customerClassifcation}, '%') - + AND a.conr_stat = #{contractSearchDTO.status} - + AND a.conr_comp_name LIKE CONCAT('%', #{contractSearchDTO.companyName}, '%') - + AND a.conr_cust_pur_cond LIKE CONCAT('%', #{contractSearchDTO.customerPurchaseCondition}, '%') - + AND a.created_at BETWEEN #{contractSearchDTO.startAt} AND #{contractSearchDTO.endAt} - - AND b.prod_name LIKE CONCAT('%', #{contractSearchDTO.productId}, '%') + + AND a.conr_car_name LIKE CONCAT('%', #{contractSearchDTO.carName}, '%') @@ -424,12 +427,13 @@ LIMIT #{pageSize} OFFSET #{offset} - SELECT COUNT(*) AS cnt FROM tb_contract a WHERE a.mem_id = #{ memberId } AND a.active = TRUE + AND a.conr_stat = 'APPROVED' - + SELECT + COUNT(*) AS cnt FROM tb_contract a - a.mem_id = #{memberId} - AND a.active = TRUE - - AND a.cent_id = #{centerId} + a.active = TRUE + + AND a.mem_id = #{contractSearchDTO.searchMemberId} + + + AND a.cent_id = #{contractSearchDTO.centerId} - - AND a.conr_name LIKE CONCAT('%', #{title}, '%') + + AND a.conr_ttl LIKE CONCAT('%', #{contractSearchDTO.title}, '%') - - AND a.conr_cust_name LIKE CONCAT('%', #{customerName}, '%') + + AND a.conr_cust_name LIKE CONCAT('%', #{contractSearchDTO.customerName}, '%') - - AND a.conr_cust_cla LIKE CONCAT('%', #{customerClassifcation}, '%') + + AND a.conr_cust_cla LIKE CONCAT('%', #{contractSearchDTO.customerClassifcation}, '%') - - AND a.conr_stat = #{status} + + AND a.conr_stat = #{contractSearchDTO.status} - - AND a.conr_comp_name LIKE CONCAT('%', #{companyName}, '%') + + AND a.conr_comp_name LIKE CONCAT('%', #{contractSearchDTO.companyName}, '%') - - AND a.conr_cust_pur_cond LIKE CONCAT('%', #{customerPurchaseCondition}, '%') + + AND a.conr_cust_pur_cond LIKE CONCAT('%', #{contractSearchDTO.customerPurchaseCondition}, '%') + + + AND a.created_at >= #{contractSearchDTO.startAt} - - AND a.created_at BETWEEN #{startAt} AND #{endAt} + + AND a.created_at <= #{contractSearchDTO.endAt} - - AND b.prod_name LIKE CONCAT('%', #{productId}, '%') + + AND a.conr_car_name LIKE CONCAT('%', #{contractSearchDTO.carName}, '%') @@ -517,100 +527,32 @@ - SELECT - a.conr_id, - a.conr_cust_name, - a.conr_comp_name, - a.conr_ttl, - a.conr_stat, - a.created_url, - a.conr_cust_pur_cond, - a.conr_car_name, - a.created_at, - a.updated_at, - a.deleted_at, - a.active, - a.conr_cust_cla, - a.conr_cust_pur_cond - FROM tb_contract a - WHERE a.active = TRUE - AND a.cent_id = #{centerId} - - - - - - ORDER BY a.conr_cust_name ${sortOrder} - - - ORDER BY a.conr_ttl ${sortOrder} - - - ORDER BY a.conr_car_name ${sortOrder} - - - ORDER BY a.conr_stat ${sortOrder} - - - ORDER BY a.conr_comp_name ${sortOrder} - - - ORDER BY a.conr_cust_cla ${sortOrder} - - - ORDER BY a.conr_cust_pur_cond ${sortOrder} - - - ORDER BY a.conr_id ${sortOrder} - - - ORDER BY a.created_at DESC - - - - - - ORDER BY a.created_at DESC - - + a.conr_id, + a.conr_cust_name, + a.conr_comp_name, + a.conr_car_name, + a.conr_ttl, + a.conr_stat, + a.conr_cust_pur_cond, + a.conr_cust_cla, + a.prod_id, + a.created_at + FROM tb_contract a + WHERE a.active = TRUE + AND a.cent_id = #{centerId} + AND a.conr_stat = 'APPROVED' LIMIT #{pageSize} OFFSET #{offset} - + SELECT + COUNT(*) AS cnt FROM tb_contract a - - a.active = TRUE - AND a.cent_id = #{centerId} - - AND a.cent_id = #{centerId} - - - AND a.conr_name LIKE CONCAT('%', #{title}, '%') - - - AND a.conr_cust_name LIKE CONCAT('%', #{customerName}, '%') - - - AND a.conr_cust_cla LIKE CONCAT('%', #{customerClassifcation}, '%') - - - AND a.conr_stat = #{status} - - - AND a.conr_comp_name LIKE CONCAT('%', #{companyName}, '%') - - - AND a.conr_cust_pur_cond LIKE CONCAT('%', #{customerPurchaseCondition}, '%') - - - AND a.created_at BETWEEN #{startAt} AND #{endAt} - - - AND b.prod_name LIKE CONCAT('%', #{productId}, '%') - - + WHERE a.active = TRUE + AND a.cent_id = #{centerId} + AND a.conr_stat = 'APPROVED' - SELECT a.conr_id, a.conr_cust_name, a.conr_comp_name, + a.conr_car_name, a.conr_ttl, a.conr_stat, - a.created_url, a.conr_cust_pur_cond, - a.conr_car_name, - a.created_at, - a.updated_at, - a.deleted_at, - a.active + a.conr_cust_cla, + a.created_at FROM tb_contract a - a.cent_id = #{centerId} - AND a.active = TRUE - + a.active = TRUE + AND a.cent_id = #{centerId} + AND a.conr_stat = 'APPROVED' + + AND a.mem_id = #{contractSearchDTO.searchMemberId} + + AND a.cent_id = #{contractSearchDTO.centerId} - - AND a.conr_name LIKE CONCAT('%', #{contractSearchDTO.title}, '%') + + AND a.conr_ttl LIKE CONCAT('%', #{contractSearchDTO.title}, '%') - + AND a.conr_cust_name LIKE CONCAT('%', #{contractSearchDTO.customerName}, '%') - + AND a.conr_cust_cla LIKE CONCAT('%', #{contractSearchDTO.customerClassifcation}, '%') - + AND a.conr_stat = #{contractSearchDTO.status} - + AND a.conr_comp_name LIKE CONCAT('%', #{contractSearchDTO.companyName}, '%') - + AND a.conr_cust_pur_cond LIKE CONCAT('%', #{contractSearchDTO.customerPurchaseCondition}, '%') - + AND a.created_at BETWEEN #{contractSearchDTO.startAt} AND #{contractSearchDTO.endAt} - - AND b.prod_name LIKE CONCAT('%', #{contractSearchDTO.productId}, '%') + + AND a.conr_car_name LIKE CONCAT('%', #{contractSearchDTO.carName}, '%') - + ORDER BY a.conr_cust_name ${sortOrder} @@ -740,38 +684,41 @@ LIMIT #{pageSize} OFFSET #{offset} - SELECT COUNT(*) FROM tb_contract a a.cent_id = #{centerId} AND a.active = TRUE - - AND a.cent_id = #{centerId} + + AND a.mem_id = #{contractSearchDTO.searchMemberId} + + + AND a.cent_id = #{contractSearchDTO.centerId} - - AND a.conr_name LIKE CONCAT('%', #{title}, '%') + + AND a.conr_ttl LIKE CONCAT('%', #{contractSearchDTO.title}, '%') - - AND a.conr_cust_name LIKE CONCAT('%', #{customerName}, '%') + + AND a.conr_cust_name LIKE CONCAT('%', #{contractSearchDTO.customerName}, '%') - - AND a.conr_cust_cla LIKE CONCAT('%', #{customerClassifcation}, '%') + + AND a.conr_cust_cla LIKE CONCAT('%', #{contractSearchDTO.customerClassifcation}, '%') - - AND a.conr_stat = #{status} + + AND a.conr_stat = #{contractSearchDTO.status} - - AND a.conr_comp_name LIKE CONCAT('%', #{companyName}, '%') + + AND a.conr_comp_name LIKE CONCAT('%', #{contractSearchDTO.companyName}, '%') - - AND a.conr_cust_pur_cond LIKE CONCAT('%', #{customerPurchaseCondition}, '%') + + AND a.conr_cust_pur_cond LIKE CONCAT('%', #{contractSearchDTO.customerPurchaseCondition}, '%') - - AND a.created_at BETWEEN #{startAt} AND #{endAt} + + AND a.created_at BETWEEN #{contractSearchDTO.startAt} AND #{contractSearchDTO.endAt} - - AND b.prod_name LIKE CONCAT('%', #{productId}, '%') + + AND a.conr_car_name LIKE CONCAT('%', #{contractSearchDTO.carName}, '%') diff --git a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml index d4299374..a2c35c53 100644 --- a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml @@ -58,6 +58,7 @@ ON b.prod_id = e.prod_id WHERE a.mem_id = #{memberId} AND a.active = TRUE + AND a.ord_stat = 'APPROVED' ORDER BY a.created_at DESC LIMIT #{pageSize} OFFSET #{offset}; @@ -70,7 +71,7 @@ AND a.active = TRUE; - SELECT a.ord_id, a.ord_ttl, @@ -97,27 +98,24 @@ AND a.ord_id = #{orderId}; - SELECT a.ord_id, a.ord_ttl, a.ord_stat, + a.created_at, + a.mem_id, + a.admin_id, b.conr_ttl, - d.mem_name AS admin_name, - c.mem_name AS mem_name, - e.prod_name + c.prod_name FROM tb_order a LEFT JOIN tb_contract b ON a.conr_id = b.conr_id - LEFT JOIN tb_member c - ON a.mem_id = c.mem_id - LEFT JOIN tb_member d - ON a.admin_id = d.mem_id - LEFT JOIN tb_product e - ON b.prod_id = e.prod_id + LEFT JOIN tb_product c + ON b.prod_id = c.prod_id - a.mem_id = #{orderSelectSearchDTO.memberId} - AND a.active = TRUE + a.active = TRUE + AND a.mem_id = #{orderSelectSearchDTO.memberId} AND a.ord_ttl LIKE CONCAT('%', #{orderSelectSearchDTO.title}, '%') @@ -130,15 +128,52 @@ AND a.mem_id = #{searchMemberId} + + AND c.prod_name LIKE CONCAT('%', #{productName}, '%'); + AND a.created_at BETWEEN #{orderSelectSearchDTO.startDate} AND #{orderSelectSearchDTO.endDate} - ORDER BY a.created_at DESC + + + + + + ORDER BY a.ord_ttl ${sortOrder} + + + ORDER BY a.ord_stat ${sortOrder} + + + ORDER BY a.ord_id ${orderId} + + + ORDER BY a.mem_id ${memberId} + + + ORDER BY a.admin_id ${adminId} + + + ORDER BY a.created_at ${createdAt} + + + ORDER BY e.prod_name ${productName} + + + ORDER BY a.created_at DESC + + + + + + ORDER BY a.created_at DESC + + LIMIT #{pageSize} OFFSET #{offset}; - SELECT COUNT(*) AS cnt FROM tb_order a @@ -183,7 +218,8 @@ ON a.admin_id = d.mem_id LEFT JOIN tb_product e ON b.prod_id = e.prod_id - WHERE AND a.active = TRUE + WHERE a.active = TRUE + AND a.ord_stat = 'APPROVED' ORDER BY a.created_at DESC LIMIT #{pageSize} OFFSET #{offset}; @@ -279,7 +315,7 @@ ORDER BY a.created_at ${createdAt} - + ORDER BY e.prod_name ${productName} @@ -341,4 +377,138 @@ WHERE a.active = true + + + + + + + + \ No newline at end of file From 973df70497bf802285ab3d4ebf1ceafb671aec1d Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Sun, 8 Dec 2024 04:16:58 +0900 Subject: [PATCH 523/563] =?UTF-8?q?fix:=20orderMapper=20id=EA=B0=92=20?= =?UTF-8?q?=EC=88=98=EC=A0=95(#251)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/application/controller/OrderController.java | 5 +++-- .../domain/order/query/controller/OrderController.java | 2 +- .../domain/order/query/repository/OrderMapper.xml | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderController.java b/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderController.java index 9a0cd2e4..c13a85b5 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/order/command/application/controller/OrderController.java @@ -14,6 +14,7 @@ import stanl_2.final_backend.domain.order.command.application.service.OrderCommandService; import stanl_2.final_backend.domain.order.common.response.OrderResponseMessage; +import java.security.GeneralSecurityException; import java.security.Principal; @RestController("OrderCommandController") @@ -34,7 +35,7 @@ public OrderController(OrderCommandService orderCommandService) { }) @PostMapping("") public ResponseEntity postOrder(@RequestBody OrderRegistDTO orderRegistDTO, - Principal principal) { + Principal principal) throws GeneralSecurityException { orderRegistDTO.setMemberId(principal.getName()); orderCommandService.registerOrder(orderRegistDTO); @@ -54,7 +55,7 @@ public ResponseEntity postOrder(@RequestBody OrderRegistDT @PutMapping("{orderId}") public ResponseEntity putOrder(@PathVariable String orderId, @RequestBody OrderModifyDTO orderModifyDTO, - Principal principal) { + Principal principal) throws GeneralSecurityException { orderModifyDTO.setOrderId(orderId); orderModifyDTO.setMemberId(principal.getName()); diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java b/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java index 9780832c..41ab2b34 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java @@ -212,7 +212,7 @@ public ResponseEntity getSearchOrder(@RequestParam(require }) @GetMapping("center") public ResponseEntity getAllOrderCenter(@PageableDefault(size = 10)Pageable pageable, - Principal principal) { + Principal principal) throws GeneralSecurityException { String memberId = principal.getName(); Page responseOrders = orderQueryService.selectAllCenter(pageable, memberId); diff --git a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml index a2c35c53..1276f560 100644 --- a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml @@ -452,7 +452,7 @@ LIMIT #{pageSize} OFFSET #{offset}; - SELECT COUNT(*) AS cnt FROM tb_order a From f9ba7e3c24229e091aba88b5d8dd8f6be4aaddbc Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Sun, 8 Dec 2024 04:39:47 +0900 Subject: [PATCH 524/563] =?UTF-8?q?fix:=20=ED=8C=8C=EC=9D=BC=20=EC=97=85?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20=EA=B4=80=EB=A0=A8=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20#253?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/NoticeController.java | 19 +++++++++++++++++-- .../controller/ProblemController.java | 19 +++++++++++++++++-- .../aggregate/service/ProblemServiceImpl.java | 1 - .../controller/PromotionController.java | 19 +++++++++++++++++-- 4 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java index 8273e05b..f9f434a1 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -15,7 +16,9 @@ import stanl_2.final_backend.domain.notices.command.application.dto.NoticeModifyDTO; import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; import stanl_2.final_backend.domain.notices.command.application.service.NoticeCommandService; +import stanl_2.final_backend.domain.notices.command.domain.aggragate.entity.Notice; import stanl_2.final_backend.domain.notices.common.response.NoticeResponseMessage; +import stanl_2.final_backend.domain.promotion.command.domain.aggregate.entity.Promotion; import stanl_2.final_backend.domain.s3.command.domain.service.S3FileServiceImpl; import java.security.GeneralSecurityException; @@ -31,14 +34,17 @@ public class NoticeController { private final S3FileServiceImpl s3FileService; private final NoticeModifyDTO noticeModifyDTO; private final MemberQueryService memberQueryService; + private final ModelMapper modelMapper; @Autowired - public NoticeController(NoticeCommandService noticeCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService, NoticeModifyDTO noticeModifyDTO,MemberQueryService memberQueryService){ + public NoticeController(NoticeCommandService noticeCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService, NoticeModifyDTO noticeModifyDTO, + MemberQueryService memberQueryService, ModelMapper modelMapper){ this.noticeModifyDTO = noticeModifyDTO; this.noticeCommandService = noticeCommandService; this.authQueryService =authQueryService; this.s3FileService = s3FileService; this.memberQueryService = memberQueryService; + this.modelMapper = modelMapper; } @Operation(summary = "공지사항 작성") @@ -84,11 +90,20 @@ public ResponseEntity modifyNotice( noticeModifyDTO.setMemberId(memberId); noticeModifyDTO.setMemberLoginId(memberLoginId); noticeModifyDTO.setContent(noticeModifyDTO.getContent()); + Notice updateNotice = modelMapper.map(noticeModifyDTO, Notice.class); + System.out.println("1."+updateNotice.getFileUrl()); + if(noticeModifyDTO.getFileUrl()==null){ + System.out.println("테스트중"); + noticeModifyDTO.setFileUrl(updateNotice.getFileUrl()); + } if (file != null && !file.isEmpty()) { + System.out.println("1번"); noticeModifyDTO.setFileUrl(s3FileService.uploadOneFile(file)); }else if(file==null || file.isEmpty()) { - noticeModifyDTO.setFileUrl(null); + System.out.println("2번"); + noticeModifyDTO.setFileUrl(updateNotice.getFileUrl()); } else { + System.out.println("3번"); noticeModifyDTO.setFileUrl(s3FileService.uploadOneFile(file)); } noticeCommandService.modifyNotice(noticeId,noticeModifyDTO, principal); diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java b/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java index 7db71694..b4973ecc 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/application/controller/ProblemController.java @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -14,6 +15,7 @@ import stanl_2.final_backend.domain.problem.command.application.dto.ProblemModifyDTO; import stanl_2.final_backend.domain.problem.command.application.dto.ProblemRegistDTO; import stanl_2.final_backend.domain.problem.command.application.service.ProblemCommandService; +import stanl_2.final_backend.domain.problem.command.domain.aggregate.entity.Problem; import stanl_2.final_backend.domain.problem.common.response.ProblemResponseMessage; import stanl_2.final_backend.domain.s3.command.domain.service.S3FileServiceImpl; @@ -27,12 +29,15 @@ public class ProblemController { private final AuthQueryService authQueryService; private final S3FileServiceImpl s3FileService; private final MemberQueryService memberQueryService; + private final ModelMapper modelMapper; @Autowired - public ProblemController(ProblemCommandService problemCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService,MemberQueryService memberQueryService) { + public ProblemController(ProblemCommandService problemCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService, + MemberQueryService memberQueryService, ModelMapper modelMapper) { this.problemCommandService = problemCommandService; this.authQueryService = authQueryService; this.s3FileService = s3FileService; this.memberQueryService =memberQueryService; + this.modelMapper =modelMapper; } @Operation(summary = "문제사항 작성") @@ -77,11 +82,21 @@ public ResponseEntity modifyProblem(@PathVariable String problemModifyDTO.setMemberId(memberId); problemModifyDTO.setMemberLoginId(memberLoginId); problemModifyDTO.setContent(problemModifyDTO.getContent()); + problemModifyDTO.setFileUrl(problemModifyDTO.getFileUrl()); + Problem updateProblem = modelMapper.map(problemModifyDTO, Problem.class); + System.out.println("1."+updateProblem.getFileUrl()); + if(problemModifyDTO.getFileUrl()==null){ + System.out.println("테스트중"); + problemModifyDTO.setFileUrl(updateProblem.getFileUrl()); + } if (file != null && !file.isEmpty()) { + System.out.println("1번"); problemModifyDTO.setFileUrl(s3FileService.uploadOneFile(file)); }else if(file==null || file.isEmpty()) { - problemModifyDTO.setFileUrl(null); + System.out.println("2번"); + problemModifyDTO.setFileUrl(updateProblem.getFileUrl()); } else { + System.out.println("3번"); problemModifyDTO.setFileUrl(s3FileService.uploadOneFile(file)); } problemCommandService.modifyProblem(problemId,problemModifyDTO,principal); diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java index 10e79b41..8c42e79f 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceImpl.java @@ -93,7 +93,6 @@ public ProblemModifyDTO modifyProblem(String problemId, ProblemModifyDTO problem updateProblem.setActive(problem.getActive()); updateProblem.setCustomerId(problem.getCustomerId()); updateProblem.setProductId(problem.getProductId()); - problemRepository.save(updateProblem); ProblemModifyDTO problemModify = modelMapper.map(updateProblem,ProblemModifyDTO.class); diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java index 6868aa7c..b7e1a1ac 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java @@ -5,15 +5,18 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.member.query.service.MemberQueryService; +import stanl_2.final_backend.domain.problem.command.domain.aggregate.entity.Problem; import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionModifyDTO; import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionRegistDTO; import stanl_2.final_backend.domain.promotion.command.application.service.PromotionCommandService; +import stanl_2.final_backend.domain.promotion.command.domain.aggregate.entity.Promotion; import stanl_2.final_backend.domain.promotion.common.response.PromotionResponseMessage; import stanl_2.final_backend.domain.s3.command.domain.service.S3FileServiceImpl; @@ -28,12 +31,15 @@ public class PromotionController { private final S3FileServiceImpl s3FileService; private final MemberQueryService memberQueryService; + private final ModelMapper modelMapper; + @Autowired - public PromotionController(PromotionCommandService promotionCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService,MemberQueryService memberQueryService) { + public PromotionController(PromotionCommandService promotionCommandService, AuthQueryService authQueryService, S3FileServiceImpl s3FileService,MemberQueryService memberQueryService,ModelMapper modelMapper) { this.promotionCommandService = promotionCommandService; this.authQueryService =authQueryService; this.s3FileService = s3FileService; this.memberQueryService =memberQueryService; + this.modelMapper =modelMapper; } @Operation(summary = "프로모션 작성") @@ -78,11 +84,20 @@ public ResponseEntity modifyNotice( promotionModifyDTO.setMemberId(memberId); promotionModifyDTO.setMemberLoginId(memberLoginId); promotionModifyDTO.setContent(promotionModifyDTO.getContent()); + Promotion updatePromotion = modelMapper.map(promotionModifyDTO, Promotion.class); + System.out.println("1."+updatePromotion.getFileUrl()); + if(promotionModifyDTO.getFileUrl()==null){ + System.out.println("테스트중"); + promotionModifyDTO.setFileUrl(updatePromotion.getFileUrl()); + } if (file != null && !file.isEmpty()) { + System.out.println("1번"); promotionModifyDTO.setFileUrl(s3FileService.uploadOneFile(file)); }else if(file==null || file.isEmpty()) { - promotionModifyDTO.setFileUrl(null); + System.out.println("2번"); + promotionModifyDTO.setFileUrl(updatePromotion.getFileUrl()); } else { + System.out.println("3번"); promotionModifyDTO.setFileUrl(s3FileService.uploadOneFile(file)); } promotionCommandService.modifyPromotion(promotionId,promotionModifyDTO,principal); From 4108cc89ed84826d9fa942e75ed42c7bd9ae9c25 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Sun, 8 Dec 2024 09:59:58 +0900 Subject: [PATCH 525/563] =?UTF-8?q?fix:=20=EA=B6=8C=ED=95=9C=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/config/RequestMatcherConfig.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java index f83d7621..c3205bad 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java @@ -73,7 +73,7 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception // Contract API (Employee) .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee").hasAnyRole("contract-employee-get", "GOD", "EMPLOYEE") - .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee/search").hasAnyRole("contract-employee-search-get", "GOD", "EMPLOYEE") + .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee/search").hasAnyRole("contract-employee-search-get", "GOD", "EMPLOYEE", "ADMIN") .requestMatchers(HttpMethod.GET, "/api/v1/contract/employee/{contractId}").hasAnyRole("contract-employee-id-get", "GOD", "EMPLOYEE") .requestMatchers(HttpMethod.POST, "/api/v1/contract").hasAnyRole("contract-post", "GOD", "EMPLOYEE", "ADMIN") .requestMatchers(HttpMethod.PUT, "/api/v1/contract/{contractId}").hasAnyRole("contract-id-put", "GOD", "EMPLOYEE", "ADMIN") @@ -85,7 +85,7 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception .requestMatchers(HttpMethod.GET, "/api/v1/contract/center/{contractId}").hasAnyRole("contract-center-id-get", "GOD", "ADMIN") // Contract API (General) - .requestMatchers(HttpMethod.GET, "/api/v1/contract").hasAnyRole("contract-get", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.GET, "/api/v1/contract").hasAnyRole("contract-get", "GOD", "DIRECTOR", "ADMIN", "EMPLOYEE") .requestMatchers(HttpMethod.GET, "/api/v1/contract/search").hasAnyRole("contract-search-get", "GOD", "DIRECTOR") .requestMatchers(HttpMethod.GET, "/api/v1/contract/{contractId}").hasAnyRole("contract-id-get", "GOD", "DIRECTOR", "ADMIN", "EMPLOYEE") .requestMatchers(HttpMethod.PUT, "/api/v1/contract/status/{contractId}").hasAnyRole("contract-status-id-put", "GOD", "ADMIN") @@ -94,10 +94,12 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception // Order API (Employee) .requestMatchers(HttpMethod.GET, "/api/v1/order/employee").hasAnyRole("order-employee-get", "GOD", "EMPLOYEE") - .requestMatchers(HttpMethod.GET, "/api/v1/order/employee/search").hasAnyRole("order-employee-search-get", "GOD", "EMPLOYEE") + .requestMatchers(HttpMethod.GET, "/api/v1/order/employee/search").hasAnyRole("order-employee-search-get", "GOD", "EMPLOYEE", "ADMIN") .requestMatchers(HttpMethod.GET, "/api/v1/order/employee/{orderId}").hasAnyRole("order-employee-id-get", "GOD", "EMPLOYEE") // Order API (General) + .requestMatchers(HttpMethod.GET, "/api/v1/order/center").hasAnyRole("order-center", "GOD", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/order/center/search").hasAnyRole("order-center-search", "GOD", "ADMIN") .requestMatchers(HttpMethod.POST, "/api/v1/order").hasAnyRole("order-post", "GOD", "EMPLOYEE", "ADMIN") .requestMatchers(HttpMethod.PUT, "/api/v1/order/{orderId}").hasAnyRole("order-id-put", "GOD", "EMPLOYEE", "ADMIN") .requestMatchers(HttpMethod.DELETE, "/api/v1/order/{orderId}").hasAnyRole("order-id-delete", "GOD", "EMPLOYEE", "ADMIN") From eb07244235aebfa2d8a9fea0ddb9b23032d49d73 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Sun, 8 Dec 2024 16:44:27 +0900 Subject: [PATCH 526/563] =?UTF-8?q?feat:=20=EB=8C=80=EC=8B=9C=EB=B3=B4?= =?UTF-8?q?=EB=93=9C=20Admin=20=EA=B6=8C=ED=95=9C=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=A4=91=20(#239)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/config/MybatisConfiguration.java | 2 +- .../query/controller/DashBoardController.java | 15 +-- .../query/dto/DashBoardAdminDTO.java | 6 + .../query/service/DashBoardQueryService.java | 6 +- .../service/DashBoardQueryServiceImpl.java | 108 ++++++++++++++---- .../security/config/RequestMatcherConfig.java | 2 + .../query/respository/DashBoardMapper.xml | 35 ------ 7 files changed, 106 insertions(+), 68 deletions(-) delete mode 100644 src/main/resources/stanl_2/final_backend/domain/dashBoard/query/respository/DashBoardMapper.xml diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/config/MybatisConfiguration.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/config/MybatisConfiguration.java index c294c922..9ca29118 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/config/MybatisConfiguration.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/config/MybatisConfiguration.java @@ -3,7 +3,7 @@ import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; -@Configuration("customerMybatisConfiguration") +@Configuration("dashBoardMybatisConfiguration") @MapperScan(basePackages = "stanl_2.final_backend.domain.dashBoard.query.repository") public class MybatisConfiguration { } diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/controller/DashBoardController.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/controller/DashBoardController.java index 664ad74b..161c75e3 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/controller/DashBoardController.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/controller/DashBoardController.java @@ -16,6 +16,7 @@ import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardAdminDTO; import stanl_2.final_backend.domain.dashBoard.query.service.DashBoardQueryService; +import java.security.GeneralSecurityException; import java.security.Principal; @Slf4j @@ -35,17 +36,17 @@ public DashBoardController(DashBoardQueryService dashBoardQueryService) { @ApiResponse(responseCode = "200", description = "대시보드 조회 성공(사원)", content = {@Content(schema = @Schema(implementation = CustomerResponseMessage.class))}) }) - @GetMapping("") - public ResponseEntity selectDashBoardForEmployee(Principal principal){ + @GetMapping("/employee") + public ResponseEntity selectDashBoardForEmployee(Principal principal) throws GeneralSecurityException { String memberLoginId = principal.getName(); - DashBoardAdminDTO boardResponseDTO = dashBoardQueryService.selectInfoForEmployee(memberLoginId); +// DashBoardAdminDTO boardResponseDTO = dashBoardQueryService.selectInfoForEmployee(memberLoginId); return ResponseEntity.ok(DashBoardResponseMessage.builder() .httpStatus(200) .msg("성공") - .result(boardResponseDTO) +// .result(boardResponseDTO) .build()); } @@ -54,8 +55,8 @@ public ResponseEntity selectDashBoardForEmployee(Princ @ApiResponse(responseCode = "200", description = "대시보드 조회 성공(관리자)", content = {@Content(schema = @Schema(implementation = CustomerResponseMessage.class))}) }) - @GetMapping("") - public ResponseEntity selectDashBoardForAdmin(Principal principal){ + @GetMapping("/admin") + public ResponseEntity selectDashBoardForAdmin(Principal principal) throws GeneralSecurityException { String memberLoginId = principal.getName(); @@ -78,7 +79,7 @@ public ResponseEntity selectDashBoardForDirector(Princ String memberLoginId = principal.getName(); - DashBoardAdminDTO boardResponseDTO = dashBoardQueryService.selectInfoForEmployeeAndAdmin(memberLoginId); +// DashBoardAdminDTO boardResponseDTO = dashBoardQueryService.selectInfoForEmployeeAndAdmin(memberLoginId); return ResponseEntity.ok(DashBoardResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardAdminDTO.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardAdminDTO.java index 1b5070a6..325cc021 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardAdminDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardAdminDTO.java @@ -5,6 +5,9 @@ import lombok.NoArgsConstructor; import lombok.Setter; +import java.util.List; +import java.util.Map; + @NoArgsConstructor @AllArgsConstructor @Getter @@ -18,6 +21,9 @@ public class DashBoardAdminDTO { private String noticeTitle; private Integer totalPrice; + private List> noticeList; + + private List memberList; private String memberLoginId; private String memberId; } diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryService.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryService.java index 70a34c74..ae96a15f 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryService.java @@ -2,8 +2,10 @@ import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardAdminDTO; +import java.security.GeneralSecurityException; + public interface DashBoardQueryService { - DashBoardAdminDTO selectInfoForEmployee(String memberLoginId); +// DashBoardAdminDTO selectInfoForEmployee(String memberLoginId) throws GeneralSecurityException; - DashBoardAdminDTO selectInfoForAdmin(String memberLoginId); + DashBoardAdminDTO selectInfoForAdmin(String memberLoginId) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java index 9d0ee5fc..c4a69be4 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java @@ -1,31 +1,39 @@ package stanl_2.final_backend.domain.dashBoard.query.service; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Qualifier; 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 stanl_2.final_backend.domain.contract.query.dto.ContractSearchDTO; import stanl_2.final_backend.domain.contract.query.service.ContractQueryService; import stanl_2.final_backend.domain.customer.query.service.CustomerQueryService; -import stanl_2.final_backend.domain.dashBoard.common.exception.DashBoardCommonException; -import stanl_2.final_backend.domain.dashBoard.common.exception.DashBoardErrorCode; import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardAdminDTO; import stanl_2.final_backend.domain.dashBoard.query.repository.DashBoardMapper; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; +import stanl_2.final_backend.domain.notices.query.dto.NoticeDTO; +import stanl_2.final_backend.domain.notices.query.dto.SearchDTO; import stanl_2.final_backend.domain.notices.query.service.NoticeService; import stanl_2.final_backend.domain.order.query.dto.OrderSelectSearchDTO; import stanl_2.final_backend.domain.order.query.service.OrderQueryService; +import stanl_2.final_backend.domain.purchase_order.query.dto.PurchaseOrderSelectSearchDTO; import stanl_2.final_backend.domain.purchase_order.query.service.PurchaseOrderQueryService; +import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistoryRankedDataDTO; import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySearchDTO; import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectDTO; import stanl_2.final_backend.domain.sales_history.query.service.SalesHistoryQueryService; +import java.security.GeneralSecurityException; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; @Slf4j @Service("queryDashBoardService") @@ -39,6 +47,8 @@ public class DashBoardQueryServiceImpl implements DashBoardQueryService { private final SalesHistoryQueryService salesHistoryQueryService; private final CustomerQueryService customerQueryService; private final NoticeService noticeService; + private final MemberQueryService memberQueryService; + private String getCurrentTime() { ZonedDateTime nowKst = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); @@ -47,7 +57,8 @@ private String getCurrentTime() { public DashBoardQueryServiceImpl(DashBoardMapper dashBoardMapper, AuthQueryService authQueryService, ContractQueryService contractQueryService, OrderQueryService orderQueryService, PurchaseOrderQueryService purchaseOrderQueryService, SalesHistoryQueryService salesHistoryQueryService, - CustomerQueryService customerQueryService, NoticeService noticeService) { + CustomerQueryService customerQueryService, NoticeService noticeService, + MemberQueryService memberQueryService) { this.dashBoardMapper = dashBoardMapper; this.authQueryService = authQueryService; this.contractQueryService = contractQueryService; @@ -56,38 +67,57 @@ public DashBoardQueryServiceImpl(DashBoardMapper dashBoardMapper, AuthQueryServi this.salesHistoryQueryService = salesHistoryQueryService; this.customerQueryService = customerQueryService; this.noticeService = noticeService; + this.memberQueryService = memberQueryService; } @Override @Transactional(readOnly = true) - public DashBoardAdminDTO selectInfoForEmployee(String memberLoginId) { + public DashBoardAdminDTO selectInfoForAdmin(String memberLoginId) throws GeneralSecurityException { DashBoardAdminDTO dashBoardAdminDTO = new DashBoardAdminDTO(); + String memberId = authQueryService.selectMemberIdByLoginId(memberLoginId); + String centerId = memberQueryService.selectMemberInfo(memberLoginId).getCenterId(); + System.out.println("centerId = " + centerId); + + // Pageable을 null로 전달하거나 유효한 Pageable 사용 - Pageable pageable = Pageable.unpaged(); + Pageable pageable = PageRequest.of(0, 100); // 이번달 조회를 위한 날짜 지정 String startAt = getCurrentTime().substring(0,7) + "-01"; - String endAt = getCurrentTime().substring(0,9); - + String endAt = getCurrentTime().substring(0,10); // 이번달 Contract 받아오기 ContractSearchDTO contractSearchDTO = new ContractSearchDTO(); - contractSearchDTO.setMemberId(memberId); + contractSearchDTO.setMemberId(memberLoginId); + contractSearchDTO.setSearchMemberId(memberId); contractSearchDTO.setStartAt(startAt); contractSearchDTO.setEndAt(endAt); - Integer unreadContract = contractQueryService.selectBySearchEmployee(contractSearchDTO, pageable).getNumberOfElements(); + contractSearchDTO.setActive(true); + Integer unreadContract = Math.toIntExact(contractQueryService.selectBySearchEmployee(contractSearchDTO, pageable).getTotalElements()); dashBoardAdminDTO.setUnreadContract(unreadContract); - + System.out.println("unreadContract" + unreadContract); // 이번달 Order 받아오기 OrderSelectSearchDTO orderSelectSearchDTO = new OrderSelectSearchDTO(); - orderSelectSearchDTO.setMemberId(memberId); + orderSelectSearchDTO.setMemberId(memberLoginId); orderSelectSearchDTO.setStartDate(startAt); orderSelectSearchDTO.setEndDate(endAt); - Integer unreadOrder = orderQueryService.selectSearchOrdersEmployee(orderSelectSearchDTO, pageable).getNumberOfElements(); + Integer unreadOrder = Math.toIntExact(orderQueryService.selectSearchOrdersEmployee(orderSelectSearchDTO, pageable).getTotalElements()); dashBoardAdminDTO.setUnreadOrder(unreadOrder); + System.out.println("unreadOrder" + unreadOrder); + + // 이번달 PurchaseOrder 받아오기 + PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO = new PurchaseOrderSelectSearchDTO(); + purchaseOrderSelectSearchDTO.setMemberId(memberLoginId); + purchaseOrderSelectSearchDTO.setSearchMemberId(memberId); + purchaseOrderSelectSearchDTO.setStartDate(startAt); + purchaseOrderSelectSearchDTO.setEndDate(endAt); + log.info("=====================2"); + Integer unreadPurchaseOrder = Math.toIntExact(purchaseOrderQueryService.selectSearchPurchaseOrderAdmin(purchaseOrderSelectSearchDTO, pageable).getTotalElements()); + log.info("=====================2"); + dashBoardAdminDTO.setUnreadPurchaseOrder(unreadPurchaseOrder); // 이번달 판매내역 받아오기 ArrayList memberList = new ArrayList(); @@ -97,28 +127,60 @@ public DashBoardAdminDTO selectInfoForEmployee(String memberLoginId) { salesHistorySearchDTO.setMemberList(memberList); salesHistorySearchDTO.setStartDate(startAt); salesHistorySearchDTO.setEndDate(endAt); + log.info("=====================3"); Page resultPage = salesHistoryQueryService.selectSalesHistorySearchByEmployee(salesHistorySearchDTO, pageable); Integer totalPrice = resultPage.getContent().isEmpty() ? null : resultPage.getContent().get(0).getSalesHistoryTotalSales(); dashBoardAdminDTO.setTotalPrice(totalPrice); // 이번달 내 고객 순위 조회 - // 이번달 판매사원 순위 + ArrayList employeeList = new ArrayList(); + ArrayList centerList = new ArrayList(); + centerList.add(centerId); + + SalesHistoryRankedDataDTO salesHistoryRankedDataDTO = new SalesHistoryRankedDataDTO(); + salesHistoryRankedDataDTO.setCenterList(centerList); + salesHistoryRankedDataDTO.setPeriod("month"); + salesHistoryRankedDataDTO.setStartDate(startAt); + salesHistoryRankedDataDTO.setEndDate(endAt); + salesHistoryRankedDataDTO.setGroupBy("employee"); + salesHistoryRankedDataDTO.setOrderBy("totalSales"); + log.info("=====================4"); + Page rankPage = salesHistoryQueryService.selectStatisticsBySearch(salesHistoryRankedDataDTO, pageable); + + List contentList = rankPage.getContent(); + for(SalesHistoryRankedDataDTO dto: contentList){ + if (dto.getMemberList() != null) { + // 0번부터 4번 인덱스까지의 memberName을 추출 + for (int i = 0; i < dto.getMemberList().size() && i < 5; i++) { + employeeList.add(memberQueryService.selectNameById(dto.getMemberList().get(i))); + } + } + } + dashBoardAdminDTO.setMemberList(employeeList); + // 공지사항 조회 (제목, 내용(redirect)) + ArrayList> noticeList = new ArrayList<>(); - // 공지사항 - - DashBoardAdminDTO responseDashBoardAdminDTO = dashBoardMapper.findDashBoardInfoByMemberId(memberId); + SearchDTO searchDTO = new SearchDTO(); + searchDTO.setTag("ADMIN"); + Page noticePage = noticeService.findNotices(pageable, searchDTO); - if(responseDashBoardAdminDTO == null){ - throw new DashBoardCommonException(DashBoardErrorCode.DATA_NOT_FOUND); + for (NoticeDTO notice : noticePage.getContent()) { + Map noticeData = new HashMap<>(); + noticeData.put("title", notice.getTitle()); // NoticeDTO의 title + noticeData.put("content", notice.getContent()); // NoticeDTO의 content + noticeList.add(noticeData); } - return responseDashBoardAdminDTO; - } + dashBoardAdminDTO.setNoticeList(noticeList); + log.info("=====================5"); - @Override - public DashBoardAdminDTO selectInfoForAdmin(String memberLoginId) { - return null; + return dashBoardAdminDTO; } + +// @Override +// public DashBoardAdminDTO selectInfoForAdmin(String memberLoginId) { +// return null; +// } } diff --git a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java index f83d7621..4dad106a 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java @@ -219,6 +219,8 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search/month").hasAnyRole("salesHistory-employee-statistics-search-month-get", "GOD", "EMPLOYEE") .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/employee/search").hasAnyRole("salesHistory-employee-search-post", "GOD", "EMPLOYEE") + // DashBoard API + .requestMatchers(HttpMethod.GET, "/api/v1/dashBoard/admin").hasAnyRole("dashboard-get", "GOD", "DIRECTOR", "ADMIN") // 그 외 나머지 시스템 관리자만 접근 가능 .anyRequest().hasRole("GOD") diff --git a/src/main/resources/stanl_2/final_backend/domain/dashBoard/query/respository/DashBoardMapper.xml b/src/main/resources/stanl_2/final_backend/domain/dashBoard/query/respository/DashBoardMapper.xml deleted file mode 100644 index a5c2a666..00000000 --- a/src/main/resources/stanl_2/final_backend/domain/dashBoard/query/respository/DashBoardMapper.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From ea1637636c40ec47b0c76f2023b3164a3fdcef0d Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 9 Dec 2024 00:37:03 +0900 Subject: [PATCH 527/563] =?UTF-8?q?fix:=20=EA=B3=A0=EA=B0=9D=20=EB=8B=B4?= =?UTF-8?q?=EB=8B=B9=EC=9E=90=20=EC=A0=95=EB=A0=AC=20=EC=88=98=EC=A0=95(#2?= =?UTF-8?q?55)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customer/query/controller/CustomerController.java | 3 ++- .../domain/customer/query/dto/CustomerSearchDTO.java | 7 ------- .../customer/query/service/CustomerQueryServiceImpl.java | 1 + .../member/query/service/MemberQueryServiceImpl.java | 1 - .../domain/customer/query/repository/CustomerMapper.xml | 9 +++++++++ .../domain/member/query/repository/MemberMapper.xml | 2 +- 6 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java b/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java index 7b6cf63f..9e51bb24 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/controller/CustomerController.java @@ -92,6 +92,7 @@ public ResponseEntity searchCustomer( @RequestParam(required = false) String name, @RequestParam(required = false) String sex, @RequestParam(required = false) String phone, + @RequestParam(required = false) String memberId, @RequestParam(required = false) String sortField, @RequestParam(required = false) String sortOrder, @PageableDefault(size = 10) Pageable pageable @@ -103,7 +104,7 @@ public ResponseEntity searchCustomer( pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortField)); } - CustomerSearchDTO customerSearchDTO = new CustomerSearchDTO(customerId , name, sex, phone); + CustomerSearchDTO customerSearchDTO = new CustomerSearchDTO(customerId , name, sex, phone, memberId); Page customerDTOPage = customerQueryService.findCustomerByCondition(pageable, customerSearchDTO); return ResponseEntity.ok(CustomerResponseMessage.builder() diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/dto/CustomerSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/customer/query/dto/CustomerSearchDTO.java index 66307cd7..4ca3e2b6 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/dto/CustomerSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/dto/CustomerSearchDTO.java @@ -15,11 +15,4 @@ public class CustomerSearchDTO { private String sex; private String phone; private String memberId; - - public CustomerSearchDTO(String customerId, String name, String sex, String phone) { - this.customerId = customerId; - this.name = name; - this.sex = sex; - this.phone = phone; - } } diff --git a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java index eb89c548..bf97f3a7 100644 --- a/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/customer/query/service/CustomerQueryServiceImpl.java @@ -99,6 +99,7 @@ public Page findCustomerByCondition(Pageable pageable, Custom params.put("name", customerSearchDTO.getName()); params.put("sex", customerSearchDTO.getSex()); params.put("phone", aesUtils.encrypt(customerSearchDTO.getPhone())); + params.put("memberId", customerSearchDTO.getMemberId()); params.put("sortField", sortField); params.put("sortOrder", sortOrder); diff --git a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java index d6424afb..26428b12 100644 --- a/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/member/query/service/MemberQueryServiceImpl.java @@ -210,7 +210,6 @@ public Page selectMemberBySearch(Pageable pageable, MemberSearc params.put("sortOrder", sortOrder); // 암호화 시켜서 검색 - memberSearchDTO.setLoginId(aesUtils.encrypt(memberSearchDTO.getLoginId())); memberSearchDTO.setMemberName(aesUtils.encrypt(memberSearchDTO.getMemberName())); memberSearchDTO.setPhone(aesUtils.encrypt(memberSearchDTO.getPhone())); memberSearchDTO.setEmail(aesUtils.encrypt(memberSearchDTO.getEmail())); diff --git a/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml b/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml index f5635475..40de28d4 100644 --- a/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/customer/query/repository/CustomerMapper.xml @@ -113,6 +113,9 @@ AND a.cust_id = #{ customerId } + + AND a.mem_id = #{ memberId } + @@ -130,6 +133,9 @@ ORDER BY a.cust_id ${ sortOrder } + + ORDER BY a.mem_id ${ sortOrder } + ORDER BY a.created_at DESC @@ -162,6 +168,9 @@ AND a.cust_id= #{ customerId } + + AND a.mem_id= #{ memberId } + diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml index da914a7e..9de39701 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml @@ -225,7 +225,7 @@ ORDER BY b.mem_ema ${ sortOrder } - ORDER BY b.cent_name ${ sortOrder } + ORDER BY a.cent_name ${ sortOrder } ORDER BY c.org_cha_name ${ sortOrder } From e1461c73a470bd42b5230328103f7d00cf5237ec Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Mon, 9 Dec 2024 01:06:02 +0900 Subject: [PATCH 528/563] =?UTF-8?q?refactor:=20=EA=B3=84=EC=95=BD=EC=84=9C?= =?UTF-8?q?,=20=EB=B0=9C=EC=A3=BC=EC=84=9C,=20=EC=88=98=EC=A3=BC=EC=84=9C?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20(#251)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ContractController.java | 9 +- .../service/ContractCommandServiceImpl.java | 13 ++- .../query/controller/ContractController.java | 24 ++-- .../contract/query/dto/ContractExcelDTO.java | 3 - .../contract/query/dto/ContractSearchDTO.java | 4 +- .../query/dto/ContractSelectAllDTO.java | 4 +- .../query/repository/PurchaseOrderMapper.java | 4 +- .../PurchaseOrderQueryServiceImpl.java | 14 +++ .../query/repository/ContractMapper.xml | 35 ++---- .../order/query/repository/OrderMapper.xml | 103 +++++++++++------- .../query/repository/PurchaseOrderMapper.xml | 29 +++-- 11 files changed, 140 insertions(+), 102 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java index ab44712e..777feea9 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java @@ -56,12 +56,11 @@ public ResponseEntity postContract(@RequestBody Contrac }) @PutMapping("{contractId}") public ResponseEntity putContract(@PathVariable String contractId, - @RequestBody ContractModifyDTO contractModifyRequestDTO, - Principal principal) throws GeneralSecurityException { + Principal principal, + @RequestBody ContractModifyDTO contractModifyRequestDTO) throws GeneralSecurityException { contractModifyRequestDTO.setContractId(contractId); contractModifyRequestDTO.setMemberId(principal.getName()); - contractCommandService.modifyContract(contractModifyRequestDTO); return ResponseEntity.ok(ContractResponseMessage.builder() @@ -77,12 +76,10 @@ public ResponseEntity putContract(@PathVariable String content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))}) }) @DeleteMapping("{contractId}") - public ResponseEntity deleteContract(@PathVariable String contractId, - Principal principal) { + public ResponseEntity deleteContract(@PathVariable String contractId) { ContractDeleteDTO contractDeleteDTO = new ContractDeleteDTO(); contractDeleteDTO.setContractId(contractId); - contractDeleteDTO.setMemberId(principal.getName()); contractCommandService.deleteContract(contractDeleteDTO); return ResponseEntity.ok(ContractResponseMessage.builder() diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index c138128c..ad5f63d4 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -213,8 +213,10 @@ public void modifyContract(ContractModifyDTO contractModifyRequestDTO) throws Ge // 계약 조회 - Contract contract = (Contract)contractRepository.findByContractIdAndMemberId(contractModifyRequestDTO.getContractId(), memberId) - .orElseThrow(() -> new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND)); + Contract contract = (Contract)contractRepository.findByContractId(contractModifyRequestDTO.getContractId()); + if (contract == null) { + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); + } // 계약 생성 @@ -267,10 +269,11 @@ public void modifyContract(ContractModifyDTO contractModifyRequestDTO) throws Ge @Transactional public void deleteContract(ContractDeleteDTO contractDeleteDTO) { - String memberId = authQueryService.selectMemberIdByLoginId(contractDeleteDTO.getMemberId()); + Contract contract = (Contract) contractRepository.findByContractId(contractDeleteDTO.getContractId()); - Contract contract = (Contract) contractRepository.findByContractIdAndMemberId(contractDeleteDTO.getContractId(), memberId) - .orElseThrow(() -> new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND)); + if (contract == null) { + throw new ContractCommonException(ContractErrorCode.CONTRACT_NOT_FOUND); + } contract.setActive(false); contract.setDeletedAt(getCurrentTime()); diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java index a39ba5aa..c705520f 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java @@ -88,8 +88,8 @@ public ResponseEntity getContractBySearchEmployee(Princ @RequestParam(required = false) String searchMemberId, @RequestParam(required = false) String centerId, @RequestParam(required = false) String title, - @RequestParam(required = false) String startAt, - @RequestParam(required = false) String endAt, + @RequestParam(required = false) String startDate, + @RequestParam(required = false) String endDate, @RequestParam(required = false) String customerName, @RequestParam(required = false) String customerClassifcation, @RequestParam(required = false) String carName, @@ -105,8 +105,8 @@ public ResponseEntity getContractBySearchEmployee(Princ contractSearchDTO.setSearchMemberId(searchMemberId); contractSearchDTO.setCenterId(centerId); contractSearchDTO.setTitle(title); - contractSearchDTO.setStartAt(startAt); - contractSearchDTO.setEndAt(endAt); + contractSearchDTO.setStartDate(startDate); + contractSearchDTO.setEndDate(endDate); contractSearchDTO.setCustomerName(customerName); contractSearchDTO.setCustomerClassifcation(customerClassifcation); contractSearchDTO.setCarName(carName); @@ -183,8 +183,8 @@ public ResponseEntity getContractBySearchAdmin(Principa @RequestParam(required = false) String searchMemberId, @RequestParam(required = false) String centerId, @RequestParam(required = false) String title, - @RequestParam(required = false) String startAt, - @RequestParam(required = false) String endAt, + @RequestParam(required = false) String startDate, + @RequestParam(required = false) String endDate, @RequestParam(required = false) String customerName, @RequestParam(required = false) String customerClassifcation, @RequestParam(required = false) String carName, @@ -200,8 +200,8 @@ public ResponseEntity getContractBySearchAdmin(Principa contractSearchDTO.setSearchMemberId(searchMemberId); contractSearchDTO.setCenterId(centerId); contractSearchDTO.setTitle(title); - contractSearchDTO.setStartAt(startAt); - contractSearchDTO.setEndAt(endAt); + contractSearchDTO.setStartDate(startDate); + contractSearchDTO.setEndDate(endDate); contractSearchDTO.setCustomerName(customerName); contractSearchDTO.setCustomerClassifcation(customerClassifcation); contractSearchDTO.setCarName(carName); @@ -281,8 +281,8 @@ public ResponseEntity getDetailContract(@PathVariable S public ResponseEntity getContractBySearch(@RequestParam(required = false) String searchMemberId, @RequestParam(required = false) String centerId, @RequestParam(required = false) String title, - @RequestParam(required = false) String startAt, - @RequestParam(required = false) String endAt, + @RequestParam(required = false) String startDate, + @RequestParam(required = false) String endDate, @RequestParam(required = false) String customerName, @RequestParam(required = false) String customerClassifcation, @RequestParam(required = false) String carName, @@ -303,8 +303,8 @@ public ResponseEntity getContractBySearch(@RequestParam contractSearchDTO.setSearchMemberId(searchMemberId); contractSearchDTO.setCenterId(centerId); contractSearchDTO.setTitle(title); - contractSearchDTO.setStartAt(startAt); - contractSearchDTO.setEndAt(endAt); + contractSearchDTO.setStartDate(startDate); + contractSearchDTO.setEndDate(endDate); contractSearchDTO.setCustomerName(customerName); contractSearchDTO.setCustomerClassifcation(customerClassifcation); contractSearchDTO.setCarName(carName); diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractExcelDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractExcelDTO.java index 693c9d83..0e8b8b9a 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractExcelDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractExcelDTO.java @@ -19,9 +19,6 @@ public class ContractExcelDTO { @ExcelColumnName(name = "고객명") private String customerName; - @ExcelColumnName(name = "고객 상호") - private String companyName; - @ExcelColumnName(name = "승인 상태") private String status; diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java index 0f441a25..929d90a5 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java @@ -39,7 +39,7 @@ public class ContractSearchDTO { private String searchMemberId; private String centerId; private String customerId; - private String startAt; - private String endAt; + private String startDate; + private String endDate; private String carName; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java index d0c5452c..03e2899c 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java @@ -41,6 +41,6 @@ public class ContractSelectAllDTO { private String updatedAt; private String deletedAt; private String searchMemberId; - private String startAt; - private String endAt; + private String startDate; + private String endDate; } diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java index 0356bb02..af82a582 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.java @@ -29,11 +29,11 @@ List findSearchPurchaseOrder(@Param("offset") int List findSearchPurchaseOrderMemberId(@Param("offset") int offset, @Param("pageSize") int pageSize, - @Param("PurchaseOrderSelectSearchDTO") PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO, + @Param("purchaseOrderSelectSearchDTO") PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO, @Param("sortField") String sortField, @Param("sortOrder") String sortOrder); - int findSearchPurchaseOrderCountMemberId(@Param("PurchaseOrderSelectSearchDTO") PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO); + int findSearchPurchaseOrderCountMemberId(@Param("purchaseOrderSelectSearchDTO") PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO); PurchaseOrderSelectIdDTO findPurchaseOrderByPurchaseOrderId(String purchaseOrderId); diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java index d43ecba8..46053caf 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/service/PurchaseOrderQueryServiceImpl.java @@ -138,6 +138,13 @@ public Page selectSearchPurchaseOrderAdmin(Purchas String memberName = memberQueryService.selectNameById(purchaseOrder.getMemberId()); purchaseOrder.setMemberName(memberName); } + + if (purchaseOrder.getAdminId() != null) { + String adminName = memberQueryService.selectNameById(purchaseOrder.getAdminId()); + purchaseOrder.setAdminName(adminName); + } else { + purchaseOrder.setAdminName("-"); + } } int count = purchaseOrderMapper.findSearchPurchaseOrderCountMemberId(purchaseOrderSelectSearchDTO); @@ -227,6 +234,13 @@ public Page selectSearchPurchaseOrder(PurchaseOrde String memberName = memberQueryService.selectNameById(purchaseOrder.getMemberId()); purchaseOrder.setMemberName(memberName); } + + if (purchaseOrder.getAdminId() != null) { + String adminName = memberQueryService.selectNameById(purchaseOrder.getAdminId()); + purchaseOrder.setAdminName(adminName); + } else { + purchaseOrder.setAdminName("-"); + } } int count = purchaseOrderMapper.findSearchPurchaseOrderCount(purchaseOrderSelectSearchDTO); diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index 08131b07..b8cdd75c 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -90,8 +90,6 @@ - - @@ -293,8 +291,8 @@ AND a.conr_cust_pur_cond LIKE CONCAT('%', #{contractSearchDTO.customerPurchaseCondition}, '%') - - AND a.created_at BETWEEN #{contractSearchDTO.startAt} AND #{contractSearchDTO.endAt} + + AND a.created_at BETWEEN #{contractSearchDTO.startDate} AND DATE_ADD(#{contractSearchDTO.endDate}, INTERVAL 1 DAY) AND a.conr_car_name LIKE CONCAT('%', #{contractSearchDTO.carName}, '%') @@ -379,8 +377,8 @@ AND a.conr_cust_pur_cond LIKE CONCAT('%', #{contractSearchDTO.customerPurchaseCondition}, '%') - - AND a.created_at BETWEEN #{contractSearchDTO.startAt} AND #{contractSearchDTO.endAt} + + AND a.created_at BETWEEN #{contractSearchDTO.startDate} AND DATE_ADD(#{contractSearchDTO.endDate}, INTERVAL 1 DAY) AND a.conr_car_name LIKE CONCAT('%', #{contractSearchDTO.carName}, '%') @@ -473,11 +471,8 @@ AND a.conr_cust_pur_cond LIKE CONCAT('%', #{contractSearchDTO.customerPurchaseCondition}, '%') - - AND a.created_at >= #{contractSearchDTO.startAt} - - - AND a.created_at <= #{contractSearchDTO.endAt} + + AND a.created_at BETWEEN #{contractSearchDTO.startDate} AND DATE_ADD(#{contractSearchDTO.endDate}, INTERVAL 1 DAY) AND a.conr_car_name LIKE CONCAT('%', #{contractSearchDTO.carName}, '%') @@ -515,11 +510,8 @@ AND a.conr_cust_pur_cond LIKE CONCAT('%', #{contractSearchDTO.customerPurchaseCondition}, '%') - - AND a.created_at >= #{contractSearchDTO.startAt} - - - AND a.created_at <= #{contractSearchDTO.endAt} + + AND a.created_at BETWEEN #{contractSearchDTO.startDate} AND DATE_ADD(#{contractSearchDTO.endDate}, INTERVAL 1 DAY) AND a.conr_car_name LIKE CONCAT('%', #{contractSearchDTO.carName}, '%') @@ -611,7 +603,6 @@ a.active = TRUE AND a.cent_id = #{centerId} - AND a.conr_stat = 'APPROVED' AND a.mem_id = #{contractSearchDTO.searchMemberId} @@ -636,8 +627,8 @@ AND a.conr_cust_pur_cond LIKE CONCAT('%', #{contractSearchDTO.customerPurchaseCondition}, '%') - - AND a.created_at BETWEEN #{contractSearchDTO.startAt} AND #{contractSearchDTO.endAt} + + AND a.created_at BETWEEN #{contractSearchDTO.startDate} AND DATE_ADD(#{contractSearchDTO.endDate}, INTERVAL 1 DAY) AND a.conr_car_name LIKE CONCAT('%', #{contractSearchDTO.carName}, '%') @@ -714,8 +705,8 @@ AND a.conr_cust_pur_cond LIKE CONCAT('%', #{contractSearchDTO.customerPurchaseCondition}, '%') - - AND a.created_at BETWEEN #{contractSearchDTO.startAt} AND #{contractSearchDTO.endAt} + + AND a.created_at BETWEEN #{contractSearchDTO.startDate} AND DATE_ADD(#{contractSearchDTO.endDate}, INTERVAL 1 DAY) AND a.conr_car_name LIKE CONCAT('%', #{contractSearchDTO.carName}, '%') @@ -728,8 +719,6 @@ a.conr_id, a.conr_ttl, a.conr_cust_name, - a.conr_comp_name, - a.conr_cust_cla, a.conr_car_name, a.conr_stat, a.conr_cust_pur_cond, diff --git a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml index 1276f560..c1a4aa1e 100644 --- a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml @@ -60,7 +60,7 @@ AND a.active = TRUE AND a.ord_stat = 'APPROVED' ORDER BY a.created_at DESC - LIMIT #{pageSize} OFFSET #{offset}; + LIMIT #{pageSize} OFFSET #{offset} @@ -221,14 +229,14 @@ WHERE a.active = TRUE AND a.ord_stat = 'APPROVED' ORDER BY a.created_at DESC - LIMIT #{pageSize} OFFSET #{offset}; + LIMIT #{pageSize} OFFSET #{offset} @@ -377,7 +393,7 @@ WHERE a.active = true - SELECT a.ord_id, a.ord_ttl, @@ -394,7 +410,7 @@ ON b.prod_id = c.prod_id a.active = TRUE - a.cent_id = #{orderSelectSearchDTO.centerId} + AND a.cent_id = #{orderSelectSearchDTO.centerId} AND a.ord_ttl LIKE CONCAT('%', #{orderSelectSearchDTO.title}, '%') @@ -405,13 +421,14 @@ AND a.admin_id = #{orderSelectSearchDTO.adminId} - AND a.mem_id = #{searchMemberId} + AND a.mem_id = #{orderSelectSearchDTO.searchMemberId} - AND c.prod_name LIKE CONCAT('%', #{productName}, '%'); + AND c.prod_name LIKE CONCAT('%', #{orderSelectSearchDTO.productName}, '%') - AND a.created_at BETWEEN #{orderSelectSearchDTO.startDate} AND #{orderSelectSearchDTO.endDate} + AND a.created_at BETWEEN #{orderSelectSearchDTO.startDate} + AND DATE_ADD(#{orderSelectSearchDTO.endDate}, INTERVAL 1 DAY) @@ -425,19 +442,19 @@ ORDER BY a.ord_stat ${sortOrder} - ORDER BY a.ord_id ${orderId} + ORDER BY a.ord_id ${sortOrder} - ORDER BY a.mem_id ${memberId} + ORDER BY a.mem_id ${sortOrder} - ORDER BY a.admin_id ${adminId} + ORDER BY a.admin_id ${sortOrder} - ORDER BY a.created_at ${createdAt} + ORDER BY a.created_at ${sortOrder} - ORDER BY e.prod_name ${productName} + ORDER BY c.prod_name ${sortOrder} ORDER BY a.created_at DESC @@ -449,16 +466,20 @@ ORDER BY a.created_at DESC - LIMIT #{pageSize} OFFSET #{offset}; + LIMIT #{pageSize} OFFSET #{offset} diff --git a/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml b/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml index b47f2a59..a9984d91 100644 --- a/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml @@ -26,6 +26,7 @@ + @@ -94,12 +95,15 @@ @@ -247,6 +257,7 @@ a.pur_ord_stat, a.created_at, a.mem_id, + a.admin_id, b.ord_ttl, f.prod_name FROM tb_purchase_order a @@ -275,7 +286,8 @@ AND a.mem_id = #{purchaseOrderSelectSearchDTO.searchMemberId} - AND a.created_at BETWEEN #{purchaseOrderSelectSearchDTO.startDate} AND #{purchaseOrderSelectSearchDTO.endDate} + AND a.created_at BETWEEN #{purchaseOrderSelectSearchDTO.startDate} + AND DATE_ADD(#{purchaseOrderSelectSearchDTO.endDate}, INTERVAL 1 DAY) AND f.prod_name = #{productName} @@ -313,7 +325,7 @@ ORDER BY a.created_at DESC - LIMIT #{pageSize} OFFSET #{offset}; + LIMIT #{pageSize} OFFSET #{offset} From eb6ce854173c64ea97a77e9569e362f418f3609e Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Mon, 9 Dec 2024 01:49:46 +0900 Subject: [PATCH 529/563] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20D?= =?UTF-8?q?ashBoard=20=EC=99=84=EC=84=B1=20(#239)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/dto/DashBoardAdminDTO.java | 1 - .../service/DashBoardQueryServiceImpl.java | 151 +++++++++++++----- 2 files changed, 114 insertions(+), 38 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardAdminDTO.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardAdminDTO.java index 325cc021..5fd37cb2 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardAdminDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardAdminDTO.java @@ -18,7 +18,6 @@ public class DashBoardAdminDTO { private Integer unreadOrder; private Integer unreadPurchaseOrder; - private String noticeTitle; private Integer totalPrice; private List> noticeList; diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java index c4a69be4..0b46d761 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java @@ -24,9 +24,11 @@ import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistoryRankedDataDTO; import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySearchDTO; import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectDTO; +import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistoryStatisticsDTO; import stanl_2.final_backend.domain.sales_history.query.service.SalesHistoryQueryService; import java.security.GeneralSecurityException; +import java.time.LocalDate; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -39,7 +41,6 @@ @Service("queryDashBoardService") public class DashBoardQueryServiceImpl implements DashBoardQueryService { - private final DashBoardMapper dashBoardMapper; private final AuthQueryService authQueryService; private final ContractQueryService contractQueryService; private final OrderQueryService orderQueryService; @@ -54,12 +55,10 @@ private String getCurrentTime() { return nowKst.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } - public DashBoardQueryServiceImpl(DashBoardMapper dashBoardMapper, AuthQueryService authQueryService, - ContractQueryService contractQueryService, OrderQueryService orderQueryService, - PurchaseOrderQueryService purchaseOrderQueryService, SalesHistoryQueryService salesHistoryQueryService, - CustomerQueryService customerQueryService, NoticeService noticeService, - MemberQueryService memberQueryService) { - this.dashBoardMapper = dashBoardMapper; + public DashBoardQueryServiceImpl(AuthQueryService authQueryService, ContractQueryService contractQueryService, + OrderQueryService orderQueryService, PurchaseOrderQueryService purchaseOrderQueryService, + SalesHistoryQueryService salesHistoryQueryService, CustomerQueryService customerQueryService, + NoticeService noticeService, MemberQueryService memberQueryService) { this.authQueryService = authQueryService; this.contractQueryService = contractQueryService; this.orderQueryService = orderQueryService; @@ -70,6 +69,99 @@ public DashBoardQueryServiceImpl(DashBoardMapper dashBoardMapper, AuthQueryServi this.memberQueryService = memberQueryService; } + @Override + @Transactional(readOnly = true) + public DashBoardAdminDTO selectInfoForEmployee(String memberLoginId) throws GeneralSecurityException { + + DashBoardAdminDTO dashBoardAdminDTO = new DashBoardAdminDTO(); + + String memberId = authQueryService.selectMemberIdByLoginId(memberLoginId); + String centerId = memberQueryService.selectMemberInfo(memberLoginId).getCenterId(); + + // Pageable을 null로 전달하거나 유효한 Pageable 사용 + Pageable pageable = PageRequest.of(0, 100); + + String currentDate = getCurrentTime().substring(0, 10);; + LocalDate date = LocalDate.parse(currentDate); + LocalDate nextDate = date.plusDays(1); + + // 이번달 조회를 위한 날짜 지정 + String startAt = currentDate.substring(0,7) + "-01"; + String endAt = nextDate.toString(); + + // 이번달 Contract 받아오기 + ContractSearchDTO contractSearchDTO = new ContractSearchDTO(); + contractSearchDTO.setMemberId(memberLoginId); + contractSearchDTO.setSearchMemberId(memberId); + contractSearchDTO.setStartAt(startAt); + contractSearchDTO.setEndAt(endAt); + Integer unreadContract = Math.toIntExact(contractQueryService.selectBySearchEmployee(contractSearchDTO, pageable).getTotalElements()); + dashBoardAdminDTO.setUnreadContract(unreadContract); + System.out.println("unreadContract" + unreadContract); + + // 이번달 Order 받아오기 + OrderSelectSearchDTO orderSelectSearchDTO = new OrderSelectSearchDTO(); + orderSelectSearchDTO.setMemberId(memberLoginId); + orderSelectSearchDTO.setStartDate(startAt); + orderSelectSearchDTO.setEndDate(endAt); + Integer unreadOrder = Math.toIntExact(orderQueryService.selectSearchOrdersEmployee(orderSelectSearchDTO, pageable).getTotalElements()); + dashBoardAdminDTO.setUnreadOrder(unreadOrder); + System.out.println("unreadOrder" + unreadOrder); + + // 이번달 판매내역 받아오기 + SalesHistorySearchDTO salesHistorySearchDTO = new SalesHistorySearchDTO(); + salesHistorySearchDTO.setSearcherName(memberLoginId); + salesHistorySearchDTO.setStartDate(startAt); + salesHistorySearchDTO.setEndDate(endAt); + + SalesHistoryStatisticsDTO resultStatistics = salesHistoryQueryService.selectStatisticsSearchByEmployee(salesHistorySearchDTO); + Integer totalPrice = resultStatistics.getTotalSales(); + dashBoardAdminDTO.setTotalPrice(totalPrice); + + // 이번달 내 고객 순위 조회 + + + + // 이번달 판매사원 순위 + ArrayList employeeList = new ArrayList(); + ArrayList centerList = new ArrayList(); + centerList.add(centerId); + + SalesHistoryRankedDataDTO salesHistoryRankedDataDTO = new SalesHistoryRankedDataDTO(); + salesHistoryRankedDataDTO.setCenterList(centerList); + salesHistoryRankedDataDTO.setPeriod("month"); + salesHistoryRankedDataDTO.setStartDate(startAt); + salesHistoryRankedDataDTO.setEndDate(endAt); + salesHistoryRankedDataDTO.setGroupBy("employee"); + salesHistoryRankedDataDTO.setOrderBy("totalSales"); + + Page rankPage = salesHistoryQueryService.selectStatisticsBySearch(salesHistoryRankedDataDTO, pageable); + + List contentList = rankPage.getContent(); + + for (int i = 0; i < Math.min(contentList.size(), 5); i++) { + employeeList.add(contentList.get(i).getMemberId()); + } + dashBoardAdminDTO.setMemberList(employeeList); + + // 공지사항 조회 (제목, 내용(redirect)) + ArrayList> noticeList = new ArrayList<>(); + + SearchDTO searchDTO = new SearchDTO(); + searchDTO.setTag("ADMIN"); + Page noticePage = noticeService.findNotices(pageable, searchDTO); + + for (NoticeDTO notice : noticePage.getContent()) { + Map noticeData = new HashMap<>(); + noticeData.put("title", notice.getTitle()); // NoticeDTO의 title + noticeData.put("content", notice.getContent()); // NoticeDTO의 content + noticeList.add(noticeData); + } + dashBoardAdminDTO.setNoticeList(noticeList); + + return dashBoardAdminDTO; + } + @Override @Transactional(readOnly = true) @@ -79,14 +171,17 @@ public DashBoardAdminDTO selectInfoForAdmin(String memberLoginId) throws General String memberId = authQueryService.selectMemberIdByLoginId(memberLoginId); String centerId = memberQueryService.selectMemberInfo(memberLoginId).getCenterId(); - System.out.println("centerId = " + centerId); - // Pageable을 null로 전달하거나 유효한 Pageable 사용 Pageable pageable = PageRequest.of(0, 100); + + String currentDate = getCurrentTime().substring(0, 10);; + LocalDate date = LocalDate.parse(currentDate); + LocalDate nextDate = date.plusDays(1); + // 이번달 조회를 위한 날짜 지정 - String startAt = getCurrentTime().substring(0,7) + "-01"; - String endAt = getCurrentTime().substring(0,10); + String startAt = currentDate.substring(0,7) + "-01"; + String endAt = nextDate.toString(); // 이번달 Contract 받아오기 ContractSearchDTO contractSearchDTO = new ContractSearchDTO(); @@ -94,7 +189,6 @@ public DashBoardAdminDTO selectInfoForAdmin(String memberLoginId) throws General contractSearchDTO.setSearchMemberId(memberId); contractSearchDTO.setStartAt(startAt); contractSearchDTO.setEndAt(endAt); - contractSearchDTO.setActive(true); Integer unreadContract = Math.toIntExact(contractQueryService.selectBySearchEmployee(contractSearchDTO, pageable).getTotalElements()); dashBoardAdminDTO.setUnreadContract(unreadContract); System.out.println("unreadContract" + unreadContract); @@ -114,25 +208,18 @@ public DashBoardAdminDTO selectInfoForAdmin(String memberLoginId) throws General purchaseOrderSelectSearchDTO.setSearchMemberId(memberId); purchaseOrderSelectSearchDTO.setStartDate(startAt); purchaseOrderSelectSearchDTO.setEndDate(endAt); - log.info("=====================2"); Integer unreadPurchaseOrder = Math.toIntExact(purchaseOrderQueryService.selectSearchPurchaseOrderAdmin(purchaseOrderSelectSearchDTO, pageable).getTotalElements()); - log.info("=====================2"); dashBoardAdminDTO.setUnreadPurchaseOrder(unreadPurchaseOrder); // 이번달 판매내역 받아오기 - ArrayList memberList = new ArrayList(); - memberList.add(memberId); - SalesHistorySearchDTO salesHistorySearchDTO = new SalesHistorySearchDTO(); - salesHistorySearchDTO.setMemberList(memberList); + salesHistorySearchDTO.setSearcherName(memberLoginId); salesHistorySearchDTO.setStartDate(startAt); salesHistorySearchDTO.setEndDate(endAt); - log.info("=====================3"); - Page resultPage = salesHistoryQueryService.selectSalesHistorySearchByEmployee(salesHistorySearchDTO, pageable); - Integer totalPrice = resultPage.getContent().isEmpty() ? null : resultPage.getContent().get(0).getSalesHistoryTotalSales(); - dashBoardAdminDTO.setTotalPrice(totalPrice); - // 이번달 내 고객 순위 조회 + SalesHistoryStatisticsDTO resultStatistics = salesHistoryQueryService.selectStatisticsSearchByEmployee(salesHistorySearchDTO); + Integer totalPrice = resultStatistics.getTotalSales(); + dashBoardAdminDTO.setTotalPrice(totalPrice); // 이번달 판매사원 순위 ArrayList employeeList = new ArrayList(); @@ -146,17 +233,13 @@ public DashBoardAdminDTO selectInfoForAdmin(String memberLoginId) throws General salesHistoryRankedDataDTO.setEndDate(endAt); salesHistoryRankedDataDTO.setGroupBy("employee"); salesHistoryRankedDataDTO.setOrderBy("totalSales"); - log.info("=====================4"); + Page rankPage = salesHistoryQueryService.selectStatisticsBySearch(salesHistoryRankedDataDTO, pageable); List contentList = rankPage.getContent(); - for(SalesHistoryRankedDataDTO dto: contentList){ - if (dto.getMemberList() != null) { - // 0번부터 4번 인덱스까지의 memberName을 추출 - for (int i = 0; i < dto.getMemberList().size() && i < 5; i++) { - employeeList.add(memberQueryService.selectNameById(dto.getMemberList().get(i))); - } - } + + for (int i = 0; i < Math.min(contentList.size(), 5); i++) { + employeeList.add(contentList.get(i).getMemberId()); } dashBoardAdminDTO.setMemberList(employeeList); @@ -174,13 +257,7 @@ public DashBoardAdminDTO selectInfoForAdmin(String memberLoginId) throws General noticeList.add(noticeData); } dashBoardAdminDTO.setNoticeList(noticeList); - log.info("=====================5"); return dashBoardAdminDTO; } - -// @Override -// public DashBoardAdminDTO selectInfoForAdmin(String memberLoginId) { -// return null; -// } } From af481e2f3ce4d506a04f3dcd10b9c231641f98bb Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Mon, 9 Dec 2024 01:52:39 +0900 Subject: [PATCH 530/563] =?UTF-8?q?feat:=20dev=EC=97=90=EC=84=9C=20merge?= =?UTF-8?q?=20=EB=B0=9B=EA=B8=B0=20=EC=A0=84=20commit=20(#239)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/DashBoardQueryServiceImpl.java | 184 +++++++++--------- 1 file changed, 92 insertions(+), 92 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java index 0b46d761..f94e1a4e 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java @@ -69,98 +69,98 @@ public DashBoardQueryServiceImpl(AuthQueryService authQueryService, ContractQuer this.memberQueryService = memberQueryService; } - @Override - @Transactional(readOnly = true) - public DashBoardAdminDTO selectInfoForEmployee(String memberLoginId) throws GeneralSecurityException { - - DashBoardAdminDTO dashBoardAdminDTO = new DashBoardAdminDTO(); - - String memberId = authQueryService.selectMemberIdByLoginId(memberLoginId); - String centerId = memberQueryService.selectMemberInfo(memberLoginId).getCenterId(); - - // Pageable을 null로 전달하거나 유효한 Pageable 사용 - Pageable pageable = PageRequest.of(0, 100); - - String currentDate = getCurrentTime().substring(0, 10);; - LocalDate date = LocalDate.parse(currentDate); - LocalDate nextDate = date.plusDays(1); - - // 이번달 조회를 위한 날짜 지정 - String startAt = currentDate.substring(0,7) + "-01"; - String endAt = nextDate.toString(); - - // 이번달 Contract 받아오기 - ContractSearchDTO contractSearchDTO = new ContractSearchDTO(); - contractSearchDTO.setMemberId(memberLoginId); - contractSearchDTO.setSearchMemberId(memberId); - contractSearchDTO.setStartAt(startAt); - contractSearchDTO.setEndAt(endAt); - Integer unreadContract = Math.toIntExact(contractQueryService.selectBySearchEmployee(contractSearchDTO, pageable).getTotalElements()); - dashBoardAdminDTO.setUnreadContract(unreadContract); - System.out.println("unreadContract" + unreadContract); - - // 이번달 Order 받아오기 - OrderSelectSearchDTO orderSelectSearchDTO = new OrderSelectSearchDTO(); - orderSelectSearchDTO.setMemberId(memberLoginId); - orderSelectSearchDTO.setStartDate(startAt); - orderSelectSearchDTO.setEndDate(endAt); - Integer unreadOrder = Math.toIntExact(orderQueryService.selectSearchOrdersEmployee(orderSelectSearchDTO, pageable).getTotalElements()); - dashBoardAdminDTO.setUnreadOrder(unreadOrder); - System.out.println("unreadOrder" + unreadOrder); - - // 이번달 판매내역 받아오기 - SalesHistorySearchDTO salesHistorySearchDTO = new SalesHistorySearchDTO(); - salesHistorySearchDTO.setSearcherName(memberLoginId); - salesHistorySearchDTO.setStartDate(startAt); - salesHistorySearchDTO.setEndDate(endAt); - - SalesHistoryStatisticsDTO resultStatistics = salesHistoryQueryService.selectStatisticsSearchByEmployee(salesHistorySearchDTO); - Integer totalPrice = resultStatistics.getTotalSales(); - dashBoardAdminDTO.setTotalPrice(totalPrice); - - // 이번달 내 고객 순위 조회 - - - - // 이번달 판매사원 순위 - ArrayList employeeList = new ArrayList(); - ArrayList centerList = new ArrayList(); - centerList.add(centerId); - - SalesHistoryRankedDataDTO salesHistoryRankedDataDTO = new SalesHistoryRankedDataDTO(); - salesHistoryRankedDataDTO.setCenterList(centerList); - salesHistoryRankedDataDTO.setPeriod("month"); - salesHistoryRankedDataDTO.setStartDate(startAt); - salesHistoryRankedDataDTO.setEndDate(endAt); - salesHistoryRankedDataDTO.setGroupBy("employee"); - salesHistoryRankedDataDTO.setOrderBy("totalSales"); - - Page rankPage = salesHistoryQueryService.selectStatisticsBySearch(salesHistoryRankedDataDTO, pageable); - - List contentList = rankPage.getContent(); - - for (int i = 0; i < Math.min(contentList.size(), 5); i++) { - employeeList.add(contentList.get(i).getMemberId()); - } - dashBoardAdminDTO.setMemberList(employeeList); - - // 공지사항 조회 (제목, 내용(redirect)) - ArrayList> noticeList = new ArrayList<>(); - - SearchDTO searchDTO = new SearchDTO(); - searchDTO.setTag("ADMIN"); - Page noticePage = noticeService.findNotices(pageable, searchDTO); - - for (NoticeDTO notice : noticePage.getContent()) { - Map noticeData = new HashMap<>(); - noticeData.put("title", notice.getTitle()); // NoticeDTO의 title - noticeData.put("content", notice.getContent()); // NoticeDTO의 content - noticeList.add(noticeData); - } - dashBoardAdminDTO.setNoticeList(noticeList); - - return dashBoardAdminDTO; - } +// @Override +// @Transactional(readOnly = true) +// public DashBoardAdminDTO selectInfoForEmployee(String memberLoginId) throws GeneralSecurityException { +// +// DashBoardAdminDTO dashBoardAdminDTO = new DashBoardAdminDTO(); +// +// String memberId = authQueryService.selectMemberIdByLoginId(memberLoginId); +// String centerId = memberQueryService.selectMemberInfo(memberLoginId).getCenterId(); +// +// // Pageable을 null로 전달하거나 유효한 Pageable 사용 +// Pageable pageable = PageRequest.of(0, 100); +// +// String currentDate = getCurrentTime().substring(0, 10);; +// LocalDate date = LocalDate.parse(currentDate); +// LocalDate nextDate = date.plusDays(1); +// +// // 이번달 조회를 위한 날짜 지정 +// String startAt = currentDate.substring(0,7) + "-01"; +// String endAt = nextDate.toString(); +// +// // 이번달 Contract 받아오기 +// ContractSearchDTO contractSearchDTO = new ContractSearchDTO(); +// contractSearchDTO.setMemberId(memberLoginId); +// contractSearchDTO.setSearchMemberId(memberId); +// contractSearchDTO.setStartAt(startAt); +// contractSearchDTO.setEndAt(endAt); +// Integer unreadContract = Math.toIntExact(contractQueryService.selectBySearchEmployee(contractSearchDTO, pageable).getTotalElements()); +// dashBoardAdminDTO.setUnreadContract(unreadContract); +// System.out.println("unreadContract" + unreadContract); +// +// // 이번달 Order 받아오기 +// OrderSelectSearchDTO orderSelectSearchDTO = new OrderSelectSearchDTO(); +// orderSelectSearchDTO.setMemberId(memberLoginId); +// orderSelectSearchDTO.setStartDate(startAt); +// orderSelectSearchDTO.setEndDate(endAt); +// Integer unreadOrder = Math.toIntExact(orderQueryService.selectSearchOrdersEmployee(orderSelectSearchDTO, pageable).getTotalElements()); +// dashBoardAdminDTO.setUnreadOrder(unreadOrder); +// System.out.println("unreadOrder" + unreadOrder); +// +// // 이번달 판매내역 받아오기 +// SalesHistorySearchDTO salesHistorySearchDTO = new SalesHistorySearchDTO(); +// salesHistorySearchDTO.setSearcherName(memberLoginId); +// salesHistorySearchDTO.setStartDate(startAt); +// salesHistorySearchDTO.setEndDate(endAt); +// +// SalesHistoryStatisticsDTO resultStatistics = salesHistoryQueryService.selectStatisticsSearchByEmployee(salesHistorySearchDTO); +// Integer totalPrice = resultStatistics.getTotalSales(); +// dashBoardAdminDTO.setTotalPrice(totalPrice); +// +// // 이번달 내 고객 순위 조회 +// +// +// +// // 이번달 판매사원 순위 +// ArrayList employeeList = new ArrayList(); +// ArrayList centerList = new ArrayList(); +// centerList.add(centerId); +// +// SalesHistoryRankedDataDTO salesHistoryRankedDataDTO = new SalesHistoryRankedDataDTO(); +// salesHistoryRankedDataDTO.setCenterList(centerList); +// salesHistoryRankedDataDTO.setPeriod("month"); +// salesHistoryRankedDataDTO.setStartDate(startAt); +// salesHistoryRankedDataDTO.setEndDate(endAt); +// salesHistoryRankedDataDTO.setGroupBy("employee"); +// salesHistoryRankedDataDTO.setOrderBy("totalSales"); +// +// Page rankPage = salesHistoryQueryService.selectStatisticsBySearch(salesHistoryRankedDataDTO, pageable); +// +// List contentList = rankPage.getContent(); +// +// for (int i = 0; i < Math.min(contentList.size(), 5); i++) { +// employeeList.add(contentList.get(i).getMemberId()); +// } +// dashBoardAdminDTO.setMemberList(employeeList); +// +// // 공지사항 조회 (제목, 내용(redirect)) +// ArrayList> noticeList = new ArrayList<>(); +// +// SearchDTO searchDTO = new SearchDTO(); +// searchDTO.setTag("ADMIN"); +// Page noticePage = noticeService.findNotices(pageable, searchDTO); +// +// for (NoticeDTO notice : noticePage.getContent()) { +// Map noticeData = new HashMap<>(); +// noticeData.put("title", notice.getTitle()); // NoticeDTO의 title +// noticeData.put("content", notice.getContent()); // NoticeDTO의 content +// noticeList.add(noticeData); +// } +// dashBoardAdminDTO.setNoticeList(noticeList); +// +// return dashBoardAdminDTO; +// } @Override From 4195dfbf3d149d8225083e32b7295d0eb5660d8c Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Mon, 9 Dec 2024 02:02:10 +0900 Subject: [PATCH 531/563] =?UTF-8?q?feat:=20customer=EC=97=90=20=EA=B3=84?= =?UTF-8?q?=EC=95=BD=EC=84=9C=20=EA=B1=B4=EC=88=98=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EC=A0=84=20commit=20(#239)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/DashBoardController.java | 3 +- .../query/dto/DashBoardDirectorDTO.java | 12 ++ .../query/dto/DashBoardEmployeeDTO.java | 8 +- .../query/service/DashBoardQueryService.java | 3 + .../service/DashBoardQueryServiceImpl.java | 192 +++++++++--------- 5 files changed, 115 insertions(+), 103 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/controller/DashBoardController.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/controller/DashBoardController.java index 161c75e3..e0ece595 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/controller/DashBoardController.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/controller/DashBoardController.java @@ -14,6 +14,7 @@ import stanl_2.final_backend.domain.customer.common.response.CustomerResponseMessage; import stanl_2.final_backend.domain.dashBoard.common.response.DashBoardResponseMessage; import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardAdminDTO; +import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardEmployeeDTO; import stanl_2.final_backend.domain.dashBoard.query.service.DashBoardQueryService; import java.security.GeneralSecurityException; @@ -41,7 +42,7 @@ public ResponseEntity selectDashBoardForEmployee(Princ String memberLoginId = principal.getName(); -// DashBoardAdminDTO boardResponseDTO = dashBoardQueryService.selectInfoForEmployee(memberLoginId); + DashBoardEmployeeDTO boardResponseDTO = dashBoardQueryService.selectInfoForEmployee(memberLoginId); return ResponseEntity.ok(DashBoardResponseMessage.builder() .httpStatus(200) diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardDirectorDTO.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardDirectorDTO.java index 2ebc0e58..d491622e 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardDirectorDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardDirectorDTO.java @@ -5,12 +5,24 @@ import lombok.NoArgsConstructor; import lombok.Setter; +import java.util.List; +import java.util.Map; + @NoArgsConstructor @AllArgsConstructor @Getter @Setter public class DashBoardDirectorDTO { + private Integer unreadContract; + private Integer unreadOrder; + + private Integer totalPrice; + + private List> noticeList; + + private List customerList; + private List memberList; private String memberLoginId; private String memberId; } diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardEmployeeDTO.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardEmployeeDTO.java index e1458230..6f9b0b0c 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardEmployeeDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardEmployeeDTO.java @@ -5,6 +5,9 @@ import lombok.NoArgsConstructor; import lombok.Setter; +import java.util.List; +import java.util.Map; + @NoArgsConstructor @AllArgsConstructor @Getter @@ -15,8 +18,11 @@ public class DashBoardEmployeeDTO { private Integer unreadOrder; private Integer unreadPurchaseOrder; - private String noticeTitle; + private Integer totalPrice; + + private List> noticeList; + private List memberList; private String memberLoginId; private String memberId; } diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryService.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryService.java index ae96a15f..251941a0 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryService.java @@ -1,6 +1,7 @@ package stanl_2.final_backend.domain.dashBoard.query.service; import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardAdminDTO; +import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardEmployeeDTO; import java.security.GeneralSecurityException; @@ -8,4 +9,6 @@ public interface DashBoardQueryService { // DashBoardAdminDTO selectInfoForEmployee(String memberLoginId) throws GeneralSecurityException; DashBoardAdminDTO selectInfoForAdmin(String memberLoginId) throws GeneralSecurityException; + + DashBoardEmployeeDTO selectInfoForEmployee(String memberLoginId) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java index f94e1a4e..69e3b0c3 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java @@ -11,6 +11,7 @@ import stanl_2.final_backend.domain.contract.query.service.ContractQueryService; import stanl_2.final_backend.domain.customer.query.service.CustomerQueryService; import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardAdminDTO; +import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardEmployeeDTO; import stanl_2.final_backend.domain.dashBoard.query.repository.DashBoardMapper; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; import stanl_2.final_backend.domain.member.query.service.MemberQueryService; @@ -69,99 +70,93 @@ public DashBoardQueryServiceImpl(AuthQueryService authQueryService, ContractQuer this.memberQueryService = memberQueryService; } -// @Override -// @Transactional(readOnly = true) -// public DashBoardAdminDTO selectInfoForEmployee(String memberLoginId) throws GeneralSecurityException { -// -// DashBoardAdminDTO dashBoardAdminDTO = new DashBoardAdminDTO(); -// -// String memberId = authQueryService.selectMemberIdByLoginId(memberLoginId); -// String centerId = memberQueryService.selectMemberInfo(memberLoginId).getCenterId(); -// -// // Pageable을 null로 전달하거나 유효한 Pageable 사용 -// Pageable pageable = PageRequest.of(0, 100); -// -// String currentDate = getCurrentTime().substring(0, 10);; -// LocalDate date = LocalDate.parse(currentDate); -// LocalDate nextDate = date.plusDays(1); -// -// // 이번달 조회를 위한 날짜 지정 -// String startAt = currentDate.substring(0,7) + "-01"; -// String endAt = nextDate.toString(); -// -// // 이번달 Contract 받아오기 -// ContractSearchDTO contractSearchDTO = new ContractSearchDTO(); -// contractSearchDTO.setMemberId(memberLoginId); -// contractSearchDTO.setSearchMemberId(memberId); -// contractSearchDTO.setStartAt(startAt); -// contractSearchDTO.setEndAt(endAt); -// Integer unreadContract = Math.toIntExact(contractQueryService.selectBySearchEmployee(contractSearchDTO, pageable).getTotalElements()); -// dashBoardAdminDTO.setUnreadContract(unreadContract); -// System.out.println("unreadContract" + unreadContract); -// -// // 이번달 Order 받아오기 -// OrderSelectSearchDTO orderSelectSearchDTO = new OrderSelectSearchDTO(); -// orderSelectSearchDTO.setMemberId(memberLoginId); -// orderSelectSearchDTO.setStartDate(startAt); -// orderSelectSearchDTO.setEndDate(endAt); -// Integer unreadOrder = Math.toIntExact(orderQueryService.selectSearchOrdersEmployee(orderSelectSearchDTO, pageable).getTotalElements()); -// dashBoardAdminDTO.setUnreadOrder(unreadOrder); -// System.out.println("unreadOrder" + unreadOrder); -// -// // 이번달 판매내역 받아오기 -// SalesHistorySearchDTO salesHistorySearchDTO = new SalesHistorySearchDTO(); -// salesHistorySearchDTO.setSearcherName(memberLoginId); -// salesHistorySearchDTO.setStartDate(startAt); -// salesHistorySearchDTO.setEndDate(endAt); -// -// SalesHistoryStatisticsDTO resultStatistics = salesHistoryQueryService.selectStatisticsSearchByEmployee(salesHistorySearchDTO); -// Integer totalPrice = resultStatistics.getTotalSales(); -// dashBoardAdminDTO.setTotalPrice(totalPrice); -// -// // 이번달 내 고객 순위 조회 -// -// -// -// // 이번달 판매사원 순위 -// ArrayList employeeList = new ArrayList(); -// ArrayList centerList = new ArrayList(); -// centerList.add(centerId); -// -// SalesHistoryRankedDataDTO salesHistoryRankedDataDTO = new SalesHistoryRankedDataDTO(); -// salesHistoryRankedDataDTO.setCenterList(centerList); -// salesHistoryRankedDataDTO.setPeriod("month"); -// salesHistoryRankedDataDTO.setStartDate(startAt); -// salesHistoryRankedDataDTO.setEndDate(endAt); -// salesHistoryRankedDataDTO.setGroupBy("employee"); -// salesHistoryRankedDataDTO.setOrderBy("totalSales"); -// -// Page rankPage = salesHistoryQueryService.selectStatisticsBySearch(salesHistoryRankedDataDTO, pageable); -// -// List contentList = rankPage.getContent(); -// -// for (int i = 0; i < Math.min(contentList.size(), 5); i++) { -// employeeList.add(contentList.get(i).getMemberId()); -// } -// dashBoardAdminDTO.setMemberList(employeeList); -// -// // 공지사항 조회 (제목, 내용(redirect)) -// ArrayList> noticeList = new ArrayList<>(); -// -// SearchDTO searchDTO = new SearchDTO(); -// searchDTO.setTag("ADMIN"); -// Page noticePage = noticeService.findNotices(pageable, searchDTO); -// -// for (NoticeDTO notice : noticePage.getContent()) { -// Map noticeData = new HashMap<>(); -// noticeData.put("title", notice.getTitle()); // NoticeDTO의 title -// noticeData.put("content", notice.getContent()); // NoticeDTO의 content -// noticeList.add(noticeData); -// } -// dashBoardAdminDTO.setNoticeList(noticeList); -// -// return dashBoardAdminDTO; -// } + @Override + @Transactional(readOnly = true) + public DashBoardEmployeeDTO selectInfoForEmployee(String memberLoginId) throws GeneralSecurityException { + + DashBoardEmployeeDTO dashBoardEmployeeDTO = new DashBoardEmployeeDTO(); + + String memberId = authQueryService.selectMemberIdByLoginId(memberLoginId); + String centerId = memberQueryService.selectMemberInfo(memberLoginId).getCenterId(); + // Pageable을 null로 전달하거나 유효한 Pageable 사용 + Pageable pageable = PageRequest.of(0, 100); + + String startAt = getCurrentTime().substring(0, 7) + "-01"; + String endAt = getCurrentTime().substring(0,10); + + // 이번달 Contract 받아오기 + ContractSearchDTO contractSearchDTO = new ContractSearchDTO(); + contractSearchDTO.setMemberId(memberLoginId); + contractSearchDTO.setSearchMemberId(memberId); + contractSearchDTO.setStartDate(startAt); + contractSearchDTO.setEndDate(endAt); + Integer unreadContract = Math.toIntExact(contractQueryService.selectBySearchEmployee(contractSearchDTO, pageable).getTotalElements()); + dashBoardEmployeeDTO.setUnreadContract(unreadContract); + System.out.println("unreadContract" + unreadContract); + + // 이번달 Order 받아오기 + OrderSelectSearchDTO orderSelectSearchDTO = new OrderSelectSearchDTO(); + orderSelectSearchDTO.setMemberId(memberLoginId); + orderSelectSearchDTO.setStartDate(startAt); + orderSelectSearchDTO.setEndDate(endAt); + Integer unreadOrder = Math.toIntExact(orderQueryService.selectSearchOrdersEmployee(orderSelectSearchDTO, pageable).getTotalElements()); + dashBoardEmployeeDTO.setUnreadOrder(unreadOrder); + System.out.println("unreadOrder" + unreadOrder); + + // 이번달 판매내역 받아오기 + SalesHistorySearchDTO salesHistorySearchDTO = new SalesHistorySearchDTO(); + salesHistorySearchDTO.setSearcherName(memberLoginId); + salesHistorySearchDTO.setStartDate(startAt); + salesHistorySearchDTO.setEndDate(endAt); + + SalesHistoryStatisticsDTO resultStatistics = salesHistoryQueryService.selectStatisticsSearchByEmployee(salesHistorySearchDTO); + Integer totalPrice = resultStatistics.getTotalSales(); + dashBoardEmployeeDTO.setTotalPrice(totalPrice); + + // 이번달 내 고객 순위 조회 + + + + // 이번달 판매사원 순위 + ArrayList employeeList = new ArrayList(); + ArrayList centerList = new ArrayList(); + centerList.add(centerId); + + SalesHistoryRankedDataDTO salesHistoryRankedDataDTO = new SalesHistoryRankedDataDTO(); + salesHistoryRankedDataDTO.setCenterList(centerList); + salesHistoryRankedDataDTO.setPeriod("month"); + salesHistoryRankedDataDTO.setStartDate(startAt); + salesHistoryRankedDataDTO.setEndDate(endAt); + salesHistoryRankedDataDTO.setGroupBy("employee"); + salesHistoryRankedDataDTO.setOrderBy("totalSales"); + + Page rankPage = salesHistoryQueryService.selectStatisticsBySearch(salesHistoryRankedDataDTO, pageable); + + List contentList = rankPage.getContent(); + + for (int i = 0; i < Math.min(contentList.size(), 5); i++) { + employeeList.add(contentList.get(i).getMemberId()); + } + dashBoardEmployeeDTO.setMemberList(employeeList); + + // 공지사항 조회 (제목, 내용(redirect)) + ArrayList> noticeList = new ArrayList<>(); + + SearchDTO searchDTO = new SearchDTO(); + searchDTO.setTag("ALL"); + Page noticePage = noticeService.findNotices(pageable, searchDTO); + + for (NoticeDTO notice : noticePage.getContent()) { + Map noticeData = new HashMap<>(); + noticeData.put("title", notice.getTitle()); // NoticeDTO의 title + noticeData.put("content", notice.getContent()); // NoticeDTO의 content + noticeList.add(noticeData); + } + dashBoardEmployeeDTO.setNoticeList(noticeList); + + return dashBoardEmployeeDTO; + } @Override @Transactional(readOnly = true) @@ -175,20 +170,15 @@ public DashBoardAdminDTO selectInfoForAdmin(String memberLoginId) throws General // Pageable을 null로 전달하거나 유효한 Pageable 사용 Pageable pageable = PageRequest.of(0, 100); - String currentDate = getCurrentTime().substring(0, 10);; - LocalDate date = LocalDate.parse(currentDate); - LocalDate nextDate = date.plusDays(1); - - // 이번달 조회를 위한 날짜 지정 - String startAt = currentDate.substring(0,7) + "-01"; - String endAt = nextDate.toString(); + String startAt = getCurrentTime().substring(0, 7) + "-01"; + String endAt = getCurrentTime().substring(0,10); // 이번달 Contract 받아오기 ContractSearchDTO contractSearchDTO = new ContractSearchDTO(); contractSearchDTO.setMemberId(memberLoginId); contractSearchDTO.setSearchMemberId(memberId); - contractSearchDTO.setStartAt(startAt); - contractSearchDTO.setEndAt(endAt); + contractSearchDTO.setStartDate(startAt); + contractSearchDTO.setEndDate(endAt); Integer unreadContract = Math.toIntExact(contractQueryService.selectBySearchEmployee(contractSearchDTO, pageable).getTotalElements()); dashBoardAdminDTO.setUnreadContract(unreadContract); System.out.println("unreadContract" + unreadContract); From 3f351b1f9675aa23450927deb10c20461a3d465f Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Mon, 9 Dec 2024 02:31:58 +0900 Subject: [PATCH 532/563] =?UTF-8?q?feat:=20=EC=98=81=EC=97=85=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=EC=9E=90=20DashBoard=20=EC=99=84=EB=A3=8C=20(#239)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/DashBoardController.java | 9 ++-- .../query/dto/DashBoardDirectorDTO.java | 9 +--- .../query/service/DashBoardQueryService.java | 3 ++ .../service/DashBoardQueryServiceImpl.java | 48 ++++++++++++++++++- .../security/config/RequestMatcherConfig.java | 2 + 5 files changed, 58 insertions(+), 13 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/controller/DashBoardController.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/controller/DashBoardController.java index e0ece595..2406b79d 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/controller/DashBoardController.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/controller/DashBoardController.java @@ -14,6 +14,7 @@ import stanl_2.final_backend.domain.customer.common.response.CustomerResponseMessage; import stanl_2.final_backend.domain.dashBoard.common.response.DashBoardResponseMessage; import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardAdminDTO; +import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardDirectorDTO; import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardEmployeeDTO; import stanl_2.final_backend.domain.dashBoard.query.service.DashBoardQueryService; @@ -47,7 +48,7 @@ public ResponseEntity selectDashBoardForEmployee(Princ return ResponseEntity.ok(DashBoardResponseMessage.builder() .httpStatus(200) .msg("성공") -// .result(boardResponseDTO) + .result(boardResponseDTO) .build()); } @@ -76,16 +77,16 @@ public ResponseEntity selectDashBoardForAdmin(Principa content = {@Content(schema = @Schema(implementation = CustomerResponseMessage.class))}) }) @GetMapping("/director") - public ResponseEntity selectDashBoardForDirector(Principal principal){ + public ResponseEntity selectDashBoardForDirector(Principal principal) throws GeneralSecurityException { String memberLoginId = principal.getName(); -// DashBoardAdminDTO boardResponseDTO = dashBoardQueryService.selectInfoForEmployeeAndAdmin(memberLoginId); + DashBoardDirectorDTO boardResponseDTO = dashBoardQueryService.selectInfoForDirector(memberLoginId); return ResponseEntity.ok(DashBoardResponseMessage.builder() .httpStatus(200) .msg("성공") - .result(null) + .result(boardResponseDTO) .build()); } diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardDirectorDTO.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardDirectorDTO.java index d491622e..bbd53b99 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardDirectorDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardDirectorDTO.java @@ -14,15 +14,10 @@ @Setter public class DashBoardDirectorDTO { - private Integer unreadContract; - private Integer unreadOrder; + private Integer unreadPurchaseOrder; - private Integer totalPrice; + private List centerList; - private List> noticeList; - - private List customerList; - private List memberList; private String memberLoginId; private String memberId; } diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryService.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryService.java index 251941a0..71e36a40 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryService.java @@ -1,6 +1,7 @@ package stanl_2.final_backend.domain.dashBoard.query.service; import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardAdminDTO; +import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardDirectorDTO; import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardEmployeeDTO; import java.security.GeneralSecurityException; @@ -11,4 +12,6 @@ public interface DashBoardQueryService { DashBoardAdminDTO selectInfoForAdmin(String memberLoginId) throws GeneralSecurityException; DashBoardEmployeeDTO selectInfoForEmployee(String memberLoginId) throws GeneralSecurityException; + + DashBoardDirectorDTO selectInfoForDirector(String memberLoginId) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java index 69e3b0c3..30bd51b6 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java @@ -11,6 +11,7 @@ import stanl_2.final_backend.domain.contract.query.service.ContractQueryService; import stanl_2.final_backend.domain.customer.query.service.CustomerQueryService; import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardAdminDTO; +import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardDirectorDTO; import stanl_2.final_backend.domain.dashBoard.query.dto.DashBoardEmployeeDTO; import stanl_2.final_backend.domain.dashBoard.query.repository.DashBoardMapper; import stanl_2.final_backend.domain.member.query.service.AuthQueryService; @@ -181,7 +182,6 @@ public DashBoardAdminDTO selectInfoForAdmin(String memberLoginId) throws General contractSearchDTO.setEndDate(endAt); Integer unreadContract = Math.toIntExact(contractQueryService.selectBySearchEmployee(contractSearchDTO, pageable).getTotalElements()); dashBoardAdminDTO.setUnreadContract(unreadContract); - System.out.println("unreadContract" + unreadContract); // 이번달 Order 받아오기 OrderSelectSearchDTO orderSelectSearchDTO = new OrderSelectSearchDTO(); @@ -190,7 +190,6 @@ public DashBoardAdminDTO selectInfoForAdmin(String memberLoginId) throws General orderSelectSearchDTO.setEndDate(endAt); Integer unreadOrder = Math.toIntExact(orderQueryService.selectSearchOrdersEmployee(orderSelectSearchDTO, pageable).getTotalElements()); dashBoardAdminDTO.setUnreadOrder(unreadOrder); - System.out.println("unreadOrder" + unreadOrder); // 이번달 PurchaseOrder 받아오기 PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO = new PurchaseOrderSelectSearchDTO(); @@ -250,4 +249,49 @@ public DashBoardAdminDTO selectInfoForAdmin(String memberLoginId) throws General return dashBoardAdminDTO; } + + @Override + @Transactional(readOnly = true) + public DashBoardDirectorDTO selectInfoForDirector(String memberLoginId) throws GeneralSecurityException { + + DashBoardDirectorDTO dashBoardDirectorDTO = new DashBoardDirectorDTO(); + + // Pageable을 null로 전달하거나 유효한 Pageable 사용 + Pageable pageable = PageRequest.of(0, 100); + + String startAt = getCurrentTime().substring(0, 7) + "-01"; + String endAt = getCurrentTime().substring(0,10); + + // 승인되지 않은 발주서 건수 + PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO = new PurchaseOrderSelectSearchDTO(); + purchaseOrderSelectSearchDTO.setMemberId(memberLoginId); + purchaseOrderSelectSearchDTO.setStatus("WAIT"); + purchaseOrderSelectSearchDTO.setStartDate(startAt); + purchaseOrderSelectSearchDTO.setEndDate(endAt); + Integer unreadPurchaseOrderCnt = Math.toIntExact(purchaseOrderQueryService.selectSearchPurchaseOrder(purchaseOrderSelectSearchDTO, pageable).getTotalElements()); + dashBoardDirectorDTO.setUnreadPurchaseOrder(unreadPurchaseOrderCnt); + + // 판매 매장 실적 순위 + ArrayList centerList = new ArrayList(); + + SalesHistoryRankedDataDTO salesHistoryRankedDataDTO = new SalesHistoryRankedDataDTO(); + salesHistoryRankedDataDTO.setPeriod("month"); + salesHistoryRankedDataDTO.setStartDate(startAt); + salesHistoryRankedDataDTO.setEndDate(endAt); + + salesHistoryRankedDataDTO.setGroupBy("center"); + salesHistoryRankedDataDTO.setOrderBy("totalSales"); + + Page rankPage = salesHistoryQueryService.selectStatisticsBySearch(salesHistoryRankedDataDTO, pageable); + + List content = rankPage.getContent(); + + for (int i = 0; i < Math.min(content.size(), 5); i++) { + centerList.add(content.get(i).getCenterId()); + } + + dashBoardDirectorDTO.setCenterList(centerList); + + return dashBoardDirectorDTO; + } } diff --git a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java index c4c0cac3..f7b572e8 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java @@ -222,7 +222,9 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/employee/search").hasAnyRole("salesHistory-employee-search-post", "GOD", "EMPLOYEE") // DashBoard API + .requestMatchers(HttpMethod.GET, "/api/v1/dashBoard/employee").hasAnyRole("dashboard-get", "GOD", "DIRECTOR", "ADMIN", "EMPLOYEE") .requestMatchers(HttpMethod.GET, "/api/v1/dashBoard/admin").hasAnyRole("dashboard-get", "GOD", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/dashBoard/director").hasAnyRole("dashboard-get", "GOD", "DIRECTOR") // 그 외 나머지 시스템 관리자만 접근 가능 .anyRequest().hasRole("GOD") From 3a3872d23bdcce09bcf9fe921c629cb141116b11 Mon Sep 17 00:00:00 2001 From: giuseog Date: Mon, 9 Dec 2024 03:20:49 +0900 Subject: [PATCH 533/563] =?UTF-8?q?fix:=20=EA=B7=B8=EB=A3=B9=ED=99=94=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=82=AC=EC=95=88(#235)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sales_history/query/repository/SalesHistoryMapper.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml index 82e37e0d..6ca8f53e 100644 --- a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml @@ -732,10 +732,10 @@ a.created_at, - + COALESCE(a.mem_id, 'Unknown Employee') AS group_key -- 사원별 - + COALESCE(a.cent_id, 'Unknown Center') AS group_key -- 매장별 @@ -936,10 +936,10 @@ a.created_at, - + COALESCE(a.mem_id, 'Unknown Employee') AS group_key -- 사원별 - + COALESCE(a.cent_id, 'Unknown Center') AS group_key -- 매장별 From 629af183d9a306c7be3bd439ac8927496eb83291 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Mon, 9 Dec 2024 03:41:09 +0900 Subject: [PATCH 534/563] =?UTF-8?q?feat:=20=EC=98=81=EC=97=85=20=EC=82=AC?= =?UTF-8?q?=EC=9B=90=20DashBoard=20=EC=99=84=EC=84=B1=20(#239)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/dto/DashBoardEmployeeDTO.java | 2 +- .../service/DashBoardQueryServiceImpl.java | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardEmployeeDTO.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardEmployeeDTO.java index 6f9b0b0c..e134c5c7 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardEmployeeDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/dto/DashBoardEmployeeDTO.java @@ -16,10 +16,10 @@ public class DashBoardEmployeeDTO { private Integer unreadContract; private Integer unreadOrder; - private Integer unreadPurchaseOrder; private Integer totalPrice; + private List scheduleTitle; private List> noticeList; private List memberList; diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java index 30bd51b6..31cbecbc 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java @@ -28,6 +28,8 @@ import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistorySelectDTO; import stanl_2.final_backend.domain.sales_history.query.dto.SalesHistoryStatisticsDTO; import stanl_2.final_backend.domain.sales_history.query.service.SalesHistoryQueryService; +import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDayDTO; +import stanl_2.final_backend.domain.schedule.query.service.ScheduleQueryService; import java.security.GeneralSecurityException; import java.time.LocalDate; @@ -48,7 +50,7 @@ public class DashBoardQueryServiceImpl implements DashBoardQueryService { private final OrderQueryService orderQueryService; private final PurchaseOrderQueryService purchaseOrderQueryService; private final SalesHistoryQueryService salesHistoryQueryService; - private final CustomerQueryService customerQueryService; + private final ScheduleQueryService scheduleQueryService; private final NoticeService noticeService; private final MemberQueryService memberQueryService; @@ -59,14 +61,14 @@ private String getCurrentTime() { public DashBoardQueryServiceImpl(AuthQueryService authQueryService, ContractQueryService contractQueryService, OrderQueryService orderQueryService, PurchaseOrderQueryService purchaseOrderQueryService, - SalesHistoryQueryService salesHistoryQueryService, CustomerQueryService customerQueryService, + SalesHistoryQueryService salesHistoryQueryService, ScheduleQueryService scheduleQueryService, NoticeService noticeService, MemberQueryService memberQueryService) { this.authQueryService = authQueryService; this.contractQueryService = contractQueryService; this.orderQueryService = orderQueryService; this.purchaseOrderQueryService = purchaseOrderQueryService; this.salesHistoryQueryService = salesHistoryQueryService; - this.customerQueryService = customerQueryService; + this.scheduleQueryService = scheduleQueryService; this.noticeService = noticeService; this.memberQueryService = memberQueryService; } @@ -94,7 +96,6 @@ public DashBoardEmployeeDTO selectInfoForEmployee(String memberLoginId) throws G contractSearchDTO.setEndDate(endAt); Integer unreadContract = Math.toIntExact(contractQueryService.selectBySearchEmployee(contractSearchDTO, pageable).getTotalElements()); dashBoardEmployeeDTO.setUnreadContract(unreadContract); - System.out.println("unreadContract" + unreadContract); // 이번달 Order 받아오기 OrderSelectSearchDTO orderSelectSearchDTO = new OrderSelectSearchDTO(); @@ -103,7 +104,6 @@ public DashBoardEmployeeDTO selectInfoForEmployee(String memberLoginId) throws G orderSelectSearchDTO.setEndDate(endAt); Integer unreadOrder = Math.toIntExact(orderQueryService.selectSearchOrdersEmployee(orderSelectSearchDTO, pageable).getTotalElements()); dashBoardEmployeeDTO.setUnreadOrder(unreadOrder); - System.out.println("unreadOrder" + unreadOrder); // 이번달 판매내역 받아오기 SalesHistorySearchDTO salesHistorySearchDTO = new SalesHistorySearchDTO(); @@ -115,9 +115,15 @@ public DashBoardEmployeeDTO selectInfoForEmployee(String memberLoginId) throws G Integer totalPrice = resultStatistics.getTotalSales(); dashBoardEmployeeDTO.setTotalPrice(totalPrice); - // 이번달 내 고객 순위 조회 + // 오늘 일정조회 + ArrayList scheduleList = new ArrayList(); + List todaySchedules = scheduleQueryService.findSchedulesByDate(endAt); + for (ScheduleDayDTO schedule : todaySchedules) { + scheduleList.add(schedule.getName()); + } + dashBoardEmployeeDTO.setScheduleTitle(scheduleList); // 이번달 판매사원 순위 ArrayList employeeList = new ArrayList(); From 0223fc5c95065eac65e52c7abb159db51acae777 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Mon, 9 Dec 2024 03:58:37 +0900 Subject: [PATCH 535/563] =?UTF-8?q?feat:=20salesHistory=20=EB=82=A0?= =?UTF-8?q?=EC=A7=9C=20=EB=A7=9E=EC=B6=B0=EC=84=9C=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#239)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/DashBoardQueryServiceImpl.java | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java index 31cbecbc..8b76c031 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java @@ -88,6 +88,12 @@ public DashBoardEmployeeDTO selectInfoForEmployee(String memberLoginId) throws G String startAt = getCurrentTime().substring(0, 7) + "-01"; String endAt = getCurrentTime().substring(0,10); + LocalDate date = LocalDate.parse(endAt); + LocalDate nextDate = date.plusDays(1); + // 프론트에서 +1 처리했기 떄문에 따로 백에서 처리 + String salesEndAt = nextDate.toString(); + + // 이번달 Contract 받아오기 ContractSearchDTO contractSearchDTO = new ContractSearchDTO(); contractSearchDTO.setMemberId(memberLoginId); @@ -109,7 +115,7 @@ public DashBoardEmployeeDTO selectInfoForEmployee(String memberLoginId) throws G SalesHistorySearchDTO salesHistorySearchDTO = new SalesHistorySearchDTO(); salesHistorySearchDTO.setSearcherName(memberLoginId); salesHistorySearchDTO.setStartDate(startAt); - salesHistorySearchDTO.setEndDate(endAt); + salesHistorySearchDTO.setEndDate(salesEndAt); SalesHistoryStatisticsDTO resultStatistics = salesHistoryQueryService.selectStatisticsSearchByEmployee(salesHistorySearchDTO); Integer totalPrice = resultStatistics.getTotalSales(); @@ -134,7 +140,7 @@ public DashBoardEmployeeDTO selectInfoForEmployee(String memberLoginId) throws G salesHistoryRankedDataDTO.setCenterList(centerList); salesHistoryRankedDataDTO.setPeriod("month"); salesHistoryRankedDataDTO.setStartDate(startAt); - salesHistoryRankedDataDTO.setEndDate(endAt); + salesHistoryRankedDataDTO.setEndDate(salesEndAt); salesHistoryRankedDataDTO.setGroupBy("employee"); salesHistoryRankedDataDTO.setOrderBy("totalSales"); @@ -180,6 +186,11 @@ public DashBoardAdminDTO selectInfoForAdmin(String memberLoginId) throws General String startAt = getCurrentTime().substring(0, 7) + "-01"; String endAt = getCurrentTime().substring(0,10); + LocalDate date = LocalDate.parse(endAt); + LocalDate nextDate = date.plusDays(1); + // 프론트에서 +1 처리했기 떄문에 따로 백에서 처리 + String salesEndAt = nextDate.toString(); + // 이번달 Contract 받아오기 ContractSearchDTO contractSearchDTO = new ContractSearchDTO(); contractSearchDTO.setMemberId(memberLoginId); @@ -210,7 +221,7 @@ public DashBoardAdminDTO selectInfoForAdmin(String memberLoginId) throws General SalesHistorySearchDTO salesHistorySearchDTO = new SalesHistorySearchDTO(); salesHistorySearchDTO.setSearcherName(memberLoginId); salesHistorySearchDTO.setStartDate(startAt); - salesHistorySearchDTO.setEndDate(endAt); + salesHistorySearchDTO.setEndDate(salesEndAt); SalesHistoryStatisticsDTO resultStatistics = salesHistoryQueryService.selectStatisticsSearchByEmployee(salesHistorySearchDTO); Integer totalPrice = resultStatistics.getTotalSales(); @@ -225,7 +236,7 @@ public DashBoardAdminDTO selectInfoForAdmin(String memberLoginId) throws General salesHistoryRankedDataDTO.setCenterList(centerList); salesHistoryRankedDataDTO.setPeriod("month"); salesHistoryRankedDataDTO.setStartDate(startAt); - salesHistoryRankedDataDTO.setEndDate(endAt); + salesHistoryRankedDataDTO.setEndDate(salesEndAt); salesHistoryRankedDataDTO.setGroupBy("employee"); salesHistoryRankedDataDTO.setOrderBy("totalSales"); @@ -268,6 +279,11 @@ public DashBoardDirectorDTO selectInfoForDirector(String memberLoginId) throws G String startAt = getCurrentTime().substring(0, 7) + "-01"; String endAt = getCurrentTime().substring(0,10); + LocalDate date = LocalDate.parse(endAt); + LocalDate nextDate = date.plusDays(1); + // 프론트에서 +1 처리했기 떄문에 따로 백에서 처리 + String salesEndAt = nextDate.toString(); + // 승인되지 않은 발주서 건수 PurchaseOrderSelectSearchDTO purchaseOrderSelectSearchDTO = new PurchaseOrderSelectSearchDTO(); purchaseOrderSelectSearchDTO.setMemberId(memberLoginId); @@ -283,7 +299,7 @@ public DashBoardDirectorDTO selectInfoForDirector(String memberLoginId) throws G SalesHistoryRankedDataDTO salesHistoryRankedDataDTO = new SalesHistoryRankedDataDTO(); salesHistoryRankedDataDTO.setPeriod("month"); salesHistoryRankedDataDTO.setStartDate(startAt); - salesHistoryRankedDataDTO.setEndDate(endAt); + salesHistoryRankedDataDTO.setEndDate(salesEndAt); salesHistoryRankedDataDTO.setGroupBy("center"); salesHistoryRankedDataDTO.setOrderBy("totalSales"); From e4e38febea81c54892879c33b3311d23f802ff7e Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Mon, 9 Dec 2024 10:10:48 +0900 Subject: [PATCH 536/563] =?UTF-8?q?feat:=20=EB=8C=80=EC=8B=9C=EB=B3=B4?= =?UTF-8?q?=EB=93=9C=20=EA=B3=B5=EC=A7=80=EC=82=AC=ED=95=AD=20redirectUrl?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9=20(#239)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/service/DashBoardQueryServiceImpl.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java index 8b76c031..056d6e72 100644 --- a/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/dashBoard/query/service/DashBoardQueryServiceImpl.java @@ -160,12 +160,19 @@ public DashBoardEmployeeDTO selectInfoForEmployee(String memberLoginId) throws G searchDTO.setTag("ALL"); Page noticePage = noticeService.findNotices(pageable, searchDTO); + System.out.println("noticePage = " + noticePage); + for (NoticeDTO notice : noticePage.getContent()) { Map noticeData = new HashMap<>(); noticeData.put("title", notice.getTitle()); // NoticeDTO의 title - noticeData.put("content", notice.getContent()); // NoticeDTO의 content + noticeData.put("content", "/notice/detail?tag=" + notice.getTag() + "&classification=" + notice.getClassification() + + "¬iceTitle=" + notice.getTitle() + "¬iceContent=" + notice.getContent() + + "¬iceId=" + notice.getNoticeId()); // NoticeDTO의 redirectUrl noticeList.add(noticeData); } + + System.out.println("noticeLsit = " + noticeList); + dashBoardEmployeeDTO.setNoticeList(noticeList); return dashBoardEmployeeDTO; @@ -259,7 +266,9 @@ public DashBoardAdminDTO selectInfoForAdmin(String memberLoginId) throws General for (NoticeDTO notice : noticePage.getContent()) { Map noticeData = new HashMap<>(); noticeData.put("title", notice.getTitle()); // NoticeDTO의 title - noticeData.put("content", notice.getContent()); // NoticeDTO의 content + noticeData.put("content", "/notice/detail?tag=" + notice.getTag() + "&classification=" + notice.getClassification() + + "¬iceTitle=" + notice.getTitle() + "¬iceContent=" + notice.getContent() + + "¬iceId=" + notice.getNoticeId()); // NoticeDTO의 redirectUrl noticeList.add(noticeData); } dashBoardAdminDTO.setNoticeList(noticeList); From 5b0685174f3c02f7d7a5b8dca181e71a0e177c62 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Mon, 9 Dec 2024 14:05:34 +0900 Subject: [PATCH 537/563] =?UTF-8?q?feat=20print=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/application/controller/NoticeController.java | 5 ----- .../command/domain/service/NoticeCommandServiceImpl.java | 2 -- 2 files changed, 7 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java index f9f434a1..64c1cfd5 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/application/controller/NoticeController.java @@ -91,19 +91,14 @@ public ResponseEntity modifyNotice( noticeModifyDTO.setMemberLoginId(memberLoginId); noticeModifyDTO.setContent(noticeModifyDTO.getContent()); Notice updateNotice = modelMapper.map(noticeModifyDTO, Notice.class); - System.out.println("1."+updateNotice.getFileUrl()); if(noticeModifyDTO.getFileUrl()==null){ - System.out.println("테스트중"); noticeModifyDTO.setFileUrl(updateNotice.getFileUrl()); } if (file != null && !file.isEmpty()) { - System.out.println("1번"); noticeModifyDTO.setFileUrl(s3FileService.uploadOneFile(file)); }else if(file==null || file.isEmpty()) { - System.out.println("2번"); noticeModifyDTO.setFileUrl(updateNotice.getFileUrl()); } else { - System.out.println("3번"); noticeModifyDTO.setFileUrl(s3FileService.uploadOneFile(file)); } noticeCommandService.modifyNotice(noticeId,noticeModifyDTO, principal); diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java index 2c6e461c..c7e4de97 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeCommandServiceImpl.java @@ -94,8 +94,6 @@ public NoticeModifyDTO modifyNotice(String id, NoticeModifyDTO noticeModifyDTO,P } try { Notice updateNotice = modelMapper.map(noticeModifyDTO, Notice.class); - updateNotice.setTag(notice.getTag());//수정예정 - updateNotice.setClassification(notice.getClassification());//수정예정 updateNotice.setNoticeId(notice.getNoticeId()); updateNotice.setMemberId(notice.getMemberId()); updateNotice.setCreatedAt(notice.getCreatedAt()); From 2e2d2e3c56963a66f5ba77961aa727f5320ac107 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Mon, 9 Dec 2024 17:24:52 +0900 Subject: [PATCH 538/563] =?UTF-8?q?fix:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=9B=84=20=EA=B6=8C=ED=95=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/config/RequestMatcherConfig.java | 42 ++++++++----------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java index f7b572e8..b9ddc61b 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java @@ -145,9 +145,9 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception .requestMatchers(HttpMethod.GET, "/api/v1/notice").hasAnyRole("notice-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") .requestMatchers(HttpMethod.GET, "/api/v1/notice/{noticeId}").hasAnyRole("notice-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") .requestMatchers(HttpMethod.GET, "/api/v1/notice/excel").hasAnyRole("notice-excel-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") - .requestMatchers(HttpMethod.POST, "/api/v1/notice").hasAnyRole("notice-post", "GOD", "DIRECTOR") - .requestMatchers(HttpMethod.PUT, "/api/v1/notice/{noticeId}").hasAnyRole("notice-id-put", "GOD", "DIRECTOR") - .requestMatchers(HttpMethod.DELETE, "/api/v1/notice/{noticeId}").hasAnyRole("notice-id-delete", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.POST, "/api/v1/notice").hasAnyRole("notice-post", "GOD", "DIRECTOR", "EMPLOYEE", "ADMIN") + .requestMatchers(HttpMethod.PUT, "/api/v1/notice/{noticeId}").hasAnyRole("notice-id-put", "GOD", "DIRECTOR", "EMPLOYEE", "ADMIN") + .requestMatchers(HttpMethod.DELETE, "/api/v1/notice/{noticeId}").hasAnyRole("notice-id-delete", "GOD", "DIRECTOR", "EMPLOYEE", "ADMIN") // Problem API .requestMatchers(HttpMethod.GET, "/api/v1/problem").hasAnyRole("problem-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") @@ -162,9 +162,9 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception .requestMatchers(HttpMethod.GET, "/api/v1/promotion").hasAnyRole("promotion-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") .requestMatchers(HttpMethod.GET, "/api/v1/promotion/{promotionId}").hasAnyRole("promotion-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") .requestMatchers(HttpMethod.GET, "/api/v1/promotion/excel").hasAnyRole("promotion-excel-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") - .requestMatchers(HttpMethod.POST, "/api/v1/promotion").hasAnyRole("promotion-post", "GOD", "DIRECTOR") - .requestMatchers(HttpMethod.PUT, "/api/v1/promotion/{promotionId}").hasAnyRole("promotion-id-put", "GOD", "DIRECTOR") - .requestMatchers(HttpMethod.DELETE, "/api/v1/promotion/{promotionId}").hasAnyRole("promotion-id-delete", "GOD", "DIRECTOR") + .requestMatchers(HttpMethod.POST, "/api/v1/promotion").hasAnyRole("promotion-post", "GOD", "DIRECTOR", "EMPLOYEE", "ADMIN") + .requestMatchers(HttpMethod.PUT, "/api/v1/promotion/{promotionId}").hasAnyRole("promotion-id-put", "GOD", "DIRECTOR", "EMPLOYEE", "ADMIN") + .requestMatchers(HttpMethod.DELETE, "/api/v1/promotion/{promotionId}").hasAnyRole("promotion-id-delete", "GOD", "DIRECTOR", "EMPLOYEE", "ADMIN") // File API .requestMatchers(HttpMethod.POST, "/api/v1/file").hasAnyRole("file-post", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") @@ -181,10 +181,10 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception .requestMatchers(HttpMethod.DELETE, "/api/v1/evaluation/{id}").hasAnyRole("evaluation-id-delete", "GOD", "ADMIN") // Product API - .requestMatchers(HttpMethod.GET, "/api/v1/product").hasAnyRole("product-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") .requestMatchers(HttpMethod.GET, "/api/v1/product/excel").hasAnyRole("product-excel-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") .requestMatchers(HttpMethod.GET, "/api/v1/product/{productId}").hasAnyRole("product-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") .requestMatchers(HttpMethod.GET, "/api/v1/product/search").hasAnyRole("product-search-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/product").hasAnyRole("product-post", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") // Center API .requestMatchers(HttpMethod.GET, "/api/v1/center").hasAnyRole("center-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") @@ -198,28 +198,20 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception // Sales History API .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory").hasAnyRole("salesHistory-get", "GOD", "DIRECTOR", "ADMIN") - .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/{salesHistoryId}").hasAnyRole("salesHistory-id-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/excel").hasAnyRole("salesHistory-excel-get", "GOD", "EMPLOYEE", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics").hasAnyRole("salesHistory-statistics-post", "GOD", "DIRECTOR", "ADMIN") .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/search").hasAnyRole("salesHistory-statistics-search-post", "GOD", "DIRECTOR", "ADMIN") - .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/search/year").hasAnyRole("salesHistory-statistics-search-year-post", "GOD", "DIRECTOR", "ADMIN") .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/search/month").hasAnyRole("salesHistory-statistics-search-month-post", "GOD", "DIRECTOR", "ADMIN") - .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/center/search").hasAnyRole("salesHistory-statistics-center-search-post", "GOD", "DIRECTOR", "ADMIN") - .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/center/search/year").hasAnyRole("salesHistory-statistics-center-search-year-post", "GOD", "DIRECTOR", "ADMIN") - .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/center/search/month").hasAnyRole("salesHistory-statistics-center-search-month-post", "GOD", "DIRECTOR", "ADMIN") - .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/average/employee").hasAnyRole("salesHistory-statistics-average-employee-post", "GOD", "DIRECTOR", "ADMIN") - .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/average/center").hasAnyRole("salesHistory-statistics-average-center-post", "GOD", "DIRECTOR", "ADMIN") - .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/all").hasAnyRole("salesHistory-statistics-all-post", "GOD", "DIRECTOR", "ADMIN") - .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/all/year").hasAnyRole("salesHistory-statistics-all-year-post", "GOD", "DIRECTOR", "ADMIN") - .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/all/month").hasAnyRole("salesHistory-statistics-all-month-post", "GOD", "DIRECTOR", "ADMIN") - .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/search").hasAnyRole("salesHistory-search-post", "GOD", "DIRECTOR", "ADMIN") - .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/best").hasAnyRole("salesHistory-best", "GOD", "DIRECTOR", "ADMIN") - .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee").hasAnyRole("salesHistory-employee-get", "GOD", "EMPLOYEE") - .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics").hasAnyRole("salesHistory-employee-statistics-get", "GOD", "EMPLOYEE") - .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search").hasAnyRole("salesHistory-employee-statistics-search-get", "GOD", "EMPLOYEE") - .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search/year").hasAnyRole("salesHistory-employee-statistics-search-year-get", "GOD", "EMPLOYEE") - .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search/month").hasAnyRole("salesHistory-employee-statistics-search-month-get", "GOD", "EMPLOYEE") - .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/employee/search").hasAnyRole("salesHistory-employee-search-post", "GOD", "EMPLOYEE") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/average").hasAnyRole("salesHistory-statistics-search-average", "GOD", "DIRECTOR", "ADMIN", "EMPLOYEE") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/all").hasAnyRole("salesHistory-statistics-search-all", "GOD", "DIRECTOR", "ADMIN", "EMPLOYEE") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/search").hasAnyRole("salesHistory-statistics-search-post", "GOD", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/best").hasAnyRole("salesHistory-statistics-best-post", "GOD", "DIRECTOR", "ADMIN", "EMPLOYEE") + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics").hasAnyRole("salesHistory-employee-statistics-get", "GOD", "EMPLOYEE", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search").hasAnyRole("salesHistory-employee-statistics-search-get", "GOD", "EMPLOYEE", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search/month").hasAnyRole("salesHistory-employee-statistics-search-month-get", "GOD", "EMPLOYEE", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/employee/search").hasAnyRole("salesHistory-employee-search-post", "GOD", "EMPLOYEE", "ADMIN") + // DashBoard API .requestMatchers(HttpMethod.GET, "/api/v1/dashBoard/employee").hasAnyRole("dashboard-get", "GOD", "DIRECTOR", "ADMIN", "EMPLOYEE") From 6665df2e0ca2487b75f62c04413aeb63f36ba33b Mon Sep 17 00:00:00 2001 From: giuseog Date: Mon, 9 Dec 2024 18:05:07 +0900 Subject: [PATCH 539/563] =?UTF-8?q?fix:=20=EB=A7=A4=ED=8D=BC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95(#235)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/product/query/repository/ProductMapper.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml b/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml index f6120300..7ec87675 100644 --- a/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/product/query/repository/ProductMapper.xml @@ -88,7 +88,7 @@ b.opt_sun_roof, b.opt_sond FROM tb_product a - JOIN tb_product_option b ON a.prod_id = b.product_id + JOIN tb_product_option b ON a.prod_id = b.prod_id WHERE a.prod_id = #{productId} AND a.active = TRUE From 86c4ac442dc487fb2e07c98309dc09c870976581 Mon Sep 17 00:00:00 2001 From: EuiHyeok Date: Mon, 9 Dec 2024 18:05:33 +0900 Subject: [PATCH 540/563] =?UTF-8?q?feat:=20=EC=B0=A8=ED=8A=B8=20=EA=B6=8C?= =?UTF-8?q?=ED=95=9C=20=ED=95=B4=EC=A0=9C=20(#183)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/config/RequestMatcherConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java index f7b572e8..090bd839 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java @@ -218,7 +218,7 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics").hasAnyRole("salesHistory-employee-statistics-get", "GOD", "EMPLOYEE") .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search").hasAnyRole("salesHistory-employee-statistics-search-get", "GOD", "EMPLOYEE") .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search/year").hasAnyRole("salesHistory-employee-statistics-search-year-get", "GOD", "EMPLOYEE") - .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search/month").hasAnyRole("salesHistory-employee-statistics-search-month-get", "GOD", "EMPLOYEE") + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search/month").hasAnyRole("salesHistory-employee-statistics-search-month-get", "GOD", "DIRECTOR", "ADMIN", "EMPLOYEE") .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/employee/search").hasAnyRole("salesHistory-employee-search-post", "GOD", "EMPLOYEE") // DashBoard API From 3356c5db2ed3eb55b4376b701cf83f0d06e83ca8 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Mon, 9 Dec 2024 22:31:03 +0900 Subject: [PATCH 541/563] =?UTF-8?q?fix=20:=20=EA=B3=84=EC=95=BD=EC=84=9C?= =?UTF-8?q?=20=EC=A0=95=EB=A0=AC=20=EA=B3=84=EC=95=BD=EC=9D=BC=EC=9E=90=20?= =?UTF-8?q?(#248)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/contract/query/repository/ContractMapper.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index b8cdd75c..079eb66a 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -326,6 +326,9 @@ ORDER BY a.conr_id ${sortOrder} + + ORDER BY a.created_at ${sortOrder} + ORDER BY a.created_at DESC @@ -412,6 +415,9 @@ ORDER BY a.conr_id ${sortOrder} + + ORDER BY a.created_at ${sortOrder} + ORDER BY a.created_at DESC @@ -662,6 +668,9 @@ ORDER BY a.conr_id ${sortOrder} + + ORDER BY a.created_at ${sortOrder} + ORDER BY a.created_at DESC From ed4361464ab449d5bd546a509e155ab7f7e6b2ff Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Tue, 10 Dec 2024 00:31:30 +0900 Subject: [PATCH 542/563] =?UTF-8?q?fix:=20=EA=B3=84=EC=95=BD=EC=84=9C,=20?= =?UTF-8?q?=EC=88=98=EC=A3=BC=EC=84=9C,=20=EB=B0=9C=EC=A3=BC=EC=84=9C=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=ED=9B=84=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#263)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/OrderController.java | 4 +++ .../controller/PurchaseOrderController.java | 4 +++ .../order/query/repository/OrderMapper.xml | 2 +- .../query/repository/PurchaseOrderMapper.xml | 36 +++++++++++++++++-- 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java b/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java index 41ab2b34..058b47f0 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/controller/OrderController.java @@ -6,6 +6,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -23,6 +24,7 @@ import java.security.GeneralSecurityException; import java.security.Principal; +@Slf4j @RestController("queryOrderController") @RequestMapping("/api/v1/order") public class OrderController { @@ -96,6 +98,7 @@ public ResponseEntity getSearchOrderEmployee(@RequestParam @RequestParam(required = false) String searchMemberId, @RequestParam(required = false) String startDate, @RequestParam(required = false) String endDate, + @RequestParam(required = false) String productName, @RequestParam(required = false) String sortField, @RequestParam(required = false) String sortOrder, Principal principal, @@ -109,6 +112,7 @@ public ResponseEntity getSearchOrderEmployee(@RequestParam orderSelectSearchDTO.setStartDate(startDate); orderSelectSearchDTO.setEndDate(endDate); orderSelectSearchDTO.setMemberId(principal.getName()); + orderSelectSearchDTO.setProductName(productName); if (sortField != null && sortOrder != null) { Sort.Direction direction = sortOrder.equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC; diff --git a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java index 1a427994..0b3bb3e5 100644 --- a/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java +++ b/src/main/java/stanl_2/final_backend/domain/purchase_order/query/controller/PurchaseOrderController.java @@ -89,6 +89,7 @@ public ResponseEntity getSearchPurchaseOrdersAdmin @RequestParam(required = false) String searchMemberId, @RequestParam(required = false) String startDate, @RequestParam(required = false) String endDate, + @RequestParam(required = false) String productName, @RequestParam(required = false) String sortField, @RequestParam(required = false) String sortOrder, Principal principal, @@ -108,6 +109,7 @@ public ResponseEntity getSearchPurchaseOrdersAdmin purchaseOrderSelectSearchDTO.setEndDate(endDate); purchaseOrderSelectSearchDTO.setSearchMemberId(searchMemberId); purchaseOrderSelectSearchDTO.setMemberId(principal.getName()); + purchaseOrderSelectSearchDTO.setProductName(productName); Page responsePurchaseOrder = purchaseOrderQueryService.selectSearchPurchaseOrderAdmin(purchaseOrderSelectSearchDTO, pageable); @@ -171,6 +173,7 @@ public ResponseEntity getSearchPurchaseOrders(@Req @RequestParam(required = false) String endDate, @RequestParam(required = false) String sortField, @RequestParam(required = false) String sortOrder, + @RequestParam(required = false) String productName, @PageableDefault(size = 10) Pageable pageable) throws GeneralSecurityException { // 정렬 추가 @@ -186,6 +189,7 @@ public ResponseEntity getSearchPurchaseOrders(@Req purchaseOrderSelectSearchDTO.setStartDate(startDate); purchaseOrderSelectSearchDTO.setEndDate(endDate); purchaseOrderSelectSearchDTO.setSearchMemberId(searchMemberId); + purchaseOrderSelectSearchDTO.setProductName(productName); Page responsePurchaseOrder = purchaseOrderQueryService.selectSearchPurchaseOrder(purchaseOrderSelectSearchDTO, pageable); diff --git a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml index c1a4aa1e..6c22c9bb 100644 --- a/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/order/query/repository/OrderMapper.xml @@ -325,7 +325,7 @@ ORDER BY a.created_at ${sortOrder} - ORDER BY e.prod_name ${sortOrder} + ORDER BY c.prod_name ${sortOrder} ORDER BY a.created_at DESC diff --git a/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml b/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml index a9984d91..0c81dd83 100644 --- a/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/purchase_order/query/repository/PurchaseOrderMapper.xml @@ -135,7 +135,7 @@ AND DATE_ADD(#{purchaseOrderSelectSearchDTO.endDate}, INTERVAL 1 DAY) - AND f.prod_name = #{productName} + AND f.prod_name LIKE CONCAT('%', #{purchaseOrderSelectSearchDTO.productName}, '%') @@ -160,6 +160,9 @@ ORDER BY a.created_at ${sortOrder} + + ORDER BY a.admin_id ${sortOrder} + ORDER BY a.created_at DESC @@ -177,6 +180,16 @@ SELECT COUNT(*) AS cnt FROM tb_purchase_order a + LEFT JOIN tb_order b + ON a.ord_id = b.ord_id + LEFT JOIN tb_member c + ON a.mem_id = c.mem_id + LEFT JOIN tb_member d + ON a.admin_id = d.mem_id + LEFT JOIN tb_contract e + ON b.conr_id = e.conr_id + LEFT JOIN tb_product f + ON e.prod_id = f.prod_id a.active = TRUE AND a.mem_id = #{purchaseOrderSelectSearchDTO.memberId} @@ -192,6 +205,9 @@ AND a.mem_id = #{purchaseOrderSelectSearchDTO.searchMemberId} + + AND f.prod_name LIKE CONCAT('%', #{purchaseOrderSelectSearchDTO.productName}, '%') + AND a.created_at BETWEEN #{purchaseOrderSelectSearchDTO.startDate} AND DATE_ADD(#{purchaseOrderSelectSearchDTO.endDate}, INTERVAL 1 DAY) @@ -290,7 +306,7 @@ AND DATE_ADD(#{purchaseOrderSelectSearchDTO.endDate}, INTERVAL 1 DAY) - AND f.prod_name = #{productName} + AND f.prod_name LIKE CONCAT('%', #{purchaseOrderSelectSearchDTO.productName}, '%') @@ -315,6 +331,9 @@ ORDER BY a.created_at ${sortOrder} + + ORDER BY a.admin_id ${sortOrder} + ORDER BY a.created_at DESC @@ -332,6 +351,16 @@ SELECT COUNT(*) AS cnt FROM tb_purchase_order a + LEFT JOIN tb_order b + ON a.ord_id = b.ord_id + LEFT JOIN tb_member c + ON a.mem_id = c.mem_id + LEFT JOIN tb_member d + ON a.admin_id = d.mem_id + LEFT JOIN tb_contract e + ON b.conr_id = e.conr_id + LEFT JOIN tb_product f + ON e.prod_id = f.prod_id a.active = TRUE @@ -346,6 +375,9 @@ AND a.mem_id = #{purchaseOrderSelectSearchDTO.searchMemberId} + + AND f.prod_name LIKE CONCAT('%', #{purchaseOrderSelectSearchDTO.productName}, '%') + AND a.created_at BETWEEN #{purchaseOrderSelectSearchDTO.startDate} AND DATE_ADD(#{purchaseOrderSelectSearchDTO.endDate}, INTERVAL 1 DAY) From bcd383dcc6a32c51239f8395c0527a0312ff1f87 Mon Sep 17 00:00:00 2001 From: giuseog Date: Tue, 10 Dec 2024 00:53:27 +0900 Subject: [PATCH 543/563] =?UTF-8?q?feat:=20=EC=9D=BC=EB=B3=84=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80(#183)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SalesHistoryController.java | 30 +++++++++++++++++++ .../query/dto/SalesHistorySearchDTO.java | 1 + .../query/repository/SalesHistoryMapper.java | 2 ++ .../service/SalesHistoryQueryService.java | 2 ++ .../service/SalesHistoryQueryServiceImpl.java | 20 ++++++++++++- .../security/config/RequestMatcherConfig.java | 15 +++++----- .../query/repository/SalesHistoryMapper.xml | 29 ++++++++++++++++-- 7 files changed, 89 insertions(+), 10 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java index 3e919fad..616a93a0 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java @@ -211,6 +211,7 @@ public ResponseEntity getStatisticsSearchByEmployee salesHistorySearchDTO.setSearcherName(principal.getName()); salesHistorySearchDTO.setStartDate(params.get("startDate")); salesHistorySearchDTO.setEndDate(params.get("endDate")); + salesHistorySearchDTO.setPeriod(params.get("period")); SalesHistoryStatisticsDTO responseSalesHistory = salesHistoryQueryService.selectStatisticsSearchByEmployee(salesHistorySearchDTO); @@ -221,6 +222,35 @@ public ResponseEntity getStatisticsSearchByEmployee .build()); } + @Operation(summary = "사원 통계(실적,수당,매출액) 일자별 검색") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @GetMapping("employee/statistics/search/daily") + public ResponseEntity getStatisticsSearchByEmployeeDaily(@RequestParam Map params + ,Principal principal, + @PageableDefault(size = 20) Pageable pageable){ + + SalesHistorySearchDTO salesHistorySearchDTO = new SalesHistorySearchDTO(); + + salesHistorySearchDTO.setSearcherName(principal.getName()); + salesHistorySearchDTO.setStartDate(params.get("startDate")); + salesHistorySearchDTO.setEndDate(params.get("endDate")); + salesHistorySearchDTO.setPeriod(params.get("period")); + + List responseSalesHistory = salesHistoryQueryService.selectStatisticsSearchByEmployeeDaily(salesHistorySearchDTO); + + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("사원 통계(실적,수당,매출액) 일자별 검색 성공") + .result(responseSalesHistory) + .build()); + } + + @Operation(summary = "사원 통계(실적,수당,매출액) 월 별 검색") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java index f388a289..360c2d70 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/dto/SalesHistorySearchDTO.java @@ -24,4 +24,5 @@ public class SalesHistorySearchDTO { private List contractList; private String contractId; private String productId; + private String period; } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java index 30d8312f..0444af39 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.java @@ -30,6 +30,8 @@ List findAllSalesHistory(@Param("size") int size SalesHistoryStatisticsDTO findStatisticsSearchByEmployee(@Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); + List findStatisticsSearchByEmployeeDaily(@Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); + List findStatisticsSearchMonthByEmployee(@Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); List findStatisticsSearchYearByEmployee(@Param("salesHistorySearchDTO") SalesHistorySearchDTO salesHistorySearchDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java index 181c66b0..47579510 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java @@ -19,6 +19,8 @@ public interface SalesHistoryQueryService { SalesHistoryStatisticsDTO selectStatisticsSearchByEmployee(SalesHistorySearchDTO salesHistorySearchDTO); + List selectStatisticsSearchByEmployeeDaily(SalesHistorySearchDTO salesHistorySearchDTO); + List selectStatisticsSearchMonthByEmployee(SalesHistorySearchDTO salesHistorySearchDTO); List selectStatisticsSearchYearByEmployee(SalesHistorySearchDTO salesHistorySearchDTO); diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java index 21451c0d..2a2b071d 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java @@ -272,9 +272,27 @@ public SalesHistoryStatisticsDTO selectStatisticsSearchByEmployee(SalesHistorySe SalesHistoryStatisticsDTO salesHistoryStatisticsDTO = salesHistoryMapper.findStatisticsSearchByEmployee(salesHistorySearchDTO); + if(salesHistoryStatisticsDTO == null){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } return salesHistoryStatisticsDTO; } + @Override + @Transactional(readOnly = true) + public List selectStatisticsSearchByEmployeeDaily(SalesHistorySearchDTO salesHistorySearchDTO) { + + salesHistorySearchDTO.setSearcherName(authQueryService.selectMemberIdByLoginId(salesHistorySearchDTO.getSearcherName())); + + List salesHistoryStatisticsDTOList = salesHistoryMapper.findStatisticsSearchByEmployeeDaily(salesHistorySearchDTO); + + if(salesHistoryStatisticsDTOList == null || salesHistoryStatisticsDTOList.isEmpty()){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } + return salesHistoryStatisticsDTOList; + } + + @Override @Transactional(readOnly = true) public List selectStatisticsSearchMonthByEmployee(SalesHistorySearchDTO salesHistorySearchDTO) { @@ -289,7 +307,7 @@ public List selectStatisticsSearchMonthByEmployee(Sal List salesHistoryStatisticsDTOList = salesHistoryMapper.findStatisticsSearchMonthByEmployee(salesHistorySearchDTO); - if(salesHistoryStatisticsDTOList == null){ + if(salesHistoryStatisticsDTOList == null || salesHistoryStatisticsDTOList.isEmpty()){ throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); } return salesHistoryStatisticsDTOList; diff --git a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java index f7b572e8..41bc14f9 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java @@ -207,19 +207,20 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/center/search").hasAnyRole("salesHistory-statistics-center-search-post", "GOD", "DIRECTOR", "ADMIN") .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/center/search/year").hasAnyRole("salesHistory-statistics-center-search-year-post", "GOD", "DIRECTOR", "ADMIN") .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/center/search/month").hasAnyRole("salesHistory-statistics-center-search-month-post", "GOD", "DIRECTOR", "ADMIN") - .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/average/employee").hasAnyRole("salesHistory-statistics-average-employee-post", "GOD", "DIRECTOR", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/average").hasAnyRole("salesHistory-statistics-average-employee-post", "GOD", "DIRECTOR", "ADMIN", "EMPLOYEE") .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/average/center").hasAnyRole("salesHistory-statistics-average-center-post", "GOD", "DIRECTOR", "ADMIN") .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/all").hasAnyRole("salesHistory-statistics-all-post", "GOD", "DIRECTOR", "ADMIN") .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/all/year").hasAnyRole("salesHistory-statistics-all-year-post", "GOD", "DIRECTOR", "ADMIN") .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/all/month").hasAnyRole("salesHistory-statistics-all-month-post", "GOD", "DIRECTOR", "ADMIN") .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/search").hasAnyRole("salesHistory-search-post", "GOD", "DIRECTOR", "ADMIN") - .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/best").hasAnyRole("salesHistory-best", "GOD", "DIRECTOR", "ADMIN") - .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee").hasAnyRole("salesHistory-employee-get", "GOD", "EMPLOYEE") - .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics").hasAnyRole("salesHistory-employee-statistics-get", "GOD", "EMPLOYEE") - .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search").hasAnyRole("salesHistory-employee-statistics-search-get", "GOD", "EMPLOYEE") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/best").hasAnyRole("salesHistory-best", "GOD", "DIRECTOR", "ADMIN", "EMPLOYEE") + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee").hasAnyRole("salesHistory-employee-get", "GOD", "EMPLOYEE", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics").hasAnyRole("salesHistory-employee-statistics-get", "GOD", "EMPLOYEE", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search").hasAnyRole("salesHistory-employee-statistics-search-get", "GOD", "EMPLOYEE", "ADMIN") + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search/daily").hasAnyRole("salesHistory-employee-statistics-search-get", "GOD", "EMPLOYEE", "ADMIN") .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search/year").hasAnyRole("salesHistory-employee-statistics-search-year-get", "GOD", "EMPLOYEE") - .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search/month").hasAnyRole("salesHistory-employee-statistics-search-month-get", "GOD", "EMPLOYEE") - .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/employee/search").hasAnyRole("salesHistory-employee-search-post", "GOD", "EMPLOYEE") + .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search/month").hasAnyRole("salesHistory-employee-statistics-search-month-get", "GOD", "EMPLOYEE", "ADMIN") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/employee/search").hasAnyRole("salesHistory-employee-search-post", "GOD", "EMPLOYEE", "ADMIN") // DashBoard API .requestMatchers(HttpMethod.GET, "/api/v1/dashBoard/employee").hasAnyRole("dashboard-get", "GOD", "DIRECTOR", "ADMIN", "EMPLOYEE") diff --git a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml index 6ca8f53e..4cf32e85 100644 --- a/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/sales_history/query/repository/SalesHistoryMapper.xml @@ -442,7 +442,9 @@ + + + + + \ No newline at end of file From fedd4b5498d8878bb59e5fd79e8304fec289c705 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Tue, 10 Dec 2024 14:52:34 +0900 Subject: [PATCH 547/563] =?UTF-8?q?fix:=20=EB=8D=94=EB=AF=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/dataloader/Initializer.java | 232 +++++++----------- 1 file changed, 89 insertions(+), 143 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java index c705bc8e..8347087f 100644 --- a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java +++ b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java @@ -70,10 +70,10 @@ public class Initializer implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { - // 우리 계정 + // 우리 계정1 createOrUpdateMember( - "god", - "god", + "M000000000", + "pass", "신하늘", "god@stanl.com", 0, @@ -82,9 +82,9 @@ public void run(ApplicationArguments args) throws Exception { "010-0000-0000", "서울 동작구 보라매로 87", "시스템 관리자", - "중졸", + "Associate", "REGULAR", - "미필", + "FULFILLED", "한국은행", "000-0000-000000-00000", "CEN_000000000", @@ -93,21 +93,21 @@ public void run(ApplicationArguments args) throws Exception { loadImage("god.png") ); - // 심사위원 1 계정 + // 우리 계정2 createOrUpdateMember( - "god1", - "god1", - "이름1", + "M99999999", + "pass", + "신하늘", "god@stanl.com", 0, "MALE", "000000-0000000", "010-0000-0000", "서울 동작구 보라매로 87", - "심사위원", - "중졸", - "TEMPORARY", - "미필", + "시스템 관리자", + "Associate", + "REGULAR", + "FULFILLED", "한국은행", "000-0000-000000-00000", "CEN_000000000", @@ -116,116 +116,95 @@ public void run(ApplicationArguments args) throws Exception { loadImage("god.png") ); - // 심사위원 2 계정 + // 심사위원 1 계정 createOrUpdateMember( - "god2", - "god2", - "이름2", - "god@stanl.com", - 0, + "M000000001", + "pass1", + "고윤정", + "Yunn29@stanl.com", + 31, "MALE", - "000000-0000000", + "951109-1000000", "010-0000-0000", "서울 동작구 보라매로 87", - "심사위원", - "중졸", + "INTERN", + "Associate", "TEMPORARY", - "미필", + "FULFILLED", "한국은행", "000-0000-000000-00000", - "CEN_000000000", - "ORG_000000000", - "GOD", + "CEN_000000001", + "ORG_000000001", + "EMPLOYEE", loadImage("god.png") ); - // 심사위원 3 계정 + // 심사위원 2 계정 createOrUpdateMember( - "god3", - "god3", - "이름3", - "god@stanl.com", - 0, + "M000000002", + "pass2", + "차은우", + "enUU22@stanl.com", + 43, "MALE", - "000000-0000000", - "010-0000-0000", + "871204-1000000", + "010-1234-2321", "서울 동작구 보라매로 87", - "심사위원", - "중졸", + "ASSISTANT", + "Associate", "TEMPORARY", - "미필", + "FULFILLED", "한국은행", - "000-0000-000000-00000", - "CEN_000000000", - "ORG_000000000", - "GOD", + "000-0200-000100-00030", + "CEN_000000001", + "ORG_000000001", + "ADMIN", loadImage("god.png") ); - + // 심사위원 3 계정 createOrUpdateMember( - "eee", - "eee", - "사원1", - "employee@stanl.com", - 0, - "MALE", - "000000-0000000", - "010-0000-0000", + "M000000003", + "pass3", + "윤세연", + "sey1@stanl.com", + 54, + "FEMALE", + "800912-2000000", + "010-3231-4573", "서울 동작구 보라매로 87", - "사원", - "중졸", + "EXECUTIVE", + "Master", "TEMPORARY", - "미필", + "FULFILLED", "한국은행", "000-0000-000000-00000", "CEN_000000001", - "ORG_000000000", - "EMPLOYEE", - loadImage("default.png") + "ORG_000000001", + "DIRECTOR", + loadImage("god.png") ); - createOrUpdateMember( - "aaa", - "aaa", - "관리자1", - "admin@stanl.com", - 0, - "MALE", - "000000-0000000", - "010-0000-0000", - "서울 동작구 보라매로 87", - "영업 관리자", - "중졸", - "TEMPORARY", - "미필", - "한국은행", - "000-0000-000000-00000", - "CEN_000000001", - "ORG_000000000", - "ADMIN", - loadImage("default.png") - ); createOrUpdateMember( - "ddd", - "ddd", - "담당자1", - "director@stanl.com", - 0, + "M000000004", + "pass4", + "이재용", + "gdragon11@stanl.com", + 77, "MALE", - "000000-0000000", - "010-0000-0000", + "760122-1000000", + "010-5830-2842", "서울 동작구 보라매로 87", - "영업 담당자", - "중졸", + "CEO", + "Doctoral", "TEMPORARY", - "미필", + "FULFILLED", "한국은행", "000-0000-000000-00000", "CEN_000000001", - "ORG_000000000", - "DIRECTOR", + "ORG_000000001", + "GOD", loadImage("default.png") ); @@ -237,8 +216,25 @@ public void run(ApplicationArguments args) throws Exception { String[] militaryStatus = {"FULFILLED", "EXEMPTION", "UNFULFILLED"}; String[] genders = {"MALE", "FEMALE"}; String[] roles = {"EMPLOYEE", "ADMIN", "DIRECTOR"}; - String[] lastNames = {"김", "이", "박", "최", "정", "강", "조", "유", "윤", "장", "임", "기", "방", "하", "도", "한", "손", "송", "오", "조", "서", "배", "홍", "류", "신", "권", "곽"}; - String[] firstNames = {"민수", "지훈", "서연", "예준", "하은", "도현", "지원", "유진", "현우", "수아", "수빈", "주희", "지아", "준호", "혜린", "지민", "은지", "시우", "다영", "태현", "연우"}; + String[] lastNames = { + "김", "이", "박", "최", "정", "강", "조", "유", "윤", "장", "임", "기", "방", "하", "도", "한", "손", "송", "오", "조", "서", "배", "홍", "류", "신", "권", "곽", + "황", "안", "전", "문", "탁", "모", "남", "우", "차", "백", "표", "양", "변", "설", "염", "석", "심", "함", "노", "채", "진", "민", "엄", "원", "천", "방", "공", + "현", "나", "제", "고", "성", "라", "마", "탁", "하", "사", "여", "용", "호", "범", "소", "운", "계", "도", "서", + "주", "두", "해", "율", "민", "익", "선", "학", "판", "예", "형", "양", "천", "어", "현", "종", "운", "필", "탁", "중", + "후", "은", "치", "간", "일", "규", "화", "순", "평", "다", "목", "택", "봉" + }; + + String[] firstNames = { + "민수", "지훈", "서연", "예준", "하은", "도현", "지원", "유진", "현우", "수아", "수빈", "주희", "지아", "준호", "혜린", "지민", "은지", "시우", "다영", "태현", "연우", + "가은", "민준", "서준", "예진", "윤서", "지우", "승현", "시현", "다현", "태민", "소윤", "해준", "유정", "현서", "윤하", "수현", "혜수", "가연", "지호", "정우", + "다빈", "채영", "우진", "민아", "성민", "윤호", "지훈", "하린", "승우", "지윤", "소현", "예슬", "아린", "주원", "희준", "은채", "서아", "주영", "도윤", "정민", + "하윤", "나연", "규현", "수영", "시윤", "도경", "서윤", "지율", "혜진", "민혁", "태훈", "유나", "재민", "세연", "은서", "재현", "다윤", "연서", "예서", "하율", + "준영", "현진", "승민", "재윤", "희연", "시영", "수진", "서우", "태연", "준서", "수영", "나영", "다희", "채린", "윤채", "다훈", "민지", "현민", "선우", "하경", + "지안", "수빈", "은호", "아윤", "재희", "태영", "정현", "예원", "은혜", "소라", "다은", "우빈", "예린", "서희", "유빈", "하진", "선호", "은우", "예빈", "혜연", + "지혁", "다훈", "채희", "지완", "민호", "다온", "수경", "은빈", "채원", "하연", "정빈", "나희", "소희", "시후", "태후", "민후", "서영", "정윤", "채윤", "도은", + "가빈", "나영", "현우", "세빈", "도현", "혜빈", "준혁", "은성", "다솔", "유영", "태솔", "희영", "채우", "소연", "나윤", "수환", "정환", "승환", "도환", "은환" + }; + String[] addresses = { "서울특별시 강남구 테헤란로", "부산광역시 해운대구 센텀중앙로", @@ -254,7 +250,7 @@ public void run(ApplicationArguments args) throws Exception { // 회원 및 역할 생성 로직 - for (int i = 1; i <= 93; i++) { + for (int i = 5; i <= 102; i++) { int centerId = random.nextInt(10) + 1; int orgId = random.nextInt(10) + 5; String sex = genders[(i+1) % 2]; @@ -617,7 +613,7 @@ public void run(ApplicationArguments args) throws Exception { // 고객 인당 100명 씩 if(customerRepository.count() == 0){ - for (int i = 1; i <= 100; i++) { + for (int i = 1; i <= 4; i++) { String memberId = String.format("MEM_%09d", i); for (int j = 0; j < 100; j++) { String sex = genders[i % 2]; @@ -837,56 +833,6 @@ public void run(ApplicationArguments args) throws Exception { log.info("Product(제품) 테이블에 이미 데이터가 존재합니다."); } - - // 일정 저장 - // 2024년 9월 - ScheduleRegistDTO newSchedule1 = new ScheduleRegistDTO("자동차 영업 정책 교육", "영업 전략 및 판매 방향성 교육", "TRAINING", "2024-09-04 10:00:00", "2024-09-06 12:30:00", "god", null); - scheduleCommandService.registSchedule(newSchedule1); - ScheduleRegistDTO newSchedule2 = new ScheduleRegistDTO("기우석 고객님 미팅", "신형 ME25 차량 견적 관련 미팅", "MEETING", "2024-09-10 14:00:00", "2024-09-10 16:00:00", "god", null); - scheduleCommandService.registSchedule(newSchedule2); - ScheduleRegistDTO newSchedule3 = new ScheduleRegistDTO("팀별 정기 회의", "사내 불편사항 및 개선점 여부 회의", "SESSION", "2024-09-15 09:00:00", "2024-09-15 11:00:00", "god", null); - scheduleCommandService.registSchedule(newSchedule3); - -// 2024년 10월 - ScheduleRegistDTO newSchedule4 = new ScheduleRegistDTO("영업 전략 워크샵", "판매 방법론 교육 일정 (서초)", "TRAINING", "2024-10-16 10:00:00", "2024-10-18 18:30:00", "god", null); - scheduleCommandService.registSchedule(newSchedule4); - ScheduleRegistDTO newSchedule5 = new ScheduleRegistDTO("휴가", "가족과 함께하는 휴가", "VACATION", "2024-10-07 00:00:00", "2024-10-10 23:59:59", "god", null); - scheduleCommandService.registSchedule(newSchedule5); - ScheduleRegistDTO newSchedule6 = new ScheduleRegistDTO("김민석 고객님 미팅", "KM7 차량 견적 상담 미팅", "MEETING", "2024-10-14 13:00:00", "2024-10-14 15:00:00", "god", null); - scheduleCommandService.registSchedule(newSchedule6); - -// 2024년 11월 - ScheduleRegistDTO newSchedule7 = new ScheduleRegistDTO("영업 기술 교육", "상담 스킬 강화 교육", "TRAINING", "2024-11-05 10:00:00", "2024-11-05 12:30:00", "god", null); - scheduleCommandService.registSchedule(newSchedule7); - ScheduleRegistDTO newSchedule8 = new ScheduleRegistDTO("내부 회의", "성과 리뷰 회의", "SESSION", "2024-11-10 09:00:00", "2024-11-10 11:00:00", "god", null); - scheduleCommandService.registSchedule(newSchedule8); - ScheduleRegistDTO newSchedule9 = new ScheduleRegistDTO("고객 미팅", "중요 고객과의 미팅", "MEETING", "2024-11-15 14:00:00", "2024-11-15 16:00:00", "god", null); - scheduleCommandService.registSchedule(newSchedule9); - -// 2024년 12월 - ScheduleRegistDTO newSchedule10 = new ScheduleRegistDTO("영업 전략 교육", "판매 방법론 및 전략 교육", "TRAINING", "2024-12-02 10:00:00", "2024-12-02 12:30:00", "god", null); - scheduleCommandService.registSchedule(newSchedule10); - ScheduleRegistDTO newSchedule11 = new ScheduleRegistDTO("휴가", "연말 휴가", "VACATION", "2024-12-10 00:00:00", "2024-12-12 23:59:59", "god", null); - scheduleCommandService.registSchedule(newSchedule11); - ScheduleRegistDTO newSchedule12 = new ScheduleRegistDTO("내부 회의", "팀워크 강화 미팅", "SESSION", "2024-12-20 10:00:00", "2024-12-20 12:00:00", "god", null); - scheduleCommandService.registSchedule(newSchedule12); - -// 2025년 1월 - ScheduleRegistDTO newSchedule13 = new ScheduleRegistDTO("고객 미팅", "새해 첫 고객 미팅", "MEETING", "2025-01-05 13:00:00", "2025-01-05 15:00:00", "god", null); - scheduleCommandService.registSchedule(newSchedule13); - ScheduleRegistDTO newSchedule14 = new ScheduleRegistDTO("영업 정책 교육", "새로운 영업 정책 교육", "TRAINING", "2025-01-10 10:00:00", "2025-01-10 12:30:00", "god", null); - scheduleCommandService.registSchedule(newSchedule14); - ScheduleRegistDTO newSchedule15 = new ScheduleRegistDTO("내부 회의", "전략 회의", "SESSION", "2025-01-15 09:00:00", "2025-01-15 11:00:00", "god", null); - scheduleCommandService.registSchedule(newSchedule15); - -// 2025년 2월 - ScheduleRegistDTO newSchedule16 = new ScheduleRegistDTO("고객 미팅", "고객 니즈 분석 미팅", "MEETING", "2025-02-03 13:00:00", "2025-02-03 15:00:00", "god", null); - scheduleCommandService.registSchedule(newSchedule16); - ScheduleRegistDTO newSchedule17 = new ScheduleRegistDTO("휴가", "겨울 휴가", "VACATION", "2025-02-07 00:00:00", "2025-02-10 23:59:59", "god", null); - scheduleCommandService.registSchedule(newSchedule17); - ScheduleRegistDTO newSchedule18 = new ScheduleRegistDTO("영업 회의", "전략 회의 및 피드백", "SESSION", "2025-02-15 10:00:00", "2025-02-15 12:00:00", "god", null); - scheduleCommandService.registSchedule(newSchedule18); - // 제품 옵션 등록 if(productOptionRepository.count() == 0){ ProductOption newProductOption1 = new ProductOption('K', 'N', 'A', 'A', 'L', '4', '2', 'A', 'P', 'A', 'U', '1', '0', '1', 'B', 'W', '1', '1', '1', '1', '1', '0', '1', '1', '1'); From 83b5d417542f034630f0882df6421ad2b0527fd1 Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Tue, 10 Dec 2024 15:15:43 +0900 Subject: [PATCH 548/563] =?UTF-8?q?fix:=20=EC=9A=B0=EB=A6=AC=20=EA=B3=84?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stanl_2/final_backend/global/dataloader/Initializer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java index 8347087f..ee4c1590 100644 --- a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java +++ b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java @@ -95,7 +95,7 @@ public void run(ApplicationArguments args) throws Exception { // 우리 계정2 createOrUpdateMember( - "M99999999", + "M999999999", "pass", "신하늘", "god@stanl.com", From aff9b02c61f304d6b718b91fd3ce24f10b25be3b Mon Sep 17 00:00:00 2001 From: Bang1999 Date: Tue, 10 Dec 2024 15:16:15 +0900 Subject: [PATCH 549/563] =?UTF-8?q?fix:=20=EC=9A=B0=EB=A6=AC=EA=B3=84?= =?UTF-8?q?=EC=A0=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stanl_2/final_backend/global/dataloader/Initializer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java index ee4c1590..29675e79 100644 --- a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java +++ b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java @@ -75,7 +75,7 @@ public void run(ApplicationArguments args) throws Exception { "M000000000", "pass", "신하늘", - "god@stanl.com", + "god1@stanl.com", 0, "MALE", "000000-0000000", @@ -98,7 +98,7 @@ public void run(ApplicationArguments args) throws Exception { "M999999999", "pass", "신하늘", - "god@stanl.com", + "god2@stanl.com", 0, "MALE", "000000-0000000", From 3f5d53ff91b4674851128c70a7824a313edc1a26 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 10 Dec 2024 16:33:25 +0900 Subject: [PATCH 550/563] =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/config/ProdSecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index 11a27de5..7ac56c44 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -80,7 +80,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); - config.setAllowedOrigins(Collections.singletonList("http://localhost:5173")); + config.setAllowedOrigins(Collections.singletonList("https://stanl2motive.com/")); config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); config.setAllowCredentials(true); config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "X-XSRF-TOKEN")); From 130c74201ee080dc8d4dc1ca53908d75ef82da8c Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Tue, 10 Dec 2024 17:32:08 +0900 Subject: [PATCH 551/563] =?UTF-8?q?=ED=94=84=EB=A1=9C=EB=AA=A8=EC=85=98=20?= =?UTF-8?q?=EA=B3=A0=EA=B0=9D=EC=95=84=EC=9D=B4=EB=94=94=20nullable?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/problem/command/domain/aggregate/entity/Problem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java index 3827d556..65eb4b3b 100644 --- a/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java +++ b/src/main/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/entity/Problem.java @@ -46,7 +46,7 @@ public class Problem { @Column(name = "ACTIVE", nullable = false) private Boolean active = true; - @Column(name = "CST_ID", nullable = false) + @Column(name = "CST_ID") private String customerId; @Column(name = "MEM_ID", nullable = false) From 1d55fb37c0e0fc18c7a79175235e8ce42b9540f3 Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Tue, 10 Dec 2024 19:55:46 +0900 Subject: [PATCH 552/563] =?UTF-8?q?fix:=20=EA=B3=84=EC=95=BD=EC=84=9C=20?= =?UTF-8?q?=EC=82=AC=EC=9B=90=20=EB=82=B4=EA=BA=BC=EB=A7=8C=20=EB=B3=B4?= =?UTF-8?q?=EA=B8=B0=20=EC=88=98=EC=A0=95=20(#263)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/contract/query/repository/ContractMapper.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index 079eb66a..50d76f92 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -267,6 +267,7 @@ FROM tb_contract a a.active = TRUE + AND a.mem_id = #{contractSearchDTO.memberId} AND a.mem_id = #{contractSearchDTO.searchMemberId} @@ -453,6 +454,7 @@ FROM tb_contract a a.active = TRUE + AND a.mem_id = #{contractSearchDTO.memberId} AND a.mem_id = #{contractSearchDTO.searchMemberId} From 4b60dc0cab813b60330c9756c9646d9589a0b971 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 11 Dec 2024 02:16:22 +0900 Subject: [PATCH 553/563] =?UTF-8?q?=ED=94=84=EB=A1=9C=EB=AA=A8=EC=85=98=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20=EC=A4=91=20=EC=82=AD=EC=A0=9C=20=EC=8B=9C=20?= =?UTF-8?q?=EC=9D=B4=EC=8A=88=20=EB=B0=9C=EA=B2=AC=20=EB=B0=8F=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PromotionController.java | 2 +- .../service/PromotionCommandService.java | 2 +- .../service/PromotionServiceImpl.java | 15 +-- .../security/config/ProdSecurityConfig.java | 2 +- .../domain/service/NoticeServiceTests.java | 93 ++++++++++++++++++ .../service/ProblemServiceTests.java | 94 +++++++++++++++++++ .../service/PromotionServiceTests.java | 92 ++++++++++++++++++ 7 files changed, 291 insertions(+), 9 deletions(-) create mode 100644 src/test/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeServiceTests.java create mode 100644 src/test/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceTests.java create mode 100644 src/test/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceTests.java diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java index b7e1a1ac..259e591a 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/controller/PromotionController.java @@ -115,7 +115,7 @@ public ResponseEntity modifyNotice( content = {@Content(schema = @Schema(implementation = PromotionResponseMessage.class))}) }) @DeleteMapping("{promotionId}") - public ResponseEntity deleteNotice(@PathVariable String promotionId, Principal principal) { + public ResponseEntity deleteNotice(@PathVariable String promotionId, Principal principal) throws GeneralSecurityException { promotionCommandService.deletePromotion(promotionId,principal); diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/service/PromotionCommandService.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/service/PromotionCommandService.java index a69c2dc0..db34fc6e 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/application/service/PromotionCommandService.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/application/service/PromotionCommandService.java @@ -12,5 +12,5 @@ public interface PromotionCommandService { PromotionModifyDTO modifyPromotion(String promotionId, PromotionModifyDTO promotionModifyDTO, Principal principal) throws GeneralSecurityException; - void deletePromotion(String promotionId, Principal principal); + void deletePromotion(String promotionId, Principal principal) throws GeneralSecurityException; } diff --git a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java index 5a9f6ba1..5f347db5 100644 --- a/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceImpl.java @@ -102,19 +102,22 @@ public PromotionModifyDTO modifyPromotion(String promotionId, PromotionModifyDTO @Transactional @Override - public void deletePromotion(String promotionId, Principal principal) { + public void deletePromotion(String promotionId, Principal principal) throws GeneralSecurityException { redisService.clearPromotionCache(); - String memberId= principal.getName(); + String loginId= principal.getName(); + String memberId = authQueryService.selectMemberIdByLoginId(loginId); + memberId=memberQueryService.selectNameById(memberId); Promotion promotion = promotionRepository.findById(promotionId) .orElseThrow(()-> new PromotionCommonException(PromotionErrorCode.PROMOTION_NOT_FOUND)); - if(promotion.getMemberId().equals(memberId)){ + if(!promotion.getMemberId().equals(memberId)){ // 권한 오류 throw new PromotionCommonException(PromotionErrorCode.AUTHORIZATION_VIOLATION); } - promotion.setActive(false); - promotion.setDeletedAt(getCurrentTimestamp()); - + else { + promotion.setActive(false); + promotion.setDeletedAt(getCurrentTimestamp()); + } try { promotionRepository.save(promotion); } catch (DataIntegrityViolationException e) { diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index 7ac56c44..11a27de5 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -80,7 +80,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); - config.setAllowedOrigins(Collections.singletonList("https://stanl2motive.com/")); + config.setAllowedOrigins(Collections.singletonList("http://localhost:5173")); config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); config.setAllowCredentials(true); config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "X-XSRF-TOKEN")); diff --git a/src/test/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeServiceTests.java b/src/test/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeServiceTests.java new file mode 100644 index 00000000..bbaa8eec --- /dev/null +++ b/src/test/java/stanl_2/final_backend/domain/notices/command/domain/service/NoticeServiceTests.java @@ -0,0 +1,93 @@ +package stanl_2.final_backend.domain.notices.command.domain.service; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.modelmapper.ModelMapper; +import stanl_2.final_backend.domain.alarm.command.application.service.AlarmCommandService; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; +import stanl_2.final_backend.domain.notices.command.application.dto.NoticeAlarmDTO; +import stanl_2.final_backend.domain.notices.command.application.dto.NoticeDeleteDTO; +import stanl_2.final_backend.domain.notices.command.application.dto.NoticeRegistDTO; +import stanl_2.final_backend.domain.notices.command.domain.aggragate.entity.Notice; +import stanl_2.final_backend.domain.notices.command.domain.repository.NoticeRepository; +import stanl_2.final_backend.global.redis.RedisService; + +import java.security.Principal; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class NoticeServiceTests { + + @InjectMocks + private NoticeCommandServiceImpl noticeCommandService; + + @Mock + private NoticeRepository noticeRepository; + + @Mock + private RedisService redisService; + + @Mock + private AuthQueryService authQueryService; + + @Mock + private AlarmCommandService alarmCommandService; + + @Mock + private MemberQueryService memberQueryService; + + @Mock + private ModelMapper modelMapper; + + @Mock + private Principal principal; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void 게시글_등록() throws Exception { + NoticeRegistDTO registDTO = new NoticeRegistDTO(); + registDTO.setMemberLoginId("loginId"); + Notice notice = new Notice(); + when(modelMapper.map(registDTO, Notice.class)).thenReturn(notice); + when(noticeRepository.save(notice)).thenReturn(notice); + NoticeAlarmDTO alarmDTO = new NoticeAlarmDTO(); + when(modelMapper.map(notice, NoticeAlarmDTO.class)).thenReturn(alarmDTO); + + + noticeCommandService.registerNotice(registDTO, principal); + + + verify(alarmCommandService).sendNoticeAlarm(alarmDTO); + } + + + + @Test + void 게시글_삭제() { + // Arrange + NoticeDeleteDTO deleteDTO = new NoticeDeleteDTO(); + deleteDTO.setNoticeId("validId"); + + Notice notice = new Notice(); + notice.setMemberId("memberId"); + when(noticeRepository.findByNoticeId("validId")).thenReturn(Optional.of(notice)); + when(principal.getName()).thenReturn("memberId"); + + // Act + noticeCommandService.deleteNotice(deleteDTO, principal); + + // Assert + verify(noticeRepository).save(notice); + assertNotNull(notice.getDeletedAt()); + } +} diff --git a/src/test/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceTests.java b/src/test/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceTests.java new file mode 100644 index 00000000..a50ed39e --- /dev/null +++ b/src/test/java/stanl_2/final_backend/domain/problem/command/domain/aggregate/service/ProblemServiceTests.java @@ -0,0 +1,94 @@ +package stanl_2.final_backend.domain.problem.command.domain.aggregate.service; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.modelmapper.ModelMapper; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; +import stanl_2.final_backend.domain.problem.command.application.dto.ProblemRegistDTO; +import stanl_2.final_backend.domain.problem.command.domain.aggregate.entity.Problem; +import stanl_2.final_backend.domain.problem.command.domain.aggregate.repository.ProblemRepository; +import stanl_2.final_backend.global.redis.RedisService; + +import java.security.Principal; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class ProblemServiceTests { + + @InjectMocks + private ProblemServiceImpl problemService; + @Mock + private ProblemRepository problemRepository; + + @Mock + private AuthQueryService authQueryService; + + @Mock + private MemberQueryService memberQueryService; + + @Mock + private RedisService redisService; + + @Mock + private ModelMapper modelMapper; + + @Mock + private Principal principal; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void 문제사항_등록() throws Exception { + ProblemRegistDTO registDTO = new ProblemRegistDTO(); + registDTO.setMemberLoginId("loginId"); + registDTO.setContent("contentTest"); + when(authQueryService.selectMemberIdByLoginId("loginId")).thenReturn("memberId"); + when(memberQueryService.selectNameById("memberId")).thenReturn("memberName"); + Problem problem = new Problem(); + when(modelMapper.map(registDTO, Problem.class)).thenReturn(problem); + when(problemRepository.save(problem)).thenReturn(problem); + + + problemService.registerProblem(registDTO, principal); + + + verify(redisService).clearProblemCache(); + verify(authQueryService).selectMemberIdByLoginId("loginId"); + verify(memberQueryService).selectNameById("memberId"); + verify(problemRepository).save(problem); + } + + + @Test + void 문제사항_상태수정() { + Problem problem = new Problem(); + problem.setMemberId("memberId"); + when(problemRepository.findById("problemId")).thenReturn(Optional.of(problem)); + when(principal.getName()).thenReturn("memberId1"); + + problemService.modifyStatus("problemId", principal); + + assertEquals("DONE", problem.getStatus()); + } + + @Test + void 문제사항_삭제() { + Problem problem = new Problem(); + problem.setMemberId("memberId"); + when(problemRepository.findById("problemId")).thenReturn(Optional.of(problem)); + when(principal.getName()).thenReturn("memberId"); + + problemService.deleteProblem("problemId", principal); + + assertNotNull(problem.getDeletedAt()); + } +} diff --git a/src/test/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceTests.java b/src/test/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceTests.java new file mode 100644 index 00000000..f82eebec --- /dev/null +++ b/src/test/java/stanl_2/final_backend/domain/promotion/command/domain/aggregate/service/PromotionServiceTests.java @@ -0,0 +1,92 @@ +package stanl_2.final_backend.domain.promotion.command.domain.aggregate.service; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.modelmapper.ModelMapper; +import stanl_2.final_backend.domain.member.query.service.AuthQueryService; +import stanl_2.final_backend.domain.member.query.service.MemberQueryService; +import stanl_2.final_backend.domain.promotion.command.application.dto.PromotionRegistDTO; +import stanl_2.final_backend.domain.promotion.command.domain.aggregate.entity.Promotion; +import stanl_2.final_backend.domain.promotion.command.domain.aggregate.repository.PromotionRepository; +import stanl_2.final_backend.global.redis.RedisService; + +import java.security.GeneralSecurityException; +import java.security.Principal; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class PromotionServiceTests { + + @InjectMocks + private PromotionServiceImpl promotionService; + + @Mock + private PromotionRepository promotionRepository; + + @Mock + private AuthQueryService authQueryService; + + @Mock + private MemberQueryService memberQueryService; + + @Mock + private RedisService redisService; + + @Mock + private ModelMapper modelMapper; + + @Mock + private Principal principal; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + // 테스트 1: registerPromotion 성공 케이스 + @Test + void 프로모션_등록() throws Exception { + PromotionRegistDTO registDTO = new PromotionRegistDTO(); + registDTO.setMemberLoginId("loginId"); + registDTO.setContent("promotion content"); + + when(authQueryService.selectMemberIdByLoginId("loginId")).thenReturn("memberId"); + when(memberQueryService.selectNameById("memberId")).thenReturn("memberName"); + + Promotion promotion = new Promotion(); + when(modelMapper.map(registDTO, Promotion.class)).thenReturn(promotion); + when(promotionRepository.save(promotion)).thenReturn(promotion); + + promotionService.registerPromotion(registDTO, principal); + + verify(redisService).clearPromotionCache(); + verify(authQueryService).selectMemberIdByLoginId("loginId"); + verify(memberQueryService).selectNameById("memberId"); + verify(promotionRepository).save(promotion); + } + + @Test + void 프로모션_삭제() throws GeneralSecurityException { + // Arrange + String promotionId = "test"; + String loginId = "loginId"; + String memberId = "memberId"; + + Promotion promotion = new Promotion(); + promotion.setMemberId(memberId); + + when(principal.getName()).thenReturn(loginId); + when(authQueryService.selectMemberIdByLoginId(loginId)).thenReturn(memberId); + when(memberQueryService.selectNameById(memberId)).thenReturn(memberId); + when(promotionRepository.findById(promotionId)).thenReturn(Optional.of(promotion)); + + promotionService.deletePromotion(promotionId, principal); + + assertNotNull(promotion.getDeletedAt()); + } +} \ No newline at end of file From 8fe5b506727470e41d69b5883870337ec189c63c Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 11 Dec 2024 02:43:18 +0900 Subject: [PATCH 554/563] =?UTF-8?q?=ED=94=84=EB=A1=9C=EB=AA=A8=EC=85=98=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20=EC=A4=91=20=EC=82=AD=EC=A0=9C=20=EC=8B=9C=20?= =?UTF-8?q?=EC=9D=B4=EC=8A=88=20=EB=B0=9C=EA=B2=AC=20=EB=B0=8F=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/config/ProdSecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index 11a27de5..7ac56c44 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -80,7 +80,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); - config.setAllowedOrigins(Collections.singletonList("http://localhost:5173")); + config.setAllowedOrigins(Collections.singletonList("https://stanl2motive.com/")); config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); config.setAllowCredentials(true); config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "X-XSRF-TOKEN")); From 26c6905011639526b7f22ebb87d8894edbd5f86e Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 11 Dec 2024 14:32:48 +0900 Subject: [PATCH 555/563] =?UTF-8?q?=EB=8D=94=EB=AF=B8=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/repository/NoticeRepository.java | 4 +- .../query/service/NoticeQueryServiceImpl.java | 27 ++- .../global/config/WebConfig.java | 2 +- .../global/dataloader/Initializer.java | 185 +++++++++++++++++- .../security/config/ProdSecurityConfig.java | 2 +- 5 files changed, 210 insertions(+), 10 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/repository/NoticeRepository.java b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/repository/NoticeRepository.java index c5d00488..a2c2856d 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/command/domain/repository/NoticeRepository.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/command/domain/repository/NoticeRepository.java @@ -3,12 +3,12 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; import stanl_2.final_backend.domain.notices.command.domain.aggragate.entity.Notice; -import stanl_2.final_backend.domain.notices.query.dto.NoticeExcelDownload; -import java.util.List; import java.util.Optional; +@Repository public interface NoticeRepository extends JpaRepository { Page findAll(Pageable pageable); diff --git a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeQueryServiceImpl.java index 8a5d7c33..4ba21318 100644 --- a/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/notices/query/service/NoticeQueryServiceImpl.java @@ -1,6 +1,7 @@ package stanl_2.final_backend.domain.notices.query.service; import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; @@ -18,6 +19,9 @@ import stanl_2.final_backend.global.excel.ExcelUtilsV1; import stanl_2.final_backend.global.redis.RedisService; +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DatabaseMetaData; import java.util.List; import static org.springframework.transaction.support.TransactionSynchronizationManager.isCurrentTransactionReadOnly; @@ -29,6 +33,7 @@ */ @Transactional(readOnly = true) +@Slf4j @Service("queryNoticeServiceImpl") public class NoticeQueryServiceImpl implements NoticeQueryService { private final NoticeMapper noticeMapper; @@ -36,19 +41,32 @@ public class NoticeQueryServiceImpl implements NoticeQueryService { private final RedisService redisService; private final ExcelUtilsV1 excelUtilsV1; private final MemberQueryService memberQueryService; + + private final DataSource dataSource; @Autowired - public NoticeQueryServiceImpl(NoticeMapper noticeMapper, RedisTemplate redisTemplate, RedisService redisService, ExcelUtilsV1 excelUtilsV1, MemberQueryService memberQueryService) { + public NoticeQueryServiceImpl(NoticeMapper noticeMapper, RedisTemplate redisTemplate, RedisService redisService, + ExcelUtilsV1 excelUtilsV1, MemberQueryService memberQueryService, DataSource dataSource) { this.noticeMapper = noticeMapper; this.redisTemplate = redisTemplate; this.redisService =redisService; this.excelUtilsV1 =excelUtilsV1; this.memberQueryService =memberQueryService; + this.dataSource = dataSource; } + private String getDatabaseUrl() { + try (Connection connection = dataSource.getConnection()) { + DatabaseMetaData metaData = connection.getMetaData(); + return metaData.getURL(); + } catch (Exception e) { + throw new RuntimeException("DB URL 조회 실패", e); + } + } @Transactional(readOnly = true) @Override public Page findNotices(Pageable pageable, SearchDTO searchDTO) { + log.info("readOnly = true 적용 시 DB URL: " + getDatabaseUrl()); int offset = Math.toIntExact(pageable.getOffset()); int size = pageable.getPageSize(); System.out.println("3.Transaction ReadOnly: " + isCurrentTransactionReadOnly()); @@ -58,13 +76,13 @@ public Page findNotices(Pageable pageable, SearchDTO searchDTO) { + "::startDate=" + searchDTO.getStartDate()+ "::endDate=" + searchDTO.getEndDate(); List notices = (List) redisTemplate.opsForValue().get(cacheKey); if (notices == null) { - System.out.println("데이터베이스에서 데이터 조회 중..."); + log.info("데이터베이스에서 데이터 조회 중..."); notices = noticeMapper.findNotices(offset, size, searchDTO); if (notices != null && !notices.isEmpty()) { // 데이터가 있을 때만 캐싱 redisService.setKeyWithTTL(cacheKey, notices, 30 * 60); // 캐싱 시 동일 키 사용 } } else { - System.out.println("캐시에서 데이터 조회 중..."); + log.info("캐시에서 데이터 조회 중..."); } notices.forEach(notice -> { try { @@ -75,10 +93,11 @@ public Page findNotices(Pageable pageable, SearchDTO searchDTO) { int totalElements = noticeMapper.findNoticeCount(); // 총 개수 조회 return new PageImpl<>(notices, pageable, totalElements); } - @Transactional(readOnly = true) + @Transactional @Override public NoticeDTO findNotice(String noticeId) { NoticeDTO notice = noticeMapper.findNotice(noticeId); + log.info("Default DB URL: " + getDatabaseUrl()); return notice; } @Transactional(readOnly = true) diff --git a/src/main/java/stanl_2/final_backend/global/config/WebConfig.java b/src/main/java/stanl_2/final_backend/global/config/WebConfig.java index b8508aef..74ffdbe8 100644 --- a/src/main/java/stanl_2/final_backend/global/config/WebConfig.java +++ b/src/main/java/stanl_2/final_backend/global/config/WebConfig.java @@ -10,7 +10,7 @@ public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") // API 경로 매핑 - .allowedOrigins("https://stanl2-final-frontend.vercel.app") + .allowedOrigins("https://stanl2motive.com") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 허용할 HTTP 메서드 .allowedHeaders("Content-Type", "Authorization") // 허용할 요청 헤더 .exposedHeaders("Authorization") // 응답 헤더 노출 diff --git a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java index 29675e79..8351a49f 100644 --- a/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java +++ b/src/main/java/stanl_2/final_backend/global/dataloader/Initializer.java @@ -27,12 +27,18 @@ import stanl_2.final_backend.domain.member.command.application.service.AuthCommandService; import stanl_2.final_backend.domain.member.command.domain.aggregate.entity.Member; import stanl_2.final_backend.domain.member.command.domain.repository.MemberRepository; +import stanl_2.final_backend.domain.notices.command.domain.aggragate.entity.Notice; +import stanl_2.final_backend.domain.notices.command.domain.repository.NoticeRepository; import stanl_2.final_backend.domain.organization.command.domain.aggregate.entity.Organization; import stanl_2.final_backend.domain.organization.command.domain.repository.OrganizationRepository; +import stanl_2.final_backend.domain.problem.command.domain.aggregate.entity.Problem; +import stanl_2.final_backend.domain.problem.command.domain.aggregate.repository.ProblemRepository; import stanl_2.final_backend.domain.product.command.application.domain.aggregate.entity.Product; import stanl_2.final_backend.domain.product.command.application.domain.aggregate.entity.ProductOption; import stanl_2.final_backend.domain.product.command.application.domain.repository.ProductOptionRepository; import stanl_2.final_backend.domain.product.command.application.domain.repository.ProductRepository; +import stanl_2.final_backend.domain.promotion.command.domain.aggregate.entity.Promotion; +import stanl_2.final_backend.domain.promotion.command.domain.aggregate.repository.PromotionRepository; import stanl_2.final_backend.domain.schedule.command.application.dto.ScheduleRegistDTO; import stanl_2.final_backend.domain.schedule.command.application.service.ScheduleCommandService; @@ -42,6 +48,8 @@ import java.nio.file.Files; import java.time.LocalDate; import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.List; import java.util.Random; import java.util.concurrent.ThreadLocalRandom; @@ -51,7 +59,7 @@ public class Initializer implements ApplicationRunner { private final AuthCommandService authCommandService; - + private final NoticeRepository noticeRepository; private final MemberRepository memberRepository; private final CareerRepository careerRepository; private final CertificationRepository certificationRepository; @@ -65,11 +73,123 @@ public class Initializer implements ApplicationRunner { private final ProductRepository productRepository; private final ProductOptionRepository productOptionRepository; - private final ScheduleCommandService scheduleCommandService; + private final PromotionRepository promotionRepository; + private final ProblemRepository problemRepository; @Override public void run(ApplicationArguments args) throws Exception { + if (noticeRepository.count() == 0) { + List notices = Arrays.asList( + new Notice(null, "신차 출시 기념 이벤트", null, "NORMAL", "공지사항

      신차 출시를 기념하여 대규모 이벤트를 진행합니다. 많은 참여 부탁드립니다.

      ", null, null, null, true, "도유정", null), + new Notice(null, "연말 프로모션 안내", null, "STRATEGY", "공지사항

      2024년 연말 프로모션에 대한 상세 안내입니다. 다양한 혜택이 준비되어 있으니 확인해주세요.

      ", null, null, null, true, "도유정", null), + new Notice(null, "고객 만족도 조사 결과 공유", null, "GOAL", "공지사항

      2024년 고객 만족도 조사 결과를 공유드립니다. 앞으로도 최선을 다하겠습니다.

      ", null, null, null, true, "안수환", null), + new Notice(null, "전국 영업팀 실적 발표", null, "NORMAL", "공지사항

      2024년 전국 영업팀 실적 발표 및 시상식 일정 안내입니다.

      ", null, null, null, true, "성지윤", null), + new Notice(null, "신규 고객 유치 프로모션", null, "STRATEGY", "공지사항

      신규 고객 유치를 위한 특별 프로모션에 대한 안내입니다.

      ", null, null, null, true, "양시우", null), + new Notice(null, "분기별 영업 목표 설정 회의", null, "GOAL", "공지사항

      다음 분기의 영업 목표 설정을 위한 회의 일정을 공지드립니다.

      ", null, null, null, true, "율도현", null), + new Notice(null, "신규 서비스 도입 안내", null, "NORMAL", "공지사항

      새로운 서비스를 도입합니다. 상세 내용은 본 공지를 통해 확인해주세요.

      ", null, null, null, true, "하정현", null), + new Notice(null, "자동차 판매 기술 교육", null, "STRATEGY", "공지사항

      영업사원을 위한 최신 자동차 판매 기술 교육을 실시합니다.

      ", null, null, null, true, "도유정", null), + new Notice(null, "우수 영업 사원 시상식", null, "GOAL", "공지사항

      2024년 우수 영업 사원 시상식 일정 및 장소를 안내드립니다.

      ", null, null, null, true, "표수경", null), + new Notice(null, "연말 고객 감사 이벤트", null, "NORMAL", "공지사항

      연말을 맞아 고객 감사 이벤트를 준비하였습니다. 많은 참여 부탁드립니다.

      ", null, null, null, true, "도유정", null), + new Notice(null, "팀워크 향상을 위한 워크샵", null, "STRATEGY", "공지사항

      영업팀 팀워크 강화를 위한 워크샵 일정을 공지드립니다. 많은 참여 부탁드립니다.

      ", null, null, null, true, "현하린", null), + new Notice(null, "고객 관리 시스템 업데이트", null, "NORMAL", "공지사항

      새로운 고객 관리 시스템이 도입됩니다. 상세한 사용 방법은 추가 공지를 통해 안내드립니다.

      ", null, null, null, true, "규예서", null), + new Notice(null, "2024년 상반기 판매 전략 회의", null, "GOAL", "공지사항

      2024년 상반기 판매 목표 및 전략 수립을 위한 회의 일정을 공유합니다.

      ", null, null, null, true, "천하은", null), + new Notice(null, "신규 고객 관리 방안", null, "STRATEGY", "공지사항

      신규 고객 관리를 위한 구체적인 방안과 목표를 안내드립니다.

      ", null, null, null, true, "심태솔", null), + new Notice(null, "2024년 상반기 영업 실적 발표", null, "NORMAL", "공지사항

      상반기 영업 실적 및 하반기 계획에 대해 공유드립니다.

      ", null, null, null, true, "양민지", null), + new Notice(null, "영업 효율화 방안 공유", null, "STRATEGY", "공지사항

      영업 업무를 보다 효율적으로 수행하기 위한 방안을 논의합니다.

      ", null, null, null, true, "유하율", null), + new Notice(null, "서비스 품질 향상 계획 발표", null, "GOAL", "공지사항

      서비스 품질 향상을 위한 세부 계획과 목표를 발표합니다.

      ", null, null, null, true, "봉서우", null), + new Notice(null, "자동차 서비스 부문 고객 설문 결과", null, "NORMAL", "공지사항

      2024년 상반기 고객 설문 조사 결과를 공유합니다. 항상 고객의 목소리에 귀 기울이겠습니다.

      ", null, null, null, true, "도재현", null), + new Notice(null, "고객 감사 이벤트 안내", null, "STRATEGY", "공지사항

      고객 감사의 마음을 담아 특별 이벤트를 준비했습니다.

      ", null, null, null, true, "필승환", null), + new Notice(null, "다음 분기 영업 목표 설정", null, "GOAL", "공지사항

      다음 분기 목표 설정 및 세부 전략을 논의합니다.

      ", null, null, null, true, "한정환", null), + new Notice(null, "신규 고객 초대 프로모션", null, "STRATEGY", "공지사항

      신규 고객을 초대하기 위한 특별 프로모션을 진행합니다. 많은 관심 부탁드립니다.

      ", null, null, null, true, "운예린", null), + new Notice(null, "전국 영업팀 회의 결과 보고", null, "GOAL", "공지사항

      2024년 전국 영업팀 회의 결과를 공유드립니다.

      ", null, null, null, true, "서채우", null), + new Notice(null, "고객 만족도 개선 프로젝트", null, "NORMAL", "공지사항

      고객 만족도를 높이기 위한 개선 프로젝트를 시작합니다. 자세한 내용은 공지를 확인해주세요.

      ", null, null, null, true, "안수환", null), + new Notice(null, "영업 성과 분석 회의", null, "STRATEGY", "공지사항

      영업 성과를 분석하고 개선 방향을 논의하는 회의가 예정되어 있습니다.

      ", null, null, null, true, "성지윤", null), + new Notice(null, "신제품 발표회 개최", null, "GOAL", "공지사항

      새로운 제품에 대한 발표회가 개최됩니다. 많은 관심 부탁드립니다.

      ", null, null, null, true, "양시우", null), + new Notice(null, "하반기 목표 수립 워크샵", null, "NORMAL", "공지사항

      하반기 목표를 수립하기 위한 워크샵 일정이 확정되었습니다.

      ", null, null, null, true, "율도현", null), + new Notice(null, "최신 판매 기술 교육 일정", null, "STRATEGY", "공지사항

      최신 판매 기술을 배우는 교육 프로그램이 준비되었습니다.

      ", null, null, null, true, "하정현", null), + new Notice(null, "전사 협력 강화를 위한 간담회", null, "GOAL", "공지사항

      협력 강화를 위해 간담회를 개최합니다. 많은 참석 바랍니다.

      ", null, null, null, true, "평예원", null), + new Notice(null, "고객 관리 시스템 업그레이드 안내", null, "NORMAL", "공지사항

      고객 관리 시스템이 새롭게 업그레이드됩니다. 사용법은 공지사항을 확인해주세요.

      ", null, null, null, true, "표수경", null), + new Notice(null, "상반기 실적 우수자 발표", null, "GOAL", "공지사항

      2024년 상반기 실적이 우수한 사원들을 발표합니다.

      ", null, null, null, true, "조예원", null), + new Notice(null, "영업 효율성을 위한 가이드라인", null, "STRATEGY", "공지사항

      영업 업무 효율성을 높이기 위한 가이드라인이 발표되었습니다.

      ", null, null, null, true, "현하린", null), + new Notice(null, "고객 대상 특별 감사 이벤트", null, "NORMAL", "공지사항

      고객 감사의 마음을 담아 특별 이벤트를 준비했습니다.

      ", null, null, null, true, "규예서", null), + new Notice(null, "하반기 영업 목표 공유", null, "GOAL", "공지사항

      2024년 하반기 영업 목표와 계획을 공유드립니다.

      ", null, null, null, true, "천하은", null), + new Notice(null, "신규 고객 서비스 정책 발표", null, "STRATEGY", "공지사항

      신규 고객을 위한 서비스 정책이 발표되었습니다.

      ", null, null, null, true, "심태솔", null), + new Notice(null, "우수 영업 팀 시상식 일정", null, "NORMAL", "공지사항

      우수한 실적을 기록한 영업 팀을 시상하는 행사가 열립니다.

      ", null, null, null, true, "양민지", null), + new Notice(null, "상반기 영업 실적 발표", null, "GOAL", "공지사항

      상반기 영업 실적에 대한 보고와 분석이 진행됩니다.

      ", null, null, null, true, "유하율", null), + new Notice(null, "서비스 품질 향상 캠페인", null, "NORMAL", "공지사항

      서비스 품질 향상을 위한 캠페인이 시작됩니다.

      ", null, null, null, true, "봉서우", null), + new Notice(null, "차세대 자동차 기술 세미나", null, "STRATEGY", "공지사항

      최신 자동차 기술에 대해 배우는 세미나가 개최됩니다.

      ", null, null, null, true, "도재현", null), + new Notice(null, "우수 사원 감사 프로그램", null, "GOAL", "공지사항

      우수한 실적을 기록한 사원을 대상으로 감사 프로그램이 진행됩니다.

      ", null, null, null, true, "필승환", null), + new Notice(null, "영업 효율화 방안 발표", null, "NORMAL", "공지사항

      영업 효율화를 위한 새로운 방안이 발표되었습니다.

      ", null, null, null, true, "한정환", null), + new Notice(null, "하반기 실적 우수자 발표", null, "NORMAL", " 공지사항 body>

      2024년 하반기 영업 실적 우수 사원 발표

      2024년 하반기 동안 탁월한 영업 실적을 기록한 우수 사원을 발표합니다.

      모든 영업 사원 여러분들의 노고에 진심으로 감사드리며, 아래와 같이 우수 사원을 선정하였습니다.  

      우수 사원 명단

      운예린: 매출 목표 초과 달성 및 신규 고객 유치 기여

      안수환: 기존 고객 관리 강화 및 계약 연장율 최상위

      율도현: 지역 매출 1위 및 신차 판매 부문 실적 최상위

      성지윤: 고객 만족도 1위 및 판매 후 관리 우수

       이번 우수 사원으로 선정된 분들께는 특별 포상과 함께 감사의 마음을 전합니다.

       앞으로도 모든 분들이 함께 성장하고 발전할 수 있는 환경을 만들어 가겠습니다. 

      시상식 일정

      일시: 2024년 12월 20일 (수) 오후 3시

      장소:</strong> 본사 대강당

      참석 대상:전 직원 

       많은 참석 부탁드리며, 선정된 사원들께 다시 한번 축하의 말씀을 드립니다.

      ※ 문의사항은 인사팀으로 연락 부탁드립니다.

      ", null, null, null, true, "신하늘", null) + ); + for (Notice notice : notices) { + noticeRepository.save(notice); + } + log.info("Notice 데이터가 초기화되었습니다."); + } else { + log.info("Notice 테이블에 이미 데이터가 존재합니다."); + } + + if (promotionRepository.count() == 0) { + List promotions = Arrays.asList( + new Promotion(null, "신규 프로모션 안내", "프로모션

      새로운 프로모션이 시작됩니다. 많은 관심 부탁드립니다.

      ", null, null, null, true, "봉서우", null), + new Promotion(null, "2024년 상반기 이벤트","프로모션

      상반기 동안 진행되는 특별 이벤트를 안내드립니다.

      ", null, null, null, true, "서채우", null), + new Promotion(null, "고객 감사 프로모션", "프로모션

      고객님께 감사드리며 특별 혜택을 제공합니다.

      ", null, null, null, true, "안수환", null), + new Promotion(null, "신차 출시 이벤트", "프로모션

      신차 출시를 기념하여 다양한 혜택을 제공합니다.

      ", null, null, null, true, "봉서우", null), + new Promotion(null, "여름 시즌 프로모션", "프로모션

      여름을 맞아 특별 할인 이벤트가 진행됩니다.

      ", null, null, null, true, "양시우", null), + new Promotion(null, "봄맞이 할인 프로모션", "프로모션

      봄을 맞아 다양한 할인 혜택을 제공합니다.

      ", null, null, null, true, "봉서우", null), + new Promotion(null, "고객 추천 이벤트", "프로모션

      고객 추천 이벤트에 참여하고 다양한 혜택을 받아보세요.

      ", null, null, null, true, "하정현", null), + new Promotion(null, "한정 판매 프로모션", "프로모션

      한정 판매 차량에 대한 특별 할인 혜택을 안내드립니다.

      ", null, null, null, true, "봉서우", null), + new Promotion(null, "고객 감사 특별 할인", "프로모션

      고객님을 위한 특별 할인을 제공합니다.

      ", null, null, null, true, "한정환", null), + new Promotion(null, "신규 고객 환영 이벤트", "프로모션

      신규 고객님을 위한 환영 이벤트를 진행합니다.

      ", null, null, null, true, "현하린", null), + new Promotion(null, "장기 고객 감사 행사", "프로모션

      오랜 기간 함께해주신 고객님들을 위한 감사 행사를 준비했습니다.

      ", null, null, null, true, "규예서", null), + new Promotion(null, "신규 서비스 체험단 모집", "프로모션

      신규 서비스 체험단을 모집합니다. 체험 후기를 공유해 주세요.

      ", null, null, null, true, "천하은", null), + new Promotion(null, "계절별 프로모션 가이드", "프로모션

      각 계절에 맞는 프로모션 가이드를 확인해 보세요.

      ", null, null, null, true, "한정환", null), + new Promotion(null, "고객만족도 조사 이벤트", "프로모션

      고객 만족도 조사 참여 시 특별한 혜택을 드립니다.

      ", null, null, null, true, "양민지", null) + ); + for (Promotion promotion : promotions) { + promotionRepository.save(promotion); + } + log.info("Promotion 데이터가 초기화되었습니다."); + } else { + log.info("Promotion 테이블에 이미 데이터가 존재합니다."); + } + + if (problemRepository.count() == 0) { + List problems = Arrays.asList( + new Problem(null, "차량 발화 문제", "문제사항

      소렌토 차량 관련 발화 문제가 빈번하게 발생하고 있습니다.

      리콜 조치 고려 부탁 드립니다. ", null, null, null, true, null,"율도현","PRO_000000001","PROGRESS", null), + new Problem(null, "제품 교체 관련 문제", "문제사항

      제 담당 고객님이 스팅어 부품 교체 관련 정보 제공 부탁 드립니다.

      ", null, null, null, true, "율도현","봉서우","PRO_000000020","PROGRESS", null), + new Problem(null, "브레이크 소음 문제", "문제사항

      K5 차량 브레이크에서 지속적인 소음이 발생하고 있습니다.

      원인 확인 및 조치 부탁드립니다.", null, null, null, true, "안수환","남유나", "PRO_000000002", "PROGRESS", null), + new Problem(null, "엔진 과열 문제", "문제사항

      스포티지 차량에서 엔진 과열 문제가 보고되었습니다.

      긴급 점검 요청드립니다.", null, null, null, true, "차시현", "전은호", "PRO_000000006", "PROGRESS", null), + new Problem(null, "타이어 마모 문제", "문제사항

      타이어가 비정상적으로 빠르게 마모되고 있습니다.

      교체 및 점검 필요합니다.", null, null, null, true, "운예린","고윤정", "PRO_0000000012", "PROGRESS", null), + new Problem(null, "에어컨 작동 불량", "문제사항

      스팅어 차량의 에어컨이 작동하지 않습니다.

      점검 요청드립니다.", null, null, null, true, "공유영","이재용", "PRO_000000009", "PROGRESS", null), + new Problem(null, "핸들 떨림 문제", "문제사항

      핸들 떨림 현상이 지속적으로 발생하고 있습니다.

      점검 필요합니다.", null, null, null, true, "전예슬", "봉채영", "PRO_000000006", "PROGRESS", null), + new Problem(null, "오일 누출 문제", "문제사항

      엔진 오일이 누출되고 있습니다.

      긴급 점검 바랍니다.", null, null, null, true, "천하은","이재용", "PRO_000000007", "PROGRESS", null), + new Problem(null, "소음 발생 문제", "문제사항

      차량 주행 중 소음이 심하게 발생하고 있습니다.

      원인 분석 요청드립니다.", null, null, null, true, "이시우","목다희", "PRO_000000008", "PROGRESS", null), + new Problem(null, "연비 저하 문제", "문제사항

      차량 연비가 급격히 저하되었습니다.

      점검 요청드립니다.", null, null, null, true,"성지윤", "유하율", "PRO_000000009", "PROGRESS", null), + new Problem(null, "내비게이션 오류", "문제사항

      내비게이션이 정확한 경로를 안내하지 않습니다.

      업데이트 필요합니다.", null, null, null, true, "서채우","채예슬", "PRO_000000010", "PROGRESS", null), + new Problem(null, "리어램프 불량", "문제사항

      리어램프가 정상적으로 작동하지 않습니다.

      교체 요청드립니다.", null, null, null, true, "용하은","곽민아", "PRO_00000004", "PROGRESS", null), + new Problem(null, "디젤 연료 문제", "문제사항

      디젤 차량 연료 공급이 원활하지 않습니다.

      점검 요청드립니다.", null, null, null, true, "하정현","고윤정", "PRO_00000003", "PROGRESS", null), + new Problem(null, "도어 잠김 문제", "문제사항

      운전석 도어 잠금이 해제되지 않습니다.

      점검 바랍니다.", null, null, null, true,"문지완", "은주영", "PRO_000000013", "PROGRESS", null), + new Problem(null, "엔진 소음 문제", "문제사항

      엔진에서 비정상적인 소음이 발생합니다.

      점검 요청드립니다.", null, null, null, true, "문지완","서지윤", "PRO_000000014", "PROGRESS", null), + new Problem(null, "서스펜션 문제", "문제사항

      서스펜션이 제 기능을 하지 못하고 있습니다.

      수리 요청드립니다.", null, null, null, true,"염승환", "봉소라", "PRO_000000015", "PROGRESS", null), + new Problem(null, "라디에이터 누수 문제", "문제사항

      라디에이터에서 누수가 발견되었습니다.

      점검 바랍니다.", null, null, null, true, "운은환","황수아", "PRO_000000016", "PROGRESS", null), + new Problem(null, "엔진 경고등 점등", "문제사항

      엔진 경고등이 점등된 상태입니다.

      점검 필요합니다.", null, null, null, true, "황수아","서지윤", "PRO_000000017", "PROGRESS", null), + new Problem(null, "연료 소비 과다", "문제사항

      연료 소비가 비정상적으로 많습니다.

      점검 바랍니다.", null, null, null, true, "익도환","남유나", "PRO_000000018", "PROGRESS", null), + new Problem(null, "차량 떨림 문제", "문제사항

      차량 주행 중 심한 떨림이 발생합니다.

      점검 요청드립니다.", null, null, null, true,"도유정", "채예슬", "PRO_000000019", "PROGRESS", null), + new Problem(null, "내비게이션 오류", "문제사항

      내비게이션이 정확한 경로를 안내하지 않습니다.

      업데이트 필요합니다.", null, null, null, true, "도유정","채예슬", "PRO_000000010", "PROGRESS", null), + new Problem(null, "오일 누출 문제", "문제사항

      엔진 오일이 누출되고 있습니다.

      긴급 점검 바랍니다.", null, null, null, true, "도유정","이재용", "PRO_000000007", "PROGRESS", null) + ); + for (Problem problem : problems) { + problemRepository.save(problem); + } + log.info("Problem 데이터가 초기화되었습니다."); + } else { + log.info("Problem 테이블에 이미 데이터가 존재합니다."); + } + + // 우리 계정1 createOrUpdateMember( "M000000000", @@ -280,6 +400,8 @@ public void run(ApplicationArguments args) throws Exception { } + + // 영업 관련 경력 String[] salesCareers = { "영업 대표", @@ -1128,4 +1250,63 @@ private void createOrUpdateMember(String loginId, log.info("{} 유저 정보가 이미 존재합니다.", loginId); } } + + + private void createOrUpdateNotice (String loginId, + String password, + String name, + String email, + int age, + String sex, + String identNo, + String phone, + String address, + String position, + String grade, + String jobType, + String military, + String bankName, + String account, + String centerId, + String organizationId, + String role, + MultipartFile imageUrl) throws Exception { + + // db에 정보가 있는지 확인 + Member existingMember = memberRepository.findByLoginId(loginId); + if (existingMember == null) { + // Create the user + SignupDTO signupDTO = new SignupDTO(); + signupDTO.setLoginId(loginId); + signupDTO.setPassword(password); + signupDTO.setName(name); + signupDTO.setEmail(email); + signupDTO.setAge(age); + signupDTO.setSex(sex); + signupDTO.setIdentNo(identNo); + signupDTO.setPhone(phone); + signupDTO.setAddress(address); + signupDTO.setPosition(position); + signupDTO.setGrade(grade); + signupDTO.setJobType(jobType); + signupDTO.setMilitary(military); + signupDTO.setBankName(bankName); + signupDTO.setAccount(account); + signupDTO.setCenterId(centerId); + signupDTO.setOrganizationId(organizationId); + + authCommandService.signup(signupDTO, imageUrl); + + // Grant role to the user + GrantDTO grantDTO = new GrantDTO(); + grantDTO.setLoginId(loginId); + grantDTO.setRole(role); + authCommandService.grantAuthority(grantDTO); + + log.info("{} 유저를 {} 역할로 생성합니다.", loginId, role); + } else { + log.info("{} 유저 정보가 이미 존재합니다.", loginId); + } + } } + diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index 7ac56c44..11a27de5 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -80,7 +80,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); - config.setAllowedOrigins(Collections.singletonList("https://stanl2motive.com/")); + config.setAllowedOrigins(Collections.singletonList("http://localhost:5173")); config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); config.setAllowCredentials(true); config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "X-XSRF-TOKEN")); From 2999bd024b330b0d845f265f6581032136446cf5 Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Wed, 11 Dec 2024 14:33:33 +0900 Subject: [PATCH 556/563] =?UTF-8?q?=EB=8D=94=EB=AF=B8=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/config/ProdSecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java index 11a27de5..a1518063 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/ProdSecurityConfig.java @@ -80,7 +80,7 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, Authentication @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); - config.setAllowedOrigins(Collections.singletonList("http://localhost:5173")); + config.setAllowedOrigins(Collections.singletonList("https://stanl2motive.com")); config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); config.setAllowCredentials(true); config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "X-XSRF-TOKEN")); From 876faf6cf8a95042b7f807c56bc92e4854b2ef7f Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Wed, 11 Dec 2024 16:27:15 +0900 Subject: [PATCH 557/563] =?UTF-8?q?fix:=20=EA=B3=84=EC=95=BD=EC=84=9C=20?= =?UTF-8?q?=EC=A0=9C=ED=92=88=20=ED=85=8C=EC=9D=B4=EB=B8=94=EB=A1=9C=20?= =?UTF-8?q?=EC=A0=9C=ED=92=88=EB=AA=85=20=EB=A7=A4=ED=95=91(#270)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/controller/ContractController.java | 12 +-- .../contract/query/dto/ContractSearchDTO.java | 1 + .../query/dto/ContractSelectAllDTO.java | 1 + .../query/dto/ContractSeletIdDTO.java | 1 + .../domain/order/query/dto/OrderExcelDTO.java | 2 +- .../query/repository/ContractMapper.xml | 100 ++++++++++++------ 6 files changed, 78 insertions(+), 39 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java index c705520f..ddc87aab 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/controller/ContractController.java @@ -92,7 +92,7 @@ public ResponseEntity getContractBySearchEmployee(Princ @RequestParam(required = false) String endDate, @RequestParam(required = false) String customerName, @RequestParam(required = false) String customerClassifcation, - @RequestParam(required = false) String carName, + @RequestParam(required = false) String productName, @RequestParam(required = false) String status, @RequestParam(required = false) String companyName, @RequestParam(required = false) String customerPurchaseCondition, @@ -109,7 +109,7 @@ public ResponseEntity getContractBySearchEmployee(Princ contractSearchDTO.setEndDate(endDate); contractSearchDTO.setCustomerName(customerName); contractSearchDTO.setCustomerClassifcation(customerClassifcation); - contractSearchDTO.setCarName(carName); + contractSearchDTO.setProductName(productName); contractSearchDTO.setStatus(status); contractSearchDTO.setCompanyName(companyName); contractSearchDTO.setCustomerPurchaseCondition(customerPurchaseCondition); @@ -187,7 +187,7 @@ public ResponseEntity getContractBySearchAdmin(Principa @RequestParam(required = false) String endDate, @RequestParam(required = false) String customerName, @RequestParam(required = false) String customerClassifcation, - @RequestParam(required = false) String carName, + @RequestParam(required = false) String productName, @RequestParam(required = false) String status, @RequestParam(required = false) String companyName, @RequestParam(required = false) String customerPurchaseCondition, @@ -204,7 +204,7 @@ public ResponseEntity getContractBySearchAdmin(Principa contractSearchDTO.setEndDate(endDate); contractSearchDTO.setCustomerName(customerName); contractSearchDTO.setCustomerClassifcation(customerClassifcation); - contractSearchDTO.setCarName(carName); + contractSearchDTO.setProductName(productName); contractSearchDTO.setStatus(status); contractSearchDTO.setCompanyName(companyName); contractSearchDTO.setCustomerPurchaseCondition(customerPurchaseCondition); @@ -285,7 +285,7 @@ public ResponseEntity getContractBySearch(@RequestParam @RequestParam(required = false) String endDate, @RequestParam(required = false) String customerName, @RequestParam(required = false) String customerClassifcation, - @RequestParam(required = false) String carName, + @RequestParam(required = false) String productName, @RequestParam(required = false) String status, @RequestParam(required = false) String companyName, @RequestParam(required = false) String customerPurchaseCondition, @@ -307,7 +307,7 @@ public ResponseEntity getContractBySearch(@RequestParam contractSearchDTO.setEndDate(endDate); contractSearchDTO.setCustomerName(customerName); contractSearchDTO.setCustomerClassifcation(customerClassifcation); - contractSearchDTO.setCarName(carName); + contractSearchDTO.setProductName(productName); contractSearchDTO.setStatus(status); contractSearchDTO.setCompanyName(companyName); contractSearchDTO.setCustomerPurchaseCondition(customerPurchaseCondition); diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java index 929d90a5..cbd9b56f 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSearchDTO.java @@ -42,4 +42,5 @@ public class ContractSearchDTO { private String startDate; private String endDate; private String carName; + private String productName; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java index 03e2899c..2555a849 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSelectAllDTO.java @@ -43,4 +43,5 @@ public class ContractSelectAllDTO { private String searchMemberId; private String startDate; private String endDate; + private String productName; } diff --git a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java index d8436007..4071255e 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/query/dto/ContractSeletIdDTO.java @@ -43,4 +43,5 @@ public class ContractSeletIdDTO { private String centerId; private String customerId; private String productId; + private String productName; } diff --git a/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderExcelDTO.java b/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderExcelDTO.java index e6ca3736..c7d5191e 100644 --- a/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderExcelDTO.java +++ b/src/main/java/stanl_2/final_backend/domain/order/query/dto/OrderExcelDTO.java @@ -18,7 +18,7 @@ public class OrderExcelDTO { private String status; @ExcelColumnName(name = "제품명") - private String carName; + private String productName; @ExcelColumnName(name = "수주자") private String memberId; diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index 50d76f92..74bf01d6 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -39,7 +39,7 @@ - + @@ -92,7 +92,7 @@ - + @@ -130,8 +130,11 @@ a.cust_id, a.prod_id, a.mem_id, - a.conr_vehi_pric + a.conr_vehi_pric, + b.prod_name FROM tb_contract a + LEFT JOIN tb_product b + ON a.prod_id = b.prod_id WHERE a.conr_id = #{ contractId } AND a.mem_id = #{ memberId } AND a.active = TRUE @@ -171,8 +174,11 @@ a.cust_id, a.prod_id, a.mem_id, - a.conr_vehi_pric + a.conr_vehi_pric, + b.prod_name FROM tb_contract a + LEFT JOIN tb_product b + ON a.prod_id = b.prod_id WHERE a.conr_id = #{ contractId } AND a.active = TRUE @@ -188,8 +194,11 @@ a.conr_cust_pur_cond, a.conr_cust_cla, a.prod_id, - a.created_at + a.created_at, + b.prod_name FROM tb_contract a + LEFT JOIN tb_product b + ON a.prod_id = b.prod_id WHERE a.mem_id = #{ memberId } AND a.active = TRUE AND a.conr_stat = 'APPROVED' @@ -208,8 +217,11 @@ a.conr_cust_pur_cond, a.conr_cust_cla, a.prod_id, - a.created_at + a.created_at, + b.prod_name FROM tb_contract a + LEFT JOIN tb_product b + ON a.prod_id = b.prod_id WHERE a.active = TRUE AND a.conr_stat = 'APPROVED' @@ -222,8 +234,8 @@ ORDER BY a.conr_ttl ${sortOrder} - - ORDER BY a.conr_car_name ${sortOrder} + + ORDER BY b.prod_name ${sortOrder} ORDER BY a.conr_stat ${sortOrder} @@ -263,8 +275,11 @@ a.conr_stat, a.conr_cust_pur_cond, a.conr_cust_cla, - a.created_at + a.created_at, + b.prod_name FROM tb_contract a + LEFT JOIN tb_product b + ON a.prod_id = b.prod_id a.active = TRUE AND a.mem_id = #{contractSearchDTO.memberId} @@ -295,8 +310,8 @@ AND a.created_at BETWEEN #{contractSearchDTO.startDate} AND DATE_ADD(#{contractSearchDTO.endDate}, INTERVAL 1 DAY) - - AND a.conr_car_name LIKE CONCAT('%', #{contractSearchDTO.carName}, '%') + + AND b.prod_name LIKE CONCAT('%', #{contractSearchDTO.productName}, '%') @@ -309,8 +324,8 @@ ORDER BY a.conr_ttl ${sortOrder} - - ORDER BY a.conr_car_name ${sortOrder} + + ORDER BY b.prod_name ${sortOrder} ORDER BY a.conr_stat ${sortOrder} @@ -353,8 +368,11 @@ a.conr_stat, a.conr_cust_pur_cond, a.conr_cust_cla, - a.created_at + a.created_at, + b.prod_name FROM tb_contract a + LEFT JOIN tb_product b + ON a.prod_id = b.prod_id a.active = TRUE @@ -384,8 +402,8 @@ AND a.created_at BETWEEN #{contractSearchDTO.startDate} AND DATE_ADD(#{contractSearchDTO.endDate}, INTERVAL 1 DAY) - - AND a.conr_car_name LIKE CONCAT('%', #{contractSearchDTO.carName}, '%') + + AND b.prod_name LIKE CONCAT('%', #{contractSearchDTO.productName}, '%') @@ -398,8 +416,8 @@ ORDER BY a.conr_ttl ${sortOrder} - - ORDER BY a.conr_car_name ${sortOrder} + + ORDER BY b.prod_name ${sortOrder} ORDER BY a.conr_stat ${sortOrder} @@ -452,6 +470,8 @@ SELECT COUNT(*) AS cnt FROM tb_contract a + LEFT JOIN tb_product b + ON a.prod_id = b.prod_id a.active = TRUE AND a.mem_id = #{contractSearchDTO.memberId} @@ -482,8 +502,8 @@ AND a.created_at BETWEEN #{contractSearchDTO.startDate} AND DATE_ADD(#{contractSearchDTO.endDate}, INTERVAL 1 DAY) - - AND a.conr_car_name LIKE CONCAT('%', #{contractSearchDTO.carName}, '%') + + AND b.prod_name LIKE CONCAT('%', #{contractSearchDTO.productName}, '%') @@ -492,6 +512,8 @@ SELECT COUNT(*) AS cnt FROM tb_contract a + LEFT JOIN tb_product b + ON a.prod_id = b.prod_id a.active = TRUE @@ -521,8 +543,8 @@ AND a.created_at BETWEEN #{contractSearchDTO.startDate} AND DATE_ADD(#{contractSearchDTO.endDate}, INTERVAL 1 DAY) - - AND a.conr_car_name LIKE CONCAT('%', #{contractSearchDTO.carName}, '%') + + AND b.prod_name LIKE CONCAT('%', #{contractSearchDTO.productName}, '%') @@ -538,8 +560,11 @@ a.conr_cust_pur_cond, a.conr_cust_cla, a.prod_id, - a.created_at + a.created_at, + b.prod_name FROM tb_contract a + LEFT JOIN tb_product b + ON a.prod_id = b.prod_id WHERE a.active = TRUE AND a.cent_id = #{centerId} AND a.conr_stat = 'APPROVED' @@ -589,8 +614,11 @@ a.cust_id, a.prod_id, a.mem_id, - a.conr_vehi_pric + a.conr_vehi_pric, + b.prod_name FROM tb_contract a + LEFT JOIN tb_product b + ON a.prod_id = b.prod_id WHERE a.conr_id = #{ contractId } AND a.cent_id = #{centerId} AND a.active = TRUE @@ -606,8 +634,11 @@ a.conr_stat, a.conr_cust_pur_cond, a.conr_cust_cla, - a.created_at + a.created_at, + b.prod_name FROM tb_contract a + ON a.prod_id = b.prod_id + LEFT JOIN tb_product b a.active = TRUE AND a.cent_id = #{centerId} @@ -638,8 +669,8 @@ AND a.created_at BETWEEN #{contractSearchDTO.startDate} AND DATE_ADD(#{contractSearchDTO.endDate}, INTERVAL 1 DAY) - - AND a.conr_car_name LIKE CONCAT('%', #{contractSearchDTO.carName}, '%') + + AND b.prod_name LIKE CONCAT('%', #{contractSearchDTO.productName}, '%') @@ -652,8 +683,8 @@ ORDER BY a.conr_ttl ${sortOrder} - - ORDER BY a.conr_car_name ${sortOrder} + + ORDER BY b.prod_name ${sortOrder} ORDER BY a.conr_stat ${sortOrder} @@ -689,6 +720,8 @@ @@ -733,8 +766,11 @@ a.conr_car_name, a.conr_stat, a.conr_cust_pur_cond, - a.created_at + a.created_at, + b.prod_name FROM tb_contract a + LEFT JOIN tb_product b + ON a.prod_id = b.prod_id \ No newline at end of file From 4bcf32542dad998f3e02544f3258cd5292e67edb Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Wed, 11 Dec 2024 17:07:14 +0900 Subject: [PATCH 558/563] =?UTF-8?q?fix:=20=EA=B3=84=EC=95=BD=EC=84=9C=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=EC=9E=90=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=95=88=EB=82=98=EC=98=A4=EB=8A=94=EA=B1=B0=20=EC=88=98?= =?UTF-8?q?=EC=A0=95(#270)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/contract/query/repository/ContractMapper.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index 74bf01d6..f8265549 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -637,8 +637,8 @@ a.created_at, b.prod_name FROM tb_contract a - ON a.prod_id = b.prod_id LEFT JOIN tb_product b + ON a.prod_id = b.prod_id a.active = TRUE AND a.cent_id = #{centerId} From 730a907a0e2c5844bf8e275e7ce657013b648df1 Mon Sep 17 00:00:00 2001 From: giuseog Date: Wed, 11 Dec 2024 19:53:59 +0900 Subject: [PATCH 559/563] =?UTF-8?q?fix:=20=EC=82=AC=EC=9B=90=20=ED=8C=90?= =?UTF-8?q?=EB=A7=A4=EB=82=B4=EC=97=AD=20=ED=86=B5=EA=B3=84=20=EC=88=98?= =?UTF-8?q?=EC=A0=95(#141)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SalesHistoryController.java | 27 +++++++++++++ .../service/SalesHistoryQueryService.java | 2 + .../service/SalesHistoryQueryServiceImpl.java | 39 +++++++++++++++++++ .../security/config/RequestMatcherConfig.java | 1 + 4 files changed, 69 insertions(+) diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java index 616a93a0..67eb02ea 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/controller/SalesHistoryController.java @@ -21,6 +21,7 @@ import java.security.GeneralSecurityException; import java.security.Principal; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -250,6 +251,32 @@ public ResponseEntity getStatisticsSearchByEmployee .build()); } + @Operation(summary = "본인 통계(실적,수당,매출액) 조회기간별 검색") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공", + content = {@Content(schema = @Schema(implementation = SalesHistoryResponseMessage.class))}), + @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음", + content = @Content(mediaType = "application/json")) + }) + @PostMapping("statistics/mySearch") + public ResponseEntity getMyStatisticsBySearch(@RequestBody SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, + @PageableDefault(size = 20) Pageable pageable, + Principal principal){ + + + +// salesHistoryRankedDataDTO.setMemberList(memberList); + salesHistoryRankedDataDTO.setMemberId(principal.getName()); + + Page responseSalesHistory = salesHistoryQueryService.selectMyStatisticsBySearch(salesHistoryRankedDataDTO,pageable); + + return ResponseEntity.ok(SalesHistoryResponseMessage.builder() + .httpStatus(200) + .msg("본인 통계(실적,수당,매출액) 검색 성공") + .result(responseSalesHistory) + .build()); + } + @Operation(summary = "사원 통계(실적,수당,매출액) 월 별 검색") @ApiResponses(value = { diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java index 47579510..fdb6fe4b 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryService.java @@ -40,4 +40,6 @@ public interface SalesHistoryQueryService { String selectSalesHistoryIdByContractId(String contractId); Page selectStatisticsBestBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); + + Page selectMyStatisticsBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable); } diff --git a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java index 2a2b071d..0fd47947 100644 --- a/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/sales_history/query/service/SalesHistoryQueryServiceImpl.java @@ -472,4 +472,43 @@ public Page selectStatisticsBestBySearch(SalesHistory return new PageImpl<>(salesHistoryList, pageable, total); } + + @Override + public Page selectMyStatisticsBySearch(SalesHistoryRankedDataDTO salesHistoryRankedDataDTO, Pageable pageable) { + int offset = Math.toIntExact(pageable.getOffset()); + int size = pageable.getPageSize(); + + List memberList = new java.util.ArrayList<>(List.of()); + + memberList.add(authQueryService.selectMemberIdByLoginId(salesHistoryRankedDataDTO.getMemberId())); + + salesHistoryRankedDataDTO.setMemberList(memberList); + + List salesHistoryList = salesHistoryMapper.findStatisticsBySearch(size,offset, salesHistoryRankedDataDTO); + + int total = salesHistoryMapper.findStatisticsBySearchCount(salesHistoryRankedDataDTO); + + if(salesHistoryList.isEmpty() || total == 0){ + throw new SalesHistoryCommonException(SalesHistoryErrorCode.SALES_HISTORY_NOT_FOUND); + } + + salesHistoryList.forEach(salesHistory -> { + try { + if(salesHistory.getMemberId() != null) { + salesHistory.setMemberId(memberQueryService.selectNameById(salesHistory.getMemberId())); + } + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.MEMBER_NOT_FOUND); + } + try { + if(salesHistory.getCenterId() != null) { + salesHistory.setCenterId(centerQueryService.selectNameById(salesHistory.getCenterId())); + } + } catch (Exception e) { + throw new SalesHistoryCommonException(SalesHistoryErrorCode.CENTER_NOT_FOUND); + } + }); + + return new PageImpl<>(salesHistoryList, pageable, total); + } } diff --git a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java index 1d264a5e..6bb10f40 100644 --- a/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java +++ b/src/main/java/stanl_2/final_backend/global/security/config/RequestMatcherConfig.java @@ -207,6 +207,7 @@ public static void configureRequestMatchers(HttpSecurity http) throws Exception .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/all").hasAnyRole("salesHistory-statistics-search-all", "GOD", "DIRECTOR", "ADMIN", "EMPLOYEE") .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/search").hasAnyRole("salesHistory-statistics-search-post", "GOD", "DIRECTOR", "ADMIN") .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/best").hasAnyRole("salesHistory-statistics-best-post", "GOD", "DIRECTOR", "ADMIN", "EMPLOYEE") + .requestMatchers(HttpMethod.POST, "/api/v1/salesHistory/statistics/mySearch").hasAnyRole("salesHistory-statistics-best-post", "GOD", "ADMIN", "EMPLOYEE") .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics").hasAnyRole("salesHistory-employee-statistics-get", "GOD", "EMPLOYEE", "ADMIN") .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search").hasAnyRole("salesHistory-employee-statistics-search-get", "GOD", "EMPLOYEE", "ADMIN") .requestMatchers(HttpMethod.GET, "/api/v1/salesHistory/employee/statistics/search/month").hasAnyRole("salesHistory-employee-statistics-search-month-get", "GOD", "EMPLOYEE", "ADMIN") From 77dfae025eb0ba6d3b884de843a91145bb66b0dc Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Wed, 11 Dec 2024 20:13:09 +0900 Subject: [PATCH 560/563] =?UTF-8?q?fix:=20=EA=B3=84=EC=95=BD=EC=84=9C,=20?= =?UTF-8?q?=EC=88=98=EC=A3=BC=EC=84=9C=20=EC=88=98=EC=A0=95=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EC=9C=A0=EB=AC=B4,=20=EC=83=81=EC=84=B8=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=98=A4=EB=A5=98=20(#274)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/repository/ContractMapper.xml | 18 +++++++++--------- .../member/query/repository/MemberMapper.xml | 3 ++- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml index f8265549..7a6d2bf5 100644 --- a/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/contract/query/repository/ContractMapper.xml @@ -678,31 +678,31 @@ - ORDER BY a.conr_cust_name ${sortOrder} + ORDER BY a.conr_cust_name #{sortOrder} - ORDER BY a.conr_ttl ${sortOrder} + ORDER BY a.conr_ttl #{sortOrder} - ORDER BY b.prod_name ${sortOrder} + ORDER BY b.prod_name #{sortOrder} - ORDER BY a.conr_stat ${sortOrder} + ORDER BY a.conr_stat #{sortOrder} - ORDER BY a.conr_comp_name ${sortOrder} + ORDER BY a.conr_comp_name #{sortOrder} - ORDER BY a.conr_cust_cla ${sortOrder} + ORDER BY a.conr_cust_cla #{sortOrder} - ORDER BY a.conr_cust_pur_cond ${sortOrder} + ORDER BY a.conr_cust_pur_cond #{sortOrder} - ORDER BY a.conr_id ${sortOrder} + ORDER BY a.conr_id #{sortOrder} - ORDER BY a.created_at ${sortOrder} + ORDER BY a.created_at #{sortOrder} ORDER BY a.created_at DESC diff --git a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml index 9de39701..7f5731ba 100644 --- a/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml +++ b/src/main/resources/stanl_2/final_backend/domain/member/query/repository/MemberMapper.xml @@ -75,7 +75,8 @@ a.mem_acc, a.center_id, a.created_at, - a.updated_at + a.updated_at, + a.mem_id FROM tb_member a WHERE a.mem_login_id = #{ loginId } From b03e7c9e9edbb18c67ee8fe06e2dbadc11299aee Mon Sep 17 00:00:00 2001 From: yuhyejin Date: Thu, 12 Dec 2024 09:54:08 +0900 Subject: [PATCH 561/563] =?UTF-8?q?fix:=20=EA=B3=84=EC=95=BD=EC=84=9C=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EC=8B=9C=20=EA=B3=A0=EA=B0=9D=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=EC=97=90=20=EC=84=B1=EB=B3=84=20=EB=A7=A4=ED=95=91=20?= =?UTF-8?q?=EC=88=98=EC=A0=95#276)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ContractCommandServiceImpl.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java index 7275b6db..7eb107e9 100644 --- a/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java +++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/service/ContractCommandServiceImpl.java @@ -105,7 +105,11 @@ private String handleCustomerInfo(ContractRegistDTO contractRegistRequestDTO, St customerRegistDTO.setAge(contractRegistRequestDTO.getCustomerAge()); customerRegistDTO.setPhone(contractRegistRequestDTO.getCustomerPhone()); customerRegistDTO.setEmail(contractRegistRequestDTO.getCustomerEmail()); - customerRegistDTO.setSex(contractRegistRequestDTO.getCustomerSex()); + if (contractRegistRequestDTO.getCustomerSex().equals("여자")) { + customerRegistDTO.setSex("FEMALE"); + } else if (contractRegistRequestDTO.getCustomerSex().equals("남자")) { + customerRegistDTO.setSex("MALE"); + } customerRegistDTO.setMemberId(memberId); // 고객 등록 @@ -128,7 +132,14 @@ private String updateCustomerInfo(ContractModifyDTO contractModifyDTO, String me customerModifyDTO.setAge(contractModifyDTO.getCustomerAge()); customerModifyDTO.setPhone(contractModifyDTO.getCustomerPhone()); customerModifyDTO.setEmail(contractModifyDTO.getCustomerEmail()); - customerModifyDTO.setSex(contractModifyDTO.getCustomerSex()); + + if (contractModifyDTO.getCustomerSex().equals("여자")) { + customerModifyDTO.setSex("FEMALE"); + } else if (contractModifyDTO.getCustomerSex().equals("남자")) { + customerModifyDTO.setSex("MALE"); + } + + customerModifyDTO.setMemberId(memberId); customerCommandService.modifyCustomerInfo(customerModifyDTO); @@ -166,6 +177,7 @@ public void registerContract(ContractRegistDTO contractRegistRequestDTO) throws // 계약 생성 String customerPurchaseCondition = contractRegistRequestDTO.getCustomerPurchaseCondition(); String customerClassifcation = contractRegistRequestDTO.getCustomerClassifcation(); + String customerSex = contractRegistRequestDTO.getCustomerSex(); if (customerPurchaseCondition.equals("현금")) { contractRegistRequestDTO.setCustomerPurchaseCondition("CASH"); @@ -300,7 +312,7 @@ public void modifyContractStatus(ContractStatusModifyDTO contractStatusModifyDTO contractRepository.save(contract); ContractAlarmDTO contractAlarmDTO = new ContractAlarmDTO(contract.getContractId(), contract.getCustomerName(), - contract.getMemberId(), contract.getAdminId()); + contract.getMemberId(), contract.getAdminId()); alarmCommandService.sendContractAlarm(contractAlarmDTO); From 1d3bd4b9dac233d9d5d4ea45e4725df0d563d98d Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 13 Dec 2024 16:59:03 +0900 Subject: [PATCH 562/563] pull dev --- .github/workflows/deploy.yaml | 83 --------------------- Dockerfile | 37 ---------- docker-compose.yml | 64 ---------------- src/main/resources/application-dev.yml | 10 --- src/main/resources/application-prod.yml | 25 ------- src/main/resources/application.yml | 97 ------------------------- 6 files changed, 316 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 2c37d650..e69de29b 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -1,83 +0,0 @@ -name: Deploy to Elastic Beanstalk - -on: - push: - branches: - - main - - feat/backend-operation - - feat/integration -jobs: - deploy: - runs-on: ubuntu-latest - - env: - AWS_REGION: ap-northeast-2 - APPLICATION_NAME: "motive-back" - ENVIRONMENT_NAME: "Motive-back-env" - - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Ensure Docker Compose is available - run: | - docker --version - docker compose version - - - name: Build Docker images - run: docker compose build - - - - name: Set up JDK 17 - uses: actions/setup-java@v3 - with: - java-version: '17' - distribution: 'corretto' - - - name: Set executable permission for gradlew - run: chmod +x gradlew - - - name: Install Elastic Beanstalk CLI - run: | - sudo apt update - sudo apt install -y python3-pip - pip install awsebcli --upgrade - - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v3 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }} - aws-region: ${{ env.AWS_REGION }} - - - name: Set Environment Variables - run: | - export SPRING_APP_NAME=${{ secrets.SPRING_APP_NAME }} - export DATABASE_URL=${{ secrets.DATABASE_URL }} - export MARIA_DATABASE_PORT=${{ secrets.MARIA_DATABASE_PORT }} - export MARIA_DATABASE_NAME=${{ secrets.MARIA_DATABASE_NAME }} - export DB_USERNAME=${{ secrets.DB_USERNAME }} - export DB_PASSWORD=${{ secrets.DB_PASSWORD }} - export REDIS_HOST=${{ secrets.REDIS_HOST }} - export REDIS_PORT=${{ secrets.REDIS_PORT }} - export SPRING_PROFILES_ACTIVE=${{ secrets.SPRING_PROFILES_ACTIVE }} - export SECRET_KEY=${{ secrets.SECRET_KEY }} - export JWT_SECRET_DEFAULT_VALUE=${{ secrets.JWT_SECRET_DEFAULT_VALUE }} - export JWT_HEADER=${{ secrets.JWT_HEADER }} - - # Gradle 빌드 - - name: Build and package the application - run: ./gradlew clean build -x test - - - - # Elastic Beanstalk 배포 - - name: Deploy to Elastic Beanstalk - run: | - eb init -p "Docker" ${{ env.APPLICATION_NAME }} --region ${{ env.AWS_REGION }} - eb use ${{ env.ENVIRONMENT_NAME }} - eb deploy --staged --timeout 50 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 3323dd06..e69de29b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,37 +0,0 @@ -# 단계 1: 빌드 단계 (Gradle 빌드) -FROM openjdk:17-jdk-slim AS build -WORKDIR /app - -# 필요한 파일 복사 -COPY gradlew ./gradlew -COPY gradle/ ./gradle -COPY build.gradle settings.gradle ./ - -# gradlew 실행 권한 추가 및 의존성 설치 -RUN chmod +x gradlew - -RUN ./gradlew dependencies --no-daemon - -# 소스 코드 복사 및 빌드 -COPY . . -RUN ./gradlew clean build -x test --no-daemon - -RUN ls -la build/libs - -# 단계 2: 실행 단계 (빌드된 JAR 파일 실행) -FROM openjdk:17-jdk-slim -WORKDIR /app - -# libfreetype6 설치 -RUN apt-get update && apt-get install -y \ - libfreetype6 fontconfig fonts-dejavu-core --no-install-recommends \ - && apt-get clean && rm -rf /var/lib/apt/lists/* - - -COPY --from=build /app/build/libs/*.jar app.jar - -# 포트 노출 -EXPOSE 8080 - -# JAR 파일 실행 -ENTRYPOINT ["java", "-jar", "app.jar", "--server.port=8080"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index ee4baf39..e69de29b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,64 +0,0 @@ -version: '3.8' - -services: - backend: - env_file: - - .env - build: - context: . - dockerfile: Dockerfile - container_name: backend - environment: - - SPRING_PROFILES_ACTIVE=prod - - REDIS_HOST=redis - - REDIS_PORT=6379 - - ALGORITHM=AES - - TRANSFORMATION=AES/GCM/NoPadding - expose: - - "8080" - depends_on: - redis: - condition: service_healthy - networks: - - app-network - restart: always - entrypoint: [ "sh", "-c", "sleep 10 && java -jar app.jar" ] - - redis: - image: redis:7.0-alpine - container_name: redis - ports: - - "6379:6379" - networks: - - app-network - volumes: - - redis-data:/data - restart: always - healthcheck: - test: ["CMD", "redis-cli", "-h", "localhost", "ping"] - interval: 5s - timeout: 5s - retries: 5 - - - nginx: - build: - context: ./nginx - dockerfile: Dockerfile - container_name: nginx - depends_on: - - backend - ports: - - "80:80" - networks: - - app-network - restart: always - entrypoint: ["sh", "-c", "sleep 30 && nginx -g 'daemon off;'"] - -networks: - app-network: - driver: bridge - -volumes: - redis-data: - driver: local \ No newline at end of file diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 25f34fa9..e69de29b 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,10 +0,0 @@ -spring: - jpa: - hibernate: - ddl-auto: update - -logging: - level: - org.springframework.security: TRACE - org.mybatis: DEBUG - java.sql: DEBUG \ No newline at end of file diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 049ddb22..e69de29b 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -1,25 +0,0 @@ -spring: - data: - redis: - host: ${REDIS_HOST:redis} - port: ${REDIS_PORT:6379} - lettuce: - pool: - max-active: 10 - max-idle: 5 - min-idle: 2 - timeout: 5000ms - jpa: - hibernate: - ddl-auto: update - open-in-view: false - - -logging: - level: - org.springframework.security: debug - org.mybatis: debug - -mybatis: - configuration: - log-impl: org.apache.ibatis.logging.stdout.StdOutImpl \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index feb23a5e..e69de29b 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,97 +0,0 @@ -server: - forward-headers-strategy: native - - -spring: - application: - name: finalbackend - - datasource: - writer: - hikari: - jdbc-url: jdbc:mariadb://motive.cdw8kw8go27z.ap-northeast-2.rds.amazonaws.com:3306/motive - username: admin - password: motivepassword - driver-class-name: org.mariadb.jdbc.Driver - - reader: - hikari: - jdbc-url: jdbc:mariadb://readonlymotive.cdw8kw8go27z.ap-northeast-2.rds.amazonaws.com:3306/motive - username: admin - password: motivepassword - driver-class-name: org.mariadb.jdbc.Driver - - - profiles: - active: ${SPRING_PROFILES_ACTIVE} - - devtools: - livereload: - enabled: false - restart: - enabled: false - - servlet: - multipart: - enabled: true - max-file-size: 100MB - max-request-size: 100MB - - mail: - host: - port: - username: stanl2e2@naver.com - password: Stanl222 - properties: - mail.smtp.auth: true - mail.smtp.ssl.enable: true - mail.smtp.ssl.trust: smtp.naver.com - mail.smtp.ssl.protocols: TLSv1.2 - mail.debug: true - -jwt: - secret-key: s4G7tL9qH8vI2eP0j5W1uF3bM6nK8rXc - header: Authorization - - -encryption: - algorithm: AES - transformation: AES/ECB/PKCS5Padding - secret-key: "haWh*9teA@2sT!nLaO$i0lEcj3cU282d" - - - - logging: - pattern: - console: ${LOGPATTERN_CONSOLE:%green(%d{HH:mm:ss.SSS}) %blue(%-5level) %red([%thread]) %yellow(%logger{15}) - %msg%n} - level: - org: - springframework: - security: ${SPRING_SECURITY_LOG_LEVEL:TRACE} - -cloud: - aws: - credentials: - access-key: AKIAQE3ROMWSWEBT7F7O - secret-key: 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 - s3: - bucket: motivebk - region: - static: ap-northeast-2 - stack: - auto: false - - mybatis: - configuration: - log-impl: org.apache.ibatis.logging.stdout.StdOutImpl - -claude: - api: - key: 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 - url: https://api.claude.ai/v1/summary - -naver: - client-id: CdxW72qcvZLVTHrViB20 - client-secret: ZjP2cbXlfU - - From 1c394fe4daddd8f71385fd35adad6e016e2aa8cc Mon Sep 17 00:00:00 2001 From: minseokKim6823 Date: Fri, 13 Dec 2024 17:00:01 +0900 Subject: [PATCH 563/563] pull dev --- nginx/Dockerfile | 5 ----- nginx/default.conf | 18 ------------------ 2 files changed, 23 deletions(-) diff --git a/nginx/Dockerfile b/nginx/Dockerfile index e132ed6b..e69de29b 100644 --- a/nginx/Dockerfile +++ b/nginx/Dockerfile @@ -1,5 +0,0 @@ -FROM nginx:alpine - -# Nginx 설정 파일 복사 -COPY default.conf /etc/nginx/conf.d/default.conf -RUN ls -la /etc/nginx/conf.d/ # 설정 파일 복사 확인 \ No newline at end of file diff --git a/nginx/default.conf b/nginx/default.conf index 50fa47fc..e69de29b 100644 --- a/nginx/default.conf +++ b/nginx/default.conf @@ -1,18 +0,0 @@ -upstream backend { - server backend:8080; # 백엔드 컨테이너의 포트를 8080으로 수정 -} - -server { - listen 80; - - location / { - proxy_pass http://backend; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header Connection "keep-alive"; - proxy_cache_bypass $http_upgrade; - } -} \ No newline at end of file