공통코드 삭제 API 커밋, 삭제일시 컬럼 추가, 에러로그에 userId 토큰으로 로직 변경

This commit is contained in:
2025-12-05 12:07:03 +09:00
parent 88cdfa7ce7
commit eeef8c8d32
8 changed files with 130 additions and 23 deletions

View File

@@ -30,7 +30,7 @@ import org.springframework.web.bind.annotation.RestController;
@Tag(name = "공통코드 관리", description = "공통코드 관리 API") @Tag(name = "공통코드 관리", description = "공통코드 관리 API")
@RestController @RestController
@RequiredArgsConstructor @RequiredArgsConstructor
@RequestMapping({"/demo/code", "/api/code"}) @RequestMapping("/api/code")
public class CommonCodeApiController { public class CommonCodeApiController {
private final CommonCodeService commonCodeService; private final CommonCodeService commonCodeService;
@@ -148,14 +148,13 @@ public class CommonCodeApiController {
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
}) })
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
public ApiResponseDto<Long> remove( public ApiResponseDto<ApiResponseDto.ResponseObj> remove(
@io.swagger.v3.oas.annotations.parameters.RequestBody( @io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "공통코드 삭제 요청 정보", description = "공통코드 삭제 요청 정보",
required = true) required = true)
@PathVariable @PathVariable
Long id) { Long id) {
commonCodeService.remove(id); return ApiResponseDto.okObject(commonCodeService.removeCode(id));
return ApiResponseDto.deleteOk(id);
} }
@Operation(summary = "순서 변경", description = "공통코드 순서를 변경 합니다.") @Operation(summary = "순서 변경", description = "공통코드 순서를 변경 합니다.")

View File

@@ -90,6 +90,9 @@ public class CommonCodeDto {
private String props2; private String props2;
private String props3; private String props3;
@JsonFormatDttm
private ZonedDateTime deletedDttm;
public Basic( public Basic(
Long id, Long id,
String code, String code,
@@ -103,7 +106,9 @@ public class CommonCodeDto {
ZonedDateTime updatedDttm, ZonedDateTime updatedDttm,
String props1, String props1,
String props2, String props2,
String props3) { String props3,
ZonedDateTime deletedDttm
) {
this.id = id; this.id = id;
this.code = code; this.code = code;
this.description = description; this.description = description;
@@ -117,6 +122,7 @@ public class CommonCodeDto {
this.props1 = props1; this.props1 = props1;
this.props2 = props2; this.props2 = props2;
this.props3 = props3; this.props3 = props3;
this.deletedDttm = deletedDttm;
} }
} }
} }

View File

@@ -4,6 +4,7 @@ import com.kamco.cd.kamcoback.code.dto.CommonCodeDto.AddReq;
import com.kamco.cd.kamcoback.code.dto.CommonCodeDto.Basic; import com.kamco.cd.kamcoback.code.dto.CommonCodeDto.Basic;
import com.kamco.cd.kamcoback.code.dto.CommonCodeDto.ModifyReq; import com.kamco.cd.kamcoback.code.dto.CommonCodeDto.ModifyReq;
import com.kamco.cd.kamcoback.code.dto.CommonCodeDto.OrderReq; import com.kamco.cd.kamcoback.code.dto.CommonCodeDto.OrderReq;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
import com.kamco.cd.kamcoback.postgres.core.CommonCodeCoreService; import com.kamco.cd.kamcoback.postgres.core.CommonCodeCoreService;
import java.util.List; import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -64,8 +65,8 @@ public class CommonCodeService {
* @param id 코드 아이디 * @param id 코드 아이디
*/ */
@Transactional @Transactional
public void remove(Long id) { public ApiResponseDto.ResponseObj removeCode(Long id) {
commonCodeCoreService.remove(id); return commonCodeCoreService.removeCode(id);
} }
/** /**

View File

@@ -1,5 +1,6 @@
package com.kamco.cd.kamcoback.config; package com.kamco.cd.kamcoback.config;
import com.kamco.cd.kamcoback.auth.CustomUserDetails;
import com.kamco.cd.kamcoback.common.exception.CustomApiException; import com.kamco.cd.kamcoback.common.exception.CustomApiException;
import com.kamco.cd.kamcoback.config.api.ApiLogFunction; import com.kamco.cd.kamcoback.config.api.ApiLogFunction;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto; import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
@@ -23,6 +24,7 @@ import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
@@ -370,8 +372,23 @@ public class GlobalExceptionHandler {
HttpStatus httpStatus, HttpStatus httpStatus,
ErrorLogDto.LogErrorLevel logErrorLevel, ErrorLogDto.LogErrorLevel logErrorLevel,
StackTraceElement[] stackTrace) { StackTraceElement[] stackTrace) {
// TODO : 로그인 개발되면 이것도 연결해야 함
Long userid = Long.valueOf(Optional.ofNullable(ApiLogFunction.getUserId(request)).orElse("1")); Long userid = null;
/**
* servletRequest.getUserPrincipal() instanceof UsernamePasswordAuthenticationToken auth
* 이 요청이 JWT 인증을 통과한 요청인가? 그리고 Spring Security Authentication 객체가 UsernamePasswordAuthenticationToken 타입인가? 체크
*/
/**
* auth.getPrincipal() instanceof CustomUserDetails customUserDetails
* principal 안에 들어있는 객체가 내가 만든 CustomUserDetails 타입인가? 체크
*/
if (request.getUserPrincipal() instanceof UsernamePasswordAuthenticationToken auth
&& auth.getPrincipal() instanceof CustomUserDetails customUserDetails) {
// audit 에는 long 타입 user_id가 들어가지만 토큰 sub은 uuid여서 user_id 가져오기
userid = customUserDetails.getMember().getId();
}
String stackTraceStr = String stackTraceStr =
Arrays.stream(stackTrace) Arrays.stream(stackTrace)

View File

@@ -67,6 +67,14 @@ public class ApiResponseDto<T> {
return new ApiResponseDto<>(data, HttpStatus.OK); return new ApiResponseDto<>(data, HttpStatus.OK);
} }
public static <T> ApiResponseDto<ResponseObj> okObject(ResponseObj data) {
if (data.getFlag().equals(SuccFailCode.SUCCESS)) {
return new ApiResponseDto<>(data, HttpStatus.OK);
} else{
return new ApiResponseDto<>(data.getCode(), data.getMessage(), HttpStatus.OK);
}
}
public static <T> ApiResponseDto<T> deleteOk(T data) { public static <T> ApiResponseDto<T> deleteOk(T data) {
return new ApiResponseDto<>(data, HttpStatus.NO_CONTENT); return new ApiResponseDto<>(data, HttpStatus.NO_CONTENT);
} }
@@ -106,6 +114,39 @@ public class ApiResponseDto<T> {
} }
} }
@Getter
public static class ResponseObj {
private final SuccFailCode flag;
private final ApiResponseCode code;
private final String message;
public ResponseObj(SuccFailCode flag, ApiResponseCode code, String message) {
this.flag = flag;
this.code = code;
this.message = message;
}
}
@Getter
@RequiredArgsConstructor
public enum SuccFailCode implements EnumType {
SUCCESS("성공"),
FAIL("실패");
private final String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}
@Getter @Getter
@RequiredArgsConstructor @RequiredArgsConstructor
public enum ApiResponseCode implements EnumType { public enum ApiResponseCode implements EnumType {

View File

@@ -3,17 +3,21 @@ package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.code.dto.CommonCodeDto; import com.kamco.cd.kamcoback.code.dto.CommonCodeDto;
import com.kamco.cd.kamcoback.code.dto.CommonCodeDto.Basic; import com.kamco.cd.kamcoback.code.dto.CommonCodeDto.Basic;
import com.kamco.cd.kamcoback.common.service.BaseCoreService; import com.kamco.cd.kamcoback.common.service.BaseCoreService;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto.ApiResponseCode;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto.ResponseObj;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto.SuccFailCode;
import com.kamco.cd.kamcoback.postgres.entity.CommonCodeEntity; import com.kamco.cd.kamcoback.postgres.entity.CommonCodeEntity;
import com.kamco.cd.kamcoback.postgres.repository.code.CommonCodeRepository; import com.kamco.cd.kamcoback.postgres.repository.code.CommonCodeRepository;
import com.kamco.cd.kamcoback.zoo.dto.AnimalDto.SearchReq; import com.kamco.cd.kamcoback.zoo.dto.AnimalDto.SearchReq;
import jakarta.persistence.EntityNotFoundException; import jakarta.persistence.EntityNotFoundException;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class CommonCodeCoreService public class CommonCodeCoreService
@@ -92,7 +96,9 @@ public class CommonCodeCoreService
found.getDeleted(), found.getDeleted(),
req.getProps1(), req.getProps1(),
req.getProps2(), req.getProps2(),
req.getProps3()); req.getProps3(),
null
);
return commonCodeRepository.save(entity).toDto(); return commonCodeRepository.save(entity).toDto();
} }
@@ -116,17 +122,30 @@ public class CommonCodeCoreService
return commonCodeRepository.getCode(parentCodeCd, childCodeCd); return commonCodeRepository.getCode(parentCodeCd, childCodeCd);
} }
@Override /**
public void remove(Long id) { * 공통코드 삭제
* @param id
* @return
*/
public ResponseObj removeCode(Long id) {
CommonCodeEntity entity = CommonCodeEntity entity =
commonCodeRepository commonCodeRepository
.findByCodeId(id) .findByCodeId(id)
.orElseThrow(() -> new EntityNotFoundException("code를 찾을 수 없습니다. id " + id)); .orElseThrow(() -> new EntityNotFoundException("code를 찾을 수 없습니다. id " + id));
// 하위 코드 deleted = false 업데이트 // 하위코드가 있으면 삭제 불가
entity.getChildren().forEach(CommonCodeEntity::deleted); if(!entity.getChildren().isEmpty()){
return new ResponseObj(SuccFailCode.FAIL, ApiResponseCode.UNPROCESSABLE_ENTITY,"하위에 다른 공통코드를 가지고 있습니다.<br/>하위공통 코드를 이동한 후 삭제할 수 있습니다.");
}
// id 코드 deleted = false 업데이트 // id 코드 deleted = false 업데이트
entity.deleted(); entity.deleted();
return new ResponseObj(SuccFailCode.SUCCESS, ApiResponseCode.OK, "삭제되었습니다.");
}
@Override
public void remove(Long aLong) {
//미사용
} }
@Override @Override

View File

@@ -15,6 +15,8 @@ import jakarta.persistence.OneToMany;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import java.time.ZonedDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import lombok.AccessLevel; import lombok.AccessLevel;
@@ -73,6 +75,9 @@ public class CommonCodeEntity extends CommonDateEntity {
@Column(name = "props3") @Column(name = "props3")
private String props3; private String props3;
@Column(name = "deleted_dttm")
private ZonedDateTime deletedDttm;
public CommonCodeEntity( public CommonCodeEntity(
String code, String code,
String name, String name,
@@ -102,7 +107,9 @@ public class CommonCodeEntity extends CommonDateEntity {
Boolean deleted, Boolean deleted,
String props1, String props1,
String props2, String props2,
String props3) { String props3,
ZonedDateTime deletedDttm
) {
this.id = id; this.id = id;
this.code = code; this.code = code;
this.name = name; this.name = name;
@@ -113,6 +120,7 @@ public class CommonCodeEntity extends CommonDateEntity {
this.props1 = props1; this.props1 = props1;
this.props2 = props2; this.props2 = props2;
this.props3 = props3; this.props3 = props3;
this.deletedDttm = deletedDttm;
} }
public CommonCodeDto.Basic toDto() { public CommonCodeDto.Basic toDto() {
@@ -129,7 +137,9 @@ public class CommonCodeEntity extends CommonDateEntity {
super.getModifiedDate(), super.getModifiedDate(),
this.props1, this.props1,
this.props2, this.props2,
this.props3); this.props3,
this.deletedDttm
);
} }
public void addParent(CommonCodeEntity parent) { public void addParent(CommonCodeEntity parent) {
@@ -142,6 +152,7 @@ public class CommonCodeEntity extends CommonDateEntity {
public void deleted() { public void deleted() {
this.deleted = true; this.deleted = true;
this.deletedDttm = ZonedDateTime.now();
} }
public void updateOrder(int order) { public void updateOrder(int order) {

View File

@@ -29,7 +29,11 @@ public class CommonCodeRepositoryImpl implements CommonCodeRepositoryCustom {
.selectFrom(commonCodeEntity) .selectFrom(commonCodeEntity)
.leftJoin(commonCodeEntity.children, child) .leftJoin(commonCodeEntity.children, child)
.fetchJoin() .fetchJoin()
.where(commonCodeEntity.id.eq(id), commonCodeEntity.deleted.isFalse()) .where(
commonCodeEntity.id.eq(id),
commonCodeEntity.deleted.isFalse().or(commonCodeEntity.deleted.isNull()),
child.deleted.isFalse().or(child.deleted.isNull())
)
.orderBy(commonCodeEntity.order.asc(), child.order.asc()) .orderBy(commonCodeEntity.order.asc(), child.order.asc())
.fetchOne()); .fetchOne());
} }
@@ -46,7 +50,9 @@ public class CommonCodeRepositoryImpl implements CommonCodeRepositoryCustom {
commonCodeEntity.parent.isNull(), commonCodeEntity.parent.isNull(),
commonCodeEntity.code.eq(code), commonCodeEntity.code.eq(code),
commonCodeEntity.used.isTrue(), commonCodeEntity.used.isTrue(),
commonCodeEntity.deleted.isFalse()) commonCodeEntity.deleted.isFalse().or(commonCodeEntity.deleted.isNull()),
child.deleted.isFalse().or(child.deleted.isNull())
)
.orderBy(child.order.asc()) .orderBy(child.order.asc())
.fetchOne()); .fetchOne());
} }
@@ -58,7 +64,11 @@ public class CommonCodeRepositoryImpl implements CommonCodeRepositoryCustom {
.selectFrom(commonCodeEntity) .selectFrom(commonCodeEntity)
.leftJoin(commonCodeEntity.children, child) .leftJoin(commonCodeEntity.children, child)
.fetchJoin() .fetchJoin()
.where(commonCodeEntity.parent.isNull(), commonCodeEntity.deleted.isFalse()) .where(
commonCodeEntity.parent.isNull(),
commonCodeEntity.deleted.isFalse().or(commonCodeEntity.deleted.isNull()),
child.deleted.isFalse().or(child.deleted.isNull())
)
.orderBy(commonCodeEntity.order.asc(), child.order.asc()) .orderBy(commonCodeEntity.order.asc(), child.order.asc())
.fetch(); .fetch();
} }
@@ -90,7 +100,10 @@ public class CommonCodeRepositoryImpl implements CommonCodeRepositoryCustom {
.select(child.name) .select(child.name)
.from(child) .from(child)
.join(child.parent, parent) .join(child.parent, parent)
.where(parent.code.eq(parentCodeCd).and(child.code.eq(childCodeCd))) .where(
parent.code.eq(parentCodeCd).and(child.code.eq(childCodeCd)),
child.deleted.isFalse().or(child.deleted.isNull())
)
.fetchFirst(); // 단일 결과만 .fetchFirst(); // 단일 결과만
return Optional.ofNullable(result); return Optional.ofNullable(result);
@@ -120,7 +133,7 @@ public class CommonCodeRepositoryImpl implements CommonCodeRepositoryCustom {
private List<CommonCodeEntity> findAllByIds(Set<Long> ids) { private List<CommonCodeEntity> findAllByIds(Set<Long> ids) {
return queryFactory return queryFactory
.selectFrom(commonCodeEntity) .selectFrom(commonCodeEntity)
.where(commonCodeEntity.id.in(ids), commonCodeEntity.deleted.isFalse()) .where(commonCodeEntity.id.in(ids), commonCodeEntity.deleted.isFalse().or(commonCodeEntity.deleted.isNull()))
.fetch(); .fetch();
} }
} }