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

468 lines
25 KiB
Java

package com.erp.service;
import com.erp.common.BaseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
@Service
@Slf4j
public class MultilangService extends BaseService {
private static final String NS = "multilang.";
public List<Map<String, Object>> getLanguages() {
return sqlSession.selectList(NS + "getMultilangLanguageList", new HashMap<>());
}
public Map<String, Object> createLanguage(Map<String, Object> params) {
String langCode = str(params.get("lang_code"));
Map<String, Object> check = Map.of("lang_code", langCode);
if (sqlSession.selectOne(NS + "getMultilangLanguageInfo", check) != null) {
throw new IllegalArgumentException("이미 존재하는 언어 코드입니다: " + langCode);
}
if (params.get("is_active") == null) params.put("is_active", "Y");
if (params.get("sort_order") == null) params.put("sort_order", 0);
sqlSession.insert(NS + "insertMultilangLanguage", params);
return sqlSession.selectOne(NS + "getMultilangLanguageInfo", check);
}
public Map<String, Object> updateLanguage(String langCode, Map<String, Object> params) {
params.put("lang_code", langCode);
Map<String, Object> check = Map.of("lang_code", langCode);
if (sqlSession.selectOne(NS + "getMultilangLanguageInfo", check) == null) {
throw new IllegalArgumentException("언어를 찾을 수 없습니다: " + langCode);
}
sqlSession.update(NS + "updateMultilangLanguage", params);
return sqlSession.selectOne(NS + "getMultilangLanguageInfo", check);
}
public String toggleLanguage(String langCode) {
Map<String, Object> check = Map.of("lang_code", langCode);
Map<String, Object> current = sqlSession.selectOne(NS + "getMultilangLanguageInfo", check);
if (current == null) {
throw new IllegalArgumentException("언어를 찾을 수 없습니다: " + langCode);
}
String newStatus = "Y".equals(current.get("is_active")) ? "N" : "Y";
Map<String, Object> params = new HashMap<>();
params.put("lang_code", langCode);
params.put("new_status", newStatus);
params.put("updated_by", "system");
sqlSession.update(NS + "updateMultilangLanguageStatus", params);
return "Y".equals(newStatus) ? "활성화" : "비활성화";
}
@Transactional
public void deleteLanguage(String langCode) {
Map<String, Object> check = Map.of("lang_code", langCode);
if (sqlSession.selectOne(NS + "getMultilangLanguageInfo", check) == null) {
throw new IllegalArgumentException("언어를 찾을 수 없습니다: " + langCode);
}
Map<String, Object> params = Map.of("lang_code", langCode);
sqlSession.delete(NS + "deleteMultilangTextByLangCode", params);
sqlSession.delete(NS + "deleteMultilangLanguage", params);
}
public List<Map<String, Object>> getLangKeys(Map<String, Object> params) {
if (params.containsKey("company_code")) {
params.put("filter_company_code", params.get("company_code"));
}
return sqlSession.selectList(NS + "getMultilangKeyList", params);
}
public List<Map<String, Object>> getLangTexts(int keyId) {
return sqlSession.selectList(NS + "getMultilangTextList", Map.of("key_id", keyId));
}
public int createLangKey(Map<String, Object> params) {
String companyCode = str(params.get("company_code"));
String langKey = str(params.get("lang_key"));
Map<String, Object> dupCheck = Map.of("company_code", companyCode, "lang_key", langKey);
if (sqlSession.selectOne(NS + "getMultilangKeyByCompanyAndKey", dupCheck) != null) {
throw new IllegalArgumentException("동일한 회사에 이미 존재하는 언어키입니다: " + langKey);
}
if (params.get("is_active") == null) params.put("is_active", "Y");
sqlSession.insert(NS + "insertMultilangKey", params);
return toInt(params.get("key_id"));
}
public void updateLangKey(int keyId, Map<String, Object> params) {
params.put("key_id", keyId);
if (sqlSession.selectOne(NS + "getMultilangKeyInfo", Map.of("key_id", keyId)) == null) {
throw new IllegalArgumentException("다국어 키를 찾을 수 없습니다: " + keyId);
}
String companyCode = str(params.get("company_code"));
String langKey = str(params.get("lang_key"));
if (!companyCode.isEmpty() && !langKey.isEmpty()) {
Map<String, Object> dupCheck = Map.of("company_code", companyCode, "lang_key", langKey, "key_id", keyId);
if (sqlSession.selectOne(NS + "getMultilangKeyByCompanyAndKeyExclude", dupCheck) != null) {
throw new IllegalArgumentException("동일한 회사에 이미 존재하는 언어키입니다: " + langKey);
}
}
sqlSession.update(NS + "updateMultilangKey", params);
}
@Transactional
public void deleteLangKey(int keyId) {
if (sqlSession.selectOne(NS + "getMultilangKeyInfo", Map.of("key_id", keyId)) == null) {
throw new IllegalArgumentException("다국어 키를 찾을 수 없습니다: " + keyId);
}
Map<String, Object> params = Map.of("key_id", keyId);
sqlSession.delete(NS + "deleteMultilangTextByKeyId", params);
sqlSession.delete(NS + "deleteMultilangKey", params);
}
public String toggleLangKey(int keyId) {
Map<String, Object> current = sqlSession.selectOne(NS + "getMultilangKeyInfo", Map.of("key_id", keyId));
if (current == null) {
throw new IllegalArgumentException("다국어 키를 찾을 수 없습니다: " + keyId);
}
String newStatus = "Y".equals(current.get("is_active")) ? "N" : "Y";
Map<String, Object> params = new HashMap<>();
params.put("key_id", keyId);
params.put("new_status", newStatus);
params.put("updated_by", "system");
sqlSession.update(NS + "updateMultilangKeyStatus", params);
return "Y".equals(newStatus) ? "활성화" : "비활성화";
}
@Transactional
public void saveLangTexts(int keyId, List<Map<String, Object>> texts) {
if (sqlSession.selectOne(NS + "getMultilangKeyInfo", Map.of("key_id", keyId)) == null) {
throw new IllegalArgumentException("다국어 키를 찾을 수 없습니다: " + keyId);
}
sqlSession.delete(NS + "deleteMultilangTextByKeyId", Map.of("key_id", keyId));
for (Map<String, Object> text : texts) {
Map<String, Object> params = new HashMap<>(text);
params.put("key_id", keyId);
if (params.get("is_active") == null) params.put("is_active", "Y");
sqlSession.insert(NS + "insertMultilangText", params);
}
}
public String getUserText(String companyCode, String menuCode, String langKey, String userLang) {
Map<String, Object> params = Map.of(
"company_code", companyCode, "menu_code", menuCode,
"lang_key", langKey, "user_lang", userLang);
Map<String, Object> result = sqlSession.selectOne(NS + "getMultilangUserText", params);
return result != null ? str(result.get("lang_text")) : langKey;
}
public String getLangText(String companyCode, String langKey, String langCode) {
Map<String, Object> params = Map.of(
"company_code", companyCode, "lang_key", langKey, "lang_code", langCode);
Map<String, Object> result = sqlSession.selectOne(NS + "getMultilangSingleText", params);
return result != null ? str(result.get("lang_text")) : langKey;
}
public Map<String, String> getBatchTranslations(String companyCode, String menuCode,
String userLang, List<String> langKeys) {
if (langKeys == null || langKeys.isEmpty()) return new LinkedHashMap<>();
Map<String, Object> params = new HashMap<>();
params.put("company_code", companyCode);
params.put("menu_code", menuCode);
params.put("user_lang", userLang);
params.put("lang_keys", langKeys);
List<Map<String, Object>> translations = sqlSession.selectList(NS + "getMultilangBatchTranslationList", params);
Map<String, String> result = new LinkedHashMap<>();
Set<String> processed = new HashSet<>();
for (Map<String, Object> t : translations) {
String key = str(t.get("lang_key"));
if (langKeys.contains(key) && !processed.contains(key)) {
result.put(key, str(t.get("lang_text")));
processed.add(key);
}
}
List<String> missingKeys = langKeys.stream().filter(k -> !processed.contains(k)).toList();
if (!missingKeys.isEmpty() && !"KR".equals(userLang)) {
Map<String, Object> fallbackParams = new HashMap<>();
fallbackParams.put("company_code", companyCode);
fallbackParams.put("missing_keys", missingKeys);
List<Map<String, Object>> fallback = sqlSession.selectList(NS + "getMultilangFallbackTranslationList", fallbackParams);
Set<String> fbProcessed = new HashSet<>();
for (Map<String, Object> t : fallback) {
String key = str(t.get("lang_key"));
if (!result.containsKey(key) && !fbProcessed.contains(key)) {
result.put(key, str(t.get("lang_text")));
fbProcessed.add(key);
}
}
}
for (String key : langKeys) result.putIfAbsent(key, key);
return result;
}
public List<Map<String, Object>> getCategories() {
List<Map<String, Object>> flat = sqlSession.selectList(NS + "getMultilangCategoryList", new HashMap<>());
return buildCategoryTree(flat);
}
public Map<String, Object> getCategoryById(int categoryId) {
return sqlSession.selectOne(NS + "getMultilangCategoryInfo", Map.of("category_id", categoryId));
}
public List<Map<String, Object>> getCategoryPath(int categoryId) {
return sqlSession.selectList(NS + "getMultilangCategoryPath", Map.of("category_id", categoryId));
}
private List<Map<String, Object>> buildCategoryTree(List<Map<String, Object>> flat) {
Map<Integer, Map<String, Object>> byId = new LinkedHashMap<>();
for (Map<String, Object> cat : flat) {
Map<String, Object> node = new LinkedHashMap<>(cat);
node.put("children", new ArrayList<>());
byId.put(toInt(cat.get("category_id")), node);
}
List<Map<String, Object>> roots = new ArrayList<>();
for (Map<String, Object> node : byId.values()) {
Object parentIdObj = node.get("parent_id");
if (parentIdObj == null) {
roots.add(node);
} else {
int parentId = toInt(parentIdObj);
Map<String, Object> parent = byId.get(parentId);
if (parent != null) {
@SuppressWarnings("unchecked")
List<Map<String, Object>> children = (List<Map<String, Object>>) parent.get("children");
children.add(node);
} else {
roots.add(node);
}
}
}
return roots;
}
@Transactional
public int generateKey(Map<String, Object> params) {
int categoryId = toInt(params.get("category_id"));
String keyMeaning = str(params.get("key_meaning"));
String companyCode = str(params.get("company_code"));
List<Map<String, Object>> categoryPath = getCategoryPath(categoryId);
if (categoryPath.isEmpty()) throw new IllegalArgumentException("존재하지 않는 카테고리입니다");
String langKey = buildLangKey(categoryPath, keyMeaning);
if (sqlSession.selectOne(NS + "getMultilangKeyByCompanyAndKey", Map.of("company_code", companyCode, "lang_key", langKey)) != null) {
throw new IllegalArgumentException("이미 존재하는 키입니다: " + langKey);
}
Map<String, Object> insertParams = new HashMap<>();
insertParams.put("company_code", companyCode);
insertParams.put("lang_key", langKey);
insertParams.put("category_id", categoryId);
insertParams.put("key_meaning", keyMeaning);
insertParams.put("usage_note", params.get("usage_note"));
insertParams.put("description", params.get("usage_note"));
insertParams.put("created_by", params.getOrDefault("created_by", "system"));
sqlSession.insert(NS + "insertMultilangKeyWithCategory", insertParams);
int keyId = toInt(insertParams.get("key_id"));
@SuppressWarnings("unchecked")
List<Map<String, Object>> texts = (List<Map<String, Object>>) params.get("texts");
if (texts != null) {
for (Map<String, Object> text : texts) {
Map<String, Object> tp = new HashMap<>(text);
tp.put("key_id", keyId);
if (tp.get("is_active") == null) tp.put("is_active", "Y");
if (tp.get("created_by") == null) tp.put("created_by", params.getOrDefault("created_by", "system"));
if (tp.get("updated_by") == null) tp.put("updated_by", params.getOrDefault("created_by", "system"));
sqlSession.insert(NS + "insertMultilangText", tp);
}
}
return keyId;
}
public Map<String, Object> previewKey(int categoryId, String keyMeaning, String companyCode) {
List<Map<String, Object>> categoryPath = getCategoryPath(categoryId);
if (categoryPath.isEmpty()) throw new IllegalArgumentException("존재하지 않는 카테고리입니다");
String langKey = buildLangKey(categoryPath, keyMeaning);
Map<String, Object> commonKey = sqlSession.selectOne(NS + "getMultilangKeyByCompanyAndKey", Map.of("company_code", "*", "lang_key", langKey));
Map<String, Object> companyKey = sqlSession.selectOne(NS + "getMultilangKeyByCompanyAndKey", Map.of("company_code", companyCode, "lang_key", langKey));
Map<String, Object> result = new LinkedHashMap<>();
result.put("lang_key", langKey);
result.put("exists", companyKey != null);
result.put("is_override", commonKey != null && companyKey == null);
if (commonKey != null) result.put("base_key_id", toInt(commonKey.get("key_id")));
return result;
}
@Transactional
public int createOverrideKey(Map<String, Object> params) {
int baseKeyId = toInt(params.get("base_key_id"));
String companyCode = str(params.get("company_code"));
Map<String, Object> baseKey = sqlSession.selectOne(NS + "getMultilangBaseKeyInfo", Map.of("key_id", baseKeyId));
if (baseKey == null) throw new IllegalArgumentException("원본 키를 찾을 수 없습니다");
if (!"*".equals(str(baseKey.get("company_code")))) throw new IllegalArgumentException("공통 키(*)만 오버라이드 할 수 있습니다");
String langKey = str(baseKey.get("lang_key"));
if (sqlSession.selectOne(NS + "getMultilangKeyByCompanyAndKey", Map.of("company_code", companyCode, "lang_key", langKey)) != null) {
throw new IllegalArgumentException("이미 해당 회사의 오버라이드 키가 존재합니다");
}
Map<String, Object> insertParams = new HashMap<>();
insertParams.put("company_code", companyCode);
insertParams.put("lang_key", langKey);
insertParams.put("category_id", baseKey.get("category_id"));
insertParams.put("key_meaning", baseKey.get("key_meaning"));
insertParams.put("base_key_id", baseKeyId);
insertParams.put("created_by", params.getOrDefault("created_by", "system"));
sqlSession.insert(NS + "insertMultilangOverrideKey", insertParams);
int keyId = toInt(insertParams.get("key_id"));
@SuppressWarnings("unchecked")
List<Map<String, Object>> texts = (List<Map<String, Object>>) params.get("texts");
if (texts != null) {
for (Map<String, Object> text : texts) {
Map<String, Object> tp = new HashMap<>(text);
tp.put("key_id", keyId);
if (tp.get("is_active") == null) tp.put("is_active", "Y");
if (tp.get("created_by") == null) tp.put("created_by", params.getOrDefault("created_by", "system"));
if (tp.get("updated_by") == null) tp.put("updated_by", params.getOrDefault("created_by", "system"));
sqlSession.insert(NS + "insertMultilangText", tp);
}
}
return keyId;
}
public List<Map<String, Object>> getOverrideKeys(String companyCode) {
return sqlSession.selectList(NS + "getMultilangOverrideKeyList", Map.of("company_code", companyCode));
}
@Transactional
public List<Map<String, Object>> generateScreenLabelKeys(Map<String, Object> params) {
int screenId = toInt(params.get("screen_id"));
Map<String, Object> screenInfo = sqlSession.selectOne(NS + "getMultilangScreenCompanyCode", Map.of("screen_id", screenId));
String companyCode = (screenInfo != null && !str(screenInfo.get("company_code")).isEmpty())
? str(screenInfo.get("company_code")) : str(params.getOrDefault("company_code", "*"));
String companyName;
if ("*".equals(companyCode)) {
companyName = "공통";
} else {
Map<String, Object> companyInfo = sqlSession.selectOne(NS + "getMultilangCompanyName", Map.of("company_code", companyCode));
companyName = (companyInfo != null) ? str(companyInfo.get("company_name")) : companyCode;
}
List<String> groupPath = getScreenGroupPath(screenId);
int categoryId = ensureScreenGroupCategory(companyCode, companyName, groupPath);
List<Map<String, Object>> categoryPath = getCategoryPath(categoryId);
List<String> keyPrefixParts = new ArrayList<>();
for (Map<String, Object> c : categoryPath) keyPrefixParts.add(str(c.get("key_prefix")));
@SuppressWarnings("unchecked")
List<Map<String, Object>> labels = (List<Map<String, Object>>) params.get("labels");
List<Map<String, Object>> results = new ArrayList<>();
for (Map<String, Object> labelInfo : labels) {
String label = str(labelInfo.get("label"));
String componentId = str(labelInfo.get("component_id"));
String keyMeaning = labelToKeyMeaning(label);
List<String> parts = new ArrayList<>(keyPrefixParts);
parts.add(keyMeaning);
String langKey = String.join(".", parts);
Map<String, Object> existing = sqlSession.selectOne(NS + "getMultilangKeyByCompanyAndKey", Map.of("company_code", companyCode, "lang_key", langKey));
int keyId;
if (existing != null) {
keyId = toInt(existing.get("key_id"));
} else {
Map<String, Object> insertParams = new HashMap<>();
insertParams.put("company_code", companyCode);
insertParams.put("lang_key", langKey);
insertParams.put("description", "화면 " + screenId + "" + str(labelInfo.getOrDefault("type", "라벨")) + ": " + label);
insertParams.put("is_active", "Y");
insertParams.put("category_id", categoryId);
insertParams.put("key_meaning", keyMeaning);
sqlSession.insert(NS + "insertMultilangKeyWithCategory", insertParams);
keyId = toInt(insertParams.get("key_id"));
sqlSession.update(NS + "upsertMultilangText", Map.of("key_id", keyId, "lang_code", "KR", "lang_text", label));
}
Map<String, Object> entry = new LinkedHashMap<>();
entry.put("component_id", componentId);
entry.put("key_id", keyId);
entry.put("lang_key", langKey);
results.add(entry);
}
return results;
}
private List<String> getScreenGroupPath(int screenId) {
Map<String, Object> groupRow = sqlSession.selectOne(NS + "getMultilangScreenGroupId", Map.of("screen_id", screenId));
if (groupRow == null) return Collections.emptyList();
int groupId = toInt(groupRow.get("group_id"));
List<Map<String, Object>> groups = sqlSession.selectList(NS + "getMultilangScreenGroupPath", Map.of("group_id", groupId));
List<String> path = new ArrayList<>();
for (Map<String, Object> g : groups) path.add(str(g.get("group_name")));
return path;
}
private int ensureScreenGroupCategory(String companyCode, String companyName, List<String> groupPath) {
if (groupPath.isEmpty()) return ensureCompanyCategory(companyCode, companyName);
int parentId = ensureCompanyCategory(companyCode, companyName);
int currentLevel = 3;
for (String groupName : groupPath) {
Map<String, Object> existing = sqlSession.selectOne(NS + "getMultilangCategoryByNameAndParent", Map.of("category_name", groupName, "parent_id", parentId));
if (existing != null) {
parentId = toInt(existing.get("category_id"));
} else {
Map<String, Object> insertParams = new HashMap<>();
insertParams.put("category_code", (companyCode + "_GROUP_" + groupName).replace("\\s+", "_"));
insertParams.put("category_name", groupName);
insertParams.put("parent_id", parentId);
insertParams.put("level", currentLevel);
insertParams.put("key_prefix", groupName.toLowerCase().replace("\\s+", "_"));
insertParams.put("description", groupName + " 화면 그룹의 다국어");
insertParams.put("sort_order", 0);
sqlSession.insert(NS + "insertMultilangCategory", insertParams);
parentId = toInt(insertParams.get("category_id"));
}
currentLevel++;
}
return parentId;
}
private int ensureCompanyCategory(String companyCode, String companyName) {
int screenRootId = ensureScreenRootCategory();
Map<String, Object> existing = sqlSession.selectOne(NS + "getMultilangCategoryByCodeAndParent", Map.of("category_code", companyCode, "parent_id", screenRootId));
if (existing != null) return toInt(existing.get("category_id"));
String displayName = "*".equals(companyCode) ? "공통" : companyName;
String keyPrefix = "*".equals(companyCode) ? "common" : companyCode.toLowerCase();
Map<String, Object> insertParams = new HashMap<>();
insertParams.put("category_code", companyCode);
insertParams.put("category_name", displayName);
insertParams.put("parent_id", screenRootId);
insertParams.put("level", 2);
insertParams.put("key_prefix", keyPrefix);
insertParams.put("description", displayName + " 회사의 화면 다국어");
insertParams.put("sort_order", "*".equals(companyCode) ? 0 : 10);
sqlSession.insert(NS + "insertMultilangCategory", insertParams);
return toInt(insertParams.get("category_id"));
}
private int ensureScreenRootCategory() {
Map<String, Object> existing = sqlSession.selectOne(NS + "getMultilangRootCategoryByCode", Map.of("category_code", "screen"));
if (existing != null) return toInt(existing.get("category_id"));
Map<String, Object> insertParams = new HashMap<>();
insertParams.put("category_code", "screen");
insertParams.put("category_name", "화면");
insertParams.put("parent_id", null);
insertParams.put("level", 1);
insertParams.put("key_prefix", "screen");
insertParams.put("description", "화면 디자이너에서 자동 생성된 다국어 키");
insertParams.put("sort_order", 100);
sqlSession.insert(NS + "insertMultilangCategory", insertParams);
return toInt(insertParams.get("category_id"));
}
private String buildLangKey(List<Map<String, Object>> categoryPath, String keyMeaning) {
List<String> parts = new ArrayList<>();
for (Map<String, Object> c : categoryPath) parts.add(str(c.get("key_prefix")));
parts.add(keyMeaning);
return String.join(".", parts);
}
private String labelToKeyMeaning(String label) {
if (label.matches("^[a-z][a-z0-9_]*$")) return label;
if (label.matches("^[A-Za-z][A-Za-z0-9 ]*$")) return label.toLowerCase().replace(" ", "_");
return label.replaceAll("[^\\w가-힣\\s]", "").replaceAll("\\s+", "_").toLowerCase();
}
private String str(Object o) { return o == null ? "" : o.toString(); }
private int toInt(Object o) {
if (o == null) return 0;
if (o instanceof Number) return ((Number) o).intValue();
try { return Integer.parseInt(o.toString()); } catch (NumberFormatException e) { return 0; }
}
}