415 lines
20 KiB
Java
415 lines
20 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.extern.slf4j.Slf4j;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
import java.util.*;
|
|
import java.util.regex.Pattern;
|
|
|
|
@Service
|
|
@Slf4j
|
|
public class DataflowService extends BaseService {
|
|
|
|
@Autowired
|
|
private ObjectMapper objectMapper;
|
|
|
|
/** 테이블명/컬럼명 안전성 검증 패턴 */
|
|
private static final Pattern SAFE_IDENTIFIER = Pattern.compile("^[a-zA-Z_][a-zA-Z0-9_]*$");
|
|
|
|
// ═══════════════════════════════════════════════════════════════════
|
|
// table_relationships CRUD
|
|
// ═══════════════════════════════════════════════════════════════════
|
|
|
|
@Transactional
|
|
public Map<String, Object> insertTableRelationship(Map<String, Object> body,
|
|
String companyCode,
|
|
String userId) {
|
|
String relationshipName = str(body.get("relationshipName"));
|
|
String fromTableName = str(body.get("fromTableName"));
|
|
String fromColumnName = str(body.get("fromColumnName"));
|
|
String toTableName = str(body.get("toTableName"));
|
|
String toColumnName = str(body.get("toColumnName"));
|
|
String relationshipType = body.getOrDefault("relationshipType", "one-to-one").toString();
|
|
String connectionType = body.getOrDefault("connectionType", "simple-key").toString();
|
|
|
|
// diagram_id 결정: 전달된 값 or max+1
|
|
int diagramId;
|
|
Object diagramIdObj = body.get("diagramId");
|
|
if (diagramIdObj != null) {
|
|
diagramId = toInt(diagramIdObj);
|
|
} else {
|
|
Map<String, Object> maxParams = new HashMap<>();
|
|
maxParams.put("companyCode", companyCode);
|
|
Map<String, Object> maxRow = sqlSession.selectOne("dataflow.getMaxDiagramId", maxParams);
|
|
diagramId = toInt(maxRow != null ? maxRow.get("max_diagram_id") : null) + 1;
|
|
}
|
|
|
|
// 중복 확인
|
|
Map<String, Object> checkParams = new HashMap<>();
|
|
checkParams.put("diagramId", diagramId);
|
|
checkParams.put("fromTableName", fromTableName);
|
|
checkParams.put("fromColumnName", fromColumnName);
|
|
checkParams.put("toTableName", toTableName);
|
|
checkParams.put("toColumnName", toColumnName);
|
|
checkParams.put("companyCode", companyCode);
|
|
if (sqlSession.selectOne("dataflow.getExistingRelationshipInfo", checkParams) != null) {
|
|
throw new IllegalArgumentException(
|
|
"이미 존재하는 관계입니다: " + fromTableName + "." + fromColumnName
|
|
+ " → " + toTableName + "." + toColumnName);
|
|
}
|
|
|
|
Map<String, Object> params = new HashMap<>();
|
|
params.put("diagramId", diagramId);
|
|
params.put("relationshipName", relationshipName);
|
|
params.put("fromTableName", fromTableName);
|
|
params.put("fromColumnName", fromColumnName);
|
|
params.put("toTableName", toTableName);
|
|
params.put("toColumnName", toColumnName);
|
|
params.put("relationshipType", relationshipType);
|
|
params.put("connectionType", connectionType);
|
|
params.put("companyCode", companyCode);
|
|
params.put("settings", toJsonString(body.get("settings")));
|
|
params.put("createdBy", userId);
|
|
|
|
sqlSession.insert("dataflow.insertTableRelationship", params);
|
|
|
|
Map<String, Object> idParams = new HashMap<>();
|
|
idParams.put("relationshipId", params.get("relationshipId"));
|
|
idParams.put("companyCode", companyCode);
|
|
return sqlSession.selectOne("dataflow.getTableRelationshipInfo", idParams);
|
|
}
|
|
|
|
public List<Map<String, Object>> getTableRelationshipList(String companyCode) {
|
|
Map<String, Object> params = new HashMap<>();
|
|
params.put("companyCode", companyCode);
|
|
return sqlSession.selectList("dataflow.getTableRelationshipList", params);
|
|
}
|
|
|
|
public Map<String, Object> getTableRelationshipInfo(int relationshipId, String companyCode) {
|
|
Map<String, Object> params = new HashMap<>();
|
|
params.put("relationshipId", relationshipId);
|
|
params.put("companyCode", companyCode);
|
|
return sqlSession.selectOne("dataflow.getTableRelationshipInfo", params);
|
|
}
|
|
|
|
@Transactional
|
|
public Map<String, Object> updateTableRelationship(int relationshipId,
|
|
Map<String, Object> updateData,
|
|
String companyCode,
|
|
String userId) {
|
|
if (getTableRelationshipInfo(relationshipId, companyCode) == null) return null;
|
|
|
|
Map<String, Object> params = new HashMap<>();
|
|
params.put("relationshipId", relationshipId);
|
|
params.put("companyCode", companyCode);
|
|
params.put("updatedBy", userId);
|
|
|
|
copyIfPresent(updateData, params, "relationshipName");
|
|
copyIfPresent(updateData, params, "fromTableName");
|
|
copyIfPresent(updateData, params, "fromColumnName");
|
|
copyIfPresent(updateData, params, "toTableName");
|
|
copyIfPresent(updateData, params, "toColumnName");
|
|
copyIfPresent(updateData, params, "relationshipType");
|
|
copyIfPresent(updateData, params, "connectionType");
|
|
if (updateData.containsKey("settings")) {
|
|
params.put("settings", toJsonString(updateData.get("settings")));
|
|
}
|
|
|
|
sqlSession.update("dataflow.updateTableRelationship", params);
|
|
return getTableRelationshipInfo(relationshipId, companyCode);
|
|
}
|
|
|
|
@Transactional
|
|
public boolean deleteTableRelationship(int relationshipId, String companyCode) {
|
|
if (getTableRelationshipInfo(relationshipId, companyCode) == null) return false;
|
|
|
|
Map<String, Object> params = new HashMap<>();
|
|
params.put("relationshipId", relationshipId);
|
|
params.put("companyCode", companyCode);
|
|
sqlSession.update("dataflow.softDeleteTableRelationship", params);
|
|
return true;
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════════
|
|
// data_relationship_bridge
|
|
// ═══════════════════════════════════════════════════════════════════
|
|
|
|
@Transactional
|
|
public Map<String, Object> insertDataLink(Map<String, Object> body,
|
|
String companyCode,
|
|
String userId) {
|
|
Map<String, Object> params = new HashMap<>();
|
|
params.put("relationshipId", toInt(body.get("relationshipId")));
|
|
params.put("fromTableName", body.get("fromTableName"));
|
|
params.put("fromColumnName", body.get("fromColumnName"));
|
|
params.put("toTableName", body.get("toTableName"));
|
|
params.put("toColumnName", body.get("toColumnName"));
|
|
params.put("connectionType", body.get("connectionType"));
|
|
params.put("companyCode", companyCode);
|
|
params.put("bridgeData", toJsonString(body.get("bridgeData")));
|
|
params.put("createdBy", userId);
|
|
|
|
sqlSession.insert("dataflow.insertDataLink", params);
|
|
|
|
// useGeneratedKeys 로 bridgeId 자동 채워짐
|
|
Map<String, Object> result = new LinkedHashMap<>(params);
|
|
result.put("bridge_id", params.get("bridgeId"));
|
|
return result;
|
|
}
|
|
|
|
public List<Map<String, Object>> getDataLinkList(int relationshipId,
|
|
String companyCode) {
|
|
Map<String, Object> params = new HashMap<>();
|
|
params.put("relationshipId", relationshipId);
|
|
params.put("companyCode", companyCode);
|
|
return sqlSession.selectList("dataflow.getDataLinkList", params);
|
|
}
|
|
|
|
@Transactional
|
|
public void deleteDataLink(int bridgeId, String companyCode, String deletedBy) {
|
|
Map<String, Object> params = new HashMap<>();
|
|
params.put("bridgeId", bridgeId);
|
|
params.put("companyCode", companyCode);
|
|
sqlSession.update("dataflow.softDeleteDataLink", params);
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════════
|
|
// dynamic table data
|
|
// ═══════════════════════════════════════════════════════════════════
|
|
|
|
public Map<String, Object> getTableDataList(String tableName,
|
|
int page, int limit,
|
|
String search, String searchColumn,
|
|
String companyCode) {
|
|
// 안전성 검증 (SQL 인젝션 방지)
|
|
if (!SAFE_IDENTIFIER.matcher(tableName).matches()) {
|
|
throw new IllegalArgumentException("유효하지 않은 테이블명입니다.");
|
|
}
|
|
if (searchColumn != null && !searchColumn.isEmpty()
|
|
&& !SAFE_IDENTIFIER.matcher(searchColumn).matches()) {
|
|
throw new IllegalArgumentException("유효하지 않은 컬럼명입니다.");
|
|
}
|
|
|
|
// 테이블 존재 확인
|
|
Map<String, Object> checkParams = new HashMap<>();
|
|
checkParams.put("tableName", tableName.toLowerCase());
|
|
List<Map<String, Object>> tableInfo = sqlSession.selectList("dataflow.checkTableExists", checkParams);
|
|
if (tableInfo == null || tableInfo.isEmpty()) {
|
|
throw new IllegalArgumentException("테이블 '" + tableName + "'이 존재하지 않습니다.");
|
|
}
|
|
|
|
Map<String, Object> params = new HashMap<>();
|
|
params.put("tableName", tableName);
|
|
params.put("limit", limit);
|
|
params.put("offset", (page - 1) * limit);
|
|
if (search != null && !search.isEmpty()
|
|
&& searchColumn != null && !searchColumn.isEmpty()) {
|
|
params.put("search", search);
|
|
params.put("searchColumn", searchColumn);
|
|
}
|
|
|
|
Number totalNum = sqlSession.selectOne("dataflow.countTableData", params);
|
|
int total = totalNum != null ? totalNum.intValue() : 0;
|
|
List<Map<String, Object>> data = sqlSession.selectList("dataflow.getTableDataList", params);
|
|
int totalPages = (int) Math.ceil((double) total / limit);
|
|
|
|
Map<String, Object> pagination = new LinkedHashMap<>();
|
|
pagination.put("page", page);
|
|
pagination.put("limit", limit);
|
|
pagination.put("total", total);
|
|
pagination.put("totalPages", totalPages);
|
|
pagination.put("hasNext", page < totalPages);
|
|
pagination.put("hasPrev", page > 1);
|
|
|
|
Map<String, Object> result = new LinkedHashMap<>();
|
|
result.put("data", data);
|
|
result.put("pagination", pagination);
|
|
return result;
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════════
|
|
// diagram operations
|
|
// ═══════════════════════════════════════════════════════════════════
|
|
|
|
public Map<String, Object> getDiagramList(String companyCode,
|
|
int page, int size,
|
|
String searchTerm) {
|
|
Map<String, Object> params = new HashMap<>();
|
|
params.put("companyCode", companyCode);
|
|
if (searchTerm != null && !searchTerm.isEmpty()) {
|
|
params.put("searchTerm", searchTerm);
|
|
}
|
|
|
|
List<Map<String, Object>> rows = sqlSession.selectList("dataflow.getRelationshipForDiagramList", params);
|
|
|
|
// diagram_id 기준으로 그룹화
|
|
Map<Integer, Map<String, Object>> diagramMap = new LinkedHashMap<>();
|
|
Map<Integer, Set<String>> tableSetMap = new HashMap<>();
|
|
|
|
for (Map<String, Object> rel : rows) {
|
|
int diagramId = toInt(rel.get("diagram_id"));
|
|
if (diagramId == 0) continue;
|
|
|
|
if (!diagramMap.containsKey(diagramId)) {
|
|
Map<String, Object> d = new LinkedHashMap<>();
|
|
d.put("diagramId", diagramId);
|
|
d.put("diagramName", rel.get("relationship_name"));
|
|
d.put("connectionType", rel.get("connection_type"));
|
|
d.put("relationshipType", rel.get("relationship_type"));
|
|
d.put("relationshipCount", 0);
|
|
d.put("createdAt", rel.get("created_date"));
|
|
d.put("createdBy", rel.get("created_by"));
|
|
d.put("updatedAt", rel.get("updated_date"));
|
|
d.put("updatedBy", rel.get("updated_by"));
|
|
diagramMap.put(diagramId, d);
|
|
tableSetMap.put(diagramId, new LinkedHashSet<>());
|
|
}
|
|
|
|
Map<String, Object> d = diagramMap.get(diagramId);
|
|
Set<String> tables = tableSetMap.get(diagramId);
|
|
|
|
d.put("relationshipCount", toInt(d.get("relationshipCount")) + 1);
|
|
if (rel.get("from_table_name") != null) tables.add(rel.get("from_table_name").toString());
|
|
if (rel.get("to_table_name") != null) tables.add(rel.get("to_table_name").toString());
|
|
}
|
|
|
|
// tableCount / tables 설정
|
|
diagramMap.forEach((id, d) -> {
|
|
Set<String> tables = tableSetMap.get(id);
|
|
d.put("tableCount", tables.size());
|
|
d.put("tables", new ArrayList<>(tables));
|
|
});
|
|
|
|
List<Map<String, Object>> allDiagrams = new ArrayList<>(diagramMap.values());
|
|
int total = allDiagrams.size();
|
|
int offset = (page - 1) * size;
|
|
int end = Math.min(offset + size, total);
|
|
List<Map<String, Object>> pageData = (offset < total)
|
|
? allDiagrams.subList(offset, end)
|
|
: new ArrayList<>();
|
|
|
|
Map<String, Object> result = new LinkedHashMap<>();
|
|
result.put("diagrams", pageData);
|
|
result.put("total", total);
|
|
result.put("page", page);
|
|
result.put("size", size);
|
|
result.put("totalPages", (int) Math.ceil((double) total / size));
|
|
return result;
|
|
}
|
|
|
|
public List<Map<String, Object>> getDiagramRelationshipList(String companyCode,
|
|
String diagramName) {
|
|
Map<String, Object> params = new HashMap<>();
|
|
params.put("companyCode", companyCode);
|
|
params.put("diagramName", diagramName);
|
|
return sqlSession.selectList("dataflow.getDiagramRelationshipListByName", params);
|
|
}
|
|
|
|
public List<Map<String, Object>> getDiagramRelationshipListByDiagramId(String companyCode,
|
|
int diagramId) {
|
|
Map<String, Object> params = new HashMap<>();
|
|
params.put("companyCode", companyCode);
|
|
params.put("diagramId", diagramId);
|
|
return sqlSession.selectList("dataflow.getDiagramRelationshipListByDiagramId", params);
|
|
}
|
|
|
|
public List<Map<String, Object>> getDiagramRelationshipListByRelationshipId(String companyCode,
|
|
int relationshipId) {
|
|
Map<String, Object> params = new HashMap<>();
|
|
params.put("companyCode", companyCode);
|
|
params.put("relationshipId", relationshipId);
|
|
return sqlSession.selectList("dataflow.getDiagramRelationshipListByRelationshipId", params);
|
|
}
|
|
|
|
@Transactional
|
|
public String copyDiagram(String companyCode, String diagramName) {
|
|
// 원본 조회
|
|
Map<String, Object> queryParams = new HashMap<>();
|
|
queryParams.put("companyCode", companyCode);
|
|
queryParams.put("diagramName", diagramName);
|
|
List<Map<String, Object>> existing = sqlSession.selectList("dataflow.getDiagramRelationshipListByName", queryParams);
|
|
|
|
if (existing == null || existing.isEmpty()) {
|
|
throw new IllegalArgumentException("복사할 관계도를 찾을 수 없습니다.");
|
|
}
|
|
|
|
// 새 diagram_id 생성
|
|
Map<String, Object> maxParams = new HashMap<>();
|
|
maxParams.put("companyCode", companyCode);
|
|
Map<String, Object> maxRow = sqlSession.selectOne("dataflow.getMaxDiagramId", maxParams);
|
|
int newDiagramId = toInt(maxRow != null ? maxRow.get("max_diagram_id") : null) + 1;
|
|
|
|
String newDiagramName = diagramName + " Copy";
|
|
|
|
for (Map<String, Object> rel : existing) {
|
|
Map<String, Object> insertParams = new HashMap<>();
|
|
insertParams.put("diagramId", newDiagramId);
|
|
insertParams.put("relationshipName", newDiagramName);
|
|
insertParams.put("fromTableName", rel.get("from_table_name"));
|
|
insertParams.put("fromColumnName", rel.get("from_column_name"));
|
|
insertParams.put("toTableName", rel.get("to_table_name"));
|
|
insertParams.put("toColumnName", rel.get("to_column_name"));
|
|
insertParams.put("relationshipType", rel.get("relationship_type"));
|
|
insertParams.put("connectionType", rel.get("connection_type"));
|
|
insertParams.put("companyCode", companyCode);
|
|
insertParams.put("settings", toJsonString(rel.get("settings")));
|
|
insertParams.put("createdBy", "system");
|
|
sqlSession.insert("dataflow.insertTableRelationship", insertParams);
|
|
}
|
|
|
|
return newDiagramName;
|
|
}
|
|
|
|
@Transactional
|
|
public int deleteDiagram(String companyCode, String diagramName) {
|
|
Map<String, Object> queryParams = new HashMap<>();
|
|
queryParams.put("companyCode", companyCode);
|
|
queryParams.put("diagramName", diagramName);
|
|
List<Map<String, Object>> existing = sqlSession.selectList("dataflow.getDiagramRelationshipListByName", queryParams);
|
|
|
|
if (existing == null || existing.isEmpty()) return 0;
|
|
|
|
int diagramId = toInt(existing.get(0).get("diagram_id"));
|
|
|
|
Map<String, Object> deleteParams = new HashMap<>();
|
|
deleteParams.put("diagramId", diagramId);
|
|
deleteParams.put("companyCode", companyCode);
|
|
sqlSession.update("dataflow.softDeleteDiagramByDiagramId", deleteParams);
|
|
|
|
return existing.size();
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════════
|
|
// private helpers
|
|
// ═══════════════════════════════════════════════════════════════════
|
|
|
|
private String toJsonString(Object obj) {
|
|
if (obj == null) return null;
|
|
if (obj instanceof String s) return s;
|
|
try {
|
|
return objectMapper.writeValueAsString(obj);
|
|
} catch (JsonProcessingException e) {
|
|
log.warn("JSON 직렬화 실패: {}", e.getMessage());
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private void copyIfPresent(Map<String, Object> src, Map<String, Object> dest, String key) {
|
|
if (src.containsKey(key)) dest.put(key, src.get(key));
|
|
}
|
|
|
|
private int toInt(Object val) {
|
|
if (val == null) return 0;
|
|
try { return Integer.parseInt(val.toString()); } catch (NumberFormatException e) { return 0; }
|
|
}
|
|
|
|
private String str(Object val) {
|
|
return val == null ? null : val.toString();
|
|
}
|
|
}
|