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

248 lines
11 KiB
Java

package com.erp.service;
import com.erp.common.BaseService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
@Slf4j
public class CodeMergeService extends BaseService {
private final JdbcTemplate jdbcTemplate;
private static final String NS = "codeMerge.";
// ── Tables With Column ────────────────────────────────────────────────────
/**
* GET /tables-with-column/:columnName
* 해당 컬럼과 company_code 컬럼을 함께 가진 public 테이블 목록 반환
*/
public Map<String, Object> getTablesWithColumn(String columnName) {
Map<String, Object> params = new HashMap<>();
params.put("column_name", columnName);
List<Map<String, Object>> rows = sqlSession.selectList(NS + "getTablesWithColumn", params);
List<String> tables = rows.stream()
.map(r -> {
Object val = r.get("table_name");
return val != null ? val.toString() : null;
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
Map<String, Object> result = new LinkedHashMap<>();
result.put("column_name", columnName);
result.put("tables", tables);
result.put("count", tables.size());
return result;
}
// ── Preview (column-based) ────────────────────────────────────────────────
/**
* POST /preview
* columnName + oldValue 기준으로 영향받을 테이블/행 수 미리보기 (DB 변경 없음)
*/
public Map<String, Object> previewCodeMerge(Map<String, Object> body) {
String columnName = str(body.get("column_name"));
String oldValue = str(body.get("old_value"));
String companyCode = str(body.get("company_code"));
if (isBlank(columnName) || isBlank(oldValue)) {
throw new IllegalArgumentException("필수 필드가 누락되었습니다. (columnName, oldValue)");
}
log.info("코드 병합 미리보기: column={}, oldValue={}, company={}", columnName, oldValue, companyCode);
Map<String, Object> params = new HashMap<>();
params.put("column_name", columnName);
List<Map<String, Object>> tableRows = sqlSession.selectList(NS + "getTablesWithColumn", params);
List<Map<String, Object>> preview = new ArrayList<>();
int totalRows = 0;
for (Map<String, Object> tableRow : tableRows) {
Object nameVal = tableRow.get("table_name");
if (nameVal == null) continue;
String tableName = nameVal.toString();
// 테이블명·컬럼명은 information_schema에서 검증된 값 — SQL 인젝션 위험 없음
String countSql = String.format(
"SELECT COUNT(*) FROM \"%s\" WHERE \"%s\" = ? AND company_code = ?",
tableName, columnName);
try {
Integer count = jdbcTemplate.queryForObject(countSql, Integer.class, oldValue, companyCode);
if (count != null && count > 0) {
Map<String, Object> item = new LinkedHashMap<>();
item.put("table_name", tableName);
item.put("affected_rows", count);
preview.add(item);
totalRows += count;
}
} catch (Exception e) {
log.warn("테이블 {} 조회 실패 (건너뜀): {}", tableName, e.getMessage());
}
}
Map<String, Object> result = new LinkedHashMap<>();
result.put("column_name", columnName);
result.put("old_value", oldValue);
result.put("preview", preview);
result.put("total_affected_rows", totalRows);
return result;
}
// ── Merge All Tables (column-based) ───────────────────────────────────────
/**
* POST /merge-all-tables
* PostgreSQL 함수 merge_code_all_tables(columnName, oldValue, newValue, companyCode) 호출
*/
@Transactional
public Map<String, Object> mergeAllTables(Map<String, Object> body) {
String columnName = str(body.get("column_name"));
String oldValue = str(body.get("old_value"));
String newValue = str(body.get("new_value"));
String companyCode = str(body.get("company_code"));
if (isBlank(columnName) || isBlank(oldValue) || isBlank(newValue)) {
throw new IllegalArgumentException("필수 필드가 누락되었습니다. (columnName, oldValue, newValue)");
}
if (oldValue.equals(newValue)) {
throw new IllegalArgumentException("기존 값과 새 값이 동일합니다.");
}
log.info("코드 병합 시작: column={}, {} → {}, company={}", columnName, oldValue, newValue, companyCode);
List<Map<String, Object>> rows = jdbcTemplate.queryForList(
"SELECT * FROM merge_code_all_tables(?, ?, ?, ?)",
columnName, oldValue, newValue, companyCode);
int totalRows = rows.stream()
.mapToInt(r -> r.get("rows_updated") != null ? ((Number) r.get("rows_updated")).intValue() : 0)
.sum();
List<Map<String, Object>> affectedTables = rows.stream().map(r -> {
Map<String, Object> item = new LinkedHashMap<>();
item.put("table_name", r.get("table_name"));
item.put("rows_updated", r.get("rows_updated") != null ? ((Number) r.get("rows_updated")).intValue() : 0);
return item;
}).collect(Collectors.toList());
log.info("코드 병합 완료: 영향 테이블 {}개, 총 {}행", affectedTables.size(), totalRows);
Map<String, Object> result = new LinkedHashMap<>();
result.put("column_name", columnName);
result.put("old_value", oldValue);
result.put("new_value", newValue);
result.put("affected_tables", affectedTables);
result.put("total_rows_updated", totalRows);
return result;
}
// ── Merge By Value ────────────────────────────────────────────────────────
/**
* POST /merge-by-value
* PostgreSQL 함수 merge_code_by_value(oldValue, newValue, companyCode) 호출
* 컬럼명에 관계없이 해당 값을 가진 모든 위치를 변경
*/
@Transactional
public Map<String, Object> mergeByValue(Map<String, Object> body) {
String oldValue = str(body.get("old_value"));
String newValue = str(body.get("new_value"));
String companyCode = str(body.get("company_code"));
if (isBlank(oldValue) || isBlank(newValue)) {
throw new IllegalArgumentException("필수 필드가 누락되었습니다. (oldValue, newValue)");
}
if (oldValue.equals(newValue)) {
throw new IllegalArgumentException("기존 값과 새 값이 동일합니다.");
}
log.info("값 기반 코드 병합 시작: {} → {}, company={}", oldValue, newValue, companyCode);
List<Map<String, Object>> rows = jdbcTemplate.queryForList(
"SELECT * FROM merge_code_by_value(?, ?, ?)",
oldValue, newValue, companyCode);
int totalRows = rows.stream()
.mapToInt(r -> r.get("out_rows_updated") != null ? ((Number) r.get("out_rows_updated")).intValue() : 0)
.sum();
List<Map<String, Object>> affectedData = rows.stream().map(r -> {
Map<String, Object> item = new LinkedHashMap<>();
item.put("table_name", r.get("out_table_name"));
item.put("column_name", r.get("out_column_name"));
item.put("rows_updated", r.get("out_rows_updated") != null ? ((Number) r.get("out_rows_updated")).intValue() : 0);
return item;
}).collect(Collectors.toList());
log.info("값 기반 코드 병합 완료: {} → {}, 총 {}행", oldValue, newValue, totalRows);
Map<String, Object> result = new LinkedHashMap<>();
result.put("old_value", oldValue);
result.put("new_value", newValue);
result.put("affected_data", affectedData);
result.put("total_rows_updated", totalRows);
return result;
}
// ── Preview By Value ──────────────────────────────────────────────────────
/**
* POST /preview-by-value
* PostgreSQL 함수 preview_merge_code_by_value(oldValue, companyCode) 호출
*/
public Map<String, Object> previewByValue(Map<String, Object> body) {
String oldValue = str(body.get("old_value"));
String companyCode = str(body.get("company_code"));
if (isBlank(oldValue)) {
throw new IllegalArgumentException("필수 필드가 누락되었습니다. (oldValue)");
}
log.info("값 기반 코드 병합 미리보기: oldValue={}, company={}", oldValue, companyCode);
List<Map<String, Object>> rows = jdbcTemplate.queryForList(
"SELECT * FROM preview_merge_code_by_value(?, ?)",
oldValue, companyCode);
int totalRows = rows.stream()
.mapToInt(r -> r.get("out_affected_rows") != null ? ((Number) r.get("out_affected_rows")).intValue() : 0)
.sum();
List<Map<String, Object>> preview = rows.stream().map(r -> {
Map<String, Object> item = new LinkedHashMap<>();
item.put("table_name", r.get("out_table_name"));
item.put("column_name", r.get("out_column_name"));
item.put("affected_rows", r.get("out_affected_rows") != null ? ((Number) r.get("out_affected_rows")).intValue() : 0);
return item;
}).collect(Collectors.toList());
Map<String, Object> result = new LinkedHashMap<>();
result.put("old_value", oldValue);
result.put("preview", preview);
result.put("total_affected_rows", totalRows);
return result;
}
// ── Helpers ───────────────────────────────────────────────────────────────
private String str(Object val) {
return val != null ? val.toString() : null;
}
private boolean isBlank(String s) {
return s == null || s.isBlank();
}
}