feat: 코드 컴포넌트에 메뉴 스코프 적용

- useCodeOptions 훅에 menuObjid 파라미터 추가
- commonCodeApi.codes.getList에 menuObjid 전달
- SelectBasicComponent에서 menuObjid 받아서 useCodeOptions로 전달
- InteractiveScreenViewer에서 DynamicWebTypeRenderer로 menuObjid 전달
- 화면 페이지에서 RealtimePreview로 menuObjid 전달

이제 코드 위젯도 카테고리처럼 형제 메뉴별로 격리됩니다.
This commit is contained in:
kjs
2025-11-11 15:25:07 +09:00
parent 6ebe551caa
commit 32d4575fb5
8 changed files with 84 additions and 24 deletions

View File

@@ -20,8 +20,9 @@ export class CommonCodeController {
*/
async getCategories(req: AuthenticatedRequest, res: Response) {
try {
const { search, isActive, page = "1", size = "20" } = req.query;
const { search, isActive, page = "1", size = "20", menuObjid } = req.query;
const userCompanyCode = req.user?.companyCode;
const menuObjidNum = menuObjid ? Number(menuObjid) : undefined;
const categories = await this.commonCodeService.getCategories(
{
@@ -35,7 +36,8 @@ export class CommonCodeController {
page: parseInt(page as string),
size: parseInt(size as string),
},
userCompanyCode
userCompanyCode,
menuObjidNum
);
return res.json({
@@ -61,8 +63,9 @@ export class CommonCodeController {
async getCodes(req: AuthenticatedRequest, res: Response) {
try {
const { categoryCode } = req.params;
const { search, isActive, page, size } = req.query;
const { search, isActive, page, size, menuObjid } = req.query;
const userCompanyCode = req.user?.companyCode;
const menuObjidNum = menuObjid ? Number(menuObjid) : undefined;
const result = await this.commonCodeService.getCodes(
categoryCode,
@@ -77,7 +80,8 @@ export class CommonCodeController {
page: page ? parseInt(page as string) : undefined,
size: size ? parseInt(size as string) : undefined,
},
userCompanyCode
userCompanyCode,
menuObjidNum
);
// 프론트엔드가 기대하는 형식으로 데이터 변환
@@ -131,6 +135,7 @@ export class CommonCodeController {
const categoryData: CreateCategoryData = req.body;
const userId = req.user?.userId || "SYSTEM";
const companyCode = req.user?.companyCode || "*";
const menuObjid = req.body.menuObjid;
// 입력값 검증
if (!categoryData.categoryCode || !categoryData.categoryName) {
@@ -140,10 +145,18 @@ export class CommonCodeController {
});
}
if (!menuObjid) {
return res.status(400).json({
success: false,
message: "메뉴 OBJID는 필수입니다.",
});
}
const category = await this.commonCodeService.createCategory(
categoryData,
userId,
companyCode
companyCode,
Number(menuObjid)
);
return res.status(201).json({
@@ -263,6 +276,7 @@ export class CommonCodeController {
const codeData: CreateCodeData = req.body;
const userId = req.user?.userId || "SYSTEM";
const companyCode = req.user?.companyCode || "*";
const menuObjid = req.body.menuObjid;
// 입력값 검증
if (!codeData.codeValue || !codeData.codeName) {
@@ -272,11 +286,19 @@ export class CommonCodeController {
});
}
if (!menuObjid) {
return res.status(400).json({
success: false,
message: "메뉴 OBJID는 필수입니다.",
});
}
const code = await this.commonCodeService.createCode(
categoryCode,
codeData,
userId,
companyCode
companyCode,
Number(menuObjid)
);
return res.status(201).json({

View File

@@ -66,7 +66,7 @@ export class CommonCodeService {
/**
* 카테고리 목록 조회
*/
async getCategories(params: GetCategoriesParams, userCompanyCode?: string) {
async getCategories(params: GetCategoriesParams, userCompanyCode?: string, menuObjid?: number) {
try {
const { search, isActive, page = 1, size = 20 } = params;
@@ -74,6 +74,16 @@ export class CommonCodeService {
const values: any[] = [];
let paramIndex = 1;
// 메뉴별 필터링 (형제 메뉴 포함)
if (menuObjid) {
const { getSiblingMenuObjids } = await import('./menuService');
const siblingMenuObjids = await getSiblingMenuObjids(menuObjid);
whereConditions.push(`menu_objid = ANY($${paramIndex})`);
values.push(siblingMenuObjids);
paramIndex++;
logger.info(`메뉴별 코드 카테고리 필터링: ${menuObjid}, 형제 메뉴: ${siblingMenuObjids.join(', ')}`);
}
// 회사별 필터링 (최고 관리자가 아닌 경우)
if (userCompanyCode && userCompanyCode !== "*") {
whereConditions.push(`company_code = $${paramIndex}`);
@@ -142,7 +152,8 @@ export class CommonCodeService {
async getCodes(
categoryCode: string,
params: GetCodesParams,
userCompanyCode?: string
userCompanyCode?: string,
menuObjid?: number
) {
try {
const { search, isActive, page = 1, size = 20 } = params;
@@ -151,6 +162,16 @@ export class CommonCodeService {
const values: any[] = [categoryCode];
let paramIndex = 2;
// 메뉴별 필터링 (형제 메뉴 포함)
if (menuObjid) {
const { getSiblingMenuObjids } = await import('./menuService');
const siblingMenuObjids = await getSiblingMenuObjids(menuObjid);
whereConditions.push(`menu_objid = ANY($${paramIndex})`);
values.push(siblingMenuObjids);
paramIndex++;
logger.info(`메뉴별 코드 필터링: ${menuObjid}, 형제 메뉴: ${siblingMenuObjids.join(', ')}`);
}
// 회사별 필터링 (최고 관리자가 아닌 경우)
if (userCompanyCode && userCompanyCode !== "*") {
whereConditions.push(`company_code = $${paramIndex}`);
@@ -212,14 +233,15 @@ export class CommonCodeService {
async createCategory(
data: CreateCategoryData,
createdBy: string,
companyCode: string
companyCode: string,
menuObjid: number
) {
try {
const category = await queryOne<CodeCategory>(
`INSERT INTO code_category
(category_code, category_name, category_name_eng, description, sort_order,
is_active, company_code, created_by, updated_by, created_date, updated_date)
VALUES ($1, $2, $3, $4, $5, 'Y', $6, $7, $8, NOW(), NOW())
is_active, menu_objid, company_code, created_by, updated_by, created_date, updated_date)
VALUES ($1, $2, $3, $4, $5, 'Y', $6, $7, $8, $9, NOW(), NOW())
RETURNING *`,
[
data.categoryCode,
@@ -227,6 +249,7 @@ export class CommonCodeService {
data.categoryNameEng || null,
data.description || null,
data.sortOrder || 0,
menuObjid,
companyCode,
createdBy,
createdBy,
@@ -234,7 +257,7 @@ export class CommonCodeService {
);
logger.info(
`카테고리 생성 완료: ${data.categoryCode} (회사: ${companyCode})`
`카테고리 생성 완료: ${data.categoryCode} (메뉴: ${menuObjid}, 회사: ${companyCode})`
);
return category;
} catch (error) {
@@ -352,14 +375,15 @@ export class CommonCodeService {
categoryCode: string,
data: CreateCodeData,
createdBy: string,
companyCode: string
companyCode: string,
menuObjid: number
) {
try {
const code = await queryOne<CodeInfo>(
`INSERT INTO code_info
(code_category, code_value, code_name, code_name_eng, description, sort_order,
is_active, company_code, created_by, updated_by, created_date, updated_date)
VALUES ($1, $2, $3, $4, $5, $6, 'Y', $7, $8, $9, NOW(), NOW())
is_active, menu_objid, company_code, created_by, updated_by, created_date, updated_date)
VALUES ($1, $2, $3, $4, $5, $6, 'Y', $7, $8, $9, $10, NOW(), NOW())
RETURNING *`,
[
categoryCode,
@@ -368,6 +392,7 @@ export class CommonCodeService {
data.codeNameEng || null,
data.description || null,
data.sortOrder || 0,
menuObjid,
companyCode,
createdBy,
createdBy,
@@ -375,7 +400,7 @@ export class CommonCodeService {
);
logger.info(
`코드 생성 완료: ${categoryCode}.${data.codeValue} (회사: ${companyCode})`
`코드 생성 완료: ${categoryCode}.${data.codeValue} (메뉴: ${menuObjid}, 회사: ${companyCode})`
);
return code;
} catch (error) {