Files
invyone/backend-spring/src/main/java/com/erp/service/ComponentStandardService.java
T

259 lines
12 KiB
Java

package com.erp.service;
import com.erp.common.BaseService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
/**
* Component Standard 서비스
*
* Node.js componentStandardService.ts 포팅.
* component_standards 테이블 CRUD + 카테고리/통계/정렬/복제.
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class ComponentStandardService extends BaseService {
private static final String NS = "componentStandard.";
private final CommonService commonService;
private final ObjectMapper objectMapper;
/** 허용된 정렬 컬럼 (SQL 인젝션 방지) */
private static final Set<String> VALID_SORT_COLUMNS = Set.of(
"sort_order", "component_name", "category", "created_date", "updated_date");
// ══════════════════════════════════════════════════════════
// 목록 조회
// ══════════════════════════════════════════════════════════
/**
* 컴포넌트 목록 조회 (필터, 정렬, 페이지네이션 포함)
*
* @return { components, total, limit, offset }
*/
public Map<String, Object> getComponents(Map<String, Object> params) {
// 기본값: active = 'Y'
if (params.get("active") == null) params.put("active", "Y");
// 정렬 컬럼 화이트리스트 검증
String sort = str(params.getOrDefault("sort", "sort_order"));
params.put("sortColumn", VALID_SORT_COLUMNS.contains(sort) ? sort : "sort_order");
params.put("sortOrder", "desc".equalsIgnoreCase(str(params.get("order"))) ? "DESC" : "ASC");
// 검색 패턴
if (params.get("search") != null) {
params.put("searchPattern", "%" + params.get("search") + "%");
}
// 페이지네이션
commonService.applyPagination(params);
List<Map<String, Object>> components = sqlSession.selectList(NS + "selectComponentList", params);
Integer totalObj = sqlSession.selectOne(NS + "countComponents", params);
int total = totalObj != null ? totalObj : 0;
Map<String, Object> result = new LinkedHashMap<>();
result.put("components", components);
result.put("total", total);
result.put("limit", params.get("limit"));
result.put("offset", params.getOrDefault("offset", 0));
return result;
}
// ══════════════════════════════════════════════════════════
// 단건 조회
// ══════════════════════════════════════════════════════════
public Map<String, Object> getComponent(String componentCode) {
Map<String, Object> params = Map.of("component_code", componentCode);
Map<String, Object> component = sqlSession.selectOne(NS + "selectComponent", params);
if (component == null) {
throw new RuntimeException("컴포넌트를 찾을 수 없습니다: " + componentCode);
}
return component;
}
// ══════════════════════════════════════════════════════════
// 생성
// ══════════════════════════════════════════════════════════
@Transactional
public Map<String, Object> createComponent(Map<String, Object> params) {
// 중복 코드 확인
if (sqlSession.selectOne(NS + "checkDuplicate", params) != null) {
throw new RuntimeException("이미 존재하는 컴포넌트 코드입니다: " + params.get("component_code"));
}
// 'active' → 'is_active' 변환
if (params.containsKey("active")) {
params.put("is_active", params.remove("active"));
}
// 기본값 설정
params.putIfAbsent("is_active", "Y");
params.putIfAbsent("is_public", "N");
params.putIfAbsent("sort_order", 0);
// JSONB 필드 직렬화
serializeJsonFields(params);
sqlSession.insert(NS + "insertComponent", params);
return sqlSession.selectOne(NS + "selectComponent", Map.of("component_code", params.get("component_code")));
}
// ══════════════════════════════════════════════════════════
// 수정
// ══════════════════════════════════════════════════════════
@Transactional
public Map<String, Object> updateComponent(String componentCode, Map<String, Object> data) {
// 존재 확인
getComponent(componentCode);
// 'active' → 'is_active' 변환
if (data.containsKey("active")) {
data.put("is_active", data.remove("active"));
}
data.put("component_code", componentCode);
serializeJsonFields(data);
sqlSession.update(NS + "updateComponent", data);
return sqlSession.selectOne(NS + "selectComponent", Map.of("component_code", componentCode));
}
// ══════════════════════════════════════════════════════════
// 삭제
// ══════════════════════════════════════════════════════════
@Transactional
public Map<String, Object> deleteComponent(String componentCode) {
getComponent(componentCode); // 존재 확인
sqlSession.delete(NS + "deleteComponent", Map.of("component_code", componentCode));
return Map.of("message", "컴포넌트가 삭제되었습니다: " + componentCode);
}
// ══════════════════════════════════════════════════════════
// 정렬 순서 업데이트 (배치)
// ══════════════════════════════════════════════════════════
@Transactional
public Map<String, Object> updateSortOrder(List<Map<String, Object>> updates) {
for (Map<String, Object> item : updates) {
sqlSession.update(NS + "updateSortOrder", item);
}
return Map.of("message", "정렬 순서가 업데이트되었습니다.");
}
// ══════════════════════════════════════════════════════════
// 복제
// ══════════════════════════════════════════════════════════
@Transactional
public Map<String, Object> duplicateComponent(String sourceCode, String newCode, String newName) {
Map<String, Object> source = getComponent(sourceCode);
// 새 코드 중복 확인
Map<String, Object> dupCheck = new HashMap<>();
dupCheck.put("component_code", newCode);
if (sqlSession.selectOne(NS + "checkDuplicate", dupCheck) != null) {
throw new RuntimeException("이미 존재하는 컴포넌트 코드입니다: " + newCode);
}
Map<String, Object> newComponent = new HashMap<>(source);
newComponent.put("component_code", newCode);
newComponent.put("component_name", newName);
serializeJsonFields(newComponent);
sqlSession.insert(NS + "insertComponent", newComponent);
return sqlSession.selectOne(NS + "selectComponent", Map.of("component_code", newCode));
}
// ══════════════════════════════════════════════════════════
// 카테고리 목록
// ══════════════════════════════════════════════════════════
public List<String> getCategories(Map<String, Object> params) {
List<Map<String, Object>> rows = sqlSession.selectList(NS + "selectCategories", params);
List<String> categories = new ArrayList<>();
for (Map<String, Object> row : rows) {
Object cat = row.get("category");
if (cat != null) categories.add(cat.toString());
}
return categories;
}
// ══════════════════════════════════════════════════════════
// 통계
// ══════════════════════════════════════════════════════════
public Map<String, Object> getStatistics(Map<String, Object> params) {
Integer totalObj = sqlSession.selectOne(NS + "countStatisticsTotal", params);
int total = totalObj != null ? totalObj : 0;
List<Map<String, Object>> byCategoryRaw = sqlSession.selectList(NS + "selectStatisticsByCategory", params);
List<Map<String, Object>> byCategory = new ArrayList<>();
for (Map<String, Object> row : byCategoryRaw) {
Map<String, Object> item = new LinkedHashMap<>();
item.put("category", row.get("category"));
item.put("count", toLong(row.get("count")));
byCategory.add(item);
}
List<Map<String, Object>> byStatusRaw = sqlSession.selectList(NS + "selectStatisticsByStatus");
List<Map<String, Object>> byStatus = new ArrayList<>();
for (Map<String, Object> row : byStatusRaw) {
Map<String, Object> item = new LinkedHashMap<>();
item.put("status", row.get("is_active"));
item.put("count", toLong(row.get("count")));
byStatus.add(item);
}
Map<String, Object> result = new LinkedHashMap<>();
result.put("total", total);
result.put("byCategory", byCategory);
result.put("byStatus", byStatus);
return result;
}
// ══════════════════════════════════════════════════════════
// 중복 코드 체크
// ══════════════════════════════════════════════════════════
public boolean checkDuplicate(Map<String, Object> params) {
return sqlSession.selectOne(NS + "checkDuplicate", params) != null;
}
// ══ private helpers ═══════════════════════════════════════
/** component_config, default_size 값이 Map/List이면 JSON 문자열로 직렬화 */
private void serializeJsonFields(Map<String, Object> params) {
for (String field : new String[]{"component_config", "default_size"}) {
Object val = params.get(field);
if (val != null && !(val instanceof String)) {
try {
params.put(field, objectMapper.writeValueAsString(val));
} catch (JsonProcessingException e) {
log.warn("JSON 직렬화 실패 ({}): {}", field, e.getMessage());
}
}
}
}
private String str(Object o) { return o == null ? "" : o.toString(); }
private long toLong(Object o) {
if (o == null) return 0L;
if (o instanceof Number n) return n.longValue();
return Long.parseLong(o.toString());
}
}