로그에 메뉴코드 매핑하여 insert 하기

This commit is contained in:
2025-12-10 10:05:42 +09:00
parent e24f6801b3
commit c9094f04c2
13 changed files with 135 additions and 79 deletions

View File

@@ -79,6 +79,7 @@ dependencies {
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.5' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.5' // JSON (Jackson) runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.5' // JSON (Jackson)
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
} }
configurations.configureEach { configurations.configureEach {

View File

@@ -5,11 +5,13 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.time.Duration; import java.time.Duration;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisConnectionFactory;
@@ -43,7 +45,8 @@ public class RedisConfig {
} }
/** Redis에서 사용할 공통 ObjectMapper */ /** Redis에서 사용할 공통 ObjectMapper */
@Bean @Bean(name = "redisObjectMapper")
@Primary
public ObjectMapper redisObjectMapper() { public ObjectMapper redisObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper(); ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule()); objectMapper.registerModule(new JavaTimeModule());
@@ -56,7 +59,8 @@ public class RedisConfig {
/** 공통 Serializer (Template + Cache 둘 다 이거 사용) */ /** 공통 Serializer (Template + Cache 둘 다 이거 사용) */
@Bean @Bean
public GenericJackson2JsonRedisSerializer redisSerializer(ObjectMapper redisObjectMapper) { public GenericJackson2JsonRedisSerializer redisSerializer(
@Qualifier("redisObjectMapper") ObjectMapper redisObjectMapper) {
return new GenericJackson2JsonRedisSerializer(redisObjectMapper); return new GenericJackson2JsonRedisSerializer(redisObjectMapper);
} }

View File

@@ -2,8 +2,10 @@ package com.kamco.cd.kamcoback.config.api;
import com.kamco.cd.kamcoback.log.dto.EventStatus; import com.kamco.cd.kamcoback.log.dto.EventStatus;
import com.kamco.cd.kamcoback.log.dto.EventType; import com.kamco.cd.kamcoback.log.dto.EventType;
import com.kamco.cd.kamcoback.menu.dto.MenuDto;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.springframework.web.util.ContentCachingRequestWrapper; import org.springframework.web.util.ContentCachingRequestWrapper;
@@ -116,4 +118,15 @@ public class ApiLogFunction {
public static EventStatus isSuccessFail(ApiResponseDto<?> apiResponse) { public static EventStatus isSuccessFail(ApiResponseDto<?> apiResponse) {
return apiResponse.getHttpStatus().is2xxSuccessful() ? EventStatus.SUCCESS : EventStatus.FAILED; return apiResponse.getHttpStatus().is2xxSuccessful() ? EventStatus.SUCCESS : EventStatus.FAILED;
} }
public static String getUriMenuInfo(List<MenuDto.Basic> menuList, String uri) {
MenuDto.Basic m =
menuList.stream()
.filter(menu -> menu.getMenuApiUrl() != null && uri.contains(menu.getMenuApiUrl()))
.findFirst()
.orElse(null);
return m != null ? m.getMenuUid() : "SYSTEM";
}
} }

View File

@@ -1,9 +1,15 @@
package com.kamco.cd.kamcoback.config.api; package com.kamco.cd.kamcoback.config.api;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kamco.cd.kamcoback.auth.CustomUserDetails; import com.kamco.cd.kamcoback.auth.CustomUserDetails;
import com.kamco.cd.kamcoback.menu.dto.MenuDto;
import com.kamco.cd.kamcoback.menu.service.MenuService;
import com.kamco.cd.kamcoback.postgres.entity.AuditLogEntity; import com.kamco.cd.kamcoback.postgres.entity.AuditLogEntity;
import com.kamco.cd.kamcoback.postgres.repository.log.AuditLogRepository; import com.kamco.cd.kamcoback.postgres.repository.log.AuditLogRepository;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import java.util.LinkedHashMap;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter;
@@ -24,9 +30,13 @@ import org.springframework.web.util.ContentCachingRequestWrapper;
public class ApiResponseAdvice implements ResponseBodyAdvice<Object> { public class ApiResponseAdvice implements ResponseBodyAdvice<Object> {
private final AuditLogRepository auditLogRepository; private final AuditLogRepository auditLogRepository;
private final MenuService menuService;
public ApiResponseAdvice(AuditLogRepository auditLogRepository) { @Autowired private ObjectMapper objectMapper;
public ApiResponseAdvice(AuditLogRepository auditLogRepository, MenuService menuService) {
this.auditLogRepository = auditLogRepository; this.auditLogRepository = auditLogRepository;
this.menuService = menuService;
} }
@Override @Override
@@ -75,13 +85,28 @@ public class ApiResponseAdvice implements ResponseBodyAdvice<Object> {
String requestBody = ApiLogFunction.getRequestBody(servletRequest, contentWrapper); String requestBody = ApiLogFunction.getRequestBody(servletRequest, contentWrapper);
requestBody = maskSensitiveFields(requestBody); // 로그 저장전에 중요정보 마스킹 requestBody = maskSensitiveFields(requestBody); // 로그 저장전에 중요정보 마스킹
// TODO: menuUid 를 동적으로 가져오게끔 해야함 List<?> list = menuService.getFindAll();
List<MenuDto.Basic> result =
list.stream()
.map(
item -> {
if (item instanceof LinkedHashMap<?, ?> map) {
return objectMapper.convertValue(map, MenuDto.Basic.class);
} else if (item instanceof MenuDto.Basic dto) {
return dto;
} else {
throw new IllegalStateException("Unsupported cache type: " + item.getClass());
}
})
.toList();
AuditLogEntity log = AuditLogEntity log =
new AuditLogEntity( new AuditLogEntity(
userid, userid,
ApiLogFunction.getEventType(servletRequest), ApiLogFunction.getEventType(servletRequest),
ApiLogFunction.isSuccessFail(apiResponse), ApiLogFunction.isSuccessFail(apiResponse),
"MU_01_01", ApiLogFunction.getUriMenuInfo(result, servletRequest.getRequestURI()),
ip, ip,
servletRequest.getRequestURI(), servletRequest.getRequestURI(),
requestBody, requestBody,

View File

@@ -1,5 +1,7 @@
package com.kamco.cd.kamcoback.menu; package com.kamco.cd.kamcoback.menu;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kamco.cd.kamcoback.config.api.ApiLogFunction;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto; import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
import com.kamco.cd.kamcoback.menu.dto.MenuDto; import com.kamco.cd.kamcoback.menu.dto.MenuDto;
import com.kamco.cd.kamcoback.menu.service.MenuService; import com.kamco.cd.kamcoback.menu.service.MenuService;
@@ -9,13 +11,15 @@ import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.LinkedHashMap;
import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Tag(name = "메뉴 관리", description = "메뉴 관리 API") @Tag(name = "메뉴 관리", description = "메뉴 관리 API")
@RestController @RestController
@RequiredArgsConstructor @RequiredArgsConstructor
@@ -24,21 +28,44 @@ public class MenuApiController {
private final MenuService menuService; private final MenuService menuService;
@Autowired private ObjectMapper objectMapper;
@Operation(summary = "목록 조회", description = "모든 메뉴 조회") @Operation(summary = "목록 조회", description = "모든 메뉴 조회")
@ApiResponses( @ApiResponses(
value = { value = {
@ApiResponse( @ApiResponse(
responseCode = "200", responseCode = "200",
description = "조회 성공", description = "조회 성공",
content = content =
@Content( @Content(
mediaType = "application/json", mediaType = "application/json",
schema = @Schema(implementation = MenuDto.Basic.class))), schema = @Schema(implementation = MenuDto.Basic.class))),
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
}) })
@GetMapping @GetMapping
public ApiResponseDto<List<MenuDto.Basic>> getFindAll() { public ApiResponseDto<List<MenuDto.Basic>> getFindAll() {
return ApiResponseDto.ok(menuService.getFindAll()); return ApiResponseDto.ok(menuService.getFindAll());
} }
@GetMapping("/find-uri")
public ApiResponseDto<String> getFindAllByUid(@RequestParam String apiUri) {
List<?> list = menuService.getFindAll();
List<MenuDto.Basic> result =
list.stream()
.map(
item -> {
if (item instanceof LinkedHashMap<?, ?> map) {
return objectMapper.convertValue(map, MenuDto.Basic.class);
} else if (item instanceof MenuDto.Basic dto) {
return dto;
} else {
throw new IllegalStateException("Unsupported cache type: " + item.getClass());
}
})
.toList();
return ApiResponseDto.ok(ApiLogFunction.getUriMenuInfo(result, apiUri));
}
} }

View File

@@ -2,11 +2,10 @@ package com.kamco.cd.kamcoback.menu.dto;
import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm; import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.List; import java.util.List;
import lombok.Getter;
import lombok.NoArgsConstructor;
public class MenuDto { public class MenuDto {
@@ -27,28 +26,26 @@ public class MenuDto {
private List<MenuDto.Basic> children; private List<MenuDto.Basic> children;
@JsonFormatDttm @JsonFormatDttm private ZonedDateTime createdDttm;
private ZonedDateTime createdDttm;
@JsonFormatDttm @JsonFormatDttm private ZonedDateTime updatedDttm;
private ZonedDateTime updatedDttm;
private String menuApiUrl; private String menuApiUrl;
public Basic( public Basic(
String menuUid, String menuUid,
String menuNm, String menuNm,
String menuUrl, String menuUrl,
String description, String description,
Long menuOrder, Long menuOrder,
Boolean isUse, Boolean isUse,
Boolean deleted, Boolean deleted,
Long createdUid, Long createdUid,
Long updatedUid, Long updatedUid,
List<MenuDto.Basic> children, List<MenuDto.Basic> children,
ZonedDateTime createdDttm, ZonedDateTime createdDttm,
ZonedDateTime updatedDttm, ZonedDateTime updatedDttm,
String menuApiUrl) { String menuApiUrl) {
this.menuUid = menuUid; this.menuUid = menuUid;
this.menuNm = menuNm; this.menuNm = menuNm;
this.menuUrl = menuUrl; this.menuUrl = menuUrl;

View File

@@ -2,19 +2,18 @@ package com.kamco.cd.kamcoback.menu.service;
import com.kamco.cd.kamcoback.menu.dto.MenuDto; import com.kamco.cd.kamcoback.menu.dto.MenuDto;
import com.kamco.cd.kamcoback.postgres.core.MenuCoreService; import com.kamco.cd.kamcoback.postgres.core.MenuCoreService;
import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class MenuService { public class MenuService {
private final MenuCoreService menuCoreService; private final MenuCoreService menuCoreService;
@Cacheable(value = "menuFindAll") @Cacheable(value = "menuFindAll")
public List<MenuDto.Basic> getFindAll(){ public List<MenuDto.Basic> getFindAll() {
return menuCoreService.getFindAll(); return menuCoreService.getFindAll();
} }
} }

View File

@@ -9,15 +9,14 @@ 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.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
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
@@ -47,15 +46,13 @@ public class CommonCodeCoreService
String regex = "^([A-Z]+|[0-9]+|[A-Z0-9]+(_[A-Z0-9]+)+)$"; String regex = "^([A-Z]+|[0-9]+|[A-Z0-9]+(_[A-Z0-9]+)+)$";
boolean isValid = req.getCode().matches(regex); boolean isValid = req.getCode().matches(regex);
if (!isValid) { if (!isValid) {
return new ResponseObj( return new ResponseObj(ApiResponseCode.CONFLICT, "공통코드에 영문 대문자, 숫자, 언더바(_)만 입력 가능합니다.");
ApiResponseCode.CONFLICT, "공통코드에 영문 대문자, 숫자, 언더바(_)만 입력 가능합니다.");
} }
Long existsCount = Long existsCount =
commonCodeRepository.findByParentIdCodeExists(req.getParentId(), req.getCode()); commonCodeRepository.findByParentIdCodeExists(req.getParentId(), req.getCode());
if (existsCount > 0) { if (existsCount > 0) {
return new ResponseObj( return new ResponseObj(ApiResponseCode.DUPLICATE_DATA, "이미 사용중인 공통코드ID 입니다.");
ApiResponseCode.DUPLICATE_DATA, "이미 사용중인 공통코드ID 입니다.");
} }
CommonCodeEntity entity = CommonCodeEntity entity =
@@ -208,13 +205,11 @@ public class CommonCodeCoreService
String regex = "^([A-Z]+|[0-9]+|[A-Z0-9]+(_[A-Z0-9]+)+)$"; String regex = "^([A-Z]+|[0-9]+|[A-Z0-9]+(_[A-Z0-9]+)+)$";
boolean isValid = code.matches(regex); boolean isValid = code.matches(regex);
if (!isValid) { if (!isValid) {
return new ResponseObj( return new ResponseObj(ApiResponseCode.CONFLICT, "공통코드에 영문 대문자, 숫자, 언더바(_)만 입력 가능합니다.");
ApiResponseCode.CONFLICT, "공통코드에 영문 대문자, 숫자, 언더바(_)만 입력 가능합니다.");
} }
if (existsCount > 0) { if (existsCount > 0) {
return new ResponseObj( return new ResponseObj(ApiResponseCode.DUPLICATE_DATA, "이미 사용중인 공통코드ID 입니다.");
ApiResponseCode.DUPLICATE_DATA, "이미 사용중인 공통코드ID 입니다.");
} }
return new ResponseObj(ApiResponseCode.OK, ""); return new ResponseObj(ApiResponseCode.OK, "");

View File

@@ -3,11 +3,10 @@ package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.menu.dto.MenuDto; import com.kamco.cd.kamcoback.menu.dto.MenuDto;
import com.kamco.cd.kamcoback.postgres.entity.MenuEntity; import com.kamco.cd.kamcoback.postgres.entity.MenuEntity;
import com.kamco.cd.kamcoback.postgres.repository.menu.MenuRepository; import com.kamco.cd.kamcoback.postgres.repository.menu.MenuRepository;
import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class MenuCoreService { public class MenuCoreService {

View File

@@ -52,21 +52,20 @@ public class MenuEntity extends CommonDateEntity {
@Column(name = "menu_api_uri") @Column(name = "menu_api_uri")
private String menuApiUri; private String menuApiUri;
public MenuDto.Basic toDto(){ public MenuDto.Basic toDto() {
return new MenuDto.Basic( return new MenuDto.Basic(
this.menuUid, this.menuUid,
this.menuNm, this.menuNm,
this.menuUrl, this.menuUrl,
this.description, this.description,
this.menuOrder, this.menuOrder,
this.isUse, this.isUse,
this.deleted, this.deleted,
this.createdUid, this.createdUid,
this.updatedUid, this.updatedUid,
this.children.stream().map(MenuEntity::toDto).toList(), this.children.stream().map(MenuEntity::toDto).toList(),
this.getCreatedDate(), this.getCreatedDate(),
this.getModifiedDate(), this.getModifiedDate(),
this.menuApiUri this.menuApiUri);
);
} }
} }

View File

@@ -5,6 +5,7 @@ import com.kamco.cd.kamcoback.postgres.CommonDateEntity;
import jakarta.persistence.*; import jakarta.persistence.*;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.ColumnDefault;
@@ -12,6 +13,7 @@ import org.hibernate.annotations.ColumnDefault;
@Setter @Setter
@Entity @Entity
@Table(name = "tb_model_mng") @Table(name = "tb_model_mng")
@NoArgsConstructor
public class ModelMngEntity extends CommonDateEntity { public class ModelMngEntity extends CommonDateEntity {
@Id @Id

View File

@@ -1,7 +1,6 @@
package com.kamco.cd.kamcoback.postgres.repository.menu; package com.kamco.cd.kamcoback.postgres.repository.menu;
import com.kamco.cd.kamcoback.postgres.entity.MenuEntity; import com.kamco.cd.kamcoback.postgres.entity.MenuEntity;
import java.util.List; import java.util.List;
public interface MenuRepositoryCustom { public interface MenuRepositoryCustom {

View File

@@ -1,14 +1,13 @@
package com.kamco.cd.kamcoback.postgres.repository.menu; package com.kamco.cd.kamcoback.postgres.repository.menu;
import static com.kamco.cd.kamcoback.postgres.entity.QMenuEntity.menuEntity;
import com.kamco.cd.kamcoback.postgres.entity.MenuEntity; import com.kamco.cd.kamcoback.postgres.entity.MenuEntity;
import com.querydsl.core.types.dsl.Expressions; import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.StringExpression; import com.querydsl.core.types.dsl.StringExpression;
import com.querydsl.jpa.impl.JPAQueryFactory; import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
import java.util.List; import java.util.List;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
import static com.kamco.cd.kamcoback.postgres.entity.QMenuEntity.menuEntity;
public class MenuRepositoryImpl extends QuerydslRepositorySupport implements MenuRepositoryCustom { public class MenuRepositoryImpl extends QuerydslRepositorySupport implements MenuRepositoryCustom {
@@ -16,15 +15,12 @@ public class MenuRepositoryImpl extends QuerydslRepositorySupport implements Men
private final StringExpression NULL_STRING = Expressions.stringTemplate("cast(null as text)"); private final StringExpression NULL_STRING = Expressions.stringTemplate("cast(null as text)");
public MenuRepositoryImpl(JPAQueryFactory queryFactory) { public MenuRepositoryImpl(JPAQueryFactory queryFactory) {
super(MenuEntity.class); super(MenuEntity.class);
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
} }
@Override @Override
public List<MenuEntity> getFindAll() { public List<MenuEntity> getFindAll() {
return queryFactory return queryFactory.selectFrom(menuEntity).where(menuEntity.deleted.isFalse()).fetch();
.selectFrom(menuEntity)
.where(menuEntity.deleted.isFalse())
.fetch();
} }
} }