Merge branch 'main' of https://demo.stam.kr/leejisun9/bio_backend
This commit is contained in:
		@@ -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 {
 | 
			
		||||
    private MultipartFile file;
 | 
			
		||||
    private String description;
 | 
			
		||||
    private Long groupOid;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,15 @@
 | 
			
		||||
package com.bio.bio_backend.domain.base.file.dto;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.annotation.JsonInclude;
 | 
			
		||||
import lombok.Builder;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
@Builder
 | 
			
		||||
@JsonInclude(JsonInclude.Include.NON_NULL)
 | 
			
		||||
public class FileUploadResponseDto {
 | 
			
		||||
    private Long oid;
 | 
			
		||||
    private Long groupOid;
 | 
			
		||||
    private String originalFileName;
 | 
			
		||||
    private String downloadUrl;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,12 @@
 | 
			
		||||
package com.bio.bio_backend.domain.base.file.dto;
 | 
			
		||||
 | 
			
		||||
import lombok.Builder;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import org.springframework.web.multipart.MultipartFile;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
@Builder
 | 
			
		||||
public class MultipleFileUploadRequestDto {
 | 
			
		||||
    private List<MultipartFile> files;
 | 
			
		||||
    private String description;
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ import java.util.List;
 | 
			
		||||
@Builder
 | 
			
		||||
public class MultipleFileUploadResponseDto {
 | 
			
		||||
    private List<FileUploadResponseDto> files;
 | 
			
		||||
    private Long groupOid;
 | 
			
		||||
    private int totalCount;
 | 
			
		||||
    private int successCount;
 | 
			
		||||
    private int failureCount;
 | 
			
		||||
 
 | 
			
		||||
@@ -57,7 +57,12 @@ public class FileServiceImpl implements FileService {
 | 
			
		||||
        File savedFile = processFileUpload(multipartFile, requestDto.getDescription(), generateOid());
 | 
			
		||||
        
 | 
			
		||||
        // 응답 DTO 생성 및 반환
 | 
			
		||||
        return createUploadResponse(savedFile);
 | 
			
		||||
        return FileUploadResponseDto.builder()
 | 
			
		||||
            .oid(savedFile.getOid())
 | 
			
		||||
            .groupOid(savedFile.getGroupOid())
 | 
			
		||||
            .originalFileName(savedFile.getOriginalFileName())
 | 
			
		||||
            .downloadUrl(contextPath + "/files/download/" + savedFile.getOid())
 | 
			
		||||
            .build();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -90,7 +95,11 @@ public class FileServiceImpl implements FileService {
 | 
			
		||||
                
 | 
			
		||||
                // 단일 파일 업로드 처리
 | 
			
		||||
                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);
 | 
			
		||||
                successCount++;
 | 
			
		||||
@@ -105,8 +114,8 @@ public class FileServiceImpl implements FileService {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // 다중 파일 업로드 결과 반환
 | 
			
		||||
        return MultipleFileUploadResponseDto.builder()
 | 
			
		||||
                .groupOid(groupOid)
 | 
			
		||||
                .files(uploadedFiles)
 | 
			
		||||
                .totalCount(files.size())
 | 
			
		||||
                .successCount(successCount)
 | 
			
		||||
@@ -135,7 +144,6 @@ public class FileServiceImpl implements FileService {
 | 
			
		||||
            
 | 
			
		||||
            // DB에 파일 정보 저장
 | 
			
		||||
            File file = createFileEntity(originalFileName, storedFileName, targetLocation, multipartFile, description, groupOid);
 | 
			
		||||
            file.setCreator(SecurityUtils.getCurrentUserOid(), SecurityUtils.getCurrentUserId());
 | 
			
		||||
            
 | 
			
		||||
            return fileRepository.save(file);
 | 
			
		||||
            
 | 
			
		||||
@@ -157,14 +165,6 @@ public class FileServiceImpl implements FileService {
 | 
			
		||||
                .groupOid(groupOid)
 | 
			
		||||
                .build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private FileUploadResponseDto createUploadResponse(File savedFile) {
 | 
			
		||||
        return FileUploadResponseDto.builder()
 | 
			
		||||
                .oid(savedFile.getOid())
 | 
			
		||||
                .originalFileName(savedFile.getOriginalFileName())
 | 
			
		||||
                .downloadUrl(contextPath + "/files/download/" + savedFile.getOid())
 | 
			
		||||
                .build();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    @Override
 | 
			
		||||
    public File getFileByOid(Long oid) {
 | 
			
		||||
@@ -191,17 +191,13 @@ public class FileServiceImpl implements FileService {
 | 
			
		||||
    public void deleteFile(Long oid) {
 | 
			
		||||
        File file = fileRepository.findByOidAndUseFlagTrue(oid)
 | 
			
		||||
                .orElseThrow(() -> new ApiException(ApiResponseCode.FILE_NOT_FOUND));
 | 
			
		||||
        
 | 
			
		||||
        Long currentUserOid = SecurityUtils.getCurrentUserOid();
 | 
			
		||||
 | 
			
		||||
        String currentUserId = SecurityUtils.getCurrentUserId();
 | 
			
		||||
        // 현재 사용자가 파일 소유자인지 확인
 | 
			
		||||
        if (currentUserId == null || !currentUserId.equals(file.getCreatedId())) {
 | 
			
		||||
            throw new ApiException(ApiResponseCode.COMMON_FORBIDDEN);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // 수정자 정보 업데이트
 | 
			
		||||
        file.setUpdater(currentUserOid, currentUserId);
 | 
			
		||||
        
 | 
			
		||||
        // 논리적 삭제: use_flag를 false로 변경
 | 
			
		||||
        file.setUseFlag(false);
 | 
			
		||||
        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.entity.Member;
 | 
			
		||||
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 org.mapstruct.factory.Mappers;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@Mapper(componentModel = "spring")
 | 
			
		||||
@Mapper(config = GlobalMapperConfig.class)
 | 
			
		||||
public interface MemberMapper {
 | 
			
		||||
    
 | 
			
		||||
    MemberMapper INSTANCE = Mappers.getMapper(MemberMapper.class);
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * CreateMemberRequestDto를 MemberDto로 변환
 | 
			
		||||
     * 기본값 설정: 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.constants.ApiResponseCode;
 | 
			
		||||
import com.bio.bio_backend.global.constants.AppConstants;
 | 
			
		||||
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.security.core.userdetails.UserDetails;
 | 
			
		||||
@@ -19,8 +20,6 @@ import org.springframework.transaction.annotation.Transactional;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import static com.bio.bio_backend.global.utils.OidUtils.generateOid;
 | 
			
		||||
 | 
			
		||||
@Service
 | 
			
		||||
@RequiredArgsConstructor
 | 
			
		||||
@Slf4j
 | 
			
		||||
@@ -53,10 +52,11 @@ public class MemberServiceImpl implements MemberService {
 | 
			
		||||
                .email(memberDto.getEmail())
 | 
			
		||||
                .role(MemberRole.getDefault())
 | 
			
		||||
                .build();
 | 
			
		||||
 | 
			
		||||
        Long oid = generateOid();
 | 
			
		||||
        member.setOid(oid);
 | 
			
		||||
        member.setCreator(AppConstants.ADMIN_OID, AppConstants.ADMIN_USER_ID);
 | 
			
		||||
        
 | 
			
		||||
        member.setCreatedOid(AppConstants.ADMIN_OID);
 | 
			
		||||
        member.setCreatedId(AppConstants.ADMIN_USER_ID);
 | 
			
		||||
        member.setUpdatedOid(AppConstants.ADMIN_OID);
 | 
			
		||||
        member.setUpdatedId(AppConstants.ADMIN_USER_ID);
 | 
			
		||||
        
 | 
			
		||||
        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*/
 | 
			
		||||
    // 200 OK
 | 
			
		||||
    COMMON_SUCCESS(HttpStatus.OK.value(), "요청 성공"),
 | 
			
		||||
    COMMON_SUCCESS(HttpStatus.OK.value(), "요청을 성공하였습니다"),
 | 
			
		||||
    COMMON_SUCCESS_CREATED(HttpStatus.CREATED.value(), "성공적으로 생성되었습니다"),
 | 
			
		||||
    COMMON_SUCCESS_UPDATED(HttpStatus.OK.value(), "성공적으로 수정되었습니다"),
 | 
			
		||||
    COMMON_SUCCESS_DELETED(HttpStatus.OK.value(), "성공적으로 삭제되었습니다"),
 | 
			
		||||
@@ -39,6 +39,7 @@ public enum ApiResponseCode {
 | 
			
		||||
 | 
			
		||||
    // 409 Conflict
 | 
			
		||||
    COMMON_CONFLICT(HttpStatus.CONFLICT.value(), "충돌이 발생했습니다"),
 | 
			
		||||
    COMMON_CODE_DUPLICATE(HttpStatus.CONFLICT.value(), "동일한 코드가 존재합니다"),
 | 
			
		||||
 | 
			
		||||
    // 500 Internal Server Error
 | 
			
		||||
    COMMON_INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR.value(), "서버에서 오류가 발생했습니다"),
 | 
			
		||||
 
 | 
			
		||||
@@ -12,14 +12,17 @@ import lombok.RequiredArgsConstructor;
 | 
			
		||||
@JsonInclude(JsonInclude.Include.NON_NULL)
 | 
			
		||||
public class ApiResponseDto<T> {
 | 
			
		||||
 | 
			
		||||
    private static final boolean SUCCESS = true;
 | 
			
		||||
    private static final boolean FAIL = false;
 | 
			
		||||
 | 
			
		||||
    private boolean success;
 | 
			
		||||
    private int code;
 | 
			
		||||
    private String message;
 | 
			
		||||
    private String description;
 | 
			
		||||
    private T data;
 | 
			
		||||
 | 
			
		||||
    private static final int SUCCESS = 200;
 | 
			
		||||
 | 
			
		||||
    private ApiResponseDto(int code, String message, String description, T data){
 | 
			
		||||
    private ApiResponseDto(boolean success, int code, String message, String description, T data){
 | 
			
		||||
        this.success = success;
 | 
			
		||||
        this.code = code;
 | 
			
		||||
        this.message = message;
 | 
			
		||||
        this.description = description;
 | 
			
		||||
@@ -27,19 +30,19 @@ public class ApiResponseDto<T> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
        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) {
 | 
			
		||||
        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) {
 | 
			
		||||
        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 static com.bio.bio_backend.global.utils.OidUtils.generateOid;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 모든 엔티티가 상속받는 기본 엔티티 클래스
 | 
			
		||||
@@ -18,7 +18,7 @@ import static com.bio.bio_backend.global.utils.OidUtils.generateOid;
 | 
			
		||||
@Getter
 | 
			
		||||
@Setter
 | 
			
		||||
@MappedSuperclass
 | 
			
		||||
@EntityListeners(AuditingEntityListener.class)
 | 
			
		||||
@EntityListeners({AuditingEntityListener.class, BaseEntityListener.class})
 | 
			
		||||
public abstract class BaseEntity {
 | 
			
		||||
 | 
			
		||||
    @Id
 | 
			
		||||
@@ -45,31 +45,4 @@ public abstract class BaseEntity {
 | 
			
		||||
    @Column(name = "updated_id")
 | 
			
		||||
    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
 | 
			
		||||
    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.web.multipart.MultipartFile;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.nio.file.Files;
 | 
			
		||||
import java.nio.file.Path;
 | 
			
		||||
import java.nio.file.Paths;
 | 
			
		||||
import java.nio.file.StandardCopyOption;
 | 
			
		||||
import java.time.LocalDate;
 | 
			
		||||
import java.time.format.DateTimeFormatter;
 | 
			
		||||
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 {
 | 
			
		||||
    
 | 
			
		||||
    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/
 | 
			
		||||
@@ -76,35 +59,6 @@ public class FileUtils {
 | 
			
		||||
        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 {
 | 
			
		||||
        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;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
@@ -137,174 +91,45 @@ public class FileUtils {
 | 
			
		||||
    public static String cleanFileName(String originalFileName) {
 | 
			
		||||
        return StringUtils.cleanPath(originalFileName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 파일 크기를 사람이 읽기 쉬운 형태로 변환
 | 
			
		||||
     * 파일 업로드 (단일/다중 파일 모두 지원)
 | 
			
		||||
     * 사용 예시:
 | 
			
		||||
     * // 단일 파일 업로드
 | 
			
		||||
     * 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 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));
 | 
			
		||||
    public FileUploadDto uploadFile(MultipartFile file, String description) {
 | 
			
		||||
        // 단일 파일도 List로 감싸서 다중 파일 업로드 방식 사용
 | 
			
		||||
        List<MultipartFile> files = List.of(file);
 | 
			
		||||
        return uploadFiles(files, description);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 파일 확장자로부터 MIME 타입 추정
 | 
			
		||||
     */
 | 
			
		||||
    public static String getMimeTypeFromExtension(String fileName) {
 | 
			
		||||
        if (fileName == null) return "application/octet-stream";
 | 
			
		||||
    public FileUploadDto uploadFiles(List<MultipartFile> files, String description) {
 | 
			
		||||
        MultipleFileUploadRequestDto requestDto = MultipleFileUploadRequestDto.builder()
 | 
			
		||||
            .files(files)
 | 
			
		||||
            .description(description)
 | 
			
		||||
            .build();
 | 
			
		||||
        
 | 
			
		||||
        String extension = extractFileExtension(fileName).toLowerCase();
 | 
			
		||||
        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 static String createSafeFileName(String originalFileName) {
 | 
			
		||||
        if (originalFileName == null) return "";
 | 
			
		||||
        MultipleFileUploadResponseDto response = fileService.uploadMultipleFiles(requestDto);
 | 
			
		||||
        
 | 
			
		||||
        // 특수문자 제거 및 공백을 언더스코어로 변경
 | 
			
		||||
        String safeName = originalFileName
 | 
			
		||||
                .replaceAll("[^a-zA-Z0-9가-힣._-]", "_")
 | 
			
		||||
                .replaceAll("_+", "_")
 | 
			
		||||
                .trim();
 | 
			
		||||
        
 | 
			
		||||
        // 파일명이 너무 길면 자르기
 | 
			
		||||
        if (safeName.length() > 100) {
 | 
			
		||||
            String extension = extractFileExtension(safeName);
 | 
			
		||||
            safeName = safeName.substring(0, 100 - extension.length()) + extension;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        return safeName;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * 파일이 이미지인지 확인
 | 
			
		||||
     */
 | 
			
		||||
    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;
 | 
			
		||||
        }
 | 
			
		||||
        return FileUploadDto.builder()
 | 
			
		||||
            .groupOid(response.getGroupOid())
 | 
			
		||||
            .files(response.getFiles())
 | 
			
		||||
            .totalCount(response.getTotalCount())
 | 
			
		||||
            .successCount(response.getSuccessCount())
 | 
			
		||||
            .failureCount(response.getFailureCount())
 | 
			
		||||
            .errorMessages(response.getErrorMessages())
 | 
			
		||||
            .build();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,11 +3,6 @@
 | 
			
		||||
# ========================================
 | 
			
		||||
server.port=8080
 | 
			
		||||
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.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/health/**
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
# 파일 업로드 설정
 | 
			
		||||
# ========================================
 | 
			
		||||
@@ -129,4 +123,36 @@ spring.servlet.multipart.max-request-size=10MB
 | 
			
		||||
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