feat: 화면 편집기에서 메뉴 기반 데이터 스코프 적용
- 백엔드: screenManagementService에 getMenuByScreen 함수 추가 - 백엔드: GET /api/screen-management/screens/:id/menu 엔드포인트 추가 - 프론트엔드: screenApi.getScreenMenu() 함수 추가 - ScreenDesigner: 화면 로드 시 menu_objid 자동 조회 - ScreenDesigner: menuObjid를 RealtimePreview와 UnifiedPropertiesPanel에 전달 - UnifiedPropertiesPanel: menuObjid를 DynamicComponentConfigPanel에 전달 이로써 화면 편집기에서 코드/카테고리/채번규칙이 해당 화면이 할당된 메뉴 기준으로 필터링됨
This commit is contained in:
@@ -158,6 +158,17 @@ export class CommonCodeService {
|
||||
try {
|
||||
const { search, isActive, page = 1, size = 20 } = params;
|
||||
|
||||
logger.info(`🔍 [getCodes] 코드 조회 시작:`, {
|
||||
categoryCode,
|
||||
menuObjid,
|
||||
hasMenuObjid: !!menuObjid,
|
||||
userCompanyCode,
|
||||
search,
|
||||
isActive,
|
||||
page,
|
||||
size,
|
||||
});
|
||||
|
||||
const whereConditions: string[] = ["code_category = $1"];
|
||||
const values: any[] = [categoryCode];
|
||||
let paramIndex = 2;
|
||||
@@ -169,7 +180,13 @@ export class CommonCodeService {
|
||||
whereConditions.push(`menu_objid = ANY($${paramIndex})`);
|
||||
values.push(siblingMenuObjids);
|
||||
paramIndex++;
|
||||
logger.info(`메뉴별 코드 필터링: ${menuObjid}, 형제 메뉴: ${siblingMenuObjids.join(', ')}`);
|
||||
logger.info(`📋 [getCodes] 메뉴별 코드 필터링:`, {
|
||||
menuObjid,
|
||||
siblingMenuObjids,
|
||||
siblingCount: siblingMenuObjids.length,
|
||||
});
|
||||
} else {
|
||||
logger.warn(`⚠️ [getCodes] menuObjid 없음 - 전역 코드 조회`);
|
||||
}
|
||||
|
||||
// 회사별 필터링 (최고 관리자가 아닌 경우)
|
||||
@@ -199,6 +216,13 @@ export class CommonCodeService {
|
||||
|
||||
const offset = (page - 1) * size;
|
||||
|
||||
logger.info(`📝 [getCodes] 실행할 쿼리:`, {
|
||||
whereClause,
|
||||
values,
|
||||
whereConditions,
|
||||
paramIndex,
|
||||
});
|
||||
|
||||
// 코드 조회
|
||||
const codes = await query<CodeInfo>(
|
||||
`SELECT * FROM code_info
|
||||
@@ -217,9 +241,20 @@ export class CommonCodeService {
|
||||
const total = parseInt(countResult?.count || "0");
|
||||
|
||||
logger.info(
|
||||
`코드 조회 완료: ${categoryCode} - ${codes.length}개, 전체: ${total}개 (회사: ${userCompanyCode || "전체"})`
|
||||
`✅ [getCodes] 코드 조회 완료: ${categoryCode} - ${codes.length}개, 전체: ${total}개 (회사: ${userCompanyCode || "전체"}, menuObjid: ${menuObjid || "없음"})`
|
||||
);
|
||||
|
||||
logger.info(`📊 [getCodes] 조회된 코드 상세:`, {
|
||||
categoryCode,
|
||||
menuObjid,
|
||||
codes: codes.map((c) => ({
|
||||
code_value: c.code_value,
|
||||
code_name: c.code_name,
|
||||
menu_objid: c.menu_objid,
|
||||
company_code: c.company_code,
|
||||
})),
|
||||
});
|
||||
|
||||
return { data: codes, total };
|
||||
} catch (error) {
|
||||
logger.error(`코드 조회 중 오류 (${categoryCode}):`, error);
|
||||
|
||||
@@ -301,16 +301,18 @@ class NumberingRuleService {
|
||||
scope_type = 'global'
|
||||
OR scope_type = 'table'
|
||||
OR (scope_type = 'menu' AND menu_objid = ANY($1))
|
||||
OR (scope_type = 'table' AND menu_objid = ANY($1)) -- ⚠️ 임시: table 스코프도 menu_objid로 필터링
|
||||
OR (scope_type = 'table' AND menu_objid IS NULL) -- ⚠️ 임시: 기존 규칙(menu_objid NULL) 포함
|
||||
ORDER BY
|
||||
CASE scope_type
|
||||
WHEN 'menu' THEN 1
|
||||
WHEN 'table' THEN 2
|
||||
WHEN 'global' THEN 3
|
||||
CASE
|
||||
WHEN scope_type = 'menu' OR (scope_type = 'table' AND menu_objid = ANY($1)) THEN 1
|
||||
WHEN scope_type = 'table' THEN 2
|
||||
WHEN scope_type = 'global' THEN 3
|
||||
END,
|
||||
created_at DESC
|
||||
`;
|
||||
params = [siblingObjids];
|
||||
logger.info("최고 관리자: 형제 메뉴 포함 채번 규칙 조회", { siblingObjids });
|
||||
logger.info("최고 관리자: 형제 메뉴 포함 채번 규칙 조회 (기존 규칙 포함)", { siblingObjids });
|
||||
} else {
|
||||
// 일반 회사: 자신의 규칙만 조회 (형제 메뉴 포함)
|
||||
query = `
|
||||
@@ -335,17 +337,19 @@ class NumberingRuleService {
|
||||
scope_type = 'global'
|
||||
OR scope_type = 'table'
|
||||
OR (scope_type = 'menu' AND menu_objid = ANY($2))
|
||||
OR (scope_type = 'table' AND menu_objid = ANY($2)) -- ⚠️ 임시: table 스코프도 menu_objid로 필터링
|
||||
OR (scope_type = 'table' AND menu_objid IS NULL) -- ⚠️ 임시: 기존 규칙(menu_objid NULL) 포함
|
||||
)
|
||||
ORDER BY
|
||||
CASE scope_type
|
||||
WHEN 'menu' THEN 1
|
||||
WHEN 'table' THEN 2
|
||||
WHEN 'global' THEN 3
|
||||
CASE
|
||||
WHEN scope_type = 'menu' OR (scope_type = 'table' AND menu_objid = ANY($2)) THEN 1
|
||||
WHEN scope_type = 'table' THEN 2
|
||||
WHEN scope_type = 'global' THEN 3
|
||||
END,
|
||||
created_at DESC
|
||||
`;
|
||||
params = [companyCode, siblingObjids];
|
||||
logger.info("회사별: 형제 메뉴 포함 채번 규칙 조회", { companyCode, siblingObjids });
|
||||
logger.info("회사별: 형제 메뉴 포함 채번 규칙 조회 (기존 규칙 포함)", { companyCode, siblingObjids });
|
||||
}
|
||||
|
||||
logger.info("🔍 채번 규칙 쿼리 실행", {
|
||||
|
||||
@@ -1547,6 +1547,39 @@ export class ScreenManagementService {
|
||||
return screens.map((screen) => this.mapToScreenDefinition(screen));
|
||||
}
|
||||
|
||||
/**
|
||||
* 화면에 할당된 메뉴 조회 (첫 번째 할당만 반환)
|
||||
* 화면 편집기에서 menuObjid를 가져오기 위해 사용
|
||||
*/
|
||||
async getMenuByScreen(
|
||||
screenId: number,
|
||||
companyCode: string
|
||||
): Promise<{ menuObjid: number; menuName?: string } | null> {
|
||||
const result = await queryOne<{
|
||||
menu_objid: string;
|
||||
menu_name_kor?: string;
|
||||
}>(
|
||||
`SELECT sma.menu_objid, mi.menu_name_kor
|
||||
FROM screen_menu_assignments sma
|
||||
LEFT JOIN menu_info mi ON sma.menu_objid = mi.objid
|
||||
WHERE sma.screen_id = $1
|
||||
AND sma.company_code = $2
|
||||
AND sma.is_active = 'Y'
|
||||
ORDER BY sma.created_at ASC
|
||||
LIMIT 1`,
|
||||
[screenId, companyCode]
|
||||
);
|
||||
|
||||
if (!result) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
menuObjid: parseInt(result.menu_objid),
|
||||
menuName: result.menu_name_kor,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 화면-메뉴 할당 해제 (✅ Raw Query 전환 완료)
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user