Merge branch 'main' of https://demo.stam.kr/leejisun9/bio_backend
This commit is contained in:
31
README.md
31
README.md
@@ -49,6 +49,7 @@ src/main/java/com/bio/bio_backend/
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public class ApiResponseDto<T> {
|
public class ApiResponseDto<T> {
|
||||||
|
private boolean success; // 성공/실패 여부 (true/false)
|
||||||
private int code; // HTTP 상태 코드
|
private int code; // HTTP 상태 코드
|
||||||
private String message; // 응답 메시지 (ApiResponseCode enum 값)
|
private String message; // 응답 메시지 (ApiResponseCode enum 값)
|
||||||
private String description; // 응답 설명
|
private String description; // 응답 설명
|
||||||
@@ -62,6 +63,7 @@ public class ApiResponseDto<T> {
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"success": true,
|
||||||
"code": 201,
|
"code": 201,
|
||||||
"message": "COMMON_SUCCESS_CREATED",
|
"message": "COMMON_SUCCESS_CREATED",
|
||||||
"description": "Created successfully",
|
"description": "Created successfully",
|
||||||
@@ -77,6 +79,7 @@ public class ApiResponseDto<T> {
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"success": false,
|
||||||
"code": 409,
|
"code": 409,
|
||||||
"message": "USER_ID_DUPLICATE",
|
"message": "USER_ID_DUPLICATE",
|
||||||
"description": "User ID already exists"
|
"description": "User ID already exists"
|
||||||
@@ -131,7 +134,8 @@ public enum ApiResponseCode {
|
|||||||
|
|
||||||
- **모든 API 응답**: `ApiResponseDto<T>`로 감싸서 반환
|
- **모든 API 응답**: `ApiResponseDto<T>`로 감싸서 반환
|
||||||
- **공용 응답 코드**: `COMMON_` 접두사로 시작하는 범용 코드 사용
|
- **공용 응답 코드**: `COMMON_` 접두사로 시작하는 범용 코드 사용
|
||||||
- **일관된 구조**: `code`, `message`, `description`, `data` 필드로 표준화
|
- **일관된 구조**: `success`, `code`, `message`, `description`, `data` 필드로 표준화
|
||||||
|
- **성공/실패 구분**: `success` 필드로 명확한 성공/실패 여부 전달
|
||||||
- **제네릭 활용**: `<T>`를 통해 다양한 데이터 타입 지원
|
- **제네릭 활용**: `<T>`를 통해 다양한 데이터 타입 지원
|
||||||
|
|
||||||
### 3. JWT 인증 시스템
|
### 3. JWT 인증 시스템
|
||||||
@@ -163,6 +167,21 @@ public enum ApiResponseCode {
|
|||||||
- **URL**: `http://localhost:8080/service/swagger-ui.html`
|
- **URL**: `http://localhost:8080/service/swagger-ui.html`
|
||||||
- **API Docs**: `http://localhost:8080/service/api-docs`
|
- **API Docs**: `http://localhost:8080/service/api-docs`
|
||||||
|
|
||||||
|
### 5. 시스템 모니터링 (Actuator)
|
||||||
|
|
||||||
|
#### Health 체크
|
||||||
|
|
||||||
|
- **기본 Health**: `http://localhost:8080/service/actuator/health`
|
||||||
|
- **Readiness Probe**: `http://localhost:8080/service/actuator/health/readiness`
|
||||||
|
- **Liveness Probe**: `http://localhost:8080/service/actuator/health/liveness`
|
||||||
|
|
||||||
|
#### 시스템 정보
|
||||||
|
|
||||||
|
- **애플리케이션 정보**: `http://localhost:8080/service/actuator/info`
|
||||||
|
- **환경 설정**: `http://localhost:8080/service/actuator/env`
|
||||||
|
- **설정 속성**: `http://localhost:8080/service/actuator/configprops`
|
||||||
|
- **메트릭**: `http://localhost:8080/service/actuator/metrics`
|
||||||
|
|
||||||
#### 주요 어노테이션
|
#### 주요 어노테이션
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@@ -182,7 +201,7 @@ public enum ApiResponseCode {
|
|||||||
- **SwaggerConfig.java**: OpenAPI 기본 정보 설정
|
- **SwaggerConfig.java**: OpenAPI 기본 정보 설정
|
||||||
- **application.properties**: Swagger UI 커스터마이징
|
- **application.properties**: Swagger UI 커스터마이징
|
||||||
|
|
||||||
### 5. 트랜잭션 관리
|
### 6. 트랜잭션 관리
|
||||||
|
|
||||||
#### 기본 설정
|
#### 기본 설정
|
||||||
|
|
||||||
@@ -206,7 +225,7 @@ public class MemberServiceImpl {
|
|||||||
- **메서드별**: 데이터 수정 시에만 `@Transactional` 개별 적용
|
- **메서드별**: 데이터 수정 시에만 `@Transactional` 개별 적용
|
||||||
- **설정**: `spring.jpa.open-in-view=false` (성능 최적화)
|
- **설정**: `spring.jpa.open-in-view=false` (성능 최적화)
|
||||||
|
|
||||||
### 6. 오류 등록 및 사용
|
### 7. 오류 등록 및 사용
|
||||||
|
|
||||||
#### 오류 코드 등록
|
#### 오류 코드 등록
|
||||||
|
|
||||||
@@ -233,7 +252,7 @@ throw new ApiException(ApiResponseCode.USER_ID_DUPLICATE);
|
|||||||
- **예외 클래스**: `ApiException`으로 비즈니스 로직 예외 처리
|
- **예외 클래스**: `ApiException`으로 비즈니스 로직 예외 처리
|
||||||
- **자동 처리**: `GlobalExceptionHandler`가 일관된 응답 형태로 변환
|
- **자동 처리**: `GlobalExceptionHandler`가 일관된 응답 형태로 변환
|
||||||
|
|
||||||
### 7. 로깅 시스템
|
### 8. 로깅 시스템
|
||||||
|
|
||||||
#### @LogExecution 어노테이션 사용법
|
#### @LogExecution 어노테이션 사용법
|
||||||
|
|
||||||
@@ -287,7 +306,7 @@ public OrderDto processOrder() { }
|
|||||||
|
|
||||||
**중요**: `@LogExecution` 어노테이션이 없으면 메서드 실행 로그가 출력되지 않습니다
|
**중요**: `@LogExecution` 어노테이션이 없으면 메서드 실행 로그가 출력되지 않습니다
|
||||||
|
|
||||||
### 8. MapStruct
|
### 9. MapStruct
|
||||||
|
|
||||||
**매퍼 인터페이스**
|
**매퍼 인터페이스**
|
||||||
|
|
||||||
@@ -311,7 +330,7 @@ Member entity = memberMapper.toEntity(dto);
|
|||||||
|
|
||||||
**자동 생성**: 컴파일 시 `MemberMapperImpl` 구현체 생성
|
**자동 생성**: 컴파일 시 `MemberMapperImpl` 구현체 생성
|
||||||
|
|
||||||
### 9. BaseEntity 상속
|
### 10. BaseEntity 상속
|
||||||
|
|
||||||
**모든 엔티티는 `BaseEntity` 상속을 원칙으로 합니다.**
|
**모든 엔티티는 `BaseEntity` 상속을 원칙으로 합니다.**
|
||||||
|
|
||||||
|
@@ -38,6 +38,9 @@ dependencies {
|
|||||||
// Validation
|
// Validation
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||||
|
|
||||||
|
// Spring Boot Actuator
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||||
|
|
||||||
// MapStruct
|
// MapStruct
|
||||||
implementation 'org.mapstruct:mapstruct:1.5.5.Final'
|
implementation 'org.mapstruct:mapstruct:1.5.5.Final'
|
||||||
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final'
|
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final'
|
||||||
|
@@ -1,4 +1,47 @@
|
|||||||
|
|
||||||
|
create table st_common_code (
|
||||||
|
sort_order integer not null,
|
||||||
|
use_flag boolean not null,
|
||||||
|
created_at timestamp(6) not null,
|
||||||
|
created_oid bigint,
|
||||||
|
oid bigint not null,
|
||||||
|
updated_at timestamp(6) not null,
|
||||||
|
updated_oid bigint,
|
||||||
|
code varchar(50) not null unique,
|
||||||
|
group_code varchar(50) not null,
|
||||||
|
parent_code varchar(50),
|
||||||
|
character_ref1 varchar(100),
|
||||||
|
character_ref2 varchar(100),
|
||||||
|
character_ref3 varchar(100),
|
||||||
|
character_ref4 varchar(100),
|
||||||
|
character_ref5 varchar(100),
|
||||||
|
name varchar(100) not null,
|
||||||
|
description varchar(500),
|
||||||
|
created_id varchar(255),
|
||||||
|
updated_id varchar(255),
|
||||||
|
primary key (oid)
|
||||||
|
);
|
||||||
|
|
||||||
|
create table st_common_group_code (
|
||||||
|
sort_order integer not null,
|
||||||
|
use_flag boolean not null,
|
||||||
|
created_at timestamp(6) not null,
|
||||||
|
created_oid bigint,
|
||||||
|
oid bigint not null,
|
||||||
|
updated_at timestamp(6) not null,
|
||||||
|
updated_oid bigint,
|
||||||
|
code varchar(50) not null unique,
|
||||||
|
character_ref1_title varchar(100),
|
||||||
|
character_ref2_title varchar(100),
|
||||||
|
character_ref3_title varchar(100),
|
||||||
|
character_ref4_title varchar(100),
|
||||||
|
character_ref5_title varchar(100),
|
||||||
|
name varchar(100) not null,
|
||||||
|
created_id varchar(255),
|
||||||
|
updated_id varchar(255),
|
||||||
|
primary key (oid)
|
||||||
|
);
|
||||||
|
|
||||||
create table st_file (
|
create table st_file (
|
||||||
use_flag boolean not null,
|
use_flag boolean not null,
|
||||||
created_at timestamp(6) not null,
|
created_at timestamp(6) not null,
|
||||||
@@ -38,5 +81,17 @@
|
|||||||
primary key (oid)
|
primary key (oid)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create index idx_common_code_code
|
||||||
|
on st_common_code (code);
|
||||||
|
|
||||||
|
create index idx_common_code_group_code
|
||||||
|
on st_common_code (group_code);
|
||||||
|
|
||||||
|
create index idx_common_code_parent_code
|
||||||
|
on st_common_code (parent_code);
|
||||||
|
|
||||||
|
create index idx_common_group_code_code
|
||||||
|
on st_common_group_code (code);
|
||||||
|
|
||||||
create index idx_member_user_id
|
create index idx_member_user_id
|
||||||
on st_member (user_id);
|
on st_member (user_id);
|
||||||
|
@@ -0,0 +1,208 @@
|
|||||||
|
package com.bio.bio_backend.domain.admin.common_code.controller;
|
||||||
|
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.dto.*;
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.service.CommonCodeService;
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.mapper.CommonCodeMapper;
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.mapper.CommonGroupCodeMapper;
|
||||||
|
import com.bio.bio_backend.global.dto.ApiResponseDto;
|
||||||
|
import com.bio.bio_backend.global.constants.ApiResponseCode;
|
||||||
|
import com.bio.bio_backend.global.annotation.LogExecution;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/admin/common-code")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Tag(name = "공통 코드 관리", description = "공통 코드 및 그룹 코드 관리 API")
|
||||||
|
public class CommonCodeController {
|
||||||
|
|
||||||
|
private final CommonCodeService commonCodeService;
|
||||||
|
private final CommonCodeMapper commonCodeMapper;
|
||||||
|
private final CommonGroupCodeMapper commonGroupCodeMapper;
|
||||||
|
|
||||||
|
// 그룹 코드 관련 API
|
||||||
|
@LogExecution("그룹 코드 생성")
|
||||||
|
@Operation(summary = "그룹 코드 생성", description = "새로운 그룹 코드를 생성합니다.")
|
||||||
|
@ApiResponses({
|
||||||
|
@ApiResponse(responseCode = "201", description = "그룹 코드 생성 성공"),
|
||||||
|
@ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content(schema = @Schema(implementation = ApiResponseDto.class))),
|
||||||
|
@ApiResponse(responseCode = "409", description = "중복된 그룹 코드", content = @Content(schema = @Schema(implementation = ApiResponseDto.class)))
|
||||||
|
})
|
||||||
|
@PostMapping("/group")
|
||||||
|
public ResponseEntity<ApiResponseDto<CreateCommonGroupCodeResponseDto>> createGroupCode(@RequestBody @Valid CreateCommonGroupCodeRequestDto requestDto) {
|
||||||
|
CommonGroupCodeDto groupCodeDto = commonGroupCodeMapper.toCommonGroupCodeDto(requestDto);
|
||||||
|
CommonGroupCodeDto createdGroupCode = commonCodeService.createGroupCode(groupCodeDto);
|
||||||
|
CreateCommonGroupCodeResponseDto responseDto = commonGroupCodeMapper.toCreateCommonGroupCodeResponseDto(createdGroupCode);
|
||||||
|
ApiResponseDto<CreateCommonGroupCodeResponseDto> apiResponse = ApiResponseDto.success(ApiResponseCode.COMMON_SUCCESS_CREATED, responseDto);
|
||||||
|
|
||||||
|
return ResponseEntity.status(HttpStatus.CREATED).body(apiResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LogExecution("그룹 코드 수정")
|
||||||
|
@Operation(summary = "그룹 코드 수정", description = "기존 그룹 코드를 수정합니다.")
|
||||||
|
@ApiResponses({
|
||||||
|
@ApiResponse(responseCode = "200", description = "그룹 코드 수정 성공"),
|
||||||
|
@ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content(schema = @Schema(implementation = ApiResponseDto.class))),
|
||||||
|
@ApiResponse(responseCode = "404", description = "그룹 코드를 찾을 수 없음", content = @Content(schema = @Schema(implementation = ApiResponseDto.class)))
|
||||||
|
})
|
||||||
|
@PutMapping("/group/{code}")
|
||||||
|
public ResponseEntity<ApiResponseDto<String>> updateGroupCode(
|
||||||
|
@PathVariable String code,
|
||||||
|
@RequestBody @Valid UpdateCommonGroupCodeRequestDto requestDto) {
|
||||||
|
CommonGroupCodeDto groupCodeDto = commonGroupCodeMapper.toCommonGroupCodeDto(requestDto);
|
||||||
|
commonCodeService.updateGroupCode(code, groupCodeDto);
|
||||||
|
return ResponseEntity.ok(ApiResponseDto.success(ApiResponseCode.COMMON_SUCCESS_UPDATED));
|
||||||
|
}
|
||||||
|
|
||||||
|
@LogExecution("그룹 코드 삭제")
|
||||||
|
@Operation(summary = "그룹 코드 삭제", description = "그룹 코드를 삭제합니다.")
|
||||||
|
@ApiResponses({
|
||||||
|
@ApiResponse(responseCode = "200", description = "그룹 코드 삭제 성공"),
|
||||||
|
@ApiResponse(responseCode = "400", description = "하위 공통 코드가 존재하여 삭제할 수 없음", content = @Content(schema = @Schema(implementation = ApiResponseDto.class))),
|
||||||
|
@ApiResponse(responseCode = "404", description = "그룹 코드를 찾을 수 없음", content = @Content(schema = @Schema(implementation = ApiResponseDto.class)))
|
||||||
|
})
|
||||||
|
@DeleteMapping("/group/{code}")
|
||||||
|
public ResponseEntity<ApiResponseDto<Void>> deleteGroupCode(@PathVariable String code) {
|
||||||
|
commonCodeService.deleteGroupCode(code);
|
||||||
|
return ResponseEntity.ok(ApiResponseDto.success(ApiResponseCode.COMMON_SUCCESS_DELETED));
|
||||||
|
}
|
||||||
|
|
||||||
|
@LogExecution("그룹 코드 조회")
|
||||||
|
@Operation(summary = "그룹 코드 조회", description = "특정 그룹 코드를 조회합니다.")
|
||||||
|
@ApiResponses({
|
||||||
|
@ApiResponse(responseCode = "200", description = "그룹 코드 조회 성공"),
|
||||||
|
@ApiResponse(responseCode = "404", description = "그룹 코드를 찾을 수 없음", content = @Content(schema = @Schema(implementation = ApiResponseDto.class)))
|
||||||
|
})
|
||||||
|
@GetMapping("/group/{code}")
|
||||||
|
public ResponseEntity<ApiResponseDto<CommonGroupCodeDto>> getGroupCode(@PathVariable String code) {
|
||||||
|
CommonGroupCodeDto groupCode = commonCodeService.getGroupCode(code);
|
||||||
|
return ResponseEntity.ok(ApiResponseDto.success(ApiResponseCode.COMMON_SUCCESS_RETRIEVED, groupCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
@LogExecution("전체 그룹 코드 조회")
|
||||||
|
@Operation(summary = "전체 그룹 코드 조회", description = "모든 그룹 코드를 조회합니다.")
|
||||||
|
@ApiResponses({
|
||||||
|
@ApiResponse(responseCode = "200", description = "전체 그룹 코드 조회 성공")
|
||||||
|
})
|
||||||
|
@GetMapping("/group")
|
||||||
|
public ResponseEntity<ApiResponseDto<List<CommonGroupCodeDto>>> getAllGroupCodes() {
|
||||||
|
List<CommonGroupCodeDto> groupCodes = commonCodeService.getAllGroupCodes();
|
||||||
|
return ResponseEntity.ok(ApiResponseDto.success(ApiResponseCode.COMMON_SUCCESS_RETRIEVED, groupCodes));
|
||||||
|
}
|
||||||
|
|
||||||
|
@LogExecution("활성 그룹 코드 조회")
|
||||||
|
@Operation(summary = "활성 그룹 코드 조회", description = "사용 중인 그룹 코드만 조회합니다.")
|
||||||
|
@ApiResponses({
|
||||||
|
@ApiResponse(responseCode = "200", description = "활성 그룹 코드 조회 성공")
|
||||||
|
})
|
||||||
|
@GetMapping("/group/active")
|
||||||
|
public ResponseEntity<ApiResponseDto<List<CommonGroupCodeDto>>> getActiveGroupCodes() {
|
||||||
|
List<CommonGroupCodeDto> groupCodes = commonCodeService.getActiveGroupCodes();
|
||||||
|
return ResponseEntity.ok(ApiResponseDto.success(ApiResponseCode.COMMON_SUCCESS_RETRIEVED, groupCodes));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 공통 코드 관련 API
|
||||||
|
@LogExecution("공통 코드 생성")
|
||||||
|
@Operation(summary = "공통 코드 생성", description = "새로운 공통 코드를 생성합니다.")
|
||||||
|
@ApiResponses({
|
||||||
|
@ApiResponse(responseCode = "201", description = "공통 코드 생성 성공"),
|
||||||
|
@ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content(schema = @Schema(implementation = ApiResponseDto.class))),
|
||||||
|
@ApiResponse(responseCode = "409", description = "중복된 공통 코드", content = @Content(schema = @Schema(implementation = ApiResponseDto.class)))
|
||||||
|
})
|
||||||
|
@PostMapping
|
||||||
|
public ResponseEntity<ApiResponseDto<CreateCommonCodeResponseDto>> createCode(@RequestBody @Valid CreateCommonCodeRequestDto requestDto) {
|
||||||
|
CommonCodeDto commonCodeDto = commonCodeMapper.toCommonCodeDto(requestDto);
|
||||||
|
CommonCodeDto createdCommonCode = commonCodeService.createCode(commonCodeDto);
|
||||||
|
CreateCommonCodeResponseDto responseDto = commonCodeMapper.toCreateCommonCodeResponseDto(createdCommonCode);
|
||||||
|
ApiResponseDto<CreateCommonCodeResponseDto> apiResponse = ApiResponseDto.success(ApiResponseCode.COMMON_SUCCESS_CREATED, responseDto);
|
||||||
|
|
||||||
|
return ResponseEntity.status(HttpStatus.CREATED).body(apiResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LogExecution("공통 코드 수정")
|
||||||
|
@Operation(summary = "공통 코드 수정", description = "기존 공통 코드를 수정합니다.")
|
||||||
|
@ApiResponses({
|
||||||
|
@ApiResponse(responseCode = "200", description = "공통 코드 수정 성공"),
|
||||||
|
@ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content(schema = @Schema(implementation = ApiResponseDto.class))),
|
||||||
|
@ApiResponse(responseCode = "404", description = "공통 코드를 찾을 수 없음", content = @Content(schema = @Schema(implementation = ApiResponseDto.class)))
|
||||||
|
})
|
||||||
|
@PutMapping("/{code}")
|
||||||
|
public ResponseEntity<ApiResponseDto<String>> updateCode(
|
||||||
|
@PathVariable String code,
|
||||||
|
@RequestBody @Valid UpdateCommonCodeRequestDto requestDto) {
|
||||||
|
CommonCodeDto commonCodeDto = commonCodeMapper.toCommonCodeDto(requestDto);
|
||||||
|
commonCodeService.updateCode(code, commonCodeDto);
|
||||||
|
return ResponseEntity.ok(ApiResponseDto.success(ApiResponseCode.COMMON_SUCCESS_UPDATED));
|
||||||
|
}
|
||||||
|
|
||||||
|
@LogExecution("공통 코드 삭제")
|
||||||
|
@Operation(summary = "공통 코드 삭제", description = "공통 코드를 삭제합니다.")
|
||||||
|
@ApiResponses({
|
||||||
|
@ApiResponse(responseCode = "200", description = "공통 코드 삭제 성공"),
|
||||||
|
@ApiResponse(responseCode = "400", description = "하위 공통 코드가 존재하여 삭제할 수 없음", content = @Content(schema = @Schema(implementation = ApiResponseDto.class))),
|
||||||
|
@ApiResponse(responseCode = "404", description = "공통 코드를 찾을 수 없음", content = @Content(schema = @Schema(implementation = ApiResponseDto.class)))
|
||||||
|
})
|
||||||
|
@DeleteMapping("/{code}")
|
||||||
|
public ResponseEntity<ApiResponseDto<Void>> deleteCode(@PathVariable String code) {
|
||||||
|
commonCodeService.deleteCode(code);
|
||||||
|
return ResponseEntity.ok(ApiResponseDto.success(ApiResponseCode.COMMON_SUCCESS_DELETED));
|
||||||
|
}
|
||||||
|
|
||||||
|
@LogExecution("공통 코드 조회")
|
||||||
|
@Operation(summary = "공통 코드 조회", description = "특정 공통 코드를 조회합니다.")
|
||||||
|
@ApiResponses({
|
||||||
|
@ApiResponse(responseCode = "200", description = "공통 코드 조회 성공"),
|
||||||
|
@ApiResponse(responseCode = "404", description = "공통 코드를 찾을 수 없음", content = @Content(schema = @Schema(implementation = ApiResponseDto.class)))
|
||||||
|
})
|
||||||
|
@GetMapping("/{code}")
|
||||||
|
public ResponseEntity<ApiResponseDto<CommonCodeDto>> getCode(@PathVariable String code) {
|
||||||
|
CommonCodeDto commonCode = commonCodeService.getCode(code);
|
||||||
|
return ResponseEntity.ok(ApiResponseDto.success(ApiResponseCode.COMMON_SUCCESS_RETRIEVED, commonCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
@LogExecution("그룹별 공통 코드 조회")
|
||||||
|
@Operation(summary = "그룹별 공통 코드 조회", description = "특정 그룹에 속한 공통 코드들을 조회합니다.")
|
||||||
|
@ApiResponses({
|
||||||
|
@ApiResponse(responseCode = "200", description = "그룹별 공통 코드 조회 성공")
|
||||||
|
})
|
||||||
|
@GetMapping("/group/{groupCode}/codes")
|
||||||
|
public ResponseEntity<ApiResponseDto<List<CommonCodeDto>>> getCodesByGroupCode(@PathVariable String groupCode) {
|
||||||
|
List<CommonCodeDto> commonCodes = commonCodeService.getActiveCodesByGroupCode(groupCode);
|
||||||
|
return ResponseEntity.ok(ApiResponseDto.success(ApiResponseCode.COMMON_SUCCESS_RETRIEVED, commonCodes));
|
||||||
|
}
|
||||||
|
|
||||||
|
@LogExecution("상위 코드별 공통 코드 조회")
|
||||||
|
@Operation(summary = "상위 코드별 공통 코드 조회", description = "특정 상위 코드에 속한 공통 코드들을 조회합니다.")
|
||||||
|
@ApiResponses({
|
||||||
|
@ApiResponse(responseCode = "200", description = "상위 코드별 공통 코드 조회 성공")
|
||||||
|
})
|
||||||
|
@GetMapping("/parent/{parentCode}/codes")
|
||||||
|
public ResponseEntity<ApiResponseDto<List<CommonCodeDto>>> getCodesByParentCode(@PathVariable String parentCode) {
|
||||||
|
List<CommonCodeDto> commonCodes = commonCodeService.getActiveCodesByParentCode(parentCode);
|
||||||
|
return ResponseEntity.ok(ApiResponseDto.success(ApiResponseCode.COMMON_SUCCESS_RETRIEVED, commonCodes));
|
||||||
|
}
|
||||||
|
|
||||||
|
@LogExecution("전체 공통 코드 조회")
|
||||||
|
@Operation(summary = "전체 공통 코드 조회", description = "모든 공통 코드를 조회합니다.")
|
||||||
|
@ApiResponses({
|
||||||
|
@ApiResponse(responseCode = "200", description = "전체 공통 코드 조회 성공")
|
||||||
|
})
|
||||||
|
@GetMapping
|
||||||
|
public ResponseEntity<ApiResponseDto<List<CommonCodeDto>>> getAllCodes() {
|
||||||
|
List<CommonCodeDto> commonCodes = commonCodeService.getAllCodes();
|
||||||
|
return ResponseEntity.ok(ApiResponseDto.success(ApiResponseCode.COMMON_SUCCESS_RETRIEVED, commonCodes));
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,27 @@
|
|||||||
|
package com.bio.bio_backend.domain.admin.common_code.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CommonCodeDto {
|
||||||
|
|
||||||
|
private Long oid;
|
||||||
|
private String code;
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private String groupCode;
|
||||||
|
private String parentCode;
|
||||||
|
private String characterRef1;
|
||||||
|
private String characterRef2;
|
||||||
|
private String characterRef3;
|
||||||
|
private String characterRef4;
|
||||||
|
private String characterRef5;
|
||||||
|
private Integer sortOrder;
|
||||||
|
private Boolean useFlag;
|
||||||
|
}
|
@@ -0,0 +1,24 @@
|
|||||||
|
package com.bio.bio_backend.domain.admin.common_code.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CommonGroupCodeDto {
|
||||||
|
|
||||||
|
private Long oid;
|
||||||
|
private String code;
|
||||||
|
private String name;
|
||||||
|
private String characterRef1Title;
|
||||||
|
private String characterRef2Title;
|
||||||
|
private String characterRef3Title;
|
||||||
|
private String characterRef4Title;
|
||||||
|
private String characterRef5Title;
|
||||||
|
private Integer sortOrder;
|
||||||
|
private Boolean useFlag;
|
||||||
|
}
|
@@ -0,0 +1,55 @@
|
|||||||
|
package com.bio.bio_backend.domain.admin.common_code.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CreateCommonCodeRequestDto {
|
||||||
|
|
||||||
|
@NotBlank(message = "코드는 필수입니다")
|
||||||
|
@Size(max = 50, message = "코드는 50자를 초과할 수 없습니다")
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
@NotBlank(message = "이름은 필수입니다")
|
||||||
|
@Size(max = 100, message = "이름은 100자를 초과할 수 없습니다")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Size(max = 500, message = "설명은 500자를 초과할 수 없습니다")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@NotBlank(message = "그룹 코드는 필수입니다")
|
||||||
|
@Size(max = 50, message = "그룹 코드는 50자를 초과할 수 없습니다")
|
||||||
|
private String groupCode;
|
||||||
|
|
||||||
|
@Size(max = 50, message = "상위 코드는 50자를 초과할 수 없습니다")
|
||||||
|
private String parentCode;
|
||||||
|
|
||||||
|
@Size(max = 100, message = "문자 참조1은 100자를 초과할 수 없습니다")
|
||||||
|
private String characterRef1;
|
||||||
|
|
||||||
|
@Size(max = 100, message = "문자 참조2는 100자를 초과할 수 없습니다")
|
||||||
|
private String characterRef2;
|
||||||
|
|
||||||
|
@Size(max = 100, message = "문자 참조3은 100자를 초과할 수 없습니다")
|
||||||
|
private String characterRef3;
|
||||||
|
|
||||||
|
@Size(max = 100, message = "문자 참조4는 100자를 초과할 수 없습니다")
|
||||||
|
private String characterRef4;
|
||||||
|
|
||||||
|
@Size(max = 100, message = "문자 참조5는 100자를 초과할 수 없습니다")
|
||||||
|
private String characterRef5;
|
||||||
|
|
||||||
|
@Builder.Default
|
||||||
|
private Integer sortOrder = 0;
|
||||||
|
|
||||||
|
@Builder.Default
|
||||||
|
private Boolean useFlag = true;
|
||||||
|
}
|
@@ -0,0 +1,27 @@
|
|||||||
|
package com.bio.bio_backend.domain.admin.common_code.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CreateCommonCodeResponseDto {
|
||||||
|
|
||||||
|
private Long oid;
|
||||||
|
private String code;
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private String groupCode;
|
||||||
|
private String parentCode;
|
||||||
|
private String characterRef1;
|
||||||
|
private String characterRef2;
|
||||||
|
private String characterRef3;
|
||||||
|
private String characterRef4;
|
||||||
|
private String characterRef5;
|
||||||
|
private Integer sortOrder;
|
||||||
|
private Boolean useFlag;
|
||||||
|
}
|
@@ -0,0 +1,45 @@
|
|||||||
|
package com.bio.bio_backend.domain.admin.common_code.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CreateCommonGroupCodeRequestDto {
|
||||||
|
|
||||||
|
@NotBlank(message = "코드는 필수입니다")
|
||||||
|
@Size(max = 50, message = "코드는 50자를 초과할 수 없습니다")
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
@NotBlank(message = "이름은 필수입니다")
|
||||||
|
@Size(max = 100, message = "이름은 100자를 초과할 수 없습니다")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Size(max = 100, message = "차트 참조1 제목은 100자를 초과할 수 없습니다")
|
||||||
|
private String characterRef1Title;
|
||||||
|
|
||||||
|
@Size(max = 100, message = "차트 참조2 제목은 100자를 초과할 수 없습니다")
|
||||||
|
private String characterRef2Title;
|
||||||
|
|
||||||
|
@Size(max = 100, message = "차트 참조3 제목은 100자를 초과할 수 없습니다")
|
||||||
|
private String characterRef3Title;
|
||||||
|
|
||||||
|
@Size(max = 100, message = "차트 참조4 제목은 100자를 초과할 수 없습니다")
|
||||||
|
private String characterRef4Title;
|
||||||
|
|
||||||
|
@Size(max = 100, message = "차트 참조5 제목은 100자를 초과할 수 없습니다")
|
||||||
|
private String characterRef5Title;
|
||||||
|
|
||||||
|
@Builder.Default
|
||||||
|
private Integer sortOrder = 0;
|
||||||
|
|
||||||
|
@Builder.Default
|
||||||
|
private Boolean useFlag = true;
|
||||||
|
}
|
@@ -0,0 +1,24 @@
|
|||||||
|
package com.bio.bio_backend.domain.admin.common_code.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CreateCommonGroupCodeResponseDto {
|
||||||
|
|
||||||
|
private Long oid;
|
||||||
|
private String code;
|
||||||
|
private String name;
|
||||||
|
private String characterRef1Title;
|
||||||
|
private String characterRef2Title;
|
||||||
|
private String characterRef3Title;
|
||||||
|
private String characterRef4Title;
|
||||||
|
private String characterRef5Title;
|
||||||
|
private Integer sortOrder;
|
||||||
|
private Boolean useFlag;
|
||||||
|
}
|
@@ -0,0 +1,49 @@
|
|||||||
|
package com.bio.bio_backend.domain.admin.common_code.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class UpdateCommonCodeRequestDto {
|
||||||
|
|
||||||
|
@NotBlank(message = "이름은 필수입니다")
|
||||||
|
@Size(max = 100, message = "이름은 100자를 초과할 수 없습니다")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Size(max = 500, message = "설명은 500자를 초과할 수 없습니다")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@NotBlank(message = "그룹 코드는 필수입니다")
|
||||||
|
@Size(max = 50, message = "그룹 코드는 50자를 초과할 수 없습니다")
|
||||||
|
private String groupCode;
|
||||||
|
|
||||||
|
@Size(max = 50, message = "상위 코드는 50자를 초과할 수 없습니다")
|
||||||
|
private String parentCode;
|
||||||
|
|
||||||
|
@Size(max = 100, message = "문자 참조1은 100자를 초과할 수 없습니다")
|
||||||
|
private String characterRef1;
|
||||||
|
|
||||||
|
@Size(max = 100, message = "문자 참조2는 100자를 초과할 수 없습니다")
|
||||||
|
private String characterRef2;
|
||||||
|
|
||||||
|
@Size(max = 100, message = "문자 참조3은 100자를 초과할 수 없습니다")
|
||||||
|
private String characterRef3;
|
||||||
|
|
||||||
|
@Size(max = 100, message = "문자 참조4는 100자를 초과할 수 없습니다")
|
||||||
|
private String characterRef4;
|
||||||
|
|
||||||
|
@Size(max = 100, message = "문자 참조5는 100자를 초과할 수 없습니다")
|
||||||
|
private String characterRef5;
|
||||||
|
|
||||||
|
private Integer sortOrder;
|
||||||
|
|
||||||
|
private Boolean useFlag;
|
||||||
|
}
|
@@ -0,0 +1,39 @@
|
|||||||
|
package com.bio.bio_backend.domain.admin.common_code.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class UpdateCommonGroupCodeRequestDto {
|
||||||
|
|
||||||
|
@NotBlank(message = "이름은 필수입니다")
|
||||||
|
@Size(max = 100, message = "이름은 100자를 초과할 수 없습니다")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Size(max = 100, message = "문자 참조1 제목은 100자를 초과할 수 없습니다")
|
||||||
|
private String characterRef1Title;
|
||||||
|
|
||||||
|
@Size(max = 100, message = "문자 참조2 제목은 100자를 초과할 수 없습니다")
|
||||||
|
private String characterRef2Title;
|
||||||
|
|
||||||
|
@Size(max = 100, message = "문자 참조3 제목은 100자를 초과할 수 없습니다")
|
||||||
|
private String characterRef3Title;
|
||||||
|
|
||||||
|
@Size(max = 100, message = "문자 참조4 제목은 100자를 초과할 수 없습니다")
|
||||||
|
private String characterRef4Title;
|
||||||
|
|
||||||
|
@Size(max = 100, message = "문자 참조5 제목은 100자를 초과할 수 없습니다")
|
||||||
|
private String characterRef5Title;
|
||||||
|
|
||||||
|
private Integer sortOrder;
|
||||||
|
|
||||||
|
private Boolean useFlag;
|
||||||
|
}
|
@@ -0,0 +1,75 @@
|
|||||||
|
package com.bio.bio_backend.domain.admin.common_code.entity;
|
||||||
|
|
||||||
|
import com.bio.bio_backend.global.constants.AppConstants;
|
||||||
|
import com.bio.bio_backend.global.entity.BaseEntity;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Getter @Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@Table(
|
||||||
|
name = AppConstants.TABLE_PREFIX + "common_code",
|
||||||
|
indexes = {
|
||||||
|
@Index(name = "idx_common_code_code", columnList = "code"),
|
||||||
|
@Index(name = "idx_common_code_group_code", columnList = "group_code"),
|
||||||
|
@Index(name = "idx_common_code_parent_code", columnList = "parent_code")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public class CommonCode extends BaseEntity {
|
||||||
|
|
||||||
|
@Column(name = "code", nullable = false, length = 50, unique = true)
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
@Column(name = "name", nullable = false, length = 100)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(name = "description", length = 500)
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Column(name = "group_code", nullable = false, length = 50)
|
||||||
|
private String groupCode;
|
||||||
|
|
||||||
|
@Column(name = "parent_code", length = 50)
|
||||||
|
private String parentCode;
|
||||||
|
|
||||||
|
@Column(name = "character_ref1", length = 100)
|
||||||
|
private String characterRef1;
|
||||||
|
|
||||||
|
@Column(name = "character_ref2", length = 100)
|
||||||
|
private String characterRef2;
|
||||||
|
|
||||||
|
@Column(name = "character_ref3", length = 100)
|
||||||
|
private String characterRef3;
|
||||||
|
|
||||||
|
@Column(name = "character_ref4", length = 100)
|
||||||
|
private String characterRef4;
|
||||||
|
|
||||||
|
@Column(name = "character_ref5", length = 100)
|
||||||
|
private String characterRef5;
|
||||||
|
|
||||||
|
@Column(name = "sort_order", nullable = false)
|
||||||
|
@Builder.Default
|
||||||
|
private Integer sortOrder = 0;
|
||||||
|
|
||||||
|
@Column(name = "use_flag", nullable = false)
|
||||||
|
@Builder.Default
|
||||||
|
private Boolean useFlag = true;
|
||||||
|
|
||||||
|
// 관계 설정
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(
|
||||||
|
name = "group_code",
|
||||||
|
referencedColumnName = "code",
|
||||||
|
insertable = false,
|
||||||
|
updatable = false,
|
||||||
|
foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT)
|
||||||
|
)
|
||||||
|
private CommonGroupCode commonGroupCode;
|
||||||
|
}
|
@@ -0,0 +1,53 @@
|
|||||||
|
package com.bio.bio_backend.domain.admin.common_code.entity;
|
||||||
|
|
||||||
|
import com.bio.bio_backend.global.constants.AppConstants;
|
||||||
|
import com.bio.bio_backend.global.entity.BaseEntity;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Getter @Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@Table(
|
||||||
|
name = AppConstants.TABLE_PREFIX + "common_group_code",
|
||||||
|
indexes = {
|
||||||
|
@Index(name = "idx_common_group_code_code", columnList = "code")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
public class CommonGroupCode extends BaseEntity {
|
||||||
|
|
||||||
|
@Column(name = "code", nullable = false, length = 50, unique = true)
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
@Column(name = "name", nullable = false, length = 100)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(name = "character_ref1_title", length = 100)
|
||||||
|
private String characterRef1Title;
|
||||||
|
|
||||||
|
@Column(name = "character_ref2_title", length = 100)
|
||||||
|
private String characterRef2Title;
|
||||||
|
|
||||||
|
@Column(name = "character_ref3_title", length = 100)
|
||||||
|
private String characterRef3Title;
|
||||||
|
|
||||||
|
@Column(name = "character_ref4_title", length = 100)
|
||||||
|
private String characterRef4Title;
|
||||||
|
|
||||||
|
@Column(name = "character_ref5_title", length = 100)
|
||||||
|
private String characterRef5Title;
|
||||||
|
|
||||||
|
@Column(name = "sort_order", nullable = false)
|
||||||
|
@Builder.Default
|
||||||
|
private Integer sortOrder = 0;
|
||||||
|
|
||||||
|
@Column(name = "use_flag", nullable = false)
|
||||||
|
@Builder.Default
|
||||||
|
private Boolean useFlag = true;
|
||||||
|
}
|
@@ -0,0 +1,58 @@
|
|||||||
|
package com.bio.bio_backend.domain.admin.common_code.mapper;
|
||||||
|
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.dto.CommonCodeDto;
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.dto.CreateCommonCodeRequestDto;
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.dto.CreateCommonCodeResponseDto;
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.dto.UpdateCommonCodeRequestDto;
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.entity.CommonCode;
|
||||||
|
import com.bio.bio_backend.global.annotation.IgnoreBaseEntityMapping;
|
||||||
|
import com.bio.bio_backend.global.config.GlobalMapperConfig;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper(config = GlobalMapperConfig.class)
|
||||||
|
public interface CommonCodeMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CommonCode 엔티티를 CommonCodeDto로 변환
|
||||||
|
*/
|
||||||
|
CommonCodeDto toCommonCodeDto(CommonCode commonCode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CommonCodeDto를 CommonCode 엔티티로 변환
|
||||||
|
*/
|
||||||
|
@Mapping(target = "commonGroupCode", ignore = true)
|
||||||
|
CommonCode toCommonCode(CommonCodeDto commonCodeDto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CommonCode 엔티티 리스트를 CommonCodeDto 리스트로 변환
|
||||||
|
*/
|
||||||
|
List<CommonCodeDto> toCommonCodeDtoList(List<CommonCode> commonCodes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CommonCodeDto를 CreateCommonCodeResponseDto로 변환
|
||||||
|
*/
|
||||||
|
CreateCommonCodeResponseDto toCreateCommonCodeResponseDto(CommonCodeDto commonCodeDto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateCommonCodeRequestDto를 CommonCodeDto로 변환
|
||||||
|
*/
|
||||||
|
@Mapping(target = "oid", ignore = true)
|
||||||
|
CommonCodeDto toCommonCodeDto(CreateCommonCodeRequestDto createRequestDto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateCommonCodeRequestDto를 CommonCodeDto로 변환
|
||||||
|
*/
|
||||||
|
@Mapping(target = "oid", ignore = true)
|
||||||
|
@Mapping(target = "code", ignore = true)
|
||||||
|
CommonCodeDto toCommonCodeDto(UpdateCommonCodeRequestDto updateRequestDto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CommonCodeDto의 값으로 CommonCode 엔티티를 업데이트
|
||||||
|
*/
|
||||||
|
@IgnoreBaseEntityMapping
|
||||||
|
@Mapping(target = "commonGroupCode", ignore = true)
|
||||||
|
void updateCommonCodeFromDto(CommonCodeDto commonCodeDto, @org.mapstruct.MappingTarget CommonCode commonCode);
|
||||||
|
}
|
@@ -0,0 +1,56 @@
|
|||||||
|
package com.bio.bio_backend.domain.admin.common_code.mapper;
|
||||||
|
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.dto.CommonGroupCodeDto;
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.dto.CreateCommonGroupCodeRequestDto;
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.dto.CreateCommonGroupCodeResponseDto;
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.dto.UpdateCommonGroupCodeRequestDto;
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.entity.CommonGroupCode;
|
||||||
|
import com.bio.bio_backend.global.annotation.IgnoreBaseEntityMapping;
|
||||||
|
import com.bio.bio_backend.global.config.GlobalMapperConfig;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper(config = GlobalMapperConfig.class)
|
||||||
|
public interface CommonGroupCodeMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CommonGroupCode 엔티티를 CommonGroupCodeDto로 변환
|
||||||
|
*/
|
||||||
|
CommonGroupCodeDto toCommonGroupCodeDto(CommonGroupCode commonGroupCode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CommonGroupCodeDto를 CommonGroupCode 엔티티로 변환
|
||||||
|
*/
|
||||||
|
CommonGroupCode toCommonGroupCode(CommonGroupCodeDto commonGroupCodeDto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CommonGroupCode 엔티티 리스트를 CommonGroupCodeDto 리스트로 변환
|
||||||
|
*/
|
||||||
|
List<CommonGroupCodeDto> toCommonGroupCodeDtoList(List<CommonGroupCode> commonGroupCodes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CommonGroupCodeDto를 CreateCommonGroupCodeResponseDto로 변환
|
||||||
|
*/
|
||||||
|
CreateCommonGroupCodeResponseDto toCreateCommonGroupCodeResponseDto(CommonGroupCodeDto commonGroupCodeDto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateCommonGroupCodeRequestDto를 CommonGroupCodeDto로 변환
|
||||||
|
*/
|
||||||
|
@Mapping(target = "oid", ignore = true)
|
||||||
|
CommonGroupCodeDto toCommonGroupCodeDto(CreateCommonGroupCodeRequestDto createRequestDto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateCommonGroupCodeRequestDto를 CommonGroupCodeDto로 변환
|
||||||
|
*/
|
||||||
|
@Mapping(target = "oid", ignore = true)
|
||||||
|
@Mapping(target = "code", ignore = true)
|
||||||
|
CommonGroupCodeDto toCommonGroupCodeDto(UpdateCommonGroupCodeRequestDto updateRequestDto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CommonGroupCodeDto의 값으로 CommonGroupCode 엔티티를 업데이트
|
||||||
|
*/
|
||||||
|
@IgnoreBaseEntityMapping
|
||||||
|
void updateCommonGroupCodeFromDto(CommonGroupCodeDto commonGroupCodeDto, @org.mapstruct.MappingTarget CommonGroupCode commonGroupCode);
|
||||||
|
}
|
@@ -0,0 +1,32 @@
|
|||||||
|
package com.bio.bio_backend.domain.admin.common_code.repository;
|
||||||
|
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.entity.CommonCode;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface CommonCodeRepository extends JpaRepository<CommonCode, Long> {
|
||||||
|
|
||||||
|
Optional<CommonCode> findByCode(String code);
|
||||||
|
|
||||||
|
List<CommonCode> findByGroupCodeAndUseFlagOrderBySortOrderAsc(String groupCode, Boolean useFlag);
|
||||||
|
|
||||||
|
List<CommonCode> findByParentCodeAndUseFlagOrderBySortOrderAsc(String parentCode, Boolean useFlag);
|
||||||
|
|
||||||
|
@Query("SELECT cc FROM CommonCode cc WHERE cc.groupCode = :groupCode AND cc.useFlag = :useFlag ORDER BY cc.sortOrder ASC")
|
||||||
|
List<CommonCode> findActiveCodesByGroupCodeOrderBySortOrder(@Param("groupCode") String groupCode, @Param("useFlag") Boolean useFlag);
|
||||||
|
|
||||||
|
@Query("SELECT cc FROM CommonCode cc WHERE cc.parentCode = :parentCode AND cc.useFlag = :useFlag ORDER BY cc.sortOrder ASC")
|
||||||
|
List<CommonCode> findActiveCodesByParentCodeOrderBySortOrder(@Param("parentCode") String parentCode, @Param("useFlag") Boolean useFlag);
|
||||||
|
|
||||||
|
boolean existsByCode(String code);
|
||||||
|
|
||||||
|
boolean existsByGroupCode(String groupCode);
|
||||||
|
|
||||||
|
boolean existsByParentCode(String parentCode);
|
||||||
|
}
|
@@ -0,0 +1,23 @@
|
|||||||
|
package com.bio.bio_backend.domain.admin.common_code.repository;
|
||||||
|
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.entity.CommonGroupCode;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface CommonGroupCodeRepository extends JpaRepository<CommonGroupCode, Long> {
|
||||||
|
|
||||||
|
Optional<CommonGroupCode> findByCode(String code);
|
||||||
|
|
||||||
|
List<CommonGroupCode> findByUseFlagOrderBySortOrderAsc(Boolean useFlag);
|
||||||
|
|
||||||
|
@Query("SELECT cgc FROM CommonGroupCode cgc WHERE cgc.useFlag = :useFlag ORDER BY cgc.sortOrder ASC")
|
||||||
|
List<CommonGroupCode> findActiveGroupCodesOrderBySortOrder(@Param("useFlag") Boolean useFlag);
|
||||||
|
|
||||||
|
boolean existsByCode(String code);
|
||||||
|
}
|
@@ -0,0 +1,28 @@
|
|||||||
|
package com.bio.bio_backend.domain.admin.common_code.service;
|
||||||
|
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.dto.CommonCodeDto;
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.dto.CommonGroupCodeDto;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface CommonCodeService {
|
||||||
|
|
||||||
|
// 그룹 코드 관련
|
||||||
|
CommonGroupCodeDto createGroupCode(CommonGroupCodeDto groupCodeDto);
|
||||||
|
void updateGroupCode(String code, CommonGroupCodeDto groupCodeDto);
|
||||||
|
void deleteGroupCode(String code);
|
||||||
|
CommonGroupCodeDto getGroupCode(String code);
|
||||||
|
List<CommonGroupCodeDto> getAllGroupCodes();
|
||||||
|
List<CommonGroupCodeDto> getActiveGroupCodes();
|
||||||
|
|
||||||
|
// 공통 코드 관련
|
||||||
|
CommonCodeDto createCode(CommonCodeDto codeDto);
|
||||||
|
void updateCode(String code, CommonCodeDto codeDto);
|
||||||
|
void deleteCode(String code);
|
||||||
|
CommonCodeDto getCode(String code);
|
||||||
|
List<CommonCodeDto> getCodesByGroupCode(String groupCode);
|
||||||
|
List<CommonCodeDto> getActiveCodesByGroupCode(String groupCode);
|
||||||
|
List<CommonCodeDto> getCodesByParentCode(String parentCode);
|
||||||
|
List<CommonCodeDto> getActiveCodesByParentCode(String parentCode);
|
||||||
|
List<CommonCodeDto> getAllCodes();
|
||||||
|
}
|
@@ -0,0 +1,166 @@
|
|||||||
|
package com.bio.bio_backend.domain.admin.common_code.service;
|
||||||
|
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.dto.CommonCodeDto;
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.dto.CommonGroupCodeDto;
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.entity.CommonCode;
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.entity.CommonGroupCode;
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.mapper.CommonCodeMapper;
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.mapper.CommonGroupCodeMapper;
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.repository.CommonCodeRepository;
|
||||||
|
import com.bio.bio_backend.domain.admin.common_code.repository.CommonGroupCodeRepository;
|
||||||
|
import com.bio.bio_backend.global.constants.AppConstants;
|
||||||
|
import com.bio.bio_backend.global.exception.ApiException;
|
||||||
|
import com.bio.bio_backend.global.constants.ApiResponseCode;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public class CommonCodeServiceImpl implements CommonCodeService {
|
||||||
|
|
||||||
|
private final CommonGroupCodeRepository commonGroupCodeRepository;
|
||||||
|
private final CommonCodeRepository commonCodeRepository;
|
||||||
|
private final CommonCodeMapper commonCodeMapper;
|
||||||
|
private final CommonGroupCodeMapper commonGroupCodeMapper;
|
||||||
|
|
||||||
|
// 그룹 코드 관련 메서드들
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public CommonGroupCodeDto createGroupCode(CommonGroupCodeDto groupCodeDto) {
|
||||||
|
if (commonGroupCodeRepository.existsByCode(groupCodeDto.getCode())) {
|
||||||
|
throw new ApiException(ApiResponseCode.COMMON_CODE_DUPLICATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
CommonGroupCode groupCode = commonGroupCodeMapper.toCommonGroupCode(groupCodeDto);
|
||||||
|
CommonGroupCode savedGroupCode = commonGroupCodeRepository.save(groupCode);
|
||||||
|
return commonGroupCodeMapper.toCommonGroupCodeDto(savedGroupCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public void updateGroupCode(String code, CommonGroupCodeDto groupCodeDto) {
|
||||||
|
CommonGroupCode existingGroupCode = commonGroupCodeRepository.findByCode(code)
|
||||||
|
.orElseThrow(() -> new ApiException(ApiResponseCode.COMMON_NOT_FOUND, "그룹 코드를 찾을 수 없습니다: " + code));
|
||||||
|
|
||||||
|
commonGroupCodeMapper.updateCommonGroupCodeFromDto(groupCodeDto, existingGroupCode);
|
||||||
|
commonGroupCodeRepository.save(existingGroupCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public void deleteGroupCode(String code) {
|
||||||
|
CommonGroupCode groupCode = commonGroupCodeRepository.findByCode(code)
|
||||||
|
.orElseThrow(() -> new ApiException(ApiResponseCode.COMMON_NOT_FOUND, "그룹 코드를 찾을 수 없습니다: " + code));
|
||||||
|
|
||||||
|
// 하위 공통 코드가 있는지 확인
|
||||||
|
if (commonCodeRepository.existsByGroupCode(code)) {
|
||||||
|
throw new ApiException(ApiResponseCode.COMMON_BAD_REQUEST, "하위 공통 코드가 존재하여 삭제할 수 없습니다: " + code);
|
||||||
|
}
|
||||||
|
|
||||||
|
commonGroupCodeRepository.delete(groupCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommonGroupCodeDto getGroupCode(String code) {
|
||||||
|
CommonGroupCode groupCode = commonGroupCodeRepository.findByCode(code)
|
||||||
|
.orElseThrow(() -> new ApiException(ApiResponseCode.COMMON_NOT_FOUND, "그룹 코드를 찾을 수 없습니다: " + code));
|
||||||
|
return commonGroupCodeMapper.toCommonGroupCodeDto(groupCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CommonGroupCodeDto> getAllGroupCodes() {
|
||||||
|
List<CommonGroupCode> groupCodes = commonGroupCodeRepository.findAll();
|
||||||
|
return commonGroupCodeMapper.toCommonGroupCodeDtoList(groupCodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CommonGroupCodeDto> getActiveGroupCodes() {
|
||||||
|
List<CommonGroupCode> groupCodes = commonGroupCodeRepository.findByUseFlagOrderBySortOrderAsc(true);
|
||||||
|
return commonGroupCodeMapper.toCommonGroupCodeDtoList(groupCodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 공통 코드 관련 메서드들
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public CommonCodeDto createCode(CommonCodeDto commonCodeDto) {
|
||||||
|
if (commonCodeRepository.existsByCode(commonCodeDto.getCode())) {
|
||||||
|
throw new ApiException(ApiResponseCode.USER_ID_DUPLICATE, "이미 존재하는 공통 코드입니다: " + commonCodeDto.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 그룹 코드 존재 여부 확인
|
||||||
|
if (!commonGroupCodeRepository.existsByCode(commonCodeDto.getGroupCode())) {
|
||||||
|
throw new ApiException(ApiResponseCode.COMMON_BAD_REQUEST, "존재하지 않는 그룹 코드입니다: " + commonCodeDto.getGroupCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
CommonCode commonCode = commonCodeMapper.toCommonCode(commonCodeDto);
|
||||||
|
CommonCode savedCommonCode = commonCodeRepository.save(commonCode);
|
||||||
|
return commonCodeMapper.toCommonCodeDto(savedCommonCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public void updateCode(String code, CommonCodeDto commonCodeDto) {
|
||||||
|
CommonCode existingCommonCode = commonCodeRepository.findByCode(code)
|
||||||
|
.orElseThrow(() -> new ApiException(ApiResponseCode.COMMON_NOT_FOUND, "공통 코드를 찾을 수 없습니다: " + code));
|
||||||
|
|
||||||
|
commonCodeMapper.updateCommonCodeFromDto(commonCodeDto, existingCommonCode);
|
||||||
|
commonCodeRepository.save(existingCommonCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public void deleteCode(String code) {
|
||||||
|
CommonCode commonCode = commonCodeRepository.findByCode(code)
|
||||||
|
.orElseThrow(() -> new ApiException(ApiResponseCode.COMMON_NOT_FOUND, "공통 코드를 찾을 수 없습니다: " + code));
|
||||||
|
|
||||||
|
// 하위 공통 코드가 있는지 확인
|
||||||
|
if (commonCodeRepository.existsByParentCode(code)) {
|
||||||
|
throw new ApiException(ApiResponseCode.COMMON_BAD_REQUEST, "하위 공통 코드가 존재하여 삭제할 수 없습니다: " + code);
|
||||||
|
}
|
||||||
|
|
||||||
|
commonCodeRepository.delete(commonCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommonCodeDto getCode(String code) {
|
||||||
|
CommonCode commonCode = commonCodeRepository.findByCode(code)
|
||||||
|
.orElseThrow(() -> new ApiException(ApiResponseCode.COMMON_NOT_FOUND, "공통 코드를 찾을 수 없습니다: " + code));
|
||||||
|
return commonCodeMapper.toCommonCodeDto(commonCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CommonCodeDto> getCodesByGroupCode(String groupCode) {
|
||||||
|
List<CommonCode> commonCodes = commonCodeRepository.findByGroupCodeAndUseFlagOrderBySortOrderAsc(groupCode, true);
|
||||||
|
return commonCodeMapper.toCommonCodeDtoList(commonCodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CommonCodeDto> getActiveCodesByGroupCode(String groupCode) {
|
||||||
|
List<CommonCode> commonCodes = commonCodeRepository.findActiveCodesByGroupCodeOrderBySortOrder(groupCode, true);
|
||||||
|
return commonCodeMapper.toCommonCodeDtoList(commonCodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CommonCodeDto> getCodesByParentCode(String parentCode) {
|
||||||
|
List<CommonCode> commonCodes = commonCodeRepository.findByParentCodeAndUseFlagOrderBySortOrderAsc(parentCode, true);
|
||||||
|
return commonCodeMapper.toCommonCodeDtoList(commonCodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CommonCodeDto> getActiveCodesByParentCode(String parentCode) {
|
||||||
|
List<CommonCode> commonCodes = commonCodeRepository.findActiveCodesByParentCodeOrderBySortOrder(parentCode, true);
|
||||||
|
return commonCodeMapper.toCommonCodeDtoList(commonCodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CommonCodeDto> getAllCodes() {
|
||||||
|
List<CommonCode> commonCodes = commonCodeRepository.findAll();
|
||||||
|
return commonCodeMapper.toCommonCodeDtoList(commonCodes);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,16 @@
|
|||||||
|
package com.bio.bio_backend.domain.base.file.dto;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class FileUploadDto {
|
||||||
|
private Long groupOid;
|
||||||
|
private List<FileUploadResponseDto> files; // 파일 정보들
|
||||||
|
private int totalCount;
|
||||||
|
private int successCount;
|
||||||
|
private int failureCount;
|
||||||
|
private List<String> errorMessages;
|
||||||
|
}
|
@@ -7,5 +7,4 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
public class FileUploadRequestDto {
|
public class FileUploadRequestDto {
|
||||||
private MultipartFile file;
|
private MultipartFile file;
|
||||||
private String description;
|
private String description;
|
||||||
private Long groupOid;
|
|
||||||
}
|
}
|
||||||
|
@@ -1,12 +1,15 @@
|
|||||||
package com.bio.bio_backend.domain.base.file.dto;
|
package com.bio.bio_backend.domain.base.file.dto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@Builder
|
@Builder
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
public class FileUploadResponseDto {
|
public class FileUploadResponseDto {
|
||||||
private Long oid;
|
private Long oid;
|
||||||
|
private Long groupOid;
|
||||||
private String originalFileName;
|
private String originalFileName;
|
||||||
private String downloadUrl;
|
private String downloadUrl;
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,12 @@
|
|||||||
package com.bio.bio_backend.domain.base.file.dto;
|
package com.bio.bio_backend.domain.base.file.dto;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
@Builder
|
||||||
public class MultipleFileUploadRequestDto {
|
public class MultipleFileUploadRequestDto {
|
||||||
private List<MultipartFile> files;
|
private List<MultipartFile> files;
|
||||||
private String description;
|
private String description;
|
||||||
|
@@ -8,6 +8,7 @@ import java.util.List;
|
|||||||
@Builder
|
@Builder
|
||||||
public class MultipleFileUploadResponseDto {
|
public class MultipleFileUploadResponseDto {
|
||||||
private List<FileUploadResponseDto> files;
|
private List<FileUploadResponseDto> files;
|
||||||
|
private Long groupOid;
|
||||||
private int totalCount;
|
private int totalCount;
|
||||||
private int successCount;
|
private int successCount;
|
||||||
private int failureCount;
|
private int failureCount;
|
||||||
|
@@ -57,7 +57,12 @@ public class FileServiceImpl implements FileService {
|
|||||||
File savedFile = processFileUpload(multipartFile, requestDto.getDescription(), generateOid());
|
File savedFile = processFileUpload(multipartFile, requestDto.getDescription(), generateOid());
|
||||||
|
|
||||||
// 응답 DTO 생성 및 반환
|
// 응답 DTO 생성 및 반환
|
||||||
return createUploadResponse(savedFile);
|
return FileUploadResponseDto.builder()
|
||||||
|
.oid(savedFile.getOid())
|
||||||
|
.groupOid(savedFile.getGroupOid())
|
||||||
|
.originalFileName(savedFile.getOriginalFileName())
|
||||||
|
.downloadUrl(contextPath + "/files/download/" + savedFile.getOid())
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -90,7 +95,11 @@ public class FileServiceImpl implements FileService {
|
|||||||
|
|
||||||
// 단일 파일 업로드 처리
|
// 단일 파일 업로드 처리
|
||||||
File savedFile = processFileUpload(multipartFile, requestDto.getDescription(), groupOid);
|
File savedFile = processFileUpload(multipartFile, requestDto.getDescription(), groupOid);
|
||||||
FileUploadResponseDto uploadedFile = createUploadResponse(savedFile);
|
FileUploadResponseDto uploadedFile = FileUploadResponseDto.builder()
|
||||||
|
.oid(savedFile.getOid())
|
||||||
|
.originalFileName(savedFile.getOriginalFileName())
|
||||||
|
.downloadUrl(contextPath + "/files/download/" + savedFile.getOid())
|
||||||
|
.build();
|
||||||
|
|
||||||
uploadedFiles.add(uploadedFile);
|
uploadedFiles.add(uploadedFile);
|
||||||
successCount++;
|
successCount++;
|
||||||
@@ -105,8 +114,8 @@ public class FileServiceImpl implements FileService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 다중 파일 업로드 결과 반환
|
|
||||||
return MultipleFileUploadResponseDto.builder()
|
return MultipleFileUploadResponseDto.builder()
|
||||||
|
.groupOid(groupOid)
|
||||||
.files(uploadedFiles)
|
.files(uploadedFiles)
|
||||||
.totalCount(files.size())
|
.totalCount(files.size())
|
||||||
.successCount(successCount)
|
.successCount(successCount)
|
||||||
@@ -135,7 +144,6 @@ public class FileServiceImpl implements FileService {
|
|||||||
|
|
||||||
// DB에 파일 정보 저장
|
// DB에 파일 정보 저장
|
||||||
File file = createFileEntity(originalFileName, storedFileName, targetLocation, multipartFile, description, groupOid);
|
File file = createFileEntity(originalFileName, storedFileName, targetLocation, multipartFile, description, groupOid);
|
||||||
file.setCreator(SecurityUtils.getCurrentUserOid(), SecurityUtils.getCurrentUserId());
|
|
||||||
|
|
||||||
return fileRepository.save(file);
|
return fileRepository.save(file);
|
||||||
|
|
||||||
@@ -158,14 +166,6 @@ public class FileServiceImpl implements FileService {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private FileUploadResponseDto createUploadResponse(File savedFile) {
|
|
||||||
return FileUploadResponseDto.builder()
|
|
||||||
.oid(savedFile.getOid())
|
|
||||||
.originalFileName(savedFile.getOriginalFileName())
|
|
||||||
.downloadUrl(contextPath + "/files/download/" + savedFile.getOid())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public File getFileByOid(Long oid) {
|
public File getFileByOid(Long oid) {
|
||||||
return fileRepository.findByOidAndUseFlagTrue(oid)
|
return fileRepository.findByOidAndUseFlagTrue(oid)
|
||||||
@@ -192,16 +192,12 @@ public class FileServiceImpl implements FileService {
|
|||||||
File file = fileRepository.findByOidAndUseFlagTrue(oid)
|
File file = fileRepository.findByOidAndUseFlagTrue(oid)
|
||||||
.orElseThrow(() -> new ApiException(ApiResponseCode.FILE_NOT_FOUND));
|
.orElseThrow(() -> new ApiException(ApiResponseCode.FILE_NOT_FOUND));
|
||||||
|
|
||||||
Long currentUserOid = SecurityUtils.getCurrentUserOid();
|
|
||||||
String currentUserId = SecurityUtils.getCurrentUserId();
|
String currentUserId = SecurityUtils.getCurrentUserId();
|
||||||
// 현재 사용자가 파일 소유자인지 확인
|
// 현재 사용자가 파일 소유자인지 확인
|
||||||
if (currentUserId == null || !currentUserId.equals(file.getCreatedId())) {
|
if (currentUserId == null || !currentUserId.equals(file.getCreatedId())) {
|
||||||
throw new ApiException(ApiResponseCode.COMMON_FORBIDDEN);
|
throw new ApiException(ApiResponseCode.COMMON_FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 수정자 정보 업데이트
|
|
||||||
file.setUpdater(currentUserOid, currentUserId);
|
|
||||||
|
|
||||||
// 논리적 삭제: use_flag를 false로 변경
|
// 논리적 삭제: use_flag를 false로 변경
|
||||||
file.setUseFlag(false);
|
file.setUseFlag(false);
|
||||||
fileRepository.save(file);
|
fileRepository.save(file);
|
||||||
|
@@ -5,16 +5,15 @@ import com.bio.bio_backend.domain.base.member.dto.CreateMemberResponseDto;
|
|||||||
import com.bio.bio_backend.domain.base.member.dto.MemberDto;
|
import com.bio.bio_backend.domain.base.member.dto.MemberDto;
|
||||||
import com.bio.bio_backend.domain.base.member.entity.Member;
|
import com.bio.bio_backend.domain.base.member.entity.Member;
|
||||||
import com.bio.bio_backend.global.annotation.IgnoreBaseEntityMapping;
|
import com.bio.bio_backend.global.annotation.IgnoreBaseEntityMapping;
|
||||||
|
import com.bio.bio_backend.global.config.GlobalMapperConfig;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.Mapping;
|
import org.mapstruct.Mapping;
|
||||||
import org.mapstruct.factory.Mappers;
|
import org.mapstruct.factory.Mappers;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Mapper(componentModel = "spring")
|
@Mapper(config = GlobalMapperConfig.class)
|
||||||
public interface MemberMapper {
|
public interface MemberMapper {
|
||||||
|
|
||||||
MemberMapper INSTANCE = Mappers.getMapper(MemberMapper.class);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CreateMemberRequestDto를 MemberDto로 변환
|
* CreateMemberRequestDto를 MemberDto로 변환
|
||||||
* 기본값 설정: role = MemberRole.MEMBER, useFlag = true
|
* 기본값 설정: role = MemberRole.MEMBER, useFlag = true
|
||||||
|
@@ -8,6 +8,7 @@ import com.bio.bio_backend.domain.base.member.repository.MemberRepository;
|
|||||||
import com.bio.bio_backend.global.exception.ApiException;
|
import com.bio.bio_backend.global.exception.ApiException;
|
||||||
import com.bio.bio_backend.global.constants.ApiResponseCode;
|
import com.bio.bio_backend.global.constants.ApiResponseCode;
|
||||||
import com.bio.bio_backend.global.constants.AppConstants;
|
import com.bio.bio_backend.global.constants.AppConstants;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
@@ -19,8 +20,6 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static com.bio.bio_backend.global.utils.OidUtils.generateOid;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -54,9 +53,10 @@ public class MemberServiceImpl implements MemberService {
|
|||||||
.role(MemberRole.getDefault())
|
.role(MemberRole.getDefault())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
Long oid = generateOid();
|
member.setCreatedOid(AppConstants.ADMIN_OID);
|
||||||
member.setOid(oid);
|
member.setCreatedId(AppConstants.ADMIN_USER_ID);
|
||||||
member.setCreator(AppConstants.ADMIN_OID, AppConstants.ADMIN_USER_ID);
|
member.setUpdatedOid(AppConstants.ADMIN_OID);
|
||||||
|
member.setUpdatedId(AppConstants.ADMIN_USER_ID);
|
||||||
|
|
||||||
Member savedMember = memberRepository.save(member);
|
Member savedMember = memberRepository.save(member);
|
||||||
|
|
||||||
|
@@ -0,0 +1,21 @@
|
|||||||
|
package com.bio.bio_backend.global.config;
|
||||||
|
|
||||||
|
import org.mapstruct.*;
|
||||||
|
|
||||||
|
@MapperConfig(
|
||||||
|
componentModel = "spring",
|
||||||
|
|
||||||
|
// null 값은 매핑하지 않음 (부분 업데이트)
|
||||||
|
nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE,
|
||||||
|
|
||||||
|
// NPE 방지용 null 체크
|
||||||
|
nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
|
||||||
|
|
||||||
|
// 매핑 누락 시 컴파일 오류
|
||||||
|
unmappedTargetPolicy = ReportingPolicy.ERROR,
|
||||||
|
|
||||||
|
// 컬렉션 매핑 전략
|
||||||
|
collectionMappingStrategy = CollectionMappingStrategy.ACCESSOR_ONLY
|
||||||
|
)
|
||||||
|
public interface GlobalMapperConfig {
|
||||||
|
}
|
@@ -14,7 +14,7 @@ public enum ApiResponseCode {
|
|||||||
|
|
||||||
/*공통 Code*/
|
/*공통 Code*/
|
||||||
// 200 OK
|
// 200 OK
|
||||||
COMMON_SUCCESS(HttpStatus.OK.value(), "요청 성공"),
|
COMMON_SUCCESS(HttpStatus.OK.value(), "요청을 성공하였습니다"),
|
||||||
COMMON_SUCCESS_CREATED(HttpStatus.CREATED.value(), "성공적으로 생성되었습니다"),
|
COMMON_SUCCESS_CREATED(HttpStatus.CREATED.value(), "성공적으로 생성되었습니다"),
|
||||||
COMMON_SUCCESS_UPDATED(HttpStatus.OK.value(), "성공적으로 수정되었습니다"),
|
COMMON_SUCCESS_UPDATED(HttpStatus.OK.value(), "성공적으로 수정되었습니다"),
|
||||||
COMMON_SUCCESS_DELETED(HttpStatus.OK.value(), "성공적으로 삭제되었습니다"),
|
COMMON_SUCCESS_DELETED(HttpStatus.OK.value(), "성공적으로 삭제되었습니다"),
|
||||||
@@ -39,6 +39,7 @@ public enum ApiResponseCode {
|
|||||||
|
|
||||||
// 409 Conflict
|
// 409 Conflict
|
||||||
COMMON_CONFLICT(HttpStatus.CONFLICT.value(), "충돌이 발생했습니다"),
|
COMMON_CONFLICT(HttpStatus.CONFLICT.value(), "충돌이 발생했습니다"),
|
||||||
|
COMMON_CODE_DUPLICATE(HttpStatus.CONFLICT.value(), "동일한 코드가 존재합니다"),
|
||||||
|
|
||||||
// 500 Internal Server Error
|
// 500 Internal Server Error
|
||||||
COMMON_INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR.value(), "서버에서 오류가 발생했습니다"),
|
COMMON_INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR.value(), "서버에서 오류가 발생했습니다"),
|
||||||
|
@@ -12,14 +12,17 @@ import lombok.RequiredArgsConstructor;
|
|||||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
public class ApiResponseDto<T> {
|
public class ApiResponseDto<T> {
|
||||||
|
|
||||||
|
private static final boolean SUCCESS = true;
|
||||||
|
private static final boolean FAIL = false;
|
||||||
|
|
||||||
|
private boolean success;
|
||||||
private int code;
|
private int code;
|
||||||
private String message;
|
private String message;
|
||||||
private String description;
|
private String description;
|
||||||
private T data;
|
private T data;
|
||||||
|
|
||||||
private static final int SUCCESS = 200;
|
private ApiResponseDto(boolean success, int code, String message, String description, T data){
|
||||||
|
this.success = success;
|
||||||
private ApiResponseDto(int code, String message, String description, T data){
|
|
||||||
this.code = code;
|
this.code = code;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
@@ -27,19 +30,19 @@ public class ApiResponseDto<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static <T> ApiResponseDto<T> success(ApiResponseCode responseCode, T data) {
|
public static <T> ApiResponseDto<T> success(ApiResponseCode responseCode, T data) {
|
||||||
return new ApiResponseDto<T>(SUCCESS, responseCode.name(), responseCode.getDescription(), data);
|
return new ApiResponseDto<T>(SUCCESS, responseCode.getStatusCode(), responseCode.name(), responseCode.getDescription(), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> ApiResponseDto<T> success(ApiResponseCode responseCode) {
|
public static <T> ApiResponseDto<T> success(ApiResponseCode responseCode) {
|
||||||
return new ApiResponseDto<T>(SUCCESS, responseCode.name(), responseCode.getDescription(), null);
|
return new ApiResponseDto<T>(SUCCESS, responseCode.getStatusCode(), responseCode.name(), responseCode.getDescription(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> ApiResponseDto<T> fail(ApiResponseCode responseCode, T data) {
|
public static <T> ApiResponseDto<T> fail(ApiResponseCode responseCode, T data) {
|
||||||
return new ApiResponseDto<T>(responseCode.getStatusCode(), responseCode.name(), responseCode.getDescription(), data);
|
return new ApiResponseDto<T>(FAIL, responseCode.getStatusCode(), responseCode.name(), responseCode.getDescription(), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> ApiResponseDto<T> fail(ApiResponseCode responseCode) {
|
public static <T> ApiResponseDto<T> fail(ApiResponseCode responseCode) {
|
||||||
return new ApiResponseDto<T>(responseCode.getStatusCode(), responseCode.name(), responseCode.getDescription(), null);
|
return new ApiResponseDto<T>(FAIL, responseCode.getStatusCode(), responseCode.name(), responseCode.getDescription(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,7 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
|||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
import static com.bio.bio_backend.global.utils.OidUtils.generateOid;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 모든 엔티티가 상속받는 기본 엔티티 클래스
|
* 모든 엔티티가 상속받는 기본 엔티티 클래스
|
||||||
@@ -18,7 +18,7 @@ import static com.bio.bio_backend.global.utils.OidUtils.generateOid;
|
|||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
@EntityListeners(AuditingEntityListener.class)
|
@EntityListeners({AuditingEntityListener.class, BaseEntityListener.class})
|
||||||
public abstract class BaseEntity {
|
public abstract class BaseEntity {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@@ -45,31 +45,4 @@ public abstract class BaseEntity {
|
|||||||
@Column(name = "updated_id")
|
@Column(name = "updated_id")
|
||||||
private String updatedId;
|
private String updatedId;
|
||||||
|
|
||||||
@PrePersist
|
|
||||||
protected void onCreate() {
|
|
||||||
if(this.oid == null) this.oid = generateOid();
|
|
||||||
if(this.createdOid != null && this.updatedOid == null) this.updatedOid = this.createdOid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 생성자 정보를 설정합니다.
|
|
||||||
* @param createdOid 생성자 OID
|
|
||||||
* @param createdId 생성자 ID
|
|
||||||
*/
|
|
||||||
public void setCreator(Long createdOid, String createdId) {
|
|
||||||
this.createdOid = createdOid;
|
|
||||||
this.createdId = createdId;
|
|
||||||
this.updatedOid = createdOid;
|
|
||||||
this.updatedId = createdId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 수정자 정보를 설정합니다.
|
|
||||||
* @param updatedOid 수정자 OID
|
|
||||||
* @param updatedId 수정자 ID
|
|
||||||
*/
|
|
||||||
public void setUpdater(Long updatedOid, String updatedId) {
|
|
||||||
this.updatedOid = updatedOid;
|
|
||||||
this.updatedId = updatedId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,61 @@
|
|||||||
|
package com.bio.bio_backend.global.entity;
|
||||||
|
|
||||||
|
import com.bio.bio_backend.global.utils.SecurityUtils;
|
||||||
|
import jakarta.persistence.PrePersist;
|
||||||
|
import jakarta.persistence.PreUpdate;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import static com.bio.bio_backend.global.utils.OidUtils.generateOid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BaseEntity의 createdOid와 updatedOid 필드를 자동으로 설정하는 엔티티 리스너
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class BaseEntityListener {
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
public void prePersist(BaseEntity entity) {
|
||||||
|
if (entity.getOid() == null) {
|
||||||
|
entity.setOid(generateOid());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String currentUserId = SecurityUtils.getCurrentUserId();
|
||||||
|
Long currentUserOid = SecurityUtils.getCurrentUserOid();
|
||||||
|
|
||||||
|
if (currentUserOid != null) {
|
||||||
|
entity.setCreatedOid(currentUserOid);
|
||||||
|
entity.setUpdatedOid(currentUserOid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentUserId != null) {
|
||||||
|
entity.setCreatedId(currentUserId);
|
||||||
|
entity.setUpdatedId(currentUserId);
|
||||||
|
}
|
||||||
|
} catch (SecurityException | IllegalStateException e) {
|
||||||
|
log.warn("등록자 정보 설정 실패: {}", e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("등록자 정보 설정 오류: {}", e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreUpdate
|
||||||
|
public void preUpdate(BaseEntity entity) {
|
||||||
|
try {
|
||||||
|
String currentUserId = SecurityUtils.getCurrentUserId();
|
||||||
|
Long currentUserOid = SecurityUtils.getCurrentUserOid();
|
||||||
|
|
||||||
|
if (currentUserOid != null) {
|
||||||
|
entity.setUpdatedOid(currentUserOid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentUserId != null) {
|
||||||
|
entity.setUpdatedId(currentUserId);
|
||||||
|
}
|
||||||
|
} catch (SecurityException | IllegalStateException e) {
|
||||||
|
log.warn("수정자 정보 설정 실패: {}", e.getMessage());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("수정자 정보 설정 오류: {}", e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -11,6 +11,6 @@ public class CustomIdGenerator implements IdentifierGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Serializable generate(SharedSessionContractImplementor session, Object object) {
|
public Serializable generate(SharedSessionContractImplementor session, Object object) {
|
||||||
return OidUtils.generateOid(); // 재사용
|
return OidUtils.generateOid();
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -2,21 +2,30 @@ package com.bio.bio_backend.global.utils;
|
|||||||
|
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardCopyOption;
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import com.bio.bio_backend.domain.base.file.dto.FileUploadDto;
|
||||||
|
import com.bio.bio_backend.domain.base.file.dto.MultipleFileUploadRequestDto;
|
||||||
|
import com.bio.bio_backend.domain.base.file.dto.MultipleFileUploadResponseDto;
|
||||||
|
import com.bio.bio_backend.domain.base.file.service.FileService;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 파일 관련 유틸리티 클래스
|
* 파일 관련 유틸리티 클래스
|
||||||
*/
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class FileUtils {
|
public class FileUtils {
|
||||||
|
|
||||||
|
private final FileService fileService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 파일 유효성 검사
|
* 파일 유효성 검사
|
||||||
*/
|
*/
|
||||||
@@ -35,32 +44,6 @@ public class FileUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 업로드 디렉토리 생성
|
|
||||||
*/
|
|
||||||
public static Path createUploadDirectory(String uploadPath) throws IOException {
|
|
||||||
Path uploadDir = Paths.get(uploadPath);
|
|
||||||
if (!Files.exists(uploadDir)) {
|
|
||||||
Files.createDirectories(uploadDir);
|
|
||||||
}
|
|
||||||
return uploadDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 년월일 기반 업로드 디렉토리 생성
|
|
||||||
* 예: uploads/2024/01/15/
|
|
||||||
*/
|
|
||||||
public static Path createDateBasedUploadDirectory(String baseUploadPath) throws IOException {
|
|
||||||
LocalDate today = LocalDate.now();
|
|
||||||
String yearMonthDay = today.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
|
|
||||||
|
|
||||||
Path dateBasedPath = Paths.get(baseUploadPath, yearMonthDay);
|
|
||||||
if (!Files.exists(dateBasedPath)) {
|
|
||||||
Files.createDirectories(dateBasedPath);
|
|
||||||
}
|
|
||||||
return dateBasedPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 년월 기반 업로드 디렉토리 생성
|
* 년월 기반 업로드 디렉토리 생성
|
||||||
* 예: uploads/2024/01/
|
* 예: uploads/2024/01/
|
||||||
@@ -76,35 +59,6 @@ public class FileUtils {
|
|||||||
return yearMonthPath;
|
return yearMonthPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 년 기반 업로드 디렉토리 생성
|
|
||||||
* 예: uploads/2024/
|
|
||||||
*/
|
|
||||||
public static Path createYearUploadDirectory(String baseUploadPath) throws IOException {
|
|
||||||
LocalDate today = LocalDate.now();
|
|
||||||
String year = today.format(DateTimeFormatter.ofPattern("yyyy"));
|
|
||||||
|
|
||||||
Path yearPath = Paths.get(baseUploadPath, year);
|
|
||||||
if (!Files.exists(yearPath)) {
|
|
||||||
Files.createDirectories(yearPath);
|
|
||||||
}
|
|
||||||
return yearPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 지정된 날짜로 업로드 디렉토리 생성
|
|
||||||
* 예: uploads/2024/01/15/
|
|
||||||
*/
|
|
||||||
public static Path createDateBasedUploadDirectory(String baseUploadPath, LocalDate date) throws IOException {
|
|
||||||
String yearMonthDay = date.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
|
|
||||||
|
|
||||||
Path dateBasedPath = Paths.get(baseUploadPath, yearMonthDay);
|
|
||||||
if (!Files.exists(dateBasedPath)) {
|
|
||||||
Files.createDirectories(dateBasedPath);
|
|
||||||
}
|
|
||||||
return dateBasedPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 파일 확장자 추출
|
* 파일 확장자 추출
|
||||||
*/
|
*/
|
||||||
@@ -127,7 +81,7 @@ public class FileUtils {
|
|||||||
*/
|
*/
|
||||||
public static Path saveFileToDisk(MultipartFile multipartFile, Path uploadDir, String storedFileName) throws IOException {
|
public static Path saveFileToDisk(MultipartFile multipartFile, Path uploadDir, String storedFileName) throws IOException {
|
||||||
Path targetLocation = uploadDir.resolve(storedFileName);
|
Path targetLocation = uploadDir.resolve(storedFileName);
|
||||||
Files.copy(multipartFile.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);
|
Files.copy(multipartFile.getInputStream(), targetLocation, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
|
||||||
return targetLocation;
|
return targetLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,173 +92,44 @@ public class FileUtils {
|
|||||||
return StringUtils.cleanPath(originalFileName);
|
return StringUtils.cleanPath(originalFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 파일 크기를 사람이 읽기 쉬운 형태로 변환
|
|
||||||
*/
|
|
||||||
public static String formatFileSize(long bytes) {
|
|
||||||
if (bytes < 1024) return bytes + " B";
|
|
||||||
if (bytes < 1024 * 1024) return String.format("%.1f KB", bytes / 1024.0);
|
|
||||||
if (bytes < 1024 * 1024 * 1024) return String.format("%.1f MB", bytes / (1024.0 * 1024.0));
|
|
||||||
return String.format("%.1f GB", bytes / (1024.0 * 1024.0 * 1024.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 파일 확장자로부터 MIME 타입 추정
|
* 파일 업로드 (단일/다중 파일 모두 지원)
|
||||||
|
* 사용 예시:
|
||||||
|
* // 단일 파일 업로드
|
||||||
|
* FileUploadDto fileResult = fileUtils.uploadFile(
|
||||||
|
* requestDto.getFile(),
|
||||||
|
* "프로필 이미지"
|
||||||
|
* );
|
||||||
|
* member.setFileGroupId(fileResult.getGroupOid());
|
||||||
|
* // 다중 파일 업로드
|
||||||
|
* FileUploadDto filesResult = fileUtils.uploadFiles(
|
||||||
|
* requestDto.getFiles(),
|
||||||
|
* "게시판 첨부파일: " + board.getTitle()
|
||||||
|
* );
|
||||||
|
* board.setFileGroupId(filesResult.getGroupOid());
|
||||||
*/
|
*/
|
||||||
public static String getMimeTypeFromExtension(String fileName) {
|
public FileUploadDto uploadFile(MultipartFile file, String description) {
|
||||||
if (fileName == null) return "application/octet-stream";
|
// 단일 파일도 List로 감싸서 다중 파일 업로드 방식 사용
|
||||||
|
List<MultipartFile> files = List.of(file);
|
||||||
String extension = extractFileExtension(fileName).toLowerCase();
|
return uploadFiles(files, description);
|
||||||
switch (extension) {
|
|
||||||
case ".txt": return "text/plain";
|
|
||||||
case ".html": case ".htm": return "text/html";
|
|
||||||
case ".css": return "text/css";
|
|
||||||
case ".js": return "application/javascript";
|
|
||||||
case ".json": return "application/json";
|
|
||||||
case ".xml": return "application/xml";
|
|
||||||
case ".pdf": return "application/pdf";
|
|
||||||
case ".zip": return "application/zip";
|
|
||||||
case ".jpg": case ".jpeg": return "image/jpeg";
|
|
||||||
case ".png": return "image/png";
|
|
||||||
case ".gif": return "image/gif";
|
|
||||||
case ".bmp": return "image/bmp";
|
|
||||||
case ".svg": return "image/svg+xml";
|
|
||||||
case ".mp4": return "video/mp4";
|
|
||||||
case ".avi": return "video/x-msvideo";
|
|
||||||
case ".mp3": return "audio/mpeg";
|
|
||||||
case ".wav": return "audio/wav";
|
|
||||||
default: return "application/octet-stream";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public FileUploadDto uploadFiles(List<MultipartFile> files, String description) {
|
||||||
* 안전한 파일명 생성 (특수문자 제거)
|
MultipleFileUploadRequestDto requestDto = MultipleFileUploadRequestDto.builder()
|
||||||
*/
|
.files(files)
|
||||||
public static String createSafeFileName(String originalFileName) {
|
.description(description)
|
||||||
if (originalFileName == null) return "";
|
.build();
|
||||||
|
|
||||||
// 특수문자 제거 및 공백을 언더스코어로 변경
|
MultipleFileUploadResponseDto response = fileService.uploadMultipleFiles(requestDto);
|
||||||
String safeName = originalFileName
|
|
||||||
.replaceAll("[^a-zA-Z0-9가-힣._-]", "_")
|
|
||||||
.replaceAll("_+", "_")
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
// 파일명이 너무 길면 자르기
|
return FileUploadDto.builder()
|
||||||
if (safeName.length() > 100) {
|
.groupOid(response.getGroupOid())
|
||||||
String extension = extractFileExtension(safeName);
|
.files(response.getFiles())
|
||||||
safeName = safeName.substring(0, 100 - extension.length()) + extension;
|
.totalCount(response.getTotalCount())
|
||||||
}
|
.successCount(response.getSuccessCount())
|
||||||
|
.failureCount(response.getFailureCount())
|
||||||
return safeName;
|
.errorMessages(response.getErrorMessages())
|
||||||
}
|
.build();
|
||||||
|
|
||||||
/**
|
|
||||||
* 파일이 이미지인지 확인
|
|
||||||
*/
|
|
||||||
public static boolean isImageFile(String fileName) {
|
|
||||||
if (fileName == null) return false;
|
|
||||||
|
|
||||||
String extension = extractFileExtension(fileName).toLowerCase();
|
|
||||||
return extension.matches("\\.(jpg|jpeg|png|gif|bmp|svg|webp)$");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 파일이 문서인지 확인
|
|
||||||
*/
|
|
||||||
public static boolean isDocumentFile(String fileName) {
|
|
||||||
if (fileName == null) return false;
|
|
||||||
|
|
||||||
String extension = extractFileExtension(fileName).toLowerCase();
|
|
||||||
return extension.matches("\\.(pdf|doc|docx|xls|xlsx|ppt|pptx|txt|rtf)$");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 파일이 압축파일인지 확인
|
|
||||||
*/
|
|
||||||
public static boolean isArchiveFile(String fileName) {
|
|
||||||
if (fileName == null) return false;
|
|
||||||
|
|
||||||
String extension = extractFileExtension(fileName).toLowerCase();
|
|
||||||
return extension.matches("\\.(zip|rar|7z|tar|gz|bz2)$");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 현재 날짜의 년월일 문자열 반환
|
|
||||||
* 예: "2024/01/15"
|
|
||||||
*/
|
|
||||||
public static String getCurrentDatePath() {
|
|
||||||
LocalDate today = LocalDate.now();
|
|
||||||
return today.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 지정된 날짜의 년월일 문자열 반환
|
|
||||||
* 예: "2024/01/15"
|
|
||||||
*/
|
|
||||||
public static String getDatePath(LocalDate date) {
|
|
||||||
return date.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 파일 경로에서 년월일 정보 추출
|
|
||||||
* 예: "uploads/2024/01/15/file.txt" -> "2024/01/15"
|
|
||||||
*/
|
|
||||||
public static String extractDateFromPath(String filePath) {
|
|
||||||
if (filePath == null || filePath.isEmpty()) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// 정규식으로 년/월/일 패턴 찾기
|
|
||||||
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("(\\d{4}/\\d{2}/\\d{2})");
|
|
||||||
java.util.regex.Matcher matcher = pattern.matcher(filePath);
|
|
||||||
|
|
||||||
if (matcher.find()) {
|
|
||||||
return matcher.group(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 년월일 폴더 구조가 유효한지 확인
|
|
||||||
* 예: "2024/01/15" -> true, "2024/13/45" -> false
|
|
||||||
*/
|
|
||||||
public static boolean isValidDatePath(String datePath) {
|
|
||||||
if (datePath == null || datePath.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
String[] parts = datePath.split("/");
|
|
||||||
if (parts.length != 3) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int year = Integer.parseInt(parts[0]);
|
|
||||||
int month = Integer.parseInt(parts[1]);
|
|
||||||
int day = Integer.parseInt(parts[2]);
|
|
||||||
|
|
||||||
// 년도 범위 체크 (1900 ~ 2100)
|
|
||||||
if (year < 1900 || year > 2100) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 월 범위 체크 (1 ~ 12)
|
|
||||||
if (month < 1 || month > 12) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 일 범위 체크 (1 ~ 31)
|
|
||||||
if (day < 1 || day > 31) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 실제 존재하는 날짜인지 확인
|
|
||||||
LocalDate.of(year, month, day);
|
|
||||||
return true;
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,11 +3,6 @@
|
|||||||
# ========================================
|
# ========================================
|
||||||
server.port=8080
|
server.port=8080
|
||||||
server.servlet.context-path=/service
|
server.servlet.context-path=/service
|
||||||
|
|
||||||
management.endpoint.health.probes.enabled=true
|
|
||||||
management.health.livenessstate.enabled=true
|
|
||||||
management.health.readinessstate.enabled=true
|
|
||||||
|
|
||||||
spring.application.name=bio_backend
|
spring.application.name=bio_backend
|
||||||
spring.output.ansi.enabled=always
|
spring.output.ansi.enabled=always
|
||||||
|
|
||||||
@@ -118,8 +113,7 @@ springdoc.default-consumes-media-type=application/json
|
|||||||
|
|
||||||
# ========================================
|
# ========================================
|
||||||
# 보안 설정 - 허용할 경로
|
# 보안 설정 - 허용할 경로
|
||||||
# ========================================
|
security.permit-all-paths=/login,/members/register,/swagger-ui/**,/swagger-ui.html,/swagger-ui/index.html,/api-docs,/api-docs/**,/v3/api-docs,/v3/api-docs/**,/ws/**,/actuator/**,/actuator/health/**,/actuator/info
|
||||||
security.permit-all-paths=/login,/members/register,/swagger-ui/**,/swagger-ui.html,/swagger-ui/index.html,/api-docs,/api-docs/**,/v3/api-docs,/v3/api-docs/**,/ws/**,/actuator/health/**
|
|
||||||
|
|
||||||
# 파일 업로드 설정
|
# 파일 업로드 설정
|
||||||
# ========================================
|
# ========================================
|
||||||
@@ -130,3 +124,35 @@ spring.servlet.multipart.file-size-threshold=2KB
|
|||||||
|
|
||||||
# 파일 저장 경로 설정
|
# 파일 저장 경로 설정
|
||||||
app.file.upload.path=./uploads/
|
app.file.upload.path=./uploads/
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# Spring Boot Actuator 설정
|
||||||
|
# ========================================
|
||||||
|
# Actuator 엔드포인트 활성화
|
||||||
|
management.endpoints.web.exposure.include=health,info,metrics,env,configprops
|
||||||
|
management.endpoint.health.show-details=always
|
||||||
|
management.endpoint.health.show-components=always
|
||||||
|
|
||||||
|
# Health 체크 상세 정보 표시
|
||||||
|
management.health.db.enabled=true
|
||||||
|
management.health.diskspace.enabled=true
|
||||||
|
management.health.defaults.enabled=true
|
||||||
|
|
||||||
|
# Actuator 기본 경로 설정
|
||||||
|
management.endpoints.web.base-path=/actuator
|
||||||
|
|
||||||
|
# Health 체크 타임아웃 설정 (밀리초)
|
||||||
|
management.health.defaults.timeout=10s
|
||||||
|
|
||||||
|
# 커스텀 Health 체크 그룹 설정
|
||||||
|
management.health.groups.readiness.include=db,diskSpace
|
||||||
|
management.health.groups.liveness.include=ping
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# 애플리케이션 정보 설정 (Actuator info 엔드포인트용)
|
||||||
|
# ========================================
|
||||||
|
info.app.name=Bio Backend Service
|
||||||
|
info.app.description=생물학 연구를 위한 백엔드 서비스
|
||||||
|
info.app.version=@project.version@
|
||||||
|
info.app.java.version=@java.version@
|
||||||
|
info.app.spring-boot.version=@spring-boot.version@
|
Reference in New Issue
Block a user