diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml
index 4b1f86cd..e69de29b 100644
--- a/.github/workflows/deploy.yaml
+++ b/.github/workflows/deploy.yaml
@@ -1,82 +0,0 @@
-name: Deploy to Elastic Beanstalk
-
-on:
- push:
- branches:
- - main
- - feat/backend-operation
-
-jobs:
- deploy:
- runs-on: ubuntu-latest
-
- env:
- AWS_REGION: ap-northeast-2
- APPLICATION_NAME: "motive-backend"
- ENVIRONMENT_NAME: "Motive-backend-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
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 89150d20..e69de29b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,30 +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
-
-COPY --from=build /app/build/libs/*.jar app.jar
-
-# 포트 노출
-EXPOSE 8081
-
-# JAR 파일 실행
-ENTRYPOINT ["java", "-jar", "app.jar", "--server.port=8081"]
\ No newline at end of file
diff --git a/README.md b/README.md
index 45d78aff..1a4c98d3 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/swagger-ui/index.html
diff --git a/build.gradle b/build.gradle
index 24a2496f..49e312ff 100644
--- a/build.gradle
+++ b/build.gradle
@@ -23,12 +23,15 @@ repositories {
mavenCentral()
}
+
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
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-thymeleaf'
+ implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:3.0.3'
compileOnly 'org.projectlombok:lombok'
@@ -43,6 +46,8 @@ dependencies {
//swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'
+
+ // modelmapper
implementation 'org.modelmapper:modelmapper:3.2.0'
// jwt 토큰 라이브러리 추가
@@ -53,9 +58,23 @@ 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'
+ // excel을 위한 poi 라이브러리
+ implementation 'org.apache.poi:poi-ooxml:5.2.2'
+ implementation 'org.apache.poi:poi:5.2.2'
+
+ // S3
+ implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
+
+ // SMTP
+ implementation 'org.springframework.boot:spring-boot-starter-mail'
+ implementation 'ognl:ognl:3.2.20'
+
}
tasks.named('test') {
diff --git a/docker-compose.yml b/docker-compose.yml
index 90d751dc..e69de29b 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,29 +0,0 @@
-version: '3.8'
-
-services:
- backend:
- build:
- context: .
- dockerfile: Dockerfile
- container_name: backend
- environment:
- - SPRING_PROFILES_ACTIVE=prod
- expose:
- - "8081"
- networks:
- - app-network
-
- nginx:
- build:
- context: ./nginx
- dockerfile: Dockerfile
- container_name: nginx
- depends_on:
- - backend
- ports:
- - "80:80" # Nginx가 8080 포트로 내부 백엔드에 접근
- networks:
- - app-network
-networks:
- app-network:
- driver: bridge
diff --git a/nginx/Dockerfile b/nginx/Dockerfile
index 8b8e0c7b..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/ # 설정 파일 복사 확인
diff --git a/nginx/default.conf b/nginx/default.conf
index d4d08e66..e69de29b 100644
--- a/nginx/default.conf
+++ b/nginx/default.conf
@@ -1,18 +0,0 @@
-upstream backend {
- server backend:8081; # 백엔드 컨테이너의 포트를 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
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/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..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
@@ -5,15 +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 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.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 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;
-import stanl_2.final_backend.domain.A_sample.common.response.ResponseMessage;
+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 {
@@ -36,20 +40,47 @@ 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 SampleRegistRequestDTO sampleRegistRequestDTO) {
+ public ResponseEntity postTest(@RequestBody SampleRegistDTO sampleRegistRequestDTO,
+ Principal principal) {
+
+
+ log.info("현재 접속한 회원정보(MEM_LOGIN_ID)");
+ log.info(principal.getName());
sampleCommandService.registerSample(sampleRegistRequestDTO);
- return ResponseEntity.ok(ResponseMessage.builder()
+ return ResponseEntity.ok(SampleResponseMessage.builder()
.httpStatus(200)
.msg("성공")
.result(null)
.build());
}
+ @Operation(summary = "샘플 파일 요청 테스트")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "성공",
+ content = {@Content(schema = @Schema(implementation = SampleResponseMessage.class))})
+ })
+ @PostMapping("/file")
+ 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
@@ -60,19 +91,23 @@ public ResponseEntity postTest(@RequestBody SampleRegistRequest
@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 SampleModifyRequestDTO sampleModifyRequestDTO) {
+ public ResponseEntity putTest(@PathVariable String id,
+ @RequestBody SampleModifyDTO sampleModifyRequestDTO,
+ Principal principal) {
+
+ log.info("현재 접속한 회원정보(MEM_LOGIN_ID)");
+ log.info(principal.getName());
sampleModifyRequestDTO.setId(id);
- SampleModifyResponseDTO sampleModifyResponseDTO = sampleCommandService.modifySample(id, sampleModifyRequestDTO);
+ SampleModifyDTO sampleModifyDTO = sampleCommandService.modifySample(id, sampleModifyRequestDTO);
- return ResponseEntity.ok(ResponseMessage.builder()
+ return ResponseEntity.ok(SampleResponseMessage.builder()
.httpStatus(200)
.msg("성공")
- .result(sampleModifyResponseDTO)
+ .result(sampleModifyDTO)
.build());
}
@@ -82,19 +117,22 @@ 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,
+ Principal principal) {
+
+ log.info("현재 접속한 회원정보(MEM_LOGIN_ID)");
+ log.info(principal.getName());
sampleCommandService.deleteSample(id);
- return ResponseEntity.ok(ResponseMessage.builder()
+ return ResponseEntity.ok(SampleResponseMessage.builder()
.httpStatus(200)
.msg("성공")
.result(null)
.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 77%
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..57e4d857 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.*;
@@ -6,8 +6,7 @@
@NoArgsConstructor
@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 76%
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..042f5d53 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.*;
@@ -6,9 +6,9 @@
@NoArgsConstructor
@Setter
@Getter
-@ToString
-public class SampleRegistRequestDTO {
+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/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..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
@@ -1,13 +1,15 @@
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 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(SampleRegistRequestDTO sampleRegistRequestDTO);
+ void registerSample(SampleRegistDTO sampleRegistRequestDTO);
- SampleModifyResponseDTO modifySample(String id, SampleModifyRequestDTO sampleModifyRequestDTO);
+ 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/aggregate/entity/Sample.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/aggregate/entity/Sample.java
index 33b0696b..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
@@ -11,6 +11,7 @@
import java.sql.Timestamp;
import java.time.ZoneId;
import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
@NoArgsConstructor
@AllArgsConstructor
@@ -36,35 +37,37 @@ 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;
+ @Column(name = "IMAGE_URL")
+ private String imageUrl;
+
/* 설명. updatedAt 자동화 */
// 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/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/command/domain/service/SampleCommandServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/A_sample/command/domain/service/SampleCommandServiceImpl.java
index ac1a2886..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
@@ -4,52 +4,65 @@
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 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;
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.query.dto.SampleDTO;
+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.command.application.service.S3FileService;
-import java.sql.Timestamp;
import java.time.ZoneId;
import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
@Service("commandSampleService")
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 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 registerSample(SampleRegistRequestDTO sampleRegistRequestDTO) {
+ public void registerSample(SampleRegistDTO sampleRegistRequestDTO) {
Sample newSample = modelMapper.map(sampleRegistRequestDTO, Sample.class);
sampleRepository.save(newSample);
}
+ @Override
+ public void registerSampleFile(SampleRegistDTO sampleRegistRequestDTO, MultipartFile imageUrl) {
+ Sample newSample = modelMapper.map(sampleRegistRequestDTO, Sample.class);
+
+
+ // s3 사용
+ newSample.setImageUrl(s3FileService.uploadOneFile(imageUrl));
+
+ sampleRepository.save(newSample);
+ }
+
@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));
+ .orElseThrow(() -> new SampleCommonException(SampleErrorCode.SAMPLE_NOT_FOUND));
sampleModifyRequestDTO.setId(id);
Sample updateSample = modelMapper.map(sampleModifyRequestDTO, Sample.class);
@@ -58,7 +71,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;
}
@@ -68,11 +81,12 @@ public SampleModifyResponseDTO modifySample(String id, SampleModifyRequestDTO sa
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());
sampleRepository.save(sample);
}
+
}
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 94%
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 c03f67bc..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)
@@ -39,10 +39,8 @@ 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 74%
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..f1fb01b7 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,8 +7,8 @@
@Builder
@Getter
@Setter
-public class ResponseMessage {
- private int httpStatus;
+public class SampleResponseMessage {
+ 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/A_sample/query/controller/SampleController.java b/src/main/java/stanl_2/final_backend/domain/A_sample/query/controller/SampleController.java
index fc0177cb..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
@@ -5,22 +5,27 @@
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.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;
+import java.security.Principal;
+
+@Slf4j
@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 +34,20 @@ 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,
+ Principal principal){
+
+ log.info("현재 접속한 회원정보(MEM_LOGIN_ID)");
+ log.info(principal.getName());
- 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,20 +60,32 @@ 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)
.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/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/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/SampleService.java b/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryService.java
similarity index 59%
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..c4538594 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
@@ -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 SampleService {
+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
new file mode 100644
index 00000000..90d7103f
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleQueryServiceImpl.java
@@ -0,0 +1,65 @@
+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;
+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.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, ExcelUtilsV1 excelUtilsV1) {
+ this.sampleMapper = sampleMapper;
+ this.excelUtilsV1 = excelUtilsV1;
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public String selectSampleName(String id) {
+
+ String name = sampleMapper.selectNameById(id);;
+
+ if(name == null){
+ throw new SampleCommonException(SampleErrorCode.SAMPLE_NOT_FOUND);
+ }
+
+ return name;
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public SampleDTO selectSampleInfo(String id) {
+
+ SampleDTO sampleDTO = sampleMapper.selectById(id);
+
+ if(sampleDTO == null){
+ throw new SampleCommonException(SampleErrorCode.SAMPLE_NOT_FOUND);
+ }
+
+ 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/domain/A_sample/query/service/SampleServiceImpl.java b/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleServiceImpl.java
deleted file mode 100644
index 880be723..00000000
--- a/src/main/java/stanl_2/final_backend/domain/A_sample/query/service/SampleServiceImpl.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package stanl_2.final_backend.domain.A_sample.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.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 stanl_2.final_backend.domain.A_sample.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/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..a6df8811
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/controller/AlarmController.java
@@ -0,0 +1,69 @@
+package stanl_2.final_backend.domain.alarm.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 jakarta.servlet.http.HttpServletResponse;
+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.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.schedule.common.response.ScheduleResponseMessage;
+
+import java.security.Principal;
+
+@RestController("commandAlarmController")
+@RequestMapping("/api/v1/alarm")
+public class AlarmController {
+
+ private final AlarmCommandService alarmCommandService;
+
+ @Autowired
+ public AlarmController(AlarmCommandService alarmCommandService) {
+ this.alarmCommandService = alarmCommandService;
+ }
+
+ @Operation(summary = "sse 연결")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "sse 연결 성공",
+ content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))})
+ })
+ @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();
+
+ AlarmRegistDTO alarmRegistDTO = new AlarmRegistDTO();
+ alarmRegistDTO.setMemberLoginId(memberLoginId);
+ alarmRegistDTO.setLastEventId(lastEventId);
+
+ 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/AlarmRegistDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/dto/AlarmRegistDTO.java
new file mode 100644
index 00000000..b8aca5bb
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/dto/AlarmRegistDTO.java
@@ -0,0 +1,15 @@
+package stanl_2.final_backend.domain.alarm.command.application.dto;
+
+import lombok.*;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Getter
+@Setter
+public class AlarmRegistDTO {
+
+ private String memberId;
+ private String memberLoginId;
+ private String lastEventId;
+
+}
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..de0169b6
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/application/service/AlarmCommandService.java
@@ -0,0 +1,37 @@
+package stanl_2.final_backend.domain.alarm.command.application.service;
+
+import jakarta.servlet.http.HttpServletResponse;
+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;
+
+import java.security.GeneralSecurityException;
+
+public interface AlarmCommandService {
+ SseEmitter subscribe(AlarmRegistDTO alarmRegistDTO, HttpServletResponse response);
+
+ void sendToClient(SseEmitter emitter, String emitterId, Object data);
+
+ void send(String memberId, String adminId, String contentId, String message, String redirectUrl, String tag,
+ String type, String createdAt);
+
+ Alarm createAlarm(String memberId, String adminId,String contentId, String message, String redirectUrl, String tag,
+ String type, String createdAt);
+
+ void sendNoticeAlarm(NoticeAlarmDTO noticeAlarmDTO);
+
+ Boolean updateReadStatus(String alarmId);
+
+ void sendContractAlarm(ContractAlarmDTO contractAlarmDTO) throws GeneralSecurityException;
+
+ void sendPurchaseOrderAlarm(PurchaseOrderAlarmDTO purchaseOrderAlarmDTO) throws GeneralSecurityException;
+
+ void sendOrderAlarm(OrderAlarmDTO orderAlarmDTO) throws GeneralSecurityException;
+}
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
new file mode 100644
index 00000000..2d9c26da
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/aggregate/entity/Alarm.java
@@ -0,0 +1,57 @@
+package stanl_2.final_backend.domain.alarm.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 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(columnDefinition = "TEXT", name = "ALR_URL", nullable = false)
+ private String redirectUrl;
+
+ @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;
+
+ @Column(name = "CREATED_AT", nullable = false)
+ private String createdAt;
+
+ @Column(name = "CONT_ID", nullable = false)
+ private String contentId;
+
+ @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/repository/AlarmRepository.java b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/repository/AlarmRepository.java
new file mode 100644
index 00000000..5421ce10
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/repository/AlarmRepository.java
@@ -0,0 +1,10 @@
+package stanl_2.final_backend.domain.alarm.command.domain.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import stanl_2.final_backend.domain.alarm.command.domain.aggregate.entity.Alarm;
+
+@Repository
+public interface AlarmRepository extends JpaRepository {
+ Alarm findByAlarmId(String alarmId);
+}
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
new file mode 100644
index 00000000..23ccda09
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/repository/EmitterRepository.java
@@ -0,0 +1,23 @@
+package stanl_2.final_backend.domain.alarm.command.domain.repository;
+
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
+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);
+
+ 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
new file mode 100644
index 00000000..723e27be
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/repository/EmitterRepositoryImpl.java
@@ -0,0 +1,81 @@
+package stanl_2.final_backend.domain.alarm.command.domain.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);
+ }
+ }
+ );
+ }
+
+ @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
new file mode 100644
index 00000000..a628d70d
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/alarm/command/domain/service/AlarmCommandServiceImpl.java
@@ -0,0 +1,312 @@
+package stanl_2.final_backend.domain.alarm.command.domain.service;
+
+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.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.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.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.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;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+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;
+
+@Slf4j
+@Service
+public class AlarmCommandServiceImpl implements AlarmCommandService {
+
+ 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 AlarmCommandServiceImpl(AlarmRepository alarmRepository, EmitterRepository emitterRepository,
+ AuthQueryService authQueryService, MemberQueryService memberQueryService){
+ this.alarmRepository = alarmRepository;
+ this.emitterRepository = emitterRepository;
+ this.authQueryService = authQueryService;
+ this.memberQueryService = memberQueryService;
+ }
+
+ @Override
+ public SseEmitter subscribe(AlarmRegistDTO alarmRegistDTO, HttpServletResponse response) {
+
+ String lastEventId = alarmRegistDTO.getLastEventId();
+ 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));
+ 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 adminId, String contentId, String message, String redirectUrl, String tag,
+ String type, String createdAt){
+
+ Alarm alarm = alarmRepository.save(createAlarm(memberId, adminId, contentId, message, redirectUrl,
+ tag, type, createdAt));
+
+ Map sseEmitters = emitterRepository.findAllEmitterStartWithByMemberId(memberId);
+ sseEmitters.forEach(
+ (key, emitter) -> {
+ emitterRepository.saveEventCache(key, alarm);
+ sendToClient(emitter, key, alarm);
+ }
+ );
+ }
+
+ @Override
+ @Transactional
+ public Alarm createAlarm(String memberId, String adminId, String contentId, String message, String redirectUrl
+ , String tag, String type, String createdAt) {
+
+ Alarm alarm = new Alarm();
+ alarm.setMemberId(memberId);
+ alarm.setAdminId(adminId);
+ alarm.setContentId(contentId);
+ alarm.setMessage(message);
+ alarm.setRedirectUrl(redirectUrl);
+ alarm.setTag(tag);
+ alarm.setType(type);
+ alarm.setReadStatus(false);
+ alarm.setCreatedAt(createdAt);
+
+ return alarm;
+ }
+
+ @Override
+ @Transactional
+ public void sendNoticeAlarm(NoticeAlarmDTO noticeAlarmDTO){
+
+ List memberIdList = new ArrayList<>();
+
+ if(noticeAlarmDTO.getTag().equals("ALL")){
+ // 결과 합치기
+ memberIdList.addAll(memberQueryService.selectMemberByRole("EMPLOYEE"));
+ memberIdList.addAll(memberQueryService.selectMemberByRole("ADMIN"));
+ memberIdList.addAll(memberQueryService.selectMemberByRole("DIRECTOR"));
+ memberIdList.addAll(memberQueryService.selectMemberByRole("GOD"));
+ // 중복 제거
+ memberIdList = new ArrayList<>(new HashSet<>(memberIdList));
+ } else if (noticeAlarmDTO.getTag().equals("ADMIN")){
+ memberIdList.addAll(memberQueryService.selectMemberByRole("ADMIN"));
+ memberIdList.addAll(memberQueryService.selectMemberByRole("DIRECTOR"));
+ memberIdList.addAll(memberQueryService.selectMemberByRole("GOD"));
+ }else {
+ memberIdList.addAll(memberQueryService.selectMemberByRole(noticeAlarmDTO.getTag()));
+ }
+
+ memberIdList.forEach(member -> {
+ String targetId = member;
+ String type = "NOTICE";
+ String tag = null;
+ if(noticeAlarmDTO.getClassification().equals("NORMAL")){
+ tag = "일반";
+ } else if(noticeAlarmDTO.getClassification().equals("GOAL")) {
+ tag = "영업 목표";
+ } else {
+ tag = "영업 전략";
+ }
+
+ String target = null;
+ if(noticeAlarmDTO.getTag().equals("ALL")){
+ target = "전체";
+ } else if(noticeAlarmDTO.getTag().equals("ADMIN")) {
+ target = "영업관리자";
+ } else {
+ target = "영업담당자";
+ }
+
+ String message = target + " 대상 공지사항이 등록되었습니다.";
+ String redirectUrl = "/notice/detail?tag=" + noticeAlarmDTO.getTag() + "&classification=" + noticeAlarmDTO.getClassification()
+ + "¬iceTitle=" + noticeAlarmDTO.getTitle() + "¬iceContent=" + noticeAlarmDTO.getContent()
+ + "¬iceId=" + noticeAlarmDTO.getNoticeId();
+ String createdAt = getCurrentTime();
+
+ send(targetId, noticeAlarmDTO.getMemberId(), noticeAlarmDTO.getNoticeId(), message, redirectUrl, tag, type, createdAt);
+ });
+ }
+
+ @Override
+ @Transactional
+ public void sendContractAlarm(ContractAlarmDTO contractAlarmDTO) throws GeneralSecurityException {
+
+ String type = "CONTRACT";
+ String tag = "계약서";
+ String message = contractAlarmDTO.getCustomerName() +" 고객님의 계약서가 승인되었습니다.";
+
+ String redirectUrl = null;
+ String memberRole = memberQueryService.selectMemberRoleById(contractAlarmDTO.getMemberId());
+
+ // 권한에 따른 경로 변경
+ if (memberRole == "EMPLOYEE") {
+ redirectUrl = "/contract/emlist";
+ } else if (memberRole == "ADMIN") {
+ redirectUrl = "/contract/Elist";
+ } else if (memberRole == "DIRECTOR") {
+ redirectUrl = "/contract/dlist";
+ } else {
+ redirectUrl = "/contract/emlist";
+ }
+
+ String createdAt = getCurrentTime();
+
+ send(contractAlarmDTO.getMemberId(), contractAlarmDTO.getAdminId(),contractAlarmDTO.getContractId(), message,
+ redirectUrl, tag, type, createdAt);
+ }
+
+ @Override
+ @Transactional
+ public void sendPurchaseOrderAlarm(PurchaseOrderAlarmDTO purchaseOrderAlarmDTO) throws GeneralSecurityException {
+
+ String type = "CONTRACT";
+ String tag = "발주서";
+ String message = purchaseOrderAlarmDTO.getTitle() +" 가 승인되었습니다.";
+
+ String redirectUrl = null;
+ String memberRole = memberQueryService.selectMemberRoleById(purchaseOrderAlarmDTO.getMemberId());
+
+ // 권한에 따른 경로 변경
+ if (memberRole == "EMPLOYEE") {
+ redirectUrl = "/purchaseOrder/emlist";
+ } else if (memberRole == "ADMIN"){
+ redirectUrl = "/purchaseOrder/Elist";
+ } else if (memberRole == "DIRECTOR"){
+ redirectUrl = "/purchaseOrder/dlist";
+ } else {
+ redirectUrl = "/purchaseOrder/emlist";
+ }
+
+ String createdAt = getCurrentTime();
+
+ send(purchaseOrderAlarmDTO.getMemberId(), purchaseOrderAlarmDTO.getAdminId(), purchaseOrderAlarmDTO.getPurchaseOrderId(),
+ message, redirectUrl, tag, type, createdAt);
+ }
+
+ @Override
+ @Transactional
+ public void sendOrderAlarm(OrderAlarmDTO orderAlarmDTO) throws GeneralSecurityException {
+
+ String type = "CONTRACT";
+ String tag = "수주서";
+ String message = orderAlarmDTO.getTitle() +" 가 승인되었습니다.";
+
+ String redirectUrl = null;
+ String memberRole = memberQueryService.selectMemberRoleById(orderAlarmDTO.getMemberId());
+
+ // 권한에 따른 경로 변경
+ if (memberRole == "EMPLOYEE") {
+ redirectUrl = "/order/emlist";
+ } else if(memberRole == "ADMIN") {
+ redirectUrl = "/order/Elist";
+ } else if (memberRole == "DIRECTOR") {
+ redirectUrl = "/order/dlist";
+ } else {
+ redirectUrl = "/order/emlist";
+ }
+ String createdAt = getCurrentTime();
+
+ send(orderAlarmDTO.getMemberId(), orderAlarmDTO.getAdminId(),orderAlarmDTO.getOrderId(), message, redirectUrl,
+ tag, type, createdAt);
+ }
+
+ @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/AlarmCommonException.java b/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmCommonException.java
new file mode 100644
index 00000000..6edea1cf
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmCommonException.java
@@ -0,0 +1,16 @@
+package stanl_2.final_backend.domain.alarm.common.exception;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@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..56f8b51a
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/alarm/common/exception/AlarmErrorCode.java
@@ -0,0 +1,54 @@
+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 대신에 전송할 수도 있습니다.
+ * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다.
+ */
+ ALARM_NOT_FOUND(404001, HttpStatus.NOT_FOUND, "해당하는 alarm 데이터를 찾지 못했습니다"),
+
+ /**
+ * 500(Internal Server Error)
+ * 서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다.
+ */
+ DATA_INTEGRITY_VIOLATION(40001, HttpStatus.BAD_REQUEST, "데이터 무결 위반하였습니다."),
+ 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/alarm/query/controller/AlarmController.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/controller/AlarmController.java
new file mode 100644
index 00000000..1f6a9e06
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/controller/AlarmController.java
@@ -0,0 +1,81 @@
+package stanl_2.final_backend.domain.alarm.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.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;
+import stanl_2.final_backend.domain.alarm.query.service.AlarmQueryService;
+import stanl_2.final_backend.domain.schedule.common.response.ScheduleResponseMessage;
+
+import java.security.Principal;
+
+@RestController("queryAlarmController")
+@RequestMapping("/api/v1/alarm")
+public class AlarmController {
+
+ private final AlarmQueryService alarmQueryService;
+
+ @Autowired
+ public AlarmController(AlarmQueryService alarmQueryService) {
+ this.alarmQueryService = alarmQueryService;
+ }
+
+ @Operation(summary = "회원 알림창 전체 조회")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "회원 알림창 전체 조회 완료",
+ content = {@Content(schema = @Schema(implementation = ScheduleResponseMessage.class))})
+ })
+ @GetMapping("")
+ public ResponseEntity selectMemberAlarmType(Principal principal){
+
+ String memberLoginId = principal.getName();
+
+ AlarmSelectTypeDTO AlarmSelectTypeDTO = alarmQueryService.selectMemberByAlarmType(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("/{type}")
+ public ResponseEntity selectReadAlarm(Principal principal,
+ @PathVariable String type,
+ @PageableDefault(size = 8) Pageable pageable){
+
+ String memberLoginId = principal.getName();
+ AlarmSelectDTO alarmSelectDTO = new AlarmSelectDTO();
+ alarmSelectDTO.setMemberLoginId(memberLoginId);
+ alarmSelectDTO.setType(type);
+
+ Page allAlarms
+ = alarmQueryService.selectAlarmByType(alarmSelectDTO , pageable);
+
+ return ResponseEntity.ok(AlarmResponseMessage.builder()
+ .httpStatus(200)
+ .msg("성공")
+ .result(allAlarms)
+ .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..62f46136
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectDTO.java
@@ -0,0 +1,26 @@
+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 alarmId;
+ private String message;
+ private String type;
+ private String tag;
+ private String redirectUrl;
+ private Boolean readStatus;
+ private String createdAt;
+
+ private String memberLoginId;
+ private String memberId;
+ private String adminId;
+ private String contentId;
+}
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
new file mode 100644
index 00000000..5b0625fb
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectReadDTO.java
@@ -0,0 +1,18 @@
+package stanl_2.final_backend.domain.alarm.query.dto;
+
+import lombok.*;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Getter
+@Setter
+public class AlarmSelectReadDTO {
+
+ private String message;
+ private String type;
+ private String tag;
+ private String redirectUrl;
+ private Boolean readStatus;
+
+ 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..af77e787
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectTypeDTO.java
@@ -0,0 +1,14 @@
+package stanl_2.final_backend.domain.alarm.query.dto;
+
+import lombok.*;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Getter
+@Setter
+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/AlarmSelectUnreadDTO.java b/src/main/java/stanl_2/final_backend/domain/alarm/query/dto/AlarmSelectUnreadDTO.java
new file mode 100644
index 00000000..036fd1a6
--- /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
+public class AlarmSelectUnreadDTO {
+
+ private String message;
+ private String type;
+ private String tag;
+ 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
new file mode 100644
index 00000000..7869bffc
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/repository/AlarmMapper.java
@@ -0,0 +1,34 @@
+package stanl_2.final_backend.domain.alarm.query.repository;
+
+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;
+
+import java.util.List;
+
+@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 findUnReadAlarmsByType(@Param("offset") Integer offset,
+ @Param("pageSize") Integer pageSize,
+ @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
new file mode 100644
index 00000000..ab46f4ea
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryService.java
@@ -0,0 +1,18 @@
+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.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;
+
+public interface AlarmQueryService {
+ AlarmSelectTypeDTO selectMemberByAlarmType(String memberLoginId);
+
+ 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
new file mode 100644
index 00000000..08e89b66
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/alarm/query/service/AlarmQueryServiceImpl.java
@@ -0,0 +1,105 @@
+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 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;
+import stanl_2.final_backend.domain.alarm.query.repository.AlarmMapper;
+import stanl_2.final_backend.domain.member.query.service.AuthQueryService;
+
+import java.util.List;
+
+@Slf4j
+@Service
+public class AlarmQueryServiceImpl implements AlarmQueryService{
+
+ private final AlarmMapper alarmMapper;
+ private final AuthQueryService authQueryService;
+
+ @Autowired
+ public AlarmQueryServiceImpl(AlarmMapper alarmMapper, AuthQueryService authQueryService) {
+ this.alarmMapper = alarmMapper;
+ this.authQueryService = authQueryService;
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public AlarmSelectTypeDTO selectMemberByAlarmType(String memberLoginId) {
+
+ String memberId = authQueryService.selectMemberIdByLoginId(memberLoginId);
+
+ AlarmSelectTypeDTO alarmSelectTypeDTO = alarmMapper.findNumberOfAlarmsByType(memberId);
+
+ if(alarmSelectTypeDTO == null){
+ AlarmSelectTypeDTO alarmNullSelectTypeDTO
+ = new AlarmSelectTypeDTO(0,0,0);
+
+ return alarmNullSelectTypeDTO;
+ }
+
+ return alarmSelectTypeDTO;
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public Page selectReadAlarmByType(AlarmSelectReadDTO alarmSelectReadDTO, Pageable pageable) {
+
+ Integer offset = Math.toIntExact(pageable.getOffset());
+ Integer pageSize = pageable.getPageSize();
+
+ String memberId = authQueryService.selectMemberIdByLoginId(alarmSelectReadDTO.getMemberLoginId());
+
+ List readAlarmList
+ = alarmMapper.findReadAlarmsByType(offset, pageSize, memberId, alarmSelectReadDTO.getType());
+
+ Integer count = alarmMapper.findReadAlarmsCountByMemberId(memberId);
+ int totalOrder = (count != null) ? count : 0;
+
+ 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());
+
+ List unReadAlarmList
+ = alarmMapper.findUnReadAlarmsByType(offset, pageSize, memberId, alarmSelectUnreadDTO.getType());
+
+ Integer count = alarmMapper.findUnreadAlarmsCountByMemberId(memberId);
+ int totalOrder = (count != null) ? count : 0;
+
+ 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
new file mode 100644
index 00000000..fa7c4478
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/alarm/scheduler/AlarmScheduler.java
@@ -0,0 +1,70 @@
+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;
+import org.springframework.transaction.annotation.Transactional;
+import stanl_2.final_backend.domain.alarm.command.application.service.AlarmCommandService;
+import stanl_2.final_backend.domain.schedule.query.dto.ScheduleDayDTO;
+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;
+
+
+@Service("AlarmSchdulerService")
+@Slf4j
+public class AlarmScheduler {
+
+ private final AlarmCommandService alarmCommandService;
+ private final ScheduleQueryService scheduleQueryService;
+ 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(AlarmCommandService alarmCommandService, ScheduleQueryService scheduleQueryService) {
+ this.alarmCommandService = alarmCommandService;
+ this.scheduleQueryService = scheduleQueryService;
+ }
+
+// @Scheduled(cron = "0 0 2 * * *") // 매일 새벽 2시에 실행)
+ @Scheduled(cron = "0 27 14 * * *")
+ @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";
+
+ 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 + "분에 '" + tag + "' 일정이 있습니다";
+ String redirectUrl = "/schedule";
+ String createdAt = getCurrentTime();
+
+ alarmCommandService.send(memberId, memberId, schedule.getScheduleId(), message, redirectUrl, tag, type, createdAt);
+ });
+ }
+}
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..cecea16b
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/career/command/application/controller/CareerController.java
@@ -0,0 +1,53 @@
+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.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+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 java.security.Principal;
+
+@Slf4j
+@RestController("commandCareerController")
+@RequestMapping("/api/v1/career")
+public class CareerController {
+
+ private final CareerCommandService careerCommandService;
+ private final AuthQueryService authQueryService;
+
+ @Autowired
+ 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 = CareerResponseMessage.class))})
+ })
+ @PostMapping("")
+ public ResponseEntity postCareer(@RequestBody CareerRegistDTO careerRegistDTO,
+ Principal principal){
+
+ careerRegistDTO.setMemberId(authQueryService.selectMemberIdByLoginId(principal.getName()));
+
+ 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
new file mode 100644
index 00000000..966cd7ab
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/career/command/application/dto/CareerRegistDTO.java
@@ -0,0 +1,15 @@
+package stanl_2.final_backend.domain.career.command.application.dto;
+
+import lombok.*;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Setter
+@Getter
+public class CareerRegistDTO {
+ private String emplDate;
+ private String resignDate;
+ private String name;
+ private String note;
+ private String memberId;
+}
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..ca136437
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/career/command/application/service/CareerCommandService.java
@@ -0,0 +1,8 @@
+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
new file mode 100644
index 00000000..1a1b32c0
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/career/command/domain/aggregate/entity/Career.java
@@ -0,0 +1,59 @@
+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 = "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/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..d37e767b
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/career/command/domain/service/CareerCommandServiceImpl.java
@@ -0,0 +1,35 @@
+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;
+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;
+
+@Slf4j
+@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;
+ }
+
+ @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/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..f695ea3b
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/career/query/controller/CareerController.java
@@ -0,0 +1,71 @@
+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.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.security.Principal;
+import java.util.List;
+
+@Slf4j
+@RestController(value = "queryCareerController")
+@RequestMapping("/api/v1/career")
+public class CareerController {
+
+ private final CareerQueryService careerQueryService;
+
+ @Autowired
+ public CareerController(CareerQueryService careerQueryService) {
+ this.careerQueryService = careerQueryService;
+ }
+
+ @Operation(summary = "경력 조회(with 사번)")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "성공",
+ content = {@Content(schema = @Schema(implementation = CareerResponseMessage.class))}),
+ @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음",
+ content = @Content(mediaType = "application/json"))
+ })
+ @GetMapping("/other/{loginId}")
+ public ResponseEntity getCareerByOther(@PathVariable String loginId){
+
+ List careerList = careerQueryService.selectCareerList(loginId);
+
+ return ResponseEntity.ok(CareerResponseMessage.builder()
+ .httpStatus(200)
+ .msg("성공")
+ .result(careerList)
+ .build());
+ }
+
+ @Operation(summary = "경력 조회(접속중인 사용자)")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "성공",
+ content = {@Content(schema = @Schema(implementation = CareerResponseMessage.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/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..2561953f
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/career/query/dto/CareerDTO.java
@@ -0,0 +1,14 @@
+package stanl_2.final_backend.domain.career.query.dto;
+
+import lombok.*;
+
+@NoArgsConstructor
+@AllArgsConstructor
+@Setter
+@Getter
+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..d5d6485c
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/career/query/repository/CareerMapper.java
@@ -0,0 +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("memberId") String memberId);
+}
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..f910dc85
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryService.java
@@ -0,0 +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 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
new file mode 100644
index 00000000..81b08c01
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/career/query/service/CareerQueryServiceImpl.java
@@ -0,0 +1,37 @@
+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,
+ AuthQueryService authQueryService) {
+ this.careerMapper = careerMapper;
+ this.authQueryService = authQueryService;
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public List selectCareerList(String loginId) {
+
+ String memberId = authQueryService.selectMemberIdByLoginId(loginId);
+
+ List careerList = careerMapper.selectCareerInfo(memberId);
+
+ return careerList;
+ }
+}
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..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
@@ -1,12 +1,18 @@
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.center.command.application.dto.request.CenterRegistRequestDTO;
-import stanl_2.final_backend.domain.center.command.application.dto.response.CenterRegistResponseDTO;
+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;
-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")
@@ -19,33 +25,59 @@ 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 = CenterResponseMessage.class))})
+ })
@PostMapping("")
- public ResponseEntity> postTest(@RequestBody CenterRegistRequestDTO centerRegistRequestDTO){
-
- CenterRegistResponseDTO centerRegistResponseDTO = centerCommandService.registCenter(centerRegistRequestDTO);
+ public ResponseEntity postTest(@RequestPart("dto") CenterRegistDTO centerRegistDTO,
+ @RequestPart("file") MultipartFile imageUrl){
+ centerCommandService.registCenter(centerRegistDTO, imageUrl);
- return ResponseEntity.ok(new ResponseMessage(200, "post 성공", centerRegistResponseDTO));
+ return ResponseEntity.ok(CenterResponseMessage.builder()
+ .httpStatus(200)
+ .msg("등록 성공")
+ .result(null)
+ .build());
}
- @PutMapping("")
- public ResponseEntity> putTest(){
+ @Operation(summary = "매장 수정")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "성공",
+ content = {@Content(schema = @Schema(implementation = CenterResponseMessage.class))})
+ })
+ @PutMapping("{centerId}")
+ public ResponseEntity putTest(@PathVariable("centerId") String centerId,
+ @RequestPart("dto") CenterModifyDTO centerModifyDTO,
+ @RequestPart("file") MultipartFile imageUrl){
+
+ centerModifyDTO.setCenterId(centerId);
+ centerCommandService.modifyCenter(centerModifyDTO, imageUrl);
- return ResponseEntity.ok(new ResponseMessage(200, "put 성공", " "));
+ return ResponseEntity.ok(CenterResponseMessage.builder()
+ .httpStatus(200)
+ .msg("수정 성공")
+ .result(null)
+ .build());
}
- @DeleteMapping("")
- public ResponseEntity> deleteTest(){
+ @Operation(summary = "매장 삭제")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "성공",
+ content = {@Content(schema = @Schema(implementation = CenterResponseMessage.class))})
+ })
+ @DeleteMapping("{id}")
+ public ResponseEntity deleteTest(@PathVariable("id") String id){
+ centerCommandService.deleteCenter(id);
- return ResponseEntity.ok(new ResponseMessage(200, "delete 성공", " "));
+ return ResponseEntity.ok(CenterResponseMessage.builder()
+ .httpStatus(200)
+ .msg("성공")
+ .result(null)
+ .build());
}
}
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/request/CenterModifyDTO.java
similarity index 51%
rename from src/main/java/stanl_2/final_backend/domain/center/command/application/dto/response/CenterRegistResponseDTO.java
rename to src/main/java/stanl_2/final_backend/domain/center/command/application/dto/request/CenterModifyDTO.java
index 2178977c..3515e4ed 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/request/CenterModifyDTO.java
@@ -1,25 +1,17 @@
-package stanl_2.final_backend.domain.center.command.application.dto.response;
+package stanl_2.final_backend.domain.center.command.application.dto.request;
import lombok.*;
-import java.sql.Timestamp;
-import java.time.LocalDateTime;
-
@AllArgsConstructor
@NoArgsConstructor
-@Getter
@Setter
-@ToString
-public class CenterRegistResponseDTO {
-
- private Long id;
+@Getter
+public class CenterModifyDTO {
+ private String centerId;
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;
+ private String imageUrl;
}
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 80%
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 d256f1a1..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,13 +6,11 @@
@NoArgsConstructor
@Setter
@Getter
-@ToString
-public class CenterRegistRequestDTO {
-
-// private Long id;
+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 47a9ad6d..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,10 +1,14 @@
package stanl_2.final_backend.domain.center.command.application.service;
-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 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 {
- CenterRegistResponseDTO registCenter(CenterRegistRequestDTO centerRegistRequestDTO);
-
+ 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/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..e8ef75dd 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,27 +2,30 @@
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;
import java.time.ZonedDateTime;
-
-/* 설명. 테스트를 위한 어노테이션(나중에 삭제 예정)*/
-@ToString
+import java.time.format.DateTimeFormatter;
@Entity
-@Table(name="CENTER")
+@Table(name="TB_CENTER")
@AllArgsConstructor
@NoArgsConstructor
@Setter
@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")
+ )
+ @Column(name ="CENT_ID")
+ private String centerId;
@Column(name = "CENT_NAME", nullable = false)
private String name;
@@ -40,36 +43,35 @@ 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 = "IMAGE_URL")
+ private String imageUrl;
@Column(name = "ACTIVE", nullable = false)
private Boolean active = true;
-
- /* 설명. updatedAt 자동화 */
- // Insert 되기 전에 실행
@PrePersist
- public void prePersist() {
- Timestamp currentTimestamp = getCurrentTimestamp();
- this.createdAt = currentTimestamp;
+ private void prePersist() {
+ 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
new file mode 100644
index 00000000..09753336
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/center/command/domain/service/CenterCommandServiceImpl.java
@@ -0,0 +1,84 @@
+package stanl_2.final_backend.domain.center.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 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;
+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 stanl_2.final_backend.domain.s3.command.application.service.S3FileService;
+
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+
+@Service("commandCenterServiceImpl")
+public class CenterCommandServiceImpl implements CenterCommandService {
+
+ private final CenterRepository centerRepository;
+ private final ModelMapper modelMapper;
+ private final S3FileService s3FileService;
+
+ @Autowired
+ public CenterCommandServiceImpl(CenterRepository centerRepository, ModelMapper modelMapper, S3FileService s3FileService) {
+ this.centerRepository = centerRepository;
+ this.modelMapper = modelMapper;
+ this.s3FileService = s3FileService;
+ }
+
+
+ @Override
+ @Transactional
+ 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, 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());
+ updateCenter.setActive(center.getActive());
+
+ centerRepository.save(updateCenter);
+ }
+
+ @Override
+ @Transactional
+ public void deleteCenter(String id) {
+ Center center = centerRepository.findById(id)
+ .orElseThrow(() -> new CenterCommonException(CenterErrorCode.CENTER_NOT_FOUND));
+
+ center.setActive(false);
+ 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/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/CenterErrorCode.java b/src/main/java/stanl_2/final_backend/domain/center/common/exception/CenterErrorCode.java
new file mode 100644
index 00000000..12175476
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/center/common/exception/CenterErrorCode.java
@@ -0,0 +1,51 @@
+package stanl_2.final_backend.domain.center.common.exception;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.springframework.http.HttpStatus;
+
+@Getter
+@AllArgsConstructor
+public enum CenterErrorCode {
+
+ /**
+ * 400(Bad Request)
+ * 이 응답은 잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음을 의미합니다.
+ */
+
+
+
+ /**
+ * 401(Unauthorized)
+ * 비록 HTTP 표준에서는 "미승인(unauthorized)"를 명확히 하고 있지만,
+ * 의미상 이 응답은 "비인증(unauthenticated)"을 의미합니다.
+ * 클라이언트는 요청한 응답을 받기 위해서는 반드시 스스로를 인증해야 합니다.
+ */
+
+
+ /**
+ * 403(Forbidden)
+ * 클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다.
+ * 예를들어 그들은 미승인이어서 서버는 거절을 위한 적절한 응답을 보냅니다. 401과 다른 점은 서버가 클라이언트가 누구인지 알고 있습니다.
+ */
+
+
+
+ /**
+ * 404(Not Found)
+ * 서버는 요청받은 리소스를 찾을 수 없습니다. 브라우저에서는 알려지지 않은 URL을 의미합니다.
+ * 이것은 API에서 종점은 적절하지만 리소스 자체는 존재하지 않음을 의미할 수도 있습니다.
+ * 서버들은 인증받지 않은 클라이언트로부터 리소스를 숨기기 위하여 이 응답을 403 대신에 전송할 수도 있습니다.
+ * 이 응답 코드는 웹에서 반복적으로 발생하기 때문에 가장 유명할지도 모릅니다.
+ */
+ CENTER_NOT_FOUND(404001, 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/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/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 2571517c..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
@@ -1,31 +1,151 @@
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 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.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 org.springframework.web.bind.annotation.*;
+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;
-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 = "영업매장 조회")
+ @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,
+ @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);
+
+ 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("{centerId}")
- public ResponseEntity> getTest(@PathVariable Long centerId){
+ public ResponseEntity getCenterById(@PathVariable("centerId") String centerId){
- CenterSelectIdDTO centerSelectIdDTO = centerService.selectByCenterId(centerId);
+ CenterSelectIdDTO centerSelectIdDTO = centerQueryService.selectByCenterId(centerId);
- return ResponseEntity.ok(new ResponseMessage(200, "get 성공", centerSelectIdDTO));
+ 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
+ ,@PageableDefault(size = 20) Pageable pageable,
+ @RequestParam(required = false) String sortField,
+ @RequestParam(required = false) String sortOrder){
+
+ CenterSearchRequestDTO centerSearchRequestDTO = new CenterSearchRequestDTO();
+ 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()
+ .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("/searchList")
+ public ResponseEntity getCenterListBySearch(@RequestParam Map params){
+
+ CenterSearchRequestDTO centerSearchRequestDTO = new CenterSearchRequestDTO();
+ centerSearchRequestDTO.setCenterId(params.get("centerId"));
+ 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());
+ }
+
+ @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
new file mode 100644
index 00000000..1b5d05ce
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/center/query/dto/CenterSearchRequestDTO.java
@@ -0,0 +1,16 @@
+package stanl_2.final_backend.domain.center.query.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Getter
+@Setter
+public class CenterSearchRequestDTO {
+ private String centerId;
+ private String name;
+ private String address;
+}
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..a786ff46
--- /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;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Getter
+@Setter
+public class CenterSelectAllDTO {
+ private String centerId;
+ private String name;
+ private String address;
+ private String phone;
+ private Integer memberCount;
+ private String operatingAt;
+ private String createdAt;
+ private String updatedAt;
+}
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..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
@@ -1,25 +1,26 @@
package stanl_2.final_backend.domain.center.query.dto;
-import lombok.*;
-import java.sql.Timestamp;
-import java.time.LocalDateTime;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
-@ToString
public class CenterSelectIdDTO {
- private Long id;
+ private String centerId;
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;
+ private String imageUrl;
}
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..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
@@ -1,9 +1,36 @@
package stanl_2.final_backend.domain.center.query.repository;
import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+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 java.util.List;
+
@Mapper
public interface CenterMapper {
- CenterSelectIdDTO findCenterById(Long centerId);
+ CenterSelectIdDTO findCenterById(String id);
+
+ List findCenterAll(@Param("size") int size
+ , @Param("offset") int offset,
+ @Param("sortField") String sortField,
+ @Param("sortOrder") String sortOrder);
+
+ Integer findCenterCount();
+
+ Integer findCenterBySearchCount(@Param("centerSearchRequestDTO") CenterSearchRequestDTO centerSearchRequestDTO);
+
+ List findCenterBySearch(@Param("size") int size
+ , @Param("offset") int offset
+ , @Param("centerSearchRequestDTO") CenterSearchRequestDTO centerSearchRequestDTO,
+ @Param("sortField") String sortField,
+ @Param("sortOrder") String sortOrder);
+
+ 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
new file mode 100644
index 00000000..95ecffd6
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryService.java
@@ -0,0 +1,25 @@
+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;
+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 java.util.List;
+
+public interface CenterQueryService {
+ CenterSelectIdDTO selectByCenterId(String id);
+
+ Page selectAll(Pageable pageable);
+
+ 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
new file mode 100644
index 00000000..6b1bdb34
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterQueryServiceImpl.java
@@ -0,0 +1,110 @@
+package stanl_2.final_backend.domain.center.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.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+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 org.springframework.data.domain.Sort;
+
+import java.util.List;
+
+@Service("queryCenterServiceImpl")
+public class CenterQueryServiceImpl implements CenterQueryService {
+
+ private final CenterMapper centerMapper;
+ private final RedisTemplate redisTemplate;
+ private final ExcelUtilsV1 excelUtilsV1;
+
+ @Autowired
+ public CenterQueryServiceImpl(CenterMapper centerMapper, RedisTemplate redisTemplate, ExcelUtilsV1 excelUtilsV1) {
+ this.centerMapper = centerMapper;
+ this.redisTemplate = redisTemplate;
+ this.excelUtilsV1 = excelUtilsV1;
+ }
+
+ @Override
+ @Transactional
+ public CenterSelectIdDTO selectByCenterId(String id) {
+
+ CenterSelectIdDTO centerSelectIdDTO = centerMapper.findCenterById(id);
+
+ return centerSelectIdDTO;
+ }
+
+ @Override
+ @Transactional
+ 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 centerList = centerMapper.findCenterAll(size, offset, sortField, sortOrder);
+
+ int total = centerMapper.findCenterCount();
+
+ return new PageImpl<>(centerList, pageable, total);
+ }
+
+ @Override
+ @Transactional
+ public Page selectBySearch(CenterSearchRequestDTO centerSearchRequestDTO, 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 centerList = centerMapper.findCenterBySearch(size, offset, centerSearchRequestDTO, sortField, sortOrder);
+ int total = centerMapper.findCenterBySearchCount(centerSearchRequestDTO);
+
+ return new PageImpl<>(centerList, pageable, total);
+ }
+
+ @Override
+ @Transactional
+ public List selectCenterListBySearch(CenterSearchRequestDTO centerSearchRequestDTO){
+
+ List centerList = centerMapper.findCenterListBySearch(centerSearchRequestDTO);
+
+ return centerList;
+ }
+
+ @Override
+ @Transactional
+ public String selectNameById(String id) {
+
+ String centerName = centerMapper.findNameById(id);
+ return centerName;
+ }
+
+ @Override
+ @Transactional
+ 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/center/query/service/CenterService.java b/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterService.java
deleted file mode 100644
index 23e8aacc..00000000
--- a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterService.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package stanl_2.final_backend.domain.center.query.service;
-
-import stanl_2.final_backend.domain.center.query.dto.CenterSelectIdDTO;
-
-public interface CenterService {
- CenterSelectIdDTO selectByCenterId(Long centerId);
-}
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
deleted file mode 100644
index 7acff264..00000000
--- a/src/main/java/stanl_2/final_backend/domain/center/query/service/CenterServiceImpl.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package stanl_2.final_backend.domain.center.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.center.query.dto.CenterSelectIdDTO;
-import stanl_2.final_backend.domain.center.query.repository.CenterMapper;
-
-@Slf4j
-@Service("queryCenterServiceImpl")
-public class CenterServiceImpl implements CenterService{
-
- private final CenterMapper centerMapper;
-
- @Autowired
- public CenterServiceImpl(CenterMapper centerMapper) {
- this.centerMapper = centerMapper;
- }
-
- @Override
- @Transactional(readOnly = true)
- public CenterSelectIdDTO selectByCenterId(Long centerId) {
-
- CenterSelectIdDTO centerSelectIdDTO = centerMapper.findCenterById(centerId);
-
- return centerSelectIdDTO;
- }
-}
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..a6547067
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/certification/command/application/controller/CertificationController.java
@@ -0,0 +1,54 @@
+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.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;
+
+import java.security.Principal;
+
+@RestController("commandCertificationController")
+@RequestMapping("/api/v1/certification")
+public class CertificationController {
+
+ private final CertificationCommandService certificationCommandService;
+ private final AuthQueryService authQueryService;
+
+ @Autowired
+ 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 = CareerResponseMessage.class))})
+ })
+ @PostMapping("")
+ public ResponseEntity postCertification(@RequestBody CertificationRegisterDTO certificationRegisterDTO,
+ Principal principal){
+
+ certificationRegisterDTO.setMemberId(authQueryService.selectMemberIdByLoginId(principal.getName()));
+
+ 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
new file mode 100644
index 00000000..4735b7c0
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/certification/command/application/dto/CertificationRegisterDTO.java
@@ -0,0 +1,16 @@
+package stanl_2.final_backend.domain.certification.command.application.dto;
+
+import lombok.*;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Setter
+@Getter
+public class CertificationRegisterDTO {
+ 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/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..980f18d3
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/certification/command/application/service/CertificationCommandService.java
@@ -0,0 +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/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..eac2c4c0
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/certification/command/domain/aggregate/entity/Certification.java
@@ -0,0 +1,62 @@
+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_SCO", nullable = false)
+ private String score;
+
+ @Column(name = "CER_NOTE")
+ private String note;
+
+ @Column(name = "CREATED_AT", nullable = false, updatable = false)
+ private String createdAt;
+
+ @Column(name = "MEM_ID", nullable = false)
+ private String memberId;
+
+ // 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..a2cf52f1
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/certification/command/domain/service/CertificationCommandServiceImpl.java
@@ -0,0 +1,33 @@
+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 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")
+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;
+ }
+
+ @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/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..70ffe9c5
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/certification/query/controller/CertificationController.java
@@ -0,0 +1,69 @@
+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.career.common.response.CareerResponseMessage;
+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 {
+
+ private final CertificationQueryService certificationQueryService;
+
+ @Autowired
+ public CertificationController(CertificationQueryService certificationQueryService) {
+ this.certificationQueryService = certificationQueryService;
+ }
+
+ @Operation(summary = "자격증/외국어 조회(with 사번)")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "성공",
+ content = {@Content(schema = @Schema(implementation = CareerResponseMessage.class))}),
+ @ApiResponse(responseCode = "404", description = "리소스를 찾을 수 없음",
+ content = @Content(mediaType = "application/json"))
+ })
+ @GetMapping("/other/{loginId}")
+ public ResponseEntity getCertificationByOther(@PathVariable String loginId){
+
+ List certificationList = certificationQueryService.selectCertificationList(loginId);
+
+ return ResponseEntity.ok(CareerResponseMessage.builder()
+ .httpStatus(200)
+ .msg("성공")
+ .result(certificationList)
+ .build());
+ }
+
+ @Operation(summary = "자격증/외국어 조회(접속중인 사용자)")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "성공",
+ content = {@Content(schema = @Schema(implementation = CareerResponseMessage.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/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..791c4cee
--- /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
+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
new file mode 100644
index 00000000..ebd7eee5
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/certification/query/repository/CertificationMapper.java
@@ -0,0 +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("memberId") String memberId);
+}
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..f138996f
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryService.java
@@ -0,0 +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 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
new file mode 100644
index 00000000..21ad4b13
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/certification/query/service/CertificationQueryServiceImpl.java
@@ -0,0 +1,35 @@
+package stanl_2.final_backend.domain.certification.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.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,
+ AuthQueryService authQueryService) {
+ this.certificationMapper = certificationMapper;
+ this.authQueryService = authQueryService;
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public List selectCertificationList(String loginId) {
+
+ String memberId = authQueryService.selectMemberIdByLoginId(loginId);
+
+ List certificationList = certificationMapper.selectCertificationInfo(memberId);
+
+ return certificationList;
+ }
+}
diff --git a/src/main/java/stanl_2/final_backend/domain/claude/ClaudeApiService.java b/src/main/java/stanl_2/final_backend/domain/claude/ClaudeApiService.java
new file mode 100644
index 00000000..99b88057
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/claude/ClaudeApiService.java
@@ -0,0 +1,47 @@
+package stanl_2.final_backend.domain.claude;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.*;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+@Service
+public class ClaudeApiService {
+
+ @Value("${claude.api.key}")
+ private String apiKey;
+
+ @Value("${claude.api.url}")
+ private String apiUrl;
+
+ private final RestTemplate restTemplate;
+
+ public ClaudeApiService(RestTemplate restTemplate) {
+ this.restTemplate = restTemplate;
+ }
+
+ public String getSummary(String comment) {
+
+ String requestBody = String.format("{\"text\": \"%s\"}", comment);
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.set("Authorization", "Bearer " + apiKey);
+
+ HttpEntity entity = new HttpEntity<>(requestBody, headers);
+
+ try {
+ ResponseEntity response = restTemplate.exchange(apiUrl, HttpMethod.POST, entity, String.class);
+
+ if (response.getStatusCode() == HttpStatus.OK) {
+ return response.getBody(); // Return the summary text
+ } else {
+ // 외부 API에서 오류가 발생한 경우
+ return "Error: " + response.getStatusCode() + " - " + response.getBody();
+ }
+ } catch (Exception e) {
+ // 예외 처리: API 호출 실패
+ return "Error: Unable to connect to Claude API. " + e.getMessage();
+ }
+ }
+}
diff --git a/src/main/java/stanl_2/final_backend/domain/claude/ClaudeController.java b/src/main/java/stanl_2/final_backend/domain/claude/ClaudeController.java
new file mode 100644
index 00000000..1cfcd2df
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/claude/ClaudeController.java
@@ -0,0 +1,37 @@
+package stanl_2.final_backend.domain.claude;
+
+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.claude.dto.RequestDTO;
+
+@RestController
+@RequestMapping("/api/v1/claude")
+public class ClaudeController {
+
+ private final ClaudeApiService claudeApiService;
+
+ @Autowired
+ public ClaudeController(ClaudeApiService claudeApiService) {
+ this.claudeApiService = claudeApiService;
+ }
+
+ @PostMapping("summary")
+ public ResponseEntity> getSummary(@RequestBody RequestDTO requestDTO) {
+
+ try {
+ String summary = claudeApiService.getSummary(requestDTO.getComment());
+ if (summary != null) {
+ return ResponseEntity.ok(summary);
+ } else {
+ return ResponseEntity.status(500).body("Failed to get summary.");
+ }
+ } catch (Exception e) {
+ // 로깅 및 예외 처리
+ return ResponseEntity.status(500).body("An error occurred while processing your request: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/stanl_2/final_backend/domain/claude/dto/RequestDTO.java b/src/main/java/stanl_2/final_backend/domain/claude/dto/RequestDTO.java
new file mode 100644
index 00000000..3100852a
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/claude/dto/RequestDTO.java
@@ -0,0 +1,14 @@
+package stanl_2.final_backend.domain.claude.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Setter
+@Getter
+public class RequestDTO {
+ private String comment;
+}
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..618c667d
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/controller/ContractController.java
@@ -0,0 +1,116 @@
+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 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.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 java.security.GeneralSecurityException;
+import java.security.Principal;
+@Slf4j
+@RestController("commandContractController")
+@RequestMapping("/api/v1/contract")
+public class ContractController {
+
+ private final ContractCommandService contractCommandService;
+
+ @Autowired
+ public ContractController(ContractCommandService contractCommandService) {
+ this.contractCommandService = contractCommandService;
+ }
+
+ @Operation(summary = "계약서 등록(영업사원, 관리자)")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "계약서 등록 성공",
+ content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))})
+ })
+ @PostMapping("")
+ public ResponseEntity postContract(@RequestBody ContractRegistDTO contractRegistRequestDTO,
+ Principal principal) throws GeneralSecurityException {
+
+ contractRegistRequestDTO.setMemberId(principal.getName());
+ contractCommandService.registerContract(contractRegistRequestDTO);
+
+ return ResponseEntity.ok(ContractResponseMessage.builder()
+ .httpStatus(200)
+ .msg("계약서가 성공적으로 등록되었습니다.")
+ .result(null)
+ .build());
+ }
+
+ @Operation(summary = "계약서 수정(영업사원, 관리자)")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "계약서 수정 성공",
+ content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))})
+ })
+ @PutMapping("{contractId}")
+ public ResponseEntity putContract(@PathVariable String contractId,
+ Principal principal,
+ @RequestBody ContractModifyDTO contractModifyRequestDTO) throws GeneralSecurityException {
+
+ contractModifyRequestDTO.setContractId(contractId);
+ contractModifyRequestDTO.setMemberId(principal.getName());
+ contractCommandService.modifyContract(contractModifyRequestDTO);
+
+ return ResponseEntity.ok(ContractResponseMessage.builder()
+ .httpStatus(200)
+ .msg("계약서가 성공적으로 수정되었습니다.")
+ .result(null)
+ .build());
+ }
+
+ @Operation(summary = "계약서 삭제(영업사원, 관리자)")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "계약서 삭제 성공",
+ content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))})
+ })
+ @DeleteMapping("{contractId}")
+ public ResponseEntity deleteContract(@PathVariable String contractId) {
+
+ ContractDeleteDTO contractDeleteDTO = new ContractDeleteDTO();
+ contractDeleteDTO.setContractId(contractId);
+ contractCommandService.deleteContract(contractDeleteDTO);
+
+ return ResponseEntity.ok(ContractResponseMessage.builder()
+ .httpStatus(200)
+ .msg("계약서를 성공적으로 삭제하였습니다.")
+ .result(null)
+ .build());
+ }
+
+ @Operation(summary = "계약서 승인상태 수정(관리자)")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "계약서 승인상태 수정 성공",
+ content = {@Content(schema = @Schema(implementation = ContractResponseMessage.class))})
+ })
+ @PutMapping("/status/{contractId}")
+ public ResponseEntity putContractStatus(@PathVariable String contractId,
+ @RequestBody ContractStatusModifyDTO contractStatusModifyDTO,
+ Principal principal) throws GeneralSecurityException {
+
+ // DTO에 설정
+ contractStatusModifyDTO.setContractId(contractId);
+ contractStatusModifyDTO.setAdminId(principal.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/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/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 extends GrantedAuthority> 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
new file mode 100644
index 00000000..4bc04bb6
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractModifyDTO.java
@@ -0,0 +1,46 @@
+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
+public class ContractModifyDTO {
+
+ private String contractId;
+ private String title;
+ private String customerName;
+ private String customerSex;
+ private String customerIdentifiNo;
+ private Integer customerAge;
+ private String customerAddress;
+ private String customerEmail;
+ private String customerPhone;
+ private String companyName;
+ private String carName;
+ private String customerClassifcation;
+ private String customerPurchaseCondition;
+ private String serialNum;
+ private String selectOption;
+ private Integer downPayment;
+ private Integer intermediatePayment;
+ private Integer vehiclePrice;
+ private Integer remainderPayment;
+ private Integer consignmentPayment;
+ private Integer totalSales;
+ private String deliveryDate;
+ private String deliveryLocation;
+ private String status;
+ private String numberOfVehicles;
+ private String createdUrl;
+ private String createdAt;
+ private String updatedAt;
+ 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
new file mode 100644
index 00000000..31c8d9fa
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractRegistDTO.java
@@ -0,0 +1,40 @@
+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
+public class ContractRegistDTO {
+
+ private String title;
+ private String customerName;
+ private String customerSex;
+ private String customerIdentifiNo;
+ private Integer customerAge;
+ private String customerAddress;
+ 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 vehiclePrice;
+ private Integer intermediatePayment;
+ private Integer remainderPayment;
+ private Integer consignmentPayment;
+ private Integer totalSales;
+ private String carName;
+ private String deliveryDate;
+ private String deliveryLocation;
+ private String status;
+ private String numberOfVehicles;
+ private String createdUrl;
+ private String memberId;
+}
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..8c479cca
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/dto/ContractStatusModifyDTO.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 ContractStatusModifyDTO {
+ private String contractId;
+ private String status;
+ private String adminId;
+}
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
new file mode 100644
index 00000000..55892140
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/contract/command/application/service/ContractCommandService.java
@@ -0,0 +1,18 @@
+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;
+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 modifyContract(ContractModifyDTO contractModifyRequestDTO) throws GeneralSecurityException;
+
+ void deleteContract(ContractDeleteDTO contractDeleteDTO);
+
+ void modifyContractStatus(ContractStatusModifyDTO contractStatusModifyDTO) throws GeneralSecurityException;
+}
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..3df5315b
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/aggregate/entity/Contract.java
@@ -0,0 +1,158 @@
+package stanl_2.final_backend.domain.contract.command.domain.aggregate.entity;
+
+import jakarta.persistence.*;
+import lombok.*;
+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;
+import java.time.format.DateTimeFormatter;
+
+@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 contractId;
+
+ @Column(name = "CONR_TTL", nullable = false)
+ private String title;
+
+ @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 customerAddress;
+
+ @Column(name = "CONR_CUST_EMA", nullable = false)
+ private String customerEmail;
+
+ @Column(name = "CONR_CUST_PHO", nullable = false)
+ private String customerPhone;
+
+ @Column(name = "CONR_COMP_NAME")
+ private String companyName;
+
+ @Column(name = "CONR_CUST_CLA", nullable = false)
+ @ColumnDefault("'PERSONAL'")
+ private String customerClassifcation;
+
+ @Column(name = "CONR_CUST_PUR_COND", nullable = false)
+ @ColumnDefault("'CASH'")
+ private String customerPurchaseCondition;
+
+ @Column(name = "CONR_SERI_NUM", nullable = false)
+ private String serialNum;
+
+ @Column(name = "CONR_SELE_OPTI", nullable = false)
+ private String selectOption;
+
+ @Column(name = "CONR_DOWN_PAY", nullable = false)
+ private Integer downPayment;
+
+ @Column(name = "CONR_INTE_PAY", nullable = false)
+ private Integer intermediatePayment;
+
+ @Column(name = "CONR_REM_PAY", nullable = false)
+ private Integer remainderPayment;
+
+ @Column(name = "CONR_CONS_PAY", nullable = false)
+ private Integer consignmentPayment;
+
+ @Column(name = "CONR_DELV_DATE")
+ private String deliveryDate;
+
+ @Column(name = "CONR_DELV_LOC")
+ private String deliveryLocation;
+
+ @Column(name = "CONR_CAR_NAME")
+ private String carName;
+
+ @Column(name = "CONR_STAT", nullable = false)
+ private String status = "WAIT";
+
+ @Column(name = "CONR_NO_OF_VEH", nullable = false)
+ private Integer numberOfVehicles = 1;
+
+ @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;
+
+ @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 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;
+
+ @Column(name = "ADMI_ID")
+ private String adminId;
+
+ @Column(name = "CENT_ID", nullable = false)
+ private String centerId;
+
+ @Column(name = "CUST_ID", nullable = false)
+ private String customerId;
+
+ @Column(name = "PROD_ID", nullable = false)
+ private String productId;
+
+ /* 설명. 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/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/ContractRepository.java b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/repository/ContractRepository.java
new file mode 100644
index 00000000..e6ba9d8c
--- /dev/null
+++ b/src/main/java/stanl_2/final_backend/domain/contract/command/domain/repository/ContractRepository.java
@@ -0,0 +1,12 @@
+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;
+
+import java.util.Optional;
+
+public interface ContractRepository extends JpaRepository {
+ Optional