백엔드 API 구현 완료
This commit is contained in:
@@ -14,6 +14,7 @@ import adminRoutes from "./routes/adminRoutes";
|
||||
import multilangRoutes from "./routes/multilangRoutes";
|
||||
import tableManagementRoutes from "./routes/tableManagementRoutes";
|
||||
import screenManagementRoutes from "./routes/screenManagementRoutes";
|
||||
import commonCodeRoutes from "./routes/commonCodeRoutes";
|
||||
// import userRoutes from './routes/userRoutes';
|
||||
// import menuRoutes from './routes/menuRoutes';
|
||||
|
||||
@@ -65,6 +66,7 @@ app.use("/api/admin", adminRoutes);
|
||||
app.use("/api/multilang", multilangRoutes);
|
||||
app.use("/api/table-management", tableManagementRoutes);
|
||||
app.use("/api/screen-management", screenManagementRoutes);
|
||||
app.use("/api/common-codes", commonCodeRoutes);
|
||||
// app.use('/api/users', userRoutes);
|
||||
// app.use('/api/menus', menuRoutes);
|
||||
|
||||
|
||||
398
backend-node/src/controllers/commonCodeController.ts
Normal file
398
backend-node/src/controllers/commonCodeController.ts
Normal file
@@ -0,0 +1,398 @@
|
||||
import { Request, Response } from "express";
|
||||
import {
|
||||
CommonCodeService,
|
||||
CreateCategoryData,
|
||||
CreateCodeData,
|
||||
} from "../services/commonCodeService";
|
||||
import { AuthenticatedRequest } from "../types/auth";
|
||||
import { logger } from "../utils/logger";
|
||||
|
||||
export class CommonCodeController {
|
||||
private commonCodeService: CommonCodeService;
|
||||
|
||||
constructor() {
|
||||
this.commonCodeService = new CommonCodeService();
|
||||
}
|
||||
|
||||
/**
|
||||
* 카테고리 목록 조회
|
||||
* GET /api/common-codes/categories
|
||||
*/
|
||||
async getCategories(req: AuthenticatedRequest, res: Response) {
|
||||
try {
|
||||
const { search, isActive, page = "1", size = "20" } = req.query;
|
||||
|
||||
const categories = await this.commonCodeService.getCategories({
|
||||
search: search as string,
|
||||
isActive:
|
||||
isActive === "true" ? true : isActive === "false" ? false : undefined,
|
||||
page: parseInt(page as string),
|
||||
size: parseInt(size as string),
|
||||
});
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
data: categories.data,
|
||||
total: categories.total,
|
||||
message: "카테고리 목록 조회 성공",
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("카테고리 목록 조회 실패:", error);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "카테고리 목록 조회 중 오류가 발생했습니다.",
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 카테고리별 코드 목록 조회
|
||||
* GET /api/common-codes/categories/:categoryCode/codes
|
||||
*/
|
||||
async getCodes(req: AuthenticatedRequest, res: Response) {
|
||||
try {
|
||||
const { categoryCode } = req.params;
|
||||
const { search, isActive } = req.query;
|
||||
|
||||
const codes = await this.commonCodeService.getCodes(categoryCode, {
|
||||
search: search as string,
|
||||
isActive:
|
||||
isActive === "true" ? true : isActive === "false" ? false : undefined,
|
||||
});
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
data: codes,
|
||||
message: `코드 목록 조회 성공 (${categoryCode})`,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(`코드 목록 조회 실패 (${req.params.categoryCode}):`, error);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "코드 목록 조회 중 오류가 발생했습니다.",
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 카테고리 생성
|
||||
* POST /api/common-codes/categories
|
||||
*/
|
||||
async createCategory(req: AuthenticatedRequest, res: Response) {
|
||||
try {
|
||||
const categoryData: CreateCategoryData = req.body;
|
||||
const userId = req.user?.userId || "SYSTEM"; // 인증 미들웨어에서 설정된 사용자 ID
|
||||
|
||||
// 입력값 검증
|
||||
if (!categoryData.categoryCode || !categoryData.categoryName) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "카테고리 코드와 이름은 필수입니다.",
|
||||
});
|
||||
}
|
||||
|
||||
const category = await this.commonCodeService.createCategory(
|
||||
categoryData,
|
||||
userId
|
||||
);
|
||||
|
||||
return res.status(201).json({
|
||||
success: true,
|
||||
data: category,
|
||||
message: "카테고리 생성 성공",
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("카테고리 생성 실패:", error);
|
||||
|
||||
// Prisma 에러 처리
|
||||
if (
|
||||
error instanceof Error &&
|
||||
error.message.includes("Unique constraint")
|
||||
) {
|
||||
return res.status(409).json({
|
||||
success: false,
|
||||
message: "이미 존재하는 카테고리 코드입니다.",
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "카테고리 생성 중 오류가 발생했습니다.",
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 카테고리 수정
|
||||
* PUT /api/common-codes/categories/:categoryCode
|
||||
*/
|
||||
async updateCategory(req: AuthenticatedRequest, res: Response) {
|
||||
try {
|
||||
const { categoryCode } = req.params;
|
||||
const categoryData: Partial<CreateCategoryData> = req.body;
|
||||
const userId = req.user?.userId || "SYSTEM";
|
||||
|
||||
const category = await this.commonCodeService.updateCategory(
|
||||
categoryCode,
|
||||
categoryData,
|
||||
userId
|
||||
);
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
data: category,
|
||||
message: "카테고리 수정 성공",
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(`카테고리 수정 실패 (${req.params.categoryCode}):`, error);
|
||||
|
||||
if (
|
||||
error instanceof Error &&
|
||||
error.message.includes("Record to update not found")
|
||||
) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "존재하지 않는 카테고리입니다.",
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "카테고리 수정 중 오류가 발생했습니다.",
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 카테고리 삭제
|
||||
* DELETE /api/common-codes/categories/:categoryCode
|
||||
*/
|
||||
async deleteCategory(req: AuthenticatedRequest, res: Response) {
|
||||
try {
|
||||
const { categoryCode } = req.params;
|
||||
|
||||
await this.commonCodeService.deleteCategory(categoryCode);
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
message: "카테고리 삭제 성공",
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(`카테고리 삭제 실패 (${req.params.categoryCode}):`, error);
|
||||
|
||||
if (
|
||||
error instanceof Error &&
|
||||
error.message.includes("Record to delete does not exist")
|
||||
) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "존재하지 않는 카테고리입니다.",
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "카테고리 삭제 중 오류가 발생했습니다.",
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 코드 생성
|
||||
* POST /api/common-codes/categories/:categoryCode/codes
|
||||
*/
|
||||
async createCode(req: AuthenticatedRequest, res: Response) {
|
||||
try {
|
||||
const { categoryCode } = req.params;
|
||||
const codeData: CreateCodeData = req.body;
|
||||
const userId = req.user?.userId || "SYSTEM";
|
||||
|
||||
// 입력값 검증
|
||||
if (!codeData.codeValue || !codeData.codeName) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "코드값과 코드명은 필수입니다.",
|
||||
});
|
||||
}
|
||||
|
||||
const code = await this.commonCodeService.createCode(
|
||||
categoryCode,
|
||||
codeData,
|
||||
userId
|
||||
);
|
||||
|
||||
return res.status(201).json({
|
||||
success: true,
|
||||
data: code,
|
||||
message: "코드 생성 성공",
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(`코드 생성 실패 (${req.params.categoryCode}):`, error);
|
||||
|
||||
if (
|
||||
error instanceof Error &&
|
||||
error.message.includes("Unique constraint")
|
||||
) {
|
||||
return res.status(409).json({
|
||||
success: false,
|
||||
message: "이미 존재하는 코드값입니다.",
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "코드 생성 중 오류가 발생했습니다.",
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 코드 수정
|
||||
* PUT /api/common-codes/categories/:categoryCode/codes/:codeValue
|
||||
*/
|
||||
async updateCode(req: AuthenticatedRequest, res: Response) {
|
||||
try {
|
||||
const { categoryCode, codeValue } = req.params;
|
||||
const codeData: Partial<CreateCodeData> = req.body;
|
||||
const userId = req.user?.userId || "SYSTEM";
|
||||
|
||||
const code = await this.commonCodeService.updateCode(
|
||||
categoryCode,
|
||||
codeValue,
|
||||
codeData,
|
||||
userId
|
||||
);
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
data: code,
|
||||
message: "코드 수정 성공",
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`코드 수정 실패 (${req.params.categoryCode}.${req.params.codeValue}):`,
|
||||
error
|
||||
);
|
||||
|
||||
if (
|
||||
error instanceof Error &&
|
||||
error.message.includes("Record to update not found")
|
||||
) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "존재하지 않는 코드입니다.",
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "코드 수정 중 오류가 발생했습니다.",
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 코드 삭제
|
||||
* DELETE /api/common-codes/categories/:categoryCode/codes/:codeValue
|
||||
*/
|
||||
async deleteCode(req: AuthenticatedRequest, res: Response) {
|
||||
try {
|
||||
const { categoryCode, codeValue } = req.params;
|
||||
|
||||
await this.commonCodeService.deleteCode(categoryCode, codeValue);
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
message: "코드 삭제 성공",
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`코드 삭제 실패 (${req.params.categoryCode}.${req.params.codeValue}):`,
|
||||
error
|
||||
);
|
||||
|
||||
if (
|
||||
error instanceof Error &&
|
||||
error.message.includes("Record to delete does not exist")
|
||||
) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "존재하지 않는 코드입니다.",
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "코드 삭제 중 오류가 발생했습니다.",
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 카테고리별 옵션 조회 (화면관리용)
|
||||
* GET /api/common-codes/categories/:categoryCode/options
|
||||
*/
|
||||
async getCodeOptions(req: AuthenticatedRequest, res: Response) {
|
||||
try {
|
||||
const { categoryCode } = req.params;
|
||||
|
||||
const options = await this.commonCodeService.getCodeOptions(categoryCode);
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
data: options,
|
||||
message: `코드 옵션 조회 성공 (${categoryCode})`,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(`코드 옵션 조회 실패 (${req.params.categoryCode}):`, error);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "코드 옵션 조회 중 오류가 발생했습니다.",
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 코드 순서 변경
|
||||
* PUT /api/common-codes/categories/:categoryCode/codes/reorder
|
||||
*/
|
||||
async reorderCodes(req: AuthenticatedRequest, res: Response) {
|
||||
try {
|
||||
const { categoryCode } = req.params;
|
||||
const { codes } = req.body as {
|
||||
codes: Array<{ codeValue: string; sortOrder: number }>;
|
||||
};
|
||||
const userId = req.user?.userId || "SYSTEM";
|
||||
|
||||
if (!codes || !Array.isArray(codes)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "코드 순서 정보가 올바르지 않습니다.",
|
||||
});
|
||||
}
|
||||
|
||||
await this.commonCodeService.reorderCodes(categoryCode, codes, userId);
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
message: "코드 순서 변경 성공",
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(`코드 순서 변경 실패 (${req.params.categoryCode}):`, error);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "코드 순서 변경 중 오류가 발생했습니다.",
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
49
backend-node/src/routes/commonCodeRoutes.ts
Normal file
49
backend-node/src/routes/commonCodeRoutes.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { Router } from "express";
|
||||
import { CommonCodeController } from "../controllers/commonCodeController";
|
||||
import { authenticateToken } from "../middleware/authMiddleware";
|
||||
|
||||
const router = Router();
|
||||
const commonCodeController = new CommonCodeController();
|
||||
|
||||
// 모든 공통코드 API는 인증이 필요
|
||||
router.use(authenticateToken);
|
||||
|
||||
// 카테고리 관련 라우트
|
||||
router.get("/categories", (req, res) =>
|
||||
commonCodeController.getCategories(req, res)
|
||||
);
|
||||
router.post("/categories", (req, res) =>
|
||||
commonCodeController.createCategory(req, res)
|
||||
);
|
||||
router.put("/categories/:categoryCode", (req, res) =>
|
||||
commonCodeController.updateCategory(req, res)
|
||||
);
|
||||
router.delete("/categories/:categoryCode", (req, res) =>
|
||||
commonCodeController.deleteCategory(req, res)
|
||||
);
|
||||
|
||||
// 코드 관련 라우트
|
||||
router.get("/categories/:categoryCode/codes", (req, res) =>
|
||||
commonCodeController.getCodes(req, res)
|
||||
);
|
||||
router.post("/categories/:categoryCode/codes", (req, res) =>
|
||||
commonCodeController.createCode(req, res)
|
||||
);
|
||||
router.put("/categories/:categoryCode/codes/:codeValue", (req, res) =>
|
||||
commonCodeController.updateCode(req, res)
|
||||
);
|
||||
router.delete("/categories/:categoryCode/codes/:codeValue", (req, res) =>
|
||||
commonCodeController.deleteCode(req, res)
|
||||
);
|
||||
|
||||
// 코드 순서 변경
|
||||
router.put("/categories/:categoryCode/codes/reorder", (req, res) =>
|
||||
commonCodeController.reorderCodes(req, res)
|
||||
);
|
||||
|
||||
// 화면관리용 옵션 조회
|
||||
router.get("/categories/:categoryCode/options", (req, res) =>
|
||||
commonCodeController.getCodeOptions(req, res)
|
||||
);
|
||||
|
||||
export default router;
|
||||
370
backend-node/src/services/commonCodeService.ts
Normal file
370
backend-node/src/services/commonCodeService.ts
Normal file
@@ -0,0 +1,370 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { logger } from "../utils/logger";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export interface CodeCategory {
|
||||
category_code: string;
|
||||
category_name: string;
|
||||
category_name_eng?: string | null;
|
||||
description?: string | null;
|
||||
sort_order: number;
|
||||
is_active: string;
|
||||
created_date?: Date | null;
|
||||
created_by?: string | null;
|
||||
updated_date?: Date | null;
|
||||
updated_by?: string | null;
|
||||
}
|
||||
|
||||
export interface CodeInfo {
|
||||
code_category: string;
|
||||
code_value: string;
|
||||
code_name: string;
|
||||
code_name_eng?: string | null;
|
||||
description?: string | null;
|
||||
sort_order: number;
|
||||
is_active: string;
|
||||
created_date?: Date | null;
|
||||
created_by?: string | null;
|
||||
updated_date?: Date | null;
|
||||
updated_by?: string | null;
|
||||
}
|
||||
|
||||
export interface GetCategoriesParams {
|
||||
search?: string;
|
||||
isActive?: boolean;
|
||||
page?: number;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
export interface GetCodesParams {
|
||||
search?: string;
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
export interface CreateCategoryData {
|
||||
categoryCode: string;
|
||||
categoryName: string;
|
||||
categoryNameEng?: string;
|
||||
description?: string;
|
||||
sortOrder?: number;
|
||||
}
|
||||
|
||||
export interface CreateCodeData {
|
||||
codeValue: string;
|
||||
codeName: string;
|
||||
codeNameEng?: string;
|
||||
description?: string;
|
||||
sortOrder?: number;
|
||||
}
|
||||
|
||||
export class CommonCodeService {
|
||||
/**
|
||||
* 카테고리 목록 조회
|
||||
*/
|
||||
async getCategories(params: GetCategoriesParams) {
|
||||
try {
|
||||
const { search, isActive, page = 1, size = 20 } = params;
|
||||
|
||||
let whereClause: any = {};
|
||||
|
||||
if (search) {
|
||||
whereClause.OR = [
|
||||
{ category_name: { contains: search, mode: "insensitive" } },
|
||||
{ category_code: { contains: search, mode: "insensitive" } },
|
||||
];
|
||||
}
|
||||
|
||||
if (isActive !== undefined) {
|
||||
whereClause.is_active = isActive ? "Y" : "N";
|
||||
}
|
||||
|
||||
const offset = (page - 1) * size;
|
||||
|
||||
const [categories, total] = await Promise.all([
|
||||
prisma.code_category.findMany({
|
||||
where: whereClause,
|
||||
orderBy: [{ sort_order: "asc" }, { category_code: "asc" }],
|
||||
skip: offset,
|
||||
take: size,
|
||||
}),
|
||||
prisma.code_category.count({ where: whereClause }),
|
||||
]);
|
||||
|
||||
logger.info(
|
||||
`카테고리 조회 완료: ${categories.length}개, 전체: ${total}개`
|
||||
);
|
||||
|
||||
return {
|
||||
data: categories,
|
||||
total,
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error("카테고리 조회 중 오류:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 카테고리별 코드 목록 조회
|
||||
*/
|
||||
async getCodes(categoryCode: string, params: GetCodesParams) {
|
||||
try {
|
||||
const { search, isActive } = params;
|
||||
|
||||
let whereClause: any = {
|
||||
code_category: categoryCode,
|
||||
};
|
||||
|
||||
if (search) {
|
||||
whereClause.OR = [
|
||||
{ code_name: { contains: search, mode: "insensitive" } },
|
||||
{ code_value: { contains: search, mode: "insensitive" } },
|
||||
];
|
||||
}
|
||||
|
||||
if (isActive !== undefined) {
|
||||
whereClause.is_active = isActive ? "Y" : "N";
|
||||
}
|
||||
|
||||
const codes = await prisma.code_info.findMany({
|
||||
where: whereClause,
|
||||
orderBy: [{ sort_order: "asc" }, { code_value: "asc" }],
|
||||
});
|
||||
|
||||
logger.info(`코드 조회 완료: ${categoryCode} - ${codes.length}개`);
|
||||
|
||||
return codes;
|
||||
} catch (error) {
|
||||
logger.error(`코드 조회 중 오류 (${categoryCode}):`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 카테고리 생성
|
||||
*/
|
||||
async createCategory(data: CreateCategoryData, createdBy: string) {
|
||||
try {
|
||||
const category = await prisma.code_category.create({
|
||||
data: {
|
||||
category_code: data.categoryCode,
|
||||
category_name: data.categoryName,
|
||||
category_name_eng: data.categoryNameEng,
|
||||
description: data.description,
|
||||
sort_order: data.sortOrder || 0,
|
||||
is_active: "Y",
|
||||
created_by: createdBy,
|
||||
updated_by: createdBy,
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(`카테고리 생성 완료: ${data.categoryCode}`);
|
||||
return category;
|
||||
} catch (error) {
|
||||
logger.error("카테고리 생성 중 오류:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 카테고리 수정
|
||||
*/
|
||||
async updateCategory(
|
||||
categoryCode: string,
|
||||
data: Partial<CreateCategoryData>,
|
||||
updatedBy: string
|
||||
) {
|
||||
try {
|
||||
const category = await prisma.code_category.update({
|
||||
where: { category_code: categoryCode },
|
||||
data: {
|
||||
category_name: data.categoryName,
|
||||
category_name_eng: data.categoryNameEng,
|
||||
description: data.description,
|
||||
sort_order: data.sortOrder,
|
||||
updated_by: updatedBy,
|
||||
updated_date: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(`카테고리 수정 완료: ${categoryCode}`);
|
||||
return category;
|
||||
} catch (error) {
|
||||
logger.error(`카테고리 수정 중 오류 (${categoryCode}):`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 카테고리 삭제
|
||||
*/
|
||||
async deleteCategory(categoryCode: string) {
|
||||
try {
|
||||
await prisma.code_category.delete({
|
||||
where: { category_code: categoryCode },
|
||||
});
|
||||
|
||||
logger.info(`카테고리 삭제 완료: ${categoryCode}`);
|
||||
} catch (error) {
|
||||
logger.error(`카테고리 삭제 중 오류 (${categoryCode}):`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 코드 생성
|
||||
*/
|
||||
async createCode(
|
||||
categoryCode: string,
|
||||
data: CreateCodeData,
|
||||
createdBy: string
|
||||
) {
|
||||
try {
|
||||
const code = await prisma.code_info.create({
|
||||
data: {
|
||||
code_category: categoryCode,
|
||||
code_value: data.codeValue,
|
||||
code_name: data.codeName,
|
||||
code_name_eng: data.codeNameEng,
|
||||
description: data.description,
|
||||
sort_order: data.sortOrder || 0,
|
||||
is_active: "Y",
|
||||
created_by: createdBy,
|
||||
updated_by: createdBy,
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(`코드 생성 완료: ${categoryCode}.${data.codeValue}`);
|
||||
return code;
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`코드 생성 중 오류 (${categoryCode}.${data.codeValue}):`,
|
||||
error
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 코드 수정
|
||||
*/
|
||||
async updateCode(
|
||||
categoryCode: string,
|
||||
codeValue: string,
|
||||
data: Partial<CreateCodeData>,
|
||||
updatedBy: string
|
||||
) {
|
||||
try {
|
||||
const code = await prisma.code_info.update({
|
||||
where: {
|
||||
code_category_code_value: {
|
||||
code_category: categoryCode,
|
||||
code_value: codeValue,
|
||||
},
|
||||
},
|
||||
data: {
|
||||
code_name: data.codeName,
|
||||
code_name_eng: data.codeNameEng,
|
||||
description: data.description,
|
||||
sort_order: data.sortOrder,
|
||||
updated_by: updatedBy,
|
||||
updated_date: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(`코드 수정 완료: ${categoryCode}.${codeValue}`);
|
||||
return code;
|
||||
} catch (error) {
|
||||
logger.error(`코드 수정 중 오류 (${categoryCode}.${codeValue}):`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 코드 삭제
|
||||
*/
|
||||
async deleteCode(categoryCode: string, codeValue: string) {
|
||||
try {
|
||||
await prisma.code_info.delete({
|
||||
where: {
|
||||
code_category_code_value: {
|
||||
code_category: categoryCode,
|
||||
code_value: codeValue,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
logger.info(`코드 삭제 완료: ${categoryCode}.${codeValue}`);
|
||||
} catch (error) {
|
||||
logger.error(`코드 삭제 중 오류 (${categoryCode}.${codeValue}):`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 카테고리별 옵션 조회 (화면관리용)
|
||||
*/
|
||||
async getCodeOptions(categoryCode: string) {
|
||||
try {
|
||||
const codes = await prisma.code_info.findMany({
|
||||
where: {
|
||||
code_category: categoryCode,
|
||||
is_active: "Y",
|
||||
},
|
||||
select: {
|
||||
code_value: true,
|
||||
code_name: true,
|
||||
code_name_eng: true,
|
||||
sort_order: true,
|
||||
},
|
||||
orderBy: [{ sort_order: "asc" }, { code_value: "asc" }],
|
||||
});
|
||||
|
||||
const options = codes.map((code) => ({
|
||||
value: code.code_value,
|
||||
label: code.code_name,
|
||||
labelEng: code.code_name_eng,
|
||||
}));
|
||||
|
||||
logger.info(`코드 옵션 조회 완료: ${categoryCode} - ${options.length}개`);
|
||||
return options;
|
||||
} catch (error) {
|
||||
logger.error(`코드 옵션 조회 중 오류 (${categoryCode}):`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 코드 순서 변경
|
||||
*/
|
||||
async reorderCodes(
|
||||
categoryCode: string,
|
||||
codes: Array<{ codeValue: string; sortOrder: number }>,
|
||||
updatedBy: string
|
||||
) {
|
||||
try {
|
||||
const updatePromises = codes.map(({ codeValue, sortOrder }) =>
|
||||
prisma.code_info.update({
|
||||
where: {
|
||||
code_category_code_value: {
|
||||
code_category: categoryCode,
|
||||
code_value: codeValue,
|
||||
},
|
||||
},
|
||||
data: {
|
||||
sort_order: sortOrder,
|
||||
updated_by: updatedBy,
|
||||
updated_date: new Date(),
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
await Promise.all(updatePromises);
|
||||
logger.info(`코드 순서 변경 완료: ${categoryCode} - ${codes.length}개`);
|
||||
} catch (error) {
|
||||
logger.error(`코드 순서 변경 중 오류 (${categoryCode}):`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
93
backend-node/src/types/commonCode.ts
Normal file
93
backend-node/src/types/commonCode.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
// 공통코드 관련 타입 정의
|
||||
|
||||
export interface CodeCategory {
|
||||
category_code: string;
|
||||
category_name: string;
|
||||
category_name_eng?: string | null;
|
||||
description?: string | null;
|
||||
sort_order: number;
|
||||
is_active: string;
|
||||
created_date?: Date | null;
|
||||
created_by?: string | null;
|
||||
updated_date?: Date | null;
|
||||
updated_by?: string | null;
|
||||
}
|
||||
|
||||
export interface CodeInfo {
|
||||
code_category: string;
|
||||
code_value: string;
|
||||
code_name: string;
|
||||
code_name_eng?: string | null;
|
||||
description?: string | null;
|
||||
sort_order: number;
|
||||
is_active: string;
|
||||
created_date?: Date | null;
|
||||
created_by?: string | null;
|
||||
updated_date?: Date | null;
|
||||
updated_by?: string | null;
|
||||
}
|
||||
|
||||
export interface CreateCategoryRequest {
|
||||
categoryCode: string;
|
||||
categoryName: string;
|
||||
categoryNameEng?: string;
|
||||
description?: string;
|
||||
sortOrder?: number;
|
||||
}
|
||||
|
||||
export interface UpdateCategoryRequest {
|
||||
categoryName?: string;
|
||||
categoryNameEng?: string;
|
||||
description?: string;
|
||||
sortOrder?: number;
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
export interface CreateCodeRequest {
|
||||
codeValue: string;
|
||||
codeName: string;
|
||||
codeNameEng?: string;
|
||||
description?: string;
|
||||
sortOrder?: number;
|
||||
}
|
||||
|
||||
export interface UpdateCodeRequest {
|
||||
codeName?: string;
|
||||
codeNameEng?: string;
|
||||
description?: string;
|
||||
sortOrder?: number;
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
export interface CodeOption {
|
||||
value: string;
|
||||
label: string;
|
||||
labelEng?: string | null;
|
||||
}
|
||||
|
||||
export interface ReorderCodesRequest {
|
||||
codes: Array<{
|
||||
codeValue: string;
|
||||
sortOrder: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface GetCategoriesQuery {
|
||||
search?: string;
|
||||
isActive?: string;
|
||||
page?: string;
|
||||
size?: string;
|
||||
}
|
||||
|
||||
export interface GetCodesQuery {
|
||||
search?: string;
|
||||
isActive?: string;
|
||||
}
|
||||
|
||||
export interface ApiResponse<T = any> {
|
||||
success: boolean;
|
||||
data?: T;
|
||||
message: string;
|
||||
error?: string;
|
||||
total?: number;
|
||||
}
|
||||
Reference in New Issue
Block a user