1185 lines
33 KiB
TypeScript
1185 lines
33 KiB
TypeScript
import { Request, Response } from "express";
|
|
import { logger } from "../utils/logger";
|
|
import { AuthenticatedRequest } from "../types/auth";
|
|
import { MultiLangService } from "../services/multilangService";
|
|
import {
|
|
CreateLanguageRequest,
|
|
UpdateLanguageRequest,
|
|
CreateLangKeyRequest,
|
|
UpdateLangKeyRequest,
|
|
SaveLangTextsRequest,
|
|
GetUserTextParams,
|
|
BatchTranslationRequest,
|
|
GenerateKeyRequest,
|
|
CreateOverrideKeyRequest,
|
|
ApiResponse,
|
|
LangCategory,
|
|
} from "../types/multilang";
|
|
|
|
/**
|
|
* GET /api/multilang/languages
|
|
* 언어 목록 조회 API
|
|
*/
|
|
export const getLanguages = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
logger.info("언어 목록 조회 요청", { user: req.user });
|
|
|
|
const multiLangService = new MultiLangService();
|
|
const languages = await multiLangService.getLanguages();
|
|
|
|
const response: ApiResponse<any[]> = {
|
|
success: true,
|
|
message: "언어 목록 조회 성공",
|
|
data: languages,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("언어 목록 조회 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "언어 목록 조회 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "LANGUAGE_LIST_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* POST /api/multilang/languages
|
|
* 언어 생성 API
|
|
*/
|
|
export const createLanguage = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const languageData: CreateLanguageRequest = req.body;
|
|
logger.info("언어 생성 요청", { languageData, user: req.user });
|
|
|
|
// 필수 입력값 검증
|
|
if (
|
|
!languageData.langCode ||
|
|
!languageData.langName ||
|
|
!languageData.langNative
|
|
) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "언어 코드, 언어명, 원어명은 필수입니다.",
|
|
error: {
|
|
code: "MISSING_REQUIRED_FIELDS",
|
|
details: "langCode, langName, langNative are required",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
const multiLangService = new MultiLangService();
|
|
const createdLanguage = await multiLangService.createLanguage({
|
|
...languageData,
|
|
createdBy: req.user?.userId || "system",
|
|
updatedBy: req.user?.userId || "system",
|
|
});
|
|
|
|
const response: ApiResponse<any> = {
|
|
success: true,
|
|
message: "언어가 성공적으로 생성되었습니다.",
|
|
data: createdLanguage,
|
|
};
|
|
|
|
res.status(201).json(response);
|
|
} catch (error) {
|
|
logger.error("언어 생성 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "언어 생성 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "LANGUAGE_CREATE_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* PUT /api/multilang/languages/:langCode
|
|
* 언어 수정 API
|
|
*/
|
|
export const updateLanguage = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const { langCode } = req.params;
|
|
const languageData: UpdateLanguageRequest = req.body;
|
|
|
|
logger.info("언어 수정 요청", { langCode, languageData, user: req.user });
|
|
|
|
const multiLangService = new MultiLangService();
|
|
const updatedLanguage = await multiLangService.updateLanguage(langCode, {
|
|
...languageData,
|
|
updatedBy: req.user?.userId || "system",
|
|
});
|
|
|
|
const response: ApiResponse<any> = {
|
|
success: true,
|
|
message: "언어가 성공적으로 수정되었습니다.",
|
|
data: updatedLanguage,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("언어 수정 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "언어 수정 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "LANGUAGE_UPDATE_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* PUT /api/multilang/languages/:langCode/toggle
|
|
* 언어 상태 토글 API
|
|
*/
|
|
export const toggleLanguage = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const { langCode } = req.params;
|
|
logger.info("언어 상태 토글 요청", { langCode, user: req.user });
|
|
|
|
const multiLangService = new MultiLangService();
|
|
const result = await multiLangService.toggleLanguage(langCode);
|
|
|
|
const response: ApiResponse<string> = {
|
|
success: true,
|
|
message: `언어가 ${result}되었습니다.`,
|
|
data: result,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("언어 상태 토글 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "언어 상태 변경 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "LANGUAGE_TOGGLE_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* GET /api/multilang/keys
|
|
* 다국어 키 목록 조회 API
|
|
*/
|
|
export const getLangKeys = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const { companyCode, menuCode, keyType, searchText, categoryId } = req.query;
|
|
logger.info("다국어 키 목록 조회 요청", {
|
|
query: req.query,
|
|
user: req.user,
|
|
});
|
|
|
|
const multiLangService = new MultiLangService();
|
|
const langKeys = await multiLangService.getLangKeys({
|
|
companyCode: companyCode as string,
|
|
menuCode: menuCode as string,
|
|
keyType: keyType as string,
|
|
searchText: searchText as string,
|
|
categoryId: categoryId ? parseInt(categoryId as string, 10) : undefined,
|
|
});
|
|
|
|
const response: ApiResponse<any[]> = {
|
|
success: true,
|
|
message: "다국어 키 목록 조회 성공",
|
|
data: langKeys,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("다국어 키 목록 조회 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "다국어 키 목록 조회 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "LANG_KEYS_LIST_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* GET /api/multilang/keys/:keyId/texts
|
|
* 특정 키의 다국어 텍스트 조회 API
|
|
*/
|
|
export const getLangTexts = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const { keyId } = req.params;
|
|
logger.info("다국어 텍스트 조회 요청", { keyId, user: req.user });
|
|
|
|
const multiLangService = new MultiLangService();
|
|
const langTexts = await multiLangService.getLangTexts(parseInt(keyId));
|
|
|
|
const response: ApiResponse<any[]> = {
|
|
success: true,
|
|
message: "다국어 텍스트 조회 성공",
|
|
data: langTexts,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("다국어 텍스트 조회 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "다국어 텍스트 조회 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "LANG_TEXTS_LIST_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* POST /api/multilang/keys
|
|
* 다국어 키 생성 API
|
|
*/
|
|
export const createLangKey = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const keyData: CreateLangKeyRequest = req.body;
|
|
logger.info("다국어 키 생성 요청", { keyData, user: req.user });
|
|
|
|
// 필수 입력값 검증
|
|
if (!keyData.companyCode || !keyData.langKey) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "회사 코드와 언어 키는 필수입니다.",
|
|
error: {
|
|
code: "MISSING_REQUIRED_FIELDS",
|
|
details: "companyCode and langKey are required",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
const multiLangService = new MultiLangService();
|
|
const keyId = await multiLangService.createLangKey({
|
|
...keyData,
|
|
createdBy: req.user?.userId || "system",
|
|
updatedBy: req.user?.userId || "system",
|
|
});
|
|
|
|
const response: ApiResponse<number> = {
|
|
success: true,
|
|
message: "다국어 키가 성공적으로 생성되었습니다.",
|
|
data: keyId,
|
|
};
|
|
|
|
res.status(201).json(response);
|
|
} catch (error) {
|
|
logger.error("다국어 키 생성 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "다국어 키 생성 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "LANG_KEY_CREATE_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* PUT /api/multilang/keys/:keyId
|
|
* 다국어 키 수정 API
|
|
*/
|
|
export const updateLangKey = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const { keyId } = req.params;
|
|
const keyData: UpdateLangKeyRequest = req.body;
|
|
|
|
logger.info("다국어 키 수정 요청", { keyId, keyData, user: req.user });
|
|
|
|
const multiLangService = new MultiLangService();
|
|
await multiLangService.updateLangKey(parseInt(keyId), {
|
|
...keyData,
|
|
updatedBy: req.user?.userId || "system",
|
|
});
|
|
|
|
const response: ApiResponse<string> = {
|
|
success: true,
|
|
message: "다국어 키가 성공적으로 수정되었습니다.",
|
|
data: "수정 완료",
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("다국어 키 수정 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "다국어 키 수정 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "LANG_KEY_UPDATE_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* DELETE /api/multilang/keys/:keyId
|
|
* 다국어 키 삭제 API
|
|
*/
|
|
export const deleteLangKey = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const { keyId } = req.params;
|
|
logger.info("다국어 키 삭제 요청", { keyId, user: req.user });
|
|
|
|
const multiLangService = new MultiLangService();
|
|
await multiLangService.deleteLangKey(parseInt(keyId));
|
|
|
|
const response: ApiResponse<string> = {
|
|
success: true,
|
|
message: "다국어 키가 성공적으로 삭제되었습니다.",
|
|
data: "삭제 완료",
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("다국어 키 삭제 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "다국어 키 삭제 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "LANG_KEY_DELETE_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* PUT /api/multilang/keys/:keyId/toggle
|
|
* 다국어 키 상태 토글 API
|
|
*/
|
|
export const toggleLangKey = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const { keyId } = req.params;
|
|
logger.info("다국어 키 상태 토글 요청", { keyId, user: req.user });
|
|
|
|
const multiLangService = new MultiLangService();
|
|
const result = await multiLangService.toggleLangKey(parseInt(keyId));
|
|
|
|
const response: ApiResponse<string> = {
|
|
success: true,
|
|
message: `다국어 키가 ${result}되었습니다.`,
|
|
data: result,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("다국어 키 상태 토글 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "다국어 키 상태 변경 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "LANG_KEY_TOGGLE_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* POST /api/multilang/keys/:keyId/texts
|
|
* 다국어 텍스트 저장/수정 API
|
|
*/
|
|
export const saveLangTexts = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const { keyId } = req.params;
|
|
const textData: SaveLangTextsRequest = req.body;
|
|
|
|
logger.info("다국어 텍스트 저장 요청", { keyId, textData, user: req.user });
|
|
|
|
// 필수 입력값 검증
|
|
if (
|
|
!textData.texts ||
|
|
!Array.isArray(textData.texts) ||
|
|
textData.texts.length === 0
|
|
) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "텍스트 데이터는 필수입니다.",
|
|
error: {
|
|
code: "MISSING_REQUIRED_FIELDS",
|
|
details: "texts array is required",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
const multiLangService = new MultiLangService();
|
|
await multiLangService.saveLangTexts(parseInt(keyId), {
|
|
texts: textData.texts.map((text) => ({
|
|
...text,
|
|
createdBy: req.user?.userId || "system",
|
|
updatedBy: req.user?.userId || "system",
|
|
})),
|
|
});
|
|
|
|
const response: ApiResponse<string> = {
|
|
success: true,
|
|
message: "다국어 텍스트가 성공적으로 저장되었습니다.",
|
|
data: "저장 완료",
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("다국어 텍스트 저장 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "다국어 텍스트 저장 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "LANG_TEXTS_SAVE_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* GET /api/multilang/user-text/:companyCode/:menuCode/:langKey
|
|
* 사용자별 다국어 텍스트 조회 API
|
|
*/
|
|
export const getUserText = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const { companyCode, menuCode, langKey } = req.params;
|
|
const { userLang } = req.query;
|
|
|
|
logger.info("사용자별 다국어 텍스트 조회 요청", {
|
|
companyCode,
|
|
menuCode,
|
|
langKey,
|
|
userLang,
|
|
user: req.user,
|
|
});
|
|
|
|
if (!userLang) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "사용자 언어는 필수입니다.",
|
|
error: {
|
|
code: "MISSING_USER_LANG",
|
|
details: "userLang query parameter is required",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
const multiLangService = new MultiLangService();
|
|
const langText = await multiLangService.getUserText({
|
|
companyCode,
|
|
menuCode,
|
|
langKey,
|
|
userLang: userLang as string,
|
|
});
|
|
|
|
const response: ApiResponse<string> = {
|
|
success: true,
|
|
message: "사용자별 다국어 텍스트 조회 성공",
|
|
data: langText,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("사용자별 다국어 텍스트 조회 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "사용자별 다국어 텍스트 조회 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "USER_TEXT_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* GET /api/multilang/text/:companyCode/:langKey/:langCode
|
|
* 특정 키의 다국어 텍스트 조회 API
|
|
*/
|
|
export const getLangText = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const { companyCode, langKey, langCode } = req.params;
|
|
|
|
logger.info("특정 키의 다국어 텍스트 조회 요청", {
|
|
companyCode,
|
|
langKey,
|
|
langCode,
|
|
user: req.user,
|
|
});
|
|
|
|
const multiLangService = new MultiLangService();
|
|
const langText = await multiLangService.getLangText(
|
|
companyCode,
|
|
langKey,
|
|
langCode
|
|
);
|
|
|
|
const response: ApiResponse<string> = {
|
|
success: true,
|
|
message: "특정 키의 다국어 텍스트 조회 성공",
|
|
data: langText,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("특정 키의 다국어 텍스트 조회 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "특정 키의 다국어 텍스트 조회 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "LANG_TEXT_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* DELETE /api/multilang/languages/:langCode
|
|
* 언어 삭제 API
|
|
*/
|
|
export const deleteLanguage = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const { langCode } = req.params;
|
|
logger.info("언어 삭제 요청", { langCode, user: req.user });
|
|
|
|
if (!langCode) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "언어 코드가 필요합니다.",
|
|
error: {
|
|
code: "MISSING_LANG_CODE",
|
|
details: "langCode parameter is required",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
const multiLangService = new MultiLangService();
|
|
await multiLangService.deleteLanguage(langCode);
|
|
|
|
const response: ApiResponse<string> = {
|
|
success: true,
|
|
message: "언어가 성공적으로 삭제되었습니다.",
|
|
data: "삭제 완료",
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("언어 삭제 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "언어 삭제 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "LANGUAGE_DELETE_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
// =====================================================
|
|
// 카테고리 관련 API
|
|
// =====================================================
|
|
|
|
/**
|
|
* GET /api/multilang/categories
|
|
* 카테고리 목록 조회 API (트리 구조)
|
|
*/
|
|
export const getCategories = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
logger.info("카테고리 목록 조회 요청", { user: req.user });
|
|
|
|
const multiLangService = new MultiLangService();
|
|
const categories = await multiLangService.getCategories();
|
|
|
|
const response: ApiResponse<LangCategory[]> = {
|
|
success: true,
|
|
message: "카테고리 목록 조회 성공",
|
|
data: categories,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("카테고리 목록 조회 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "카테고리 목록 조회 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "CATEGORY_LIST_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* GET /api/multilang/categories/:categoryId
|
|
* 카테고리 상세 조회 API
|
|
*/
|
|
export const getCategoryById = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const { categoryId } = req.params;
|
|
logger.info("카테고리 상세 조회 요청", { categoryId, user: req.user });
|
|
|
|
const multiLangService = new MultiLangService();
|
|
const category = await multiLangService.getCategoryById(parseInt(categoryId));
|
|
|
|
if (!category) {
|
|
res.status(404).json({
|
|
success: false,
|
|
message: "카테고리를 찾을 수 없습니다.",
|
|
error: {
|
|
code: "CATEGORY_NOT_FOUND",
|
|
details: `Category ID ${categoryId} not found`,
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
const response: ApiResponse<LangCategory> = {
|
|
success: true,
|
|
message: "카테고리 상세 조회 성공",
|
|
data: category,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("카테고리 상세 조회 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "카테고리 상세 조회 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "CATEGORY_DETAIL_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* GET /api/multilang/categories/:categoryId/path
|
|
* 카테고리 경로 조회 API (부모 포함)
|
|
*/
|
|
export const getCategoryPath = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const { categoryId } = req.params;
|
|
logger.info("카테고리 경로 조회 요청", { categoryId, user: req.user });
|
|
|
|
const multiLangService = new MultiLangService();
|
|
const path = await multiLangService.getCategoryPath(parseInt(categoryId));
|
|
|
|
const response: ApiResponse<LangCategory[]> = {
|
|
success: true,
|
|
message: "카테고리 경로 조회 성공",
|
|
data: path,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("카테고리 경로 조회 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "카테고리 경로 조회 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "CATEGORY_PATH_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
// =====================================================
|
|
// 자동 생성 및 오버라이드 관련 API
|
|
// =====================================================
|
|
|
|
/**
|
|
* POST /api/multilang/keys/generate
|
|
* 키 자동 생성 API
|
|
*/
|
|
export const generateKey = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const generateData: GenerateKeyRequest = req.body;
|
|
logger.info("키 자동 생성 요청", { generateData, user: req.user });
|
|
|
|
// 필수 입력값 검증
|
|
if (!generateData.companyCode || !generateData.categoryId || !generateData.keyMeaning) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "회사 코드, 카테고리 ID, 키 의미는 필수입니다.",
|
|
error: {
|
|
code: "MISSING_REQUIRED_FIELDS",
|
|
details: "companyCode, categoryId, and keyMeaning are required",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 권한 검사: 공통 키(*)는 최고 관리자만 생성 가능
|
|
if (generateData.companyCode === "*" && req.user?.companyCode !== "*") {
|
|
res.status(403).json({
|
|
success: false,
|
|
message: "공통 키는 최고 관리자만 생성할 수 있습니다.",
|
|
error: {
|
|
code: "PERMISSION_DENIED",
|
|
details: "Only super admin can create common keys",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 회사 관리자는 자기 회사 키만 생성 가능
|
|
if (generateData.companyCode !== "*" &&
|
|
req.user?.companyCode !== "*" &&
|
|
generateData.companyCode !== req.user?.companyCode) {
|
|
res.status(403).json({
|
|
success: false,
|
|
message: "다른 회사의 키를 생성할 권한이 없습니다.",
|
|
error: {
|
|
code: "PERMISSION_DENIED",
|
|
details: "Cannot create keys for other companies",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
const multiLangService = new MultiLangService();
|
|
const keyId = await multiLangService.generateKey({
|
|
...generateData,
|
|
createdBy: req.user?.userId || "system",
|
|
});
|
|
|
|
const response: ApiResponse<number> = {
|
|
success: true,
|
|
message: "키가 성공적으로 생성되었습니다.",
|
|
data: keyId,
|
|
};
|
|
|
|
res.status(201).json(response);
|
|
} catch (error) {
|
|
logger.error("키 자동 생성 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "키 자동 생성 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "KEY_GENERATE_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* POST /api/multilang/keys/preview
|
|
* 키 미리보기 API
|
|
*/
|
|
export const previewKey = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const { categoryId, keyMeaning, companyCode } = req.body;
|
|
logger.info("키 미리보기 요청", { categoryId, keyMeaning, companyCode, user: req.user });
|
|
|
|
if (!categoryId || !keyMeaning || !companyCode) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "카테고리 ID, 키 의미, 회사 코드는 필수입니다.",
|
|
error: {
|
|
code: "MISSING_REQUIRED_FIELDS",
|
|
details: "categoryId, keyMeaning, and companyCode are required",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
const multiLangService = new MultiLangService();
|
|
const preview = await multiLangService.previewGeneratedKey(
|
|
parseInt(categoryId),
|
|
keyMeaning,
|
|
companyCode
|
|
);
|
|
|
|
const response: ApiResponse<{
|
|
langKey: string;
|
|
exists: boolean;
|
|
isOverride: boolean;
|
|
baseKeyId?: number;
|
|
}> = {
|
|
success: true,
|
|
message: "키 미리보기 성공",
|
|
data: preview,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("키 미리보기 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "키 미리보기 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "KEY_PREVIEW_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* POST /api/multilang/keys/override
|
|
* 오버라이드 키 생성 API
|
|
*/
|
|
export const createOverrideKey = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const overrideData: CreateOverrideKeyRequest = req.body;
|
|
logger.info("오버라이드 키 생성 요청", { overrideData, user: req.user });
|
|
|
|
// 필수 입력값 검증
|
|
if (!overrideData.companyCode || !overrideData.baseKeyId) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "회사 코드와 원본 키 ID는 필수입니다.",
|
|
error: {
|
|
code: "MISSING_REQUIRED_FIELDS",
|
|
details: "companyCode and baseKeyId are required",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 최고 관리자(*)는 오버라이드 키를 만들 수 없음 (이미 공통 키)
|
|
if (overrideData.companyCode === "*") {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "공통 키에 대한 오버라이드는 생성할 수 없습니다.",
|
|
error: {
|
|
code: "INVALID_OVERRIDE",
|
|
details: "Cannot create override for common keys",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 회사 관리자는 자기 회사 오버라이드만 생성 가능
|
|
if (req.user?.companyCode !== "*" &&
|
|
overrideData.companyCode !== req.user?.companyCode) {
|
|
res.status(403).json({
|
|
success: false,
|
|
message: "다른 회사의 오버라이드 키를 생성할 권한이 없습니다.",
|
|
error: {
|
|
code: "PERMISSION_DENIED",
|
|
details: "Cannot create override keys for other companies",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
const multiLangService = new MultiLangService();
|
|
const keyId = await multiLangService.createOverrideKey({
|
|
...overrideData,
|
|
createdBy: req.user?.userId || "system",
|
|
});
|
|
|
|
const response: ApiResponse<number> = {
|
|
success: true,
|
|
message: "오버라이드 키가 성공적으로 생성되었습니다.",
|
|
data: keyId,
|
|
};
|
|
|
|
res.status(201).json(response);
|
|
} catch (error) {
|
|
logger.error("오버라이드 키 생성 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "오버라이드 키 생성 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "OVERRIDE_KEY_CREATE_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* GET /api/multilang/keys/overrides/:companyCode
|
|
* 회사별 오버라이드 키 목록 조회 API
|
|
*/
|
|
export const getOverrideKeys = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const { companyCode } = req.params;
|
|
logger.info("오버라이드 키 목록 조회 요청", { companyCode, user: req.user });
|
|
|
|
// 권한 검사: 최고 관리자 또는 해당 회사 관리자만 조회 가능
|
|
if (req.user?.companyCode !== "*" && companyCode !== req.user?.companyCode) {
|
|
res.status(403).json({
|
|
success: false,
|
|
message: "다른 회사의 오버라이드 키를 조회할 권한이 없습니다.",
|
|
error: {
|
|
code: "PERMISSION_DENIED",
|
|
details: "Cannot view override keys for other companies",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
const multiLangService = new MultiLangService();
|
|
const keys = await multiLangService.getOverrideKeys(companyCode);
|
|
|
|
const response: ApiResponse<any[]> = {
|
|
success: true,
|
|
message: "오버라이드 키 목록 조회 성공",
|
|
data: keys,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("오버라이드 키 목록 조회 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "오버라이드 키 목록 조회 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "OVERRIDE_KEYS_LIST_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* POST /api/multilang/batch
|
|
* 다국어 텍스트 배치 조회 API
|
|
*/
|
|
export const getBatchTranslations = async (
|
|
req: Request,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const { companyCode, menuCode, userLang } = req.query;
|
|
const {
|
|
langKeys,
|
|
companyCode: bodyCompanyCode,
|
|
menuCode: bodyMenuCode,
|
|
userLang: bodyUserLang,
|
|
} = req.body;
|
|
|
|
// query params에서 읽지 못한 경우 body에서 읽기
|
|
const finalCompanyCode = companyCode || bodyCompanyCode;
|
|
const finalMenuCode = menuCode || bodyMenuCode;
|
|
const finalUserLang = userLang || bodyUserLang;
|
|
|
|
logger.info("다국어 텍스트 배치 조회 요청", {
|
|
companyCode: finalCompanyCode,
|
|
menuCode: finalMenuCode,
|
|
userLang: finalUserLang,
|
|
keyCount: langKeys?.length || 0,
|
|
});
|
|
|
|
if (!langKeys || !Array.isArray(langKeys) || langKeys.length === 0) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "langKeys 배열이 필요합니다.",
|
|
error: {
|
|
code: "MISSING_LANG_KEYS",
|
|
details: "langKeys array is required",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!finalCompanyCode || !finalUserLang) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "companyCode와 userLang은 필수입니다.",
|
|
error: {
|
|
code: "MISSING_REQUIRED_PARAMS",
|
|
details: "companyCode and userLang are required",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
const multiLangService = new MultiLangService();
|
|
const translations = await multiLangService.getBatchTranslations({
|
|
companyCode: finalCompanyCode as string,
|
|
menuCode: finalMenuCode as string,
|
|
userLang: finalUserLang as string,
|
|
langKeys,
|
|
});
|
|
|
|
const response: ApiResponse<Record<string, string>> = {
|
|
success: true,
|
|
message: "다국어 텍스트 배치 조회 성공",
|
|
data: translations,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("다국어 텍스트 배치 조회 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "다국어 텍스트 배치 조회 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "BATCH_TRANSLATION_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* POST /api/multilang/screen-labels
|
|
* 화면 라벨 다국어 키 자동 생성 API
|
|
*/
|
|
export const generateScreenLabelKeys = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const { screenId, menuObjId, labels } = req.body;
|
|
|
|
logger.info("화면 라벨 다국어 키 생성 요청", {
|
|
screenId,
|
|
menuObjId,
|
|
labelCount: labels?.length,
|
|
user: req.user,
|
|
});
|
|
|
|
// 필수 파라미터 검증
|
|
if (!screenId) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "screenId는 필수입니다.",
|
|
error: { code: "MISSING_SCREEN_ID" },
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!labels || !Array.isArray(labels) || labels.length === 0) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "labels 배열이 필요합니다.",
|
|
error: { code: "MISSING_LABELS" },
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 화면의 회사 정보 조회 (사용자 회사가 아닌 화면 소속 회사 기준)
|
|
const { queryOne } = await import("../database/db");
|
|
const screenInfo = await queryOne<{ company_code: string }>(
|
|
`SELECT company_code FROM screen_definitions WHERE screen_id = $1`,
|
|
[screenId]
|
|
);
|
|
const companyCode = screenInfo?.company_code || req.user?.companyCode || "*";
|
|
|
|
// 회사명 조회
|
|
const companyInfo = await queryOne<{ company_name: string }>(
|
|
`SELECT company_name FROM company_mng WHERE company_code = $1`,
|
|
[companyCode]
|
|
);
|
|
const companyName = companyCode === "*" ? "공통" : (companyInfo?.company_name || companyCode);
|
|
|
|
logger.info("화면 소속 회사 정보", { screenId, companyCode, companyName });
|
|
|
|
const multiLangService = new MultiLangService();
|
|
const results = await multiLangService.generateScreenLabelKeys({
|
|
screenId: Number(screenId),
|
|
companyCode,
|
|
companyName,
|
|
menuObjId,
|
|
labels,
|
|
});
|
|
|
|
const response: ApiResponse<typeof results> = {
|
|
success: true,
|
|
message: `${results.length}개의 다국어 키가 생성되었습니다.`,
|
|
data: results,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("화면 라벨 다국어 키 생성 실패:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "화면 라벨 다국어 키 생성 중 오류가 발생했습니다.",
|
|
error: {
|
|
code: "SCREEN_LABEL_KEY_GENERATION_ERROR",
|
|
details: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
});
|
|
}
|
|
};
|