[agent-pipeline] pipe-20260327053504-cc40 round-2

This commit is contained in:
DDD1542
2026-03-27 18:11:56 +09:00
parent 12dd49fe3d
commit 3923dbefa0
398 changed files with 64367 additions and 386 deletions
@@ -0,0 +1,249 @@
package com.erp.service;
import com.erp.mapper.CodeMergeMapper;
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 {
private final CodeMergeMapper codeMergeMapper;
private final JdbcTemplate jdbcTemplate;
// ── 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("columnName", columnName);
List<Map<String, Object>> rows = codeMergeMapper.getTablesWithColumn(params);
// map-underscore-to-camel-case: true 적용 → key = "tableName"
List<String> tables = rows.stream()
.map(r -> {
Object val = r.get("tableName");
if (val == null) 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("columnName", 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("columnName"));
String oldValue = str(body.get("oldValue"));
String companyCode = str(body.get("companyCode"));
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("columnName", columnName);
List<Map<String, Object>> tableRows = codeMergeMapper.getTablesWithColumn(params);
List<Map<String, Object>> preview = new ArrayList<>();
int totalRows = 0;
for (Map<String, Object> tableRow : tableRows) {
Object nameVal = tableRow.get("tableName");
if (nameVal == null) 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("tableName", tableName);
item.put("affectedRows", count);
preview.add(item);
totalRows += count;
}
} catch (Exception e) {
log.warn("테이블 {} 조회 실패 (건너뜀): {}", tableName, e.getMessage());
}
}
Map<String, Object> result = new LinkedHashMap<>();
result.put("columnName", columnName);
result.put("oldValue", oldValue);
result.put("preview", preview);
result.put("totalAffectedRows", 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("columnName"));
String oldValue = str(body.get("oldValue"));
String newValue = str(body.get("newValue"));
String companyCode = str(body.get("companyCode"));
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("tableName", r.get("table_name"));
item.put("rowsUpdated", 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("columnName", columnName);
result.put("oldValue", oldValue);
result.put("newValue", newValue);
result.put("affectedTables", affectedTables);
result.put("totalRowsUpdated", 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("oldValue"));
String newValue = str(body.get("newValue"));
String companyCode = str(body.get("companyCode"));
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("tableName", r.get("out_table_name"));
item.put("columnName", r.get("out_column_name"));
item.put("rowsUpdated", 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("oldValue", oldValue);
result.put("newValue", newValue);
result.put("affectedData", affectedData);
result.put("totalRowsUpdated", 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("oldValue"));
String companyCode = str(body.get("companyCode"));
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("tableName", r.get("out_table_name"));
item.put("columnName", r.get("out_column_name"));
item.put("affectedRows", 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("oldValue", oldValue);
result.put("preview", preview);
result.put("totalAffectedRows", 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();
}
}