feat: Phase 3.1 MultiLangService Raw Query 전환 완료
25개 Prisma 호출을 모두 Raw Query로 전환 - 언어 관리 (getLanguages, createLanguage, updateLanguage, toggleLanguage, deleteLanguage) - 다국어 키 관리 (getLangKeys, createLangKey, updateLangKey, deleteLangKey, toggleLangKey) - 다국어 텍스트 관리 (getLangTexts, saveLangTexts, getUserText, getLangText) - 배치 번역 조회 (getBatchTranslations) 주요 기술적 해결: - 동적 WHERE 조건 생성 (ILIKE 검색 지원) - 동적 UPDATE 쿼리 (변경된 필드만 업데이트) - 트랜잭션 처리 (transaction 함수 사용) - JOIN 쿼리 (multi_lang_text + multi_lang_key_master) - IN 절 동적 파라미터 바인딩 (배치 번역) TypeScript 컴파일 성공 (linter 에러 0개) Prisma import 완전 제거 Phase 3 진행률: 25/162 (15.4%) 전체 진행률: 276/444 (62.2%)
This commit is contained in:
parent
244c47db35
commit
284c67193d
|
|
@ -1,4 +1,4 @@
|
||||||
import { PrismaClient } from "@prisma/client";
|
import { query, queryOne, transaction } from "../database/db";
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import {
|
import {
|
||||||
Language,
|
Language,
|
||||||
|
|
@ -15,8 +15,6 @@ import {
|
||||||
ApiResponse,
|
ApiResponse,
|
||||||
} from "../types/multilang";
|
} from "../types/multilang";
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
|
||||||
|
|
||||||
export class MultiLangService {
|
export class MultiLangService {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
|
|
@ -27,25 +25,27 @@ export class MultiLangService {
|
||||||
try {
|
try {
|
||||||
logger.info("언어 목록 조회 시작");
|
logger.info("언어 목록 조회 시작");
|
||||||
|
|
||||||
const languages = await prisma.language_master.findMany({
|
const languages = await query<{
|
||||||
orderBy: [{ sort_order: "asc" }, { lang_code: "asc" }],
|
lang_code: string;
|
||||||
select: {
|
lang_name: string;
|
||||||
lang_code: true,
|
lang_native: string | null;
|
||||||
lang_name: true,
|
is_active: string | null;
|
||||||
lang_native: true,
|
sort_order: number | null;
|
||||||
is_active: true,
|
created_date: Date | null;
|
||||||
sort_order: true,
|
created_by: string | null;
|
||||||
created_date: true,
|
updated_date: Date | null;
|
||||||
created_by: true,
|
updated_by: string | null;
|
||||||
updated_date: true,
|
}>(
|
||||||
updated_by: true,
|
`SELECT lang_code, lang_name, lang_native, is_active, sort_order,
|
||||||
},
|
created_date, created_by, updated_date, updated_by
|
||||||
});
|
FROM language_master
|
||||||
|
ORDER BY sort_order ASC, lang_code ASC`
|
||||||
|
);
|
||||||
|
|
||||||
const mappedLanguages: Language[] = languages.map((lang) => ({
|
const mappedLanguages: Language[] = languages.map((lang) => ({
|
||||||
langCode: lang.lang_code,
|
langCode: lang.lang_code,
|
||||||
langName: lang.lang_name,
|
langName: lang.lang_name,
|
||||||
langNative: lang.lang_native,
|
langNative: lang.lang_native || "",
|
||||||
isActive: lang.is_active || "N",
|
isActive: lang.is_active || "N",
|
||||||
sortOrder: lang.sort_order ?? undefined,
|
sortOrder: lang.sort_order ?? undefined,
|
||||||
createdDate: lang.created_date || undefined,
|
createdDate: lang.created_date || undefined,
|
||||||
|
|
@ -72,9 +72,10 @@ export class MultiLangService {
|
||||||
logger.info("언어 생성 시작", { languageData });
|
logger.info("언어 생성 시작", { languageData });
|
||||||
|
|
||||||
// 중복 체크
|
// 중복 체크
|
||||||
const existingLanguage = await prisma.language_master.findUnique({
|
const existingLanguage = await queryOne<{ lang_code: string }>(
|
||||||
where: { lang_code: languageData.langCode },
|
`SELECT lang_code FROM language_master WHERE lang_code = $1`,
|
||||||
});
|
[languageData.langCode]
|
||||||
|
);
|
||||||
|
|
||||||
if (existingLanguage) {
|
if (existingLanguage) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
@ -83,30 +84,44 @@ export class MultiLangService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 언어 생성
|
// 언어 생성
|
||||||
const createdLanguage = await prisma.language_master.create({
|
const createdLanguage = await queryOne<{
|
||||||
data: {
|
lang_code: string;
|
||||||
lang_code: languageData.langCode,
|
lang_name: string;
|
||||||
lang_name: languageData.langName,
|
lang_native: string | null;
|
||||||
lang_native: languageData.langNative,
|
is_active: string | null;
|
||||||
is_active: languageData.isActive || "Y",
|
sort_order: number | null;
|
||||||
sort_order: languageData.sortOrder || 0,
|
created_date: Date | null;
|
||||||
created_by: languageData.createdBy || "system",
|
created_by: string | null;
|
||||||
updated_by: languageData.updatedBy || "system",
|
updated_date: Date | null;
|
||||||
},
|
updated_by: string | null;
|
||||||
});
|
}>(
|
||||||
|
`INSERT INTO language_master
|
||||||
|
(lang_code, lang_name, lang_native, is_active, sort_order, created_by, updated_by)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
|
RETURNING *`,
|
||||||
|
[
|
||||||
|
languageData.langCode,
|
||||||
|
languageData.langName,
|
||||||
|
languageData.langNative,
|
||||||
|
languageData.isActive || "Y",
|
||||||
|
languageData.sortOrder || 0,
|
||||||
|
languageData.createdBy || "system",
|
||||||
|
languageData.updatedBy || "system",
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
logger.info("언어 생성 완료", { langCode: createdLanguage.lang_code });
|
logger.info("언어 생성 완료", { langCode: createdLanguage!.lang_code });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
langCode: createdLanguage.lang_code,
|
langCode: createdLanguage!.lang_code,
|
||||||
langName: createdLanguage.lang_name,
|
langName: createdLanguage!.lang_name,
|
||||||
langNative: createdLanguage.lang_native,
|
langNative: createdLanguage!.lang_native || "",
|
||||||
isActive: createdLanguage.is_active || "N",
|
isActive: createdLanguage!.is_active || "N",
|
||||||
sortOrder: createdLanguage.sort_order ?? undefined,
|
sortOrder: createdLanguage!.sort_order ?? undefined,
|
||||||
createdDate: createdLanguage.created_date || undefined,
|
createdDate: createdLanguage!.created_date || undefined,
|
||||||
createdBy: createdLanguage.created_by || undefined,
|
createdBy: createdLanguage!.created_by || undefined,
|
||||||
updatedDate: createdLanguage.updated_date || undefined,
|
updatedDate: createdLanguage!.updated_date || undefined,
|
||||||
updatedBy: createdLanguage.updated_by || undefined,
|
updatedBy: createdLanguage!.updated_by || undefined,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("언어 생성 중 오류 발생:", error);
|
logger.error("언어 생성 중 오류 발생:", error);
|
||||||
|
|
@ -127,42 +142,72 @@ export class MultiLangService {
|
||||||
logger.info("언어 수정 시작", { langCode, languageData });
|
logger.info("언어 수정 시작", { langCode, languageData });
|
||||||
|
|
||||||
// 기존 언어 확인
|
// 기존 언어 확인
|
||||||
const existingLanguage = await prisma.language_master.findUnique({
|
const existingLanguage = await queryOne<{ lang_code: string }>(
|
||||||
where: { lang_code: langCode },
|
`SELECT lang_code FROM language_master WHERE lang_code = $1`,
|
||||||
});
|
[langCode]
|
||||||
|
);
|
||||||
|
|
||||||
if (!existingLanguage) {
|
if (!existingLanguage) {
|
||||||
throw new Error(`언어를 찾을 수 없습니다: ${langCode}`);
|
throw new Error(`언어를 찾을 수 없습니다: ${langCode}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 동적 UPDATE 쿼리 생성
|
||||||
|
const updates: string[] = [];
|
||||||
|
const values: any[] = [];
|
||||||
|
let paramIndex = 1;
|
||||||
|
|
||||||
|
if (languageData.langName) {
|
||||||
|
updates.push(`lang_name = $${paramIndex++}`);
|
||||||
|
values.push(languageData.langName);
|
||||||
|
}
|
||||||
|
if (languageData.langNative) {
|
||||||
|
updates.push(`lang_native = $${paramIndex++}`);
|
||||||
|
values.push(languageData.langNative);
|
||||||
|
}
|
||||||
|
if (languageData.isActive) {
|
||||||
|
updates.push(`is_active = $${paramIndex++}`);
|
||||||
|
values.push(languageData.isActive);
|
||||||
|
}
|
||||||
|
if (languageData.sortOrder !== undefined) {
|
||||||
|
updates.push(`sort_order = $${paramIndex++}`);
|
||||||
|
values.push(languageData.sortOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
updates.push(`updated_by = $${paramIndex++}`);
|
||||||
|
values.push(languageData.updatedBy || "system");
|
||||||
|
|
||||||
|
values.push(langCode); // WHERE 조건용
|
||||||
|
|
||||||
// 언어 수정
|
// 언어 수정
|
||||||
const updatedLanguage = await prisma.language_master.update({
|
const updatedLanguage = await queryOne<{
|
||||||
where: { lang_code: langCode },
|
lang_code: string;
|
||||||
data: {
|
lang_name: string;
|
||||||
...(languageData.langName && { lang_name: languageData.langName }),
|
lang_native: string | null;
|
||||||
...(languageData.langNative && {
|
is_active: string | null;
|
||||||
lang_native: languageData.langNative,
|
sort_order: number | null;
|
||||||
}),
|
created_date: Date | null;
|
||||||
...(languageData.isActive && { is_active: languageData.isActive }),
|
created_by: string | null;
|
||||||
...(languageData.sortOrder !== undefined && {
|
updated_date: Date | null;
|
||||||
sort_order: languageData.sortOrder,
|
updated_by: string | null;
|
||||||
}),
|
}>(
|
||||||
updated_by: languageData.updatedBy || "system",
|
`UPDATE language_master SET ${updates.join(", ")}
|
||||||
},
|
WHERE lang_code = $${paramIndex}
|
||||||
});
|
RETURNING *`,
|
||||||
|
values
|
||||||
|
);
|
||||||
|
|
||||||
logger.info("언어 수정 완료", { langCode });
|
logger.info("언어 수정 완료", { langCode });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
langCode: updatedLanguage.lang_code,
|
langCode: updatedLanguage!.lang_code,
|
||||||
langName: updatedLanguage.lang_name,
|
langName: updatedLanguage!.lang_name,
|
||||||
langNative: updatedLanguage.lang_native,
|
langNative: updatedLanguage!.lang_native || "",
|
||||||
isActive: updatedLanguage.is_active || "N",
|
isActive: updatedLanguage!.is_active || "N",
|
||||||
sortOrder: updatedLanguage.sort_order ?? undefined,
|
sortOrder: updatedLanguage!.sort_order ?? undefined,
|
||||||
createdDate: updatedLanguage.created_date || undefined,
|
createdDate: updatedLanguage!.created_date || undefined,
|
||||||
createdBy: updatedLanguage.created_by || undefined,
|
createdBy: updatedLanguage!.created_by || undefined,
|
||||||
updatedDate: updatedLanguage.updated_date || undefined,
|
updatedDate: updatedLanguage!.updated_date || undefined,
|
||||||
updatedBy: updatedLanguage.updated_by || undefined,
|
updatedBy: updatedLanguage!.updated_by || undefined,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("언어 수정 중 오류 발생:", error);
|
logger.error("언어 수정 중 오류 발생:", error);
|
||||||
|
|
@ -180,10 +225,10 @@ export class MultiLangService {
|
||||||
logger.info("언어 상태 토글 시작", { langCode });
|
logger.info("언어 상태 토글 시작", { langCode });
|
||||||
|
|
||||||
// 현재 언어 조회
|
// 현재 언어 조회
|
||||||
const currentLanguage = await prisma.language_master.findUnique({
|
const currentLanguage = await queryOne<{ is_active: string | null }>(
|
||||||
where: { lang_code: langCode },
|
`SELECT is_active FROM language_master WHERE lang_code = $1`,
|
||||||
select: { is_active: true },
|
[langCode]
|
||||||
});
|
);
|
||||||
|
|
||||||
if (!currentLanguage) {
|
if (!currentLanguage) {
|
||||||
throw new Error(`언어를 찾을 수 없습니다: ${langCode}`);
|
throw new Error(`언어를 찾을 수 없습니다: ${langCode}`);
|
||||||
|
|
@ -192,13 +237,12 @@ export class MultiLangService {
|
||||||
const newStatus = currentLanguage.is_active === "Y" ? "N" : "Y";
|
const newStatus = currentLanguage.is_active === "Y" ? "N" : "Y";
|
||||||
|
|
||||||
// 상태 업데이트
|
// 상태 업데이트
|
||||||
await prisma.language_master.update({
|
await query(
|
||||||
where: { lang_code: langCode },
|
`UPDATE language_master
|
||||||
data: {
|
SET is_active = $1, updated_by = $2
|
||||||
is_active: newStatus,
|
WHERE lang_code = $3`,
|
||||||
updated_by: "system",
|
[newStatus, "system", langCode]
|
||||||
},
|
);
|
||||||
});
|
|
||||||
|
|
||||||
const result = newStatus === "Y" ? "활성화" : "비활성화";
|
const result = newStatus === "Y" ? "활성화" : "비활성화";
|
||||||
logger.info("언어 상태 토글 완료", { langCode, result });
|
logger.info("언어 상태 토글 완료", { langCode, result });
|
||||||
|
|
@ -219,47 +263,54 @@ export class MultiLangService {
|
||||||
try {
|
try {
|
||||||
logger.info("다국어 키 목록 조회 시작", { params });
|
logger.info("다국어 키 목록 조회 시작", { params });
|
||||||
|
|
||||||
const whereConditions: any = {};
|
const whereConditions: string[] = [];
|
||||||
|
const values: any[] = [];
|
||||||
|
let paramIndex = 1;
|
||||||
|
|
||||||
// 회사 코드 필터
|
// 회사 코드 필터
|
||||||
if (params.companyCode) {
|
if (params.companyCode) {
|
||||||
whereConditions.company_code = params.companyCode;
|
whereConditions.push(`company_code = $${paramIndex++}`);
|
||||||
|
values.push(params.companyCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 메뉴 코드 필터
|
// 메뉴 코드 필터
|
||||||
if (params.menuCode) {
|
if (params.menuCode) {
|
||||||
whereConditions.menu_name = params.menuCode;
|
whereConditions.push(`menu_name = $${paramIndex++}`);
|
||||||
|
values.push(params.menuCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 검색 조건
|
// 검색 조건 (OR)
|
||||||
if (params.searchText) {
|
if (params.searchText) {
|
||||||
whereConditions.OR = [
|
whereConditions.push(
|
||||||
{ lang_key: { contains: params.searchText, mode: "insensitive" } },
|
`(lang_key ILIKE $${paramIndex} OR description ILIKE $${paramIndex} OR menu_name ILIKE $${paramIndex})`
|
||||||
{ description: { contains: params.searchText, mode: "insensitive" } },
|
);
|
||||||
{ menu_name: { contains: params.searchText, mode: "insensitive" } },
|
values.push(`%${params.searchText}%`);
|
||||||
];
|
paramIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
const langKeys = await prisma.multi_lang_key_master.findMany({
|
const whereClause = whereConditions.length > 0
|
||||||
where: whereConditions,
|
? `WHERE ${whereConditions.join(" AND ")}`
|
||||||
orderBy: [
|
: "";
|
||||||
{ company_code: "asc" },
|
|
||||||
{ menu_name: "asc" },
|
const langKeys = await query<{
|
||||||
{ lang_key: "asc" },
|
key_id: number;
|
||||||
],
|
company_code: string;
|
||||||
select: {
|
menu_name: string | null;
|
||||||
key_id: true,
|
lang_key: string;
|
||||||
company_code: true,
|
description: string | null;
|
||||||
menu_name: true,
|
is_active: string | null;
|
||||||
lang_key: true,
|
created_date: Date | null;
|
||||||
description: true,
|
created_by: string | null;
|
||||||
is_active: true,
|
updated_date: Date | null;
|
||||||
created_date: true,
|
updated_by: string | null;
|
||||||
created_by: true,
|
}>(
|
||||||
updated_date: true,
|
`SELECT key_id, company_code, menu_name, lang_key, description, is_active,
|
||||||
updated_by: true,
|
created_date, created_by, updated_date, updated_by
|
||||||
},
|
FROM multi_lang_key_master
|
||||||
});
|
${whereClause}
|
||||||
|
ORDER BY company_code ASC, menu_name ASC, lang_key ASC`,
|
||||||
|
values
|
||||||
|
);
|
||||||
|
|
||||||
const mappedKeys: LangKey[] = langKeys.map((key) => ({
|
const mappedKeys: LangKey[] = langKeys.map((key) => ({
|
||||||
keyId: key.key_id,
|
keyId: key.key_id,
|
||||||
|
|
@ -291,24 +342,24 @@ export class MultiLangService {
|
||||||
try {
|
try {
|
||||||
logger.info("다국어 텍스트 조회 시작", { keyId });
|
logger.info("다국어 텍스트 조회 시작", { keyId });
|
||||||
|
|
||||||
const langTexts = await prisma.multi_lang_text.findMany({
|
const langTexts = await query<{
|
||||||
where: {
|
text_id: number;
|
||||||
key_id: keyId,
|
key_id: number;
|
||||||
is_active: "Y",
|
lang_code: string;
|
||||||
},
|
lang_text: string;
|
||||||
orderBy: { lang_code: "asc" },
|
is_active: string | null;
|
||||||
select: {
|
created_date: Date | null;
|
||||||
text_id: true,
|
created_by: string | null;
|
||||||
key_id: true,
|
updated_date: Date | null;
|
||||||
lang_code: true,
|
updated_by: string | null;
|
||||||
lang_text: true,
|
}>(
|
||||||
is_active: true,
|
`SELECT text_id, key_id, lang_code, lang_text, is_active,
|
||||||
created_date: true,
|
created_date, created_by, updated_date, updated_by
|
||||||
created_by: true,
|
FROM multi_lang_text
|
||||||
updated_date: true,
|
WHERE key_id = $1 AND is_active = $2
|
||||||
updated_by: true,
|
ORDER BY lang_code ASC`,
|
||||||
},
|
[keyId, "Y"]
|
||||||
});
|
);
|
||||||
|
|
||||||
const mappedTexts: LangText[] = langTexts.map((text) => ({
|
const mappedTexts: LangText[] = langTexts.map((text) => ({
|
||||||
textId: text.text_id,
|
textId: text.text_id,
|
||||||
|
|
@ -340,12 +391,11 @@ export class MultiLangService {
|
||||||
logger.info("다국어 키 생성 시작", { keyData });
|
logger.info("다국어 키 생성 시작", { keyData });
|
||||||
|
|
||||||
// 중복 체크
|
// 중복 체크
|
||||||
const existingKey = await prisma.multi_lang_key_master.findFirst({
|
const existingKey = await queryOne<{ key_id: number }>(
|
||||||
where: {
|
`SELECT key_id FROM multi_lang_key_master
|
||||||
company_code: keyData.companyCode,
|
WHERE company_code = $1 AND lang_key = $2`,
|
||||||
lang_key: keyData.langKey,
|
[keyData.companyCode, keyData.langKey]
|
||||||
},
|
);
|
||||||
});
|
|
||||||
|
|
||||||
if (existingKey) {
|
if (existingKey) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
@ -354,24 +404,28 @@ export class MultiLangService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 다국어 키 생성
|
// 다국어 키 생성
|
||||||
const createdKey = await prisma.multi_lang_key_master.create({
|
const createdKey = await queryOne<{ key_id: number }>(
|
||||||
data: {
|
`INSERT INTO multi_lang_key_master
|
||||||
company_code: keyData.companyCode,
|
(company_code, menu_name, lang_key, description, is_active, created_by, updated_by)
|
||||||
menu_name: keyData.menuName || null,
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
lang_key: keyData.langKey,
|
RETURNING key_id`,
|
||||||
description: keyData.description || null,
|
[
|
||||||
is_active: keyData.isActive || "Y",
|
keyData.companyCode,
|
||||||
created_by: keyData.createdBy || "system",
|
keyData.menuName || null,
|
||||||
updated_by: keyData.updatedBy || "system",
|
keyData.langKey,
|
||||||
},
|
keyData.description || null,
|
||||||
});
|
keyData.isActive || "Y",
|
||||||
|
keyData.createdBy || "system",
|
||||||
|
keyData.updatedBy || "system",
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
logger.info("다국어 키 생성 완료", {
|
logger.info("다국어 키 생성 완료", {
|
||||||
keyId: createdKey.key_id,
|
keyId: createdKey!.key_id,
|
||||||
langKey: keyData.langKey,
|
langKey: keyData.langKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
return createdKey.key_id;
|
return createdKey!.key_id;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("다국어 키 생성 중 오류 발생:", error);
|
logger.error("다국어 키 생성 중 오류 발생:", error);
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
@ -391,9 +445,10 @@ export class MultiLangService {
|
||||||
logger.info("다국어 키 수정 시작", { keyId, keyData });
|
logger.info("다국어 키 수정 시작", { keyId, keyData });
|
||||||
|
|
||||||
// 기존 키 확인
|
// 기존 키 확인
|
||||||
const existingKey = await prisma.multi_lang_key_master.findUnique({
|
const existingKey = await queryOne<{ key_id: number }>(
|
||||||
where: { key_id: keyId },
|
`SELECT key_id FROM multi_lang_key_master WHERE key_id = $1`,
|
||||||
});
|
[keyId]
|
||||||
|
);
|
||||||
|
|
||||||
if (!existingKey) {
|
if (!existingKey) {
|
||||||
throw new Error(`다국어 키를 찾을 수 없습니다: ${keyId}`);
|
throw new Error(`다국어 키를 찾을 수 없습니다: ${keyId}`);
|
||||||
|
|
@ -401,13 +456,11 @@ export class MultiLangService {
|
||||||
|
|
||||||
// 중복 체크 (자신을 제외하고)
|
// 중복 체크 (자신을 제외하고)
|
||||||
if (keyData.companyCode && keyData.langKey) {
|
if (keyData.companyCode && keyData.langKey) {
|
||||||
const duplicateKey = await prisma.multi_lang_key_master.findFirst({
|
const duplicateKey = await queryOne<{ key_id: number }>(
|
||||||
where: {
|
`SELECT key_id FROM multi_lang_key_master
|
||||||
company_code: keyData.companyCode,
|
WHERE company_code = $1 AND lang_key = $2 AND key_id != $3`,
|
||||||
lang_key: keyData.langKey,
|
[keyData.companyCode, keyData.langKey, keyId]
|
||||||
key_id: { not: keyId },
|
);
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (duplicateKey) {
|
if (duplicateKey) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
@ -416,21 +469,39 @@ export class MultiLangService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 동적 UPDATE 쿼리 생성
|
||||||
|
const updates: string[] = [];
|
||||||
|
const values: any[] = [];
|
||||||
|
let paramIndex = 1;
|
||||||
|
|
||||||
|
if (keyData.companyCode) {
|
||||||
|
updates.push(`company_code = $${paramIndex++}`);
|
||||||
|
values.push(keyData.companyCode);
|
||||||
|
}
|
||||||
|
if (keyData.menuName !== undefined) {
|
||||||
|
updates.push(`menu_name = $${paramIndex++}`);
|
||||||
|
values.push(keyData.menuName);
|
||||||
|
}
|
||||||
|
if (keyData.langKey) {
|
||||||
|
updates.push(`lang_key = $${paramIndex++}`);
|
||||||
|
values.push(keyData.langKey);
|
||||||
|
}
|
||||||
|
if (keyData.description !== undefined) {
|
||||||
|
updates.push(`description = $${paramIndex++}`);
|
||||||
|
values.push(keyData.description);
|
||||||
|
}
|
||||||
|
|
||||||
|
updates.push(`updated_by = $${paramIndex++}`);
|
||||||
|
values.push(keyData.updatedBy || "system");
|
||||||
|
|
||||||
|
values.push(keyId); // WHERE 조건용
|
||||||
|
|
||||||
// 다국어 키 수정
|
// 다국어 키 수정
|
||||||
await prisma.multi_lang_key_master.update({
|
await query(
|
||||||
where: { key_id: keyId },
|
`UPDATE multi_lang_key_master SET ${updates.join(", ")}
|
||||||
data: {
|
WHERE key_id = $${paramIndex}`,
|
||||||
...(keyData.companyCode && { company_code: keyData.companyCode }),
|
values
|
||||||
...(keyData.menuName !== undefined && {
|
);
|
||||||
menu_name: keyData.menuName,
|
|
||||||
}),
|
|
||||||
...(keyData.langKey && { lang_key: keyData.langKey }),
|
|
||||||
...(keyData.description !== undefined && {
|
|
||||||
description: keyData.description,
|
|
||||||
}),
|
|
||||||
updated_by: keyData.updatedBy || "system",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.info("다국어 키 수정 완료", { keyId });
|
logger.info("다국어 키 수정 완료", { keyId });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -449,25 +520,28 @@ export class MultiLangService {
|
||||||
logger.info("다국어 키 삭제 시작", { keyId });
|
logger.info("다국어 키 삭제 시작", { keyId });
|
||||||
|
|
||||||
// 기존 키 확인
|
// 기존 키 확인
|
||||||
const existingKey = await prisma.multi_lang_key_master.findUnique({
|
const existingKey = await queryOne<{ key_id: number }>(
|
||||||
where: { key_id: keyId },
|
`SELECT key_id FROM multi_lang_key_master WHERE key_id = $1`,
|
||||||
});
|
[keyId]
|
||||||
|
);
|
||||||
|
|
||||||
if (!existingKey) {
|
if (!existingKey) {
|
||||||
throw new Error(`다국어 키를 찾을 수 없습니다: ${keyId}`);
|
throw new Error(`다국어 키를 찾을 수 없습니다: ${keyId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 트랜잭션으로 키와 연관된 텍스트 모두 삭제
|
// 트랜잭션으로 키와 연관된 텍스트 모두 삭제
|
||||||
await prisma.$transaction(async (tx) => {
|
await transaction(async (client) => {
|
||||||
// 관련된 다국어 텍스트 삭제
|
// 관련된 다국어 텍스트 삭제
|
||||||
await tx.multi_lang_text.deleteMany({
|
await client.query(
|
||||||
where: { key_id: keyId },
|
`DELETE FROM multi_lang_text WHERE key_id = $1`,
|
||||||
});
|
[keyId]
|
||||||
|
);
|
||||||
|
|
||||||
// 다국어 키 삭제
|
// 다국어 키 삭제
|
||||||
await tx.multi_lang_key_master.delete({
|
await client.query(
|
||||||
where: { key_id: keyId },
|
`DELETE FROM multi_lang_key_master WHERE key_id = $1`,
|
||||||
});
|
[keyId]
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info("다국어 키 삭제 완료", { keyId });
|
logger.info("다국어 키 삭제 완료", { keyId });
|
||||||
|
|
@ -487,10 +561,10 @@ export class MultiLangService {
|
||||||
logger.info("다국어 키 상태 토글 시작", { keyId });
|
logger.info("다국어 키 상태 토글 시작", { keyId });
|
||||||
|
|
||||||
// 현재 키 조회
|
// 현재 키 조회
|
||||||
const currentKey = await prisma.multi_lang_key_master.findUnique({
|
const currentKey = await queryOne<{ is_active: string | null }>(
|
||||||
where: { key_id: keyId },
|
`SELECT is_active FROM multi_lang_key_master WHERE key_id = $1`,
|
||||||
select: { is_active: true },
|
[keyId]
|
||||||
});
|
);
|
||||||
|
|
||||||
if (!currentKey) {
|
if (!currentKey) {
|
||||||
throw new Error(`다국어 키를 찾을 수 없습니다: ${keyId}`);
|
throw new Error(`다국어 키를 찾을 수 없습니다: ${keyId}`);
|
||||||
|
|
@ -499,13 +573,12 @@ export class MultiLangService {
|
||||||
const newStatus = currentKey.is_active === "Y" ? "N" : "Y";
|
const newStatus = currentKey.is_active === "Y" ? "N" : "Y";
|
||||||
|
|
||||||
// 상태 업데이트
|
// 상태 업데이트
|
||||||
await prisma.multi_lang_key_master.update({
|
await query(
|
||||||
where: { key_id: keyId },
|
`UPDATE multi_lang_key_master
|
||||||
data: {
|
SET is_active = $1, updated_by = $2
|
||||||
is_active: newStatus,
|
WHERE key_id = $3`,
|
||||||
updated_by: "system",
|
[newStatus, "system", keyId]
|
||||||
},
|
);
|
||||||
});
|
|
||||||
|
|
||||||
const result = newStatus === "Y" ? "활성화" : "비활성화";
|
const result = newStatus === "Y" ? "활성화" : "비활성화";
|
||||||
logger.info("다국어 키 상태 토글 완료", { keyId, result });
|
logger.info("다국어 키 상태 토글 완료", { keyId, result });
|
||||||
|
|
@ -533,33 +606,40 @@ export class MultiLangService {
|
||||||
});
|
});
|
||||||
|
|
||||||
// 기존 키 확인
|
// 기존 키 확인
|
||||||
const existingKey = await prisma.multi_lang_key_master.findUnique({
|
const existingKey = await queryOne<{ key_id: number }>(
|
||||||
where: { key_id: keyId },
|
`SELECT key_id FROM multi_lang_key_master WHERE key_id = $1`,
|
||||||
});
|
[keyId]
|
||||||
|
);
|
||||||
|
|
||||||
if (!existingKey) {
|
if (!existingKey) {
|
||||||
throw new Error(`다국어 키를 찾을 수 없습니다: ${keyId}`);
|
throw new Error(`다국어 키를 찾을 수 없습니다: ${keyId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 트랜잭션으로 기존 텍스트 삭제 후 새로 생성
|
// 트랜잭션으로 기존 텍스트 삭제 후 새로 생성
|
||||||
await prisma.$transaction(async (tx) => {
|
await transaction(async (client) => {
|
||||||
// 기존 텍스트 삭제
|
// 기존 텍스트 삭제
|
||||||
await tx.multi_lang_text.deleteMany({
|
await client.query(
|
||||||
where: { key_id: keyId },
|
`DELETE FROM multi_lang_text WHERE key_id = $1`,
|
||||||
});
|
[keyId]
|
||||||
|
);
|
||||||
|
|
||||||
// 새로운 텍스트 삽입
|
// 새로운 텍스트 삽입
|
||||||
if (textData.texts.length > 0) {
|
if (textData.texts.length > 0) {
|
||||||
await tx.multi_lang_text.createMany({
|
for (const text of textData.texts) {
|
||||||
data: textData.texts.map((text) => ({
|
await client.query(
|
||||||
key_id: keyId,
|
`INSERT INTO multi_lang_text
|
||||||
lang_code: text.langCode,
|
(key_id, lang_code, lang_text, is_active, created_by, updated_by)
|
||||||
lang_text: text.langText,
|
VALUES ($1, $2, $3, $4, $5, $6)`,
|
||||||
is_active: text.isActive || "Y",
|
[
|
||||||
created_by: text.createdBy || "system",
|
keyId,
|
||||||
updated_by: text.updatedBy || "system",
|
text.langCode,
|
||||||
})),
|
text.langText,
|
||||||
});
|
text.isActive || "Y",
|
||||||
|
text.createdBy || "system",
|
||||||
|
text.updatedBy || "system",
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -582,21 +662,18 @@ export class MultiLangService {
|
||||||
try {
|
try {
|
||||||
logger.info("사용자별 다국어 텍스트 조회 시작", { params });
|
logger.info("사용자별 다국어 텍스트 조회 시작", { params });
|
||||||
|
|
||||||
const result = await prisma.multi_lang_text.findFirst({
|
const result = await queryOne<{ lang_text: string }>(
|
||||||
where: {
|
`SELECT mlt.lang_text
|
||||||
lang_code: params.userLang,
|
FROM multi_lang_text mlt
|
||||||
is_active: "Y",
|
INNER JOIN multi_lang_key_master mlkm ON mlt.key_id = mlkm.key_id
|
||||||
multi_lang_key_master: {
|
WHERE mlt.lang_code = $1
|
||||||
company_code: params.companyCode,
|
AND mlt.is_active = $2
|
||||||
menu_name: params.menuCode,
|
AND mlkm.company_code = $3
|
||||||
lang_key: params.langKey,
|
AND mlkm.menu_name = $4
|
||||||
is_active: "Y",
|
AND mlkm.lang_key = $5
|
||||||
},
|
AND mlkm.is_active = $6`,
|
||||||
},
|
[params.userLang, "Y", params.companyCode, params.menuCode, params.langKey, "Y"]
|
||||||
select: {
|
);
|
||||||
lang_text: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
logger.warn("사용자별 다국어 텍스트를 찾을 수 없음", { params });
|
logger.warn("사용자별 다국어 텍스트를 찾을 수 없음", { params });
|
||||||
|
|
@ -632,20 +709,17 @@ export class MultiLangService {
|
||||||
langCode,
|
langCode,
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await prisma.multi_lang_text.findFirst({
|
const result = await queryOne<{ lang_text: string }>(
|
||||||
where: {
|
`SELECT mlt.lang_text
|
||||||
lang_code: langCode,
|
FROM multi_lang_text mlt
|
||||||
is_active: "Y",
|
INNER JOIN multi_lang_key_master mlkm ON mlt.key_id = mlkm.key_id
|
||||||
multi_lang_key_master: {
|
WHERE mlt.lang_code = $1
|
||||||
company_code: companyCode,
|
AND mlt.is_active = $2
|
||||||
lang_key: langKey,
|
AND mlkm.company_code = $3
|
||||||
is_active: "Y",
|
AND mlkm.lang_key = $4
|
||||||
},
|
AND mlkm.is_active = $5`,
|
||||||
},
|
[langCode, "Y", companyCode, langKey, "Y"]
|
||||||
select: {
|
);
|
||||||
lang_text: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
logger.warn("특정 키의 다국어 텍스트를 찾을 수 없음", {
|
logger.warn("특정 키의 다국어 텍스트를 찾을 수 없음", {
|
||||||
|
|
@ -691,31 +765,24 @@ export class MultiLangService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 모든 키에 대한 번역 조회
|
// 모든 키에 대한 번역 조회
|
||||||
const translations = await prisma.multi_lang_text.findMany({
|
const placeholders = params.langKeys.map((_, i) => `$${i + 4}`).join(", ");
|
||||||
where: {
|
|
||||||
lang_code: params.userLang,
|
const translations = await query<{
|
||||||
is_active: "Y",
|
lang_text: string;
|
||||||
multi_lang_key_master: {
|
lang_key: string;
|
||||||
lang_key: { in: params.langKeys },
|
company_code: string;
|
||||||
company_code: { in: [params.companyCode, "*"] },
|
}>(
|
||||||
is_active: "Y",
|
`SELECT mlt.lang_text, mlkm.lang_key, mlkm.company_code
|
||||||
},
|
FROM multi_lang_text mlt
|
||||||
},
|
INNER JOIN multi_lang_key_master mlkm ON mlt.key_id = mlkm.key_id
|
||||||
select: {
|
WHERE mlt.lang_code = $1
|
||||||
lang_text: true,
|
AND mlt.is_active = $2
|
||||||
multi_lang_key_master: {
|
AND mlkm.lang_key IN (${placeholders})
|
||||||
select: {
|
AND mlkm.company_code IN ($3, '*')
|
||||||
lang_key: true,
|
AND mlkm.is_active = $2
|
||||||
company_code: true,
|
ORDER BY mlkm.company_code ASC`,
|
||||||
},
|
[params.userLang, "Y", params.companyCode, ...params.langKeys]
|
||||||
},
|
);
|
||||||
},
|
|
||||||
orderBy: {
|
|
||||||
multi_lang_key_master: {
|
|
||||||
company_code: "asc", // 회사별 우선, '*' 는 기본값
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const result: Record<string, string> = {};
|
const result: Record<string, string> = {};
|
||||||
|
|
||||||
|
|
@ -726,7 +793,7 @@ export class MultiLangService {
|
||||||
|
|
||||||
// 실제 번역으로 덮어쓰기 (회사별 우선)
|
// 실제 번역으로 덮어쓰기 (회사별 우선)
|
||||||
translations.forEach((translation) => {
|
translations.forEach((translation) => {
|
||||||
const langKey = translation.multi_lang_key_master.lang_key;
|
const langKey = translation.lang_key;
|
||||||
if (params.langKeys.includes(langKey)) {
|
if (params.langKeys.includes(langKey)) {
|
||||||
result[langKey] = translation.lang_text;
|
result[langKey] = translation.lang_text;
|
||||||
}
|
}
|
||||||
|
|
@ -755,29 +822,32 @@ export class MultiLangService {
|
||||||
logger.info("언어 삭제 시작", { langCode });
|
logger.info("언어 삭제 시작", { langCode });
|
||||||
|
|
||||||
// 기존 언어 확인
|
// 기존 언어 확인
|
||||||
const existingLanguage = await prisma.language_master.findUnique({
|
const existingLanguage = await queryOne<{ lang_code: string }>(
|
||||||
where: { lang_code: langCode },
|
`SELECT lang_code FROM language_master WHERE lang_code = $1`,
|
||||||
});
|
[langCode]
|
||||||
|
);
|
||||||
|
|
||||||
if (!existingLanguage) {
|
if (!existingLanguage) {
|
||||||
throw new Error(`언어를 찾을 수 없습니다: ${langCode}`);
|
throw new Error(`언어를 찾을 수 없습니다: ${langCode}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 트랜잭션으로 언어와 관련 텍스트 삭제
|
// 트랜잭션으로 언어와 관련 텍스트 삭제
|
||||||
await prisma.$transaction(async (tx) => {
|
await transaction(async (client) => {
|
||||||
// 해당 언어의 다국어 텍스트 삭제
|
// 해당 언어의 다국어 텍스트 삭제
|
||||||
const deleteResult = await tx.multi_lang_text.deleteMany({
|
const deleteResult = await client.query(
|
||||||
where: { lang_code: langCode },
|
`DELETE FROM multi_lang_text WHERE lang_code = $1`,
|
||||||
});
|
[langCode]
|
||||||
|
);
|
||||||
|
|
||||||
logger.info(`삭제된 다국어 텍스트 수: ${deleteResult.count}`, {
|
logger.info(`삭제된 다국어 텍스트 수: ${deleteResult.rowCount}`, {
|
||||||
langCode,
|
langCode,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 언어 마스터 삭제
|
// 언어 마스터 삭제
|
||||||
await tx.language_master.delete({
|
await client.query(
|
||||||
where: { lang_code: langCode },
|
`DELETE FROM language_master WHERE lang_code = $1`,
|
||||||
});
|
[langCode]
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info("언어 삭제 완료", { langCode });
|
logger.info("언어 삭제 완료", { langCode });
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue