feat: 멀티테넌시 지원을 위한 레이어 관리 기능 추가
- 레이어 목록 조회, 특정 레이어 레이아웃 조회, 레이어 삭제 및 조건 설정 업데이트 기능을 추가했습니다. - 엔티티 참조 데이터 조회 및 공통 코드 데이터 조회에 멀티테넌시 필터를 적용하여 인증된 사용자의 회사 코드에 따라 데이터 접근을 제한했습니다. - 레이어 관리 패널에서 기본 레이어와 조건부 레이어의 컴포넌트를 통합하여 조건부 영역의 표시를 개선했습니다. - 레이아웃 저장 시 레이어 ID를 포함하여 레이어별로 저장할 수 있도록 변경했습니다.
This commit is contained in:
@@ -30,10 +30,13 @@ export class EntityReferenceController {
|
||||
try {
|
||||
const { tableName, columnName } = req.params;
|
||||
const { limit = 100, search } = req.query;
|
||||
// 멀티테넌시: 인증된 사용자의 회사 코드
|
||||
const companyCode = (req as any).user?.companyCode;
|
||||
|
||||
logger.info(`엔티티 참조 데이터 조회 요청: ${tableName}.${columnName}`, {
|
||||
limit,
|
||||
search,
|
||||
companyCode,
|
||||
});
|
||||
|
||||
// 컬럼 정보 조회 (table_type_columns에서)
|
||||
@@ -89,16 +92,34 @@ export class EntityReferenceController {
|
||||
});
|
||||
}
|
||||
|
||||
// 동적 쿼리로 참조 데이터 조회
|
||||
let sqlQuery = `SELECT ${referenceColumn}, ${displayColumn} as display_name FROM ${referenceTable}`;
|
||||
// 참조 테이블에 company_code 컬럼이 있는지 확인
|
||||
const hasCompanyCode = await queryOne<any>(
|
||||
`SELECT column_name FROM information_schema.columns
|
||||
WHERE table_name = $1 AND column_name = 'company_code' AND table_schema = 'public'`,
|
||||
[referenceTable]
|
||||
);
|
||||
|
||||
// 동적 쿼리로 참조 데이터 조회 (멀티테넌시 필터 적용)
|
||||
const whereConditions: string[] = [];
|
||||
const queryParams: any[] = [];
|
||||
|
||||
// 멀티테넌시: company_code 필터링 (참조 테이블에 company_code가 있는 경우)
|
||||
if (hasCompanyCode && companyCode && companyCode !== "*") {
|
||||
queryParams.push(companyCode);
|
||||
whereConditions.push(`company_code = $${queryParams.length}`);
|
||||
logger.info(`멀티테넌시 필터 적용: company_code = ${companyCode}`, { referenceTable });
|
||||
}
|
||||
|
||||
// 검색 조건 추가
|
||||
if (search) {
|
||||
sqlQuery += ` WHERE ${displayColumn} ILIKE $1`;
|
||||
queryParams.push(`%${search}%`);
|
||||
whereConditions.push(`${displayColumn} ILIKE $${queryParams.length}`);
|
||||
}
|
||||
|
||||
let sqlQuery = `SELECT ${referenceColumn}, ${displayColumn} as display_name FROM ${referenceTable}`;
|
||||
if (whereConditions.length > 0) {
|
||||
sqlQuery += ` WHERE ${whereConditions.join(" AND ")}`;
|
||||
}
|
||||
sqlQuery += ` ORDER BY ${displayColumn} LIMIT $${queryParams.length + 1}`;
|
||||
queryParams.push(Number(limit));
|
||||
|
||||
@@ -107,6 +128,7 @@ export class EntityReferenceController {
|
||||
referenceTable,
|
||||
referenceColumn,
|
||||
displayColumn,
|
||||
companyCode,
|
||||
});
|
||||
|
||||
const referenceData = await query<any>(sqlQuery, queryParams);
|
||||
@@ -119,7 +141,7 @@ export class EntityReferenceController {
|
||||
})
|
||||
);
|
||||
|
||||
logger.info(`엔티티 참조 데이터 조회 완료: ${options.length}개 항목`);
|
||||
logger.info(`엔티티 참조 데이터 조회 완료: ${options.length}개 항목`, { companyCode });
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
@@ -149,13 +171,16 @@ export class EntityReferenceController {
|
||||
try {
|
||||
const { codeCategory } = req.params;
|
||||
const { limit = 100, search } = req.query;
|
||||
// 멀티테넌시: 인증된 사용자의 회사 코드
|
||||
const companyCode = (req as any).user?.companyCode;
|
||||
|
||||
logger.info(`공통 코드 데이터 조회 요청: ${codeCategory}`, {
|
||||
limit,
|
||||
search,
|
||||
companyCode,
|
||||
});
|
||||
|
||||
// code_info 테이블에서 코드 데이터 조회
|
||||
// code_info 테이블에서 코드 데이터 조회 (멀티테넌시 필터 적용)
|
||||
const queryParams: any[] = [codeCategory, 'Y'];
|
||||
let sqlQuery = `
|
||||
SELECT code_value, code_name
|
||||
@@ -163,9 +188,16 @@ export class EntityReferenceController {
|
||||
WHERE code_category = $1 AND is_active = $2
|
||||
`;
|
||||
|
||||
// 멀티테넌시: company_code 필터링
|
||||
if (companyCode && companyCode !== "*") {
|
||||
queryParams.push(companyCode);
|
||||
sqlQuery += ` AND company_code = $${queryParams.length}`;
|
||||
logger.info(`공통 코드 멀티테넌시 필터 적용: company_code = ${companyCode}`);
|
||||
}
|
||||
|
||||
if (search) {
|
||||
sqlQuery += ` AND code_name ILIKE $3`;
|
||||
queryParams.push(`%${search}%`);
|
||||
sqlQuery += ` AND code_name ILIKE $${queryParams.length}`;
|
||||
}
|
||||
|
||||
sqlQuery += ` ORDER BY code_name ASC LIMIT $${queryParams.length + 1}`;
|
||||
@@ -174,12 +206,12 @@ export class EntityReferenceController {
|
||||
const codeData = await query<any>(sqlQuery, queryParams);
|
||||
|
||||
// 옵션 형태로 변환
|
||||
const options: EntityReferenceOption[] = codeData.map((code) => ({
|
||||
const options: EntityReferenceOption[] = codeData.map((code: any) => ({
|
||||
value: code.code_value,
|
||||
label: code.code_name,
|
||||
}));
|
||||
|
||||
logger.info(`공통 코드 데이터 조회 완료: ${options.length}개 항목`);
|
||||
logger.info(`공통 코드 데이터 조회 완료: ${options.length}개 항목`, { companyCode });
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
|
||||
@@ -732,6 +732,61 @@ export const saveLayoutV2 = async (req: AuthenticatedRequest, res: Response) =>
|
||||
}
|
||||
};
|
||||
|
||||
// 🆕 레이어 목록 조회
|
||||
export const getScreenLayers = async (req: AuthenticatedRequest, res: Response) => {
|
||||
try {
|
||||
const { screenId } = req.params;
|
||||
const { companyCode } = req.user as any;
|
||||
const layers = await screenManagementService.getScreenLayers(parseInt(screenId), companyCode);
|
||||
res.json({ success: true, data: layers });
|
||||
} catch (error) {
|
||||
console.error("레이어 목록 조회 실패:", error);
|
||||
res.status(500).json({ success: false, message: "레이어 목록 조회에 실패했습니다." });
|
||||
}
|
||||
};
|
||||
|
||||
// 🆕 특정 레이어 레이아웃 조회
|
||||
export const getLayerLayout = async (req: AuthenticatedRequest, res: Response) => {
|
||||
try {
|
||||
const { screenId, layerId } = req.params;
|
||||
const { companyCode } = req.user as any;
|
||||
const layout = await screenManagementService.getLayerLayout(parseInt(screenId), parseInt(layerId), companyCode);
|
||||
res.json({ success: true, data: layout });
|
||||
} catch (error) {
|
||||
console.error("레이어 레이아웃 조회 실패:", error);
|
||||
res.status(500).json({ success: false, message: "레이어 레이아웃 조회에 실패했습니다." });
|
||||
}
|
||||
};
|
||||
|
||||
// 🆕 레이어 삭제
|
||||
export const deleteLayer = async (req: AuthenticatedRequest, res: Response) => {
|
||||
try {
|
||||
const { screenId, layerId } = req.params;
|
||||
const { companyCode } = req.user as any;
|
||||
await screenManagementService.deleteLayer(parseInt(screenId), parseInt(layerId), companyCode);
|
||||
res.json({ success: true, message: "레이어가 삭제되었습니다." });
|
||||
} catch (error: any) {
|
||||
console.error("레이어 삭제 실패:", error);
|
||||
res.status(400).json({ success: false, message: error.message || "레이어 삭제에 실패했습니다." });
|
||||
}
|
||||
};
|
||||
|
||||
// 🆕 레이어 조건 설정 업데이트
|
||||
export const updateLayerCondition = async (req: AuthenticatedRequest, res: Response) => {
|
||||
try {
|
||||
const { screenId, layerId } = req.params;
|
||||
const { companyCode } = req.user as any;
|
||||
const { conditionConfig, layerName } = req.body;
|
||||
await screenManagementService.updateLayerCondition(
|
||||
parseInt(screenId), parseInt(layerId), companyCode, conditionConfig, layerName
|
||||
);
|
||||
res.json({ success: true, message: "레이어 조건이 업데이트되었습니다." });
|
||||
} catch (error) {
|
||||
console.error("레이어 조건 업데이트 실패:", error);
|
||||
res.status(500).json({ success: false, message: "레이어 조건 업데이트에 실패했습니다." });
|
||||
}
|
||||
};
|
||||
|
||||
// 화면 코드 자동 생성
|
||||
export const generateScreenCode = async (
|
||||
req: AuthenticatedRequest,
|
||||
|
||||
@@ -38,6 +38,10 @@ import {
|
||||
copyCategoryMapping,
|
||||
copyTableTypeColumns,
|
||||
copyCascadingRelation,
|
||||
getScreenLayers,
|
||||
getLayerLayout,
|
||||
deleteLayer,
|
||||
updateLayerCondition,
|
||||
} from "../controllers/screenManagementController";
|
||||
|
||||
const router = express.Router();
|
||||
@@ -84,6 +88,12 @@ router.get("/screens/:screenId/layout-v1", getLayoutV1); // V1: component_url +
|
||||
router.get("/screens/:screenId/layout-v2", getLayoutV2); // V2: 1 레코드 방식 (url + overrides)
|
||||
router.post("/screens/:screenId/layout-v2", saveLayoutV2); // V2: 1 레코드 방식 저장
|
||||
|
||||
// 🆕 레이어 관리
|
||||
router.get("/screens/:screenId/layers", getScreenLayers); // 레이어 목록
|
||||
router.get("/screens/:screenId/layers/:layerId/layout", getLayerLayout); // 특정 레이어 레이아웃
|
||||
router.delete("/screens/:screenId/layers/:layerId", deleteLayer); // 레이어 삭제
|
||||
router.put("/screens/:screenId/layers/:layerId/condition", updateLayerCondition); // 레이어 조건 설정
|
||||
|
||||
// 메뉴-화면 할당 관리
|
||||
router.post("/screens/:screenId/assign-menu", assignScreenToMenu);
|
||||
router.get("/menus/:menuObjid/screens", getScreensByMenu);
|
||||
|
||||
@@ -4245,11 +4245,11 @@ export class ScreenManagementService {
|
||||
},
|
||||
);
|
||||
|
||||
// V2 레이아웃 저장 (UPSERT)
|
||||
// V2 레이아웃 저장 (UPSERT) - layer_id 포함
|
||||
await client.query(
|
||||
`INSERT INTO screen_layouts_v2 (screen_id, company_code, layout_data, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, NOW(), NOW())
|
||||
ON CONFLICT (screen_id, company_code)
|
||||
`INSERT INTO screen_layouts_v2 (screen_id, company_code, layer_id, layout_data, created_at, updated_at)
|
||||
VALUES ($1, $2, 1, $3, NOW(), NOW())
|
||||
ON CONFLICT (screen_id, company_code, layer_id)
|
||||
DO UPDATE SET layout_data = $3, updated_at = NOW()`,
|
||||
[newScreen.screen_id, targetCompanyCode, JSON.stringify(updatedLayoutData)],
|
||||
);
|
||||
@@ -5073,38 +5073,63 @@ export class ScreenManagementService {
|
||||
|
||||
let layout: { layout_data: any } | null = null;
|
||||
|
||||
// 🆕 기본 레이어(layer_id=1)를 우선 로드
|
||||
// SUPER_ADMIN인 경우: 화면의 회사 코드로 레이아웃 조회
|
||||
if (isSuperAdmin) {
|
||||
// 1. 화면 정의의 회사 코드로 레이아웃 조회
|
||||
// 1. 화면 정의의 회사 코드 + 기본 레이어
|
||||
layout = await queryOne<{ layout_data: any }>(
|
||||
`SELECT layout_data FROM screen_layouts_v2
|
||||
WHERE screen_id = $1 AND company_code = $2`,
|
||||
WHERE screen_id = $1 AND company_code = $2 AND layer_id = 1`,
|
||||
[screenId, existingScreen.company_code],
|
||||
);
|
||||
|
||||
// 2. 화면 정의의 회사 코드로 없으면, 해당 화면의 모든 레이아웃 중 첫 번째 조회
|
||||
// 2. 기본 레이어 없으면 layer_id 조건 없이 조회 (하위 호환)
|
||||
if (!layout) {
|
||||
layout = await queryOne<{ layout_data: any }>(
|
||||
`SELECT layout_data FROM screen_layouts_v2
|
||||
WHERE screen_id = $1 AND company_code = $2
|
||||
ORDER BY layer_id ASC
|
||||
LIMIT 1`,
|
||||
[screenId, existingScreen.company_code],
|
||||
);
|
||||
}
|
||||
|
||||
// 3. 화면 정의의 회사 코드로 없으면, 해당 화면의 모든 레이아웃 중 첫 번째
|
||||
if (!layout) {
|
||||
layout = await queryOne<{ layout_data: any }>(
|
||||
`SELECT layout_data FROM screen_layouts_v2
|
||||
WHERE screen_id = $1
|
||||
ORDER BY updated_at DESC
|
||||
ORDER BY layer_id ASC
|
||||
LIMIT 1`,
|
||||
[screenId],
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// 일반 사용자: 기존 로직 (회사별 우선, 없으면 공통(*) 조회)
|
||||
// 일반 사용자: 회사별 우선 + 기본 레이어
|
||||
layout = await queryOne<{ layout_data: any }>(
|
||||
`SELECT layout_data FROM screen_layouts_v2
|
||||
WHERE screen_id = $1 AND company_code = $2`,
|
||||
WHERE screen_id = $1 AND company_code = $2 AND layer_id = 1`,
|
||||
[screenId, companyCode],
|
||||
);
|
||||
|
||||
// 회사별 기본 레이어 없으면 layer_id 조건 없이 (하위 호환)
|
||||
if (!layout) {
|
||||
layout = await queryOne<{ layout_data: any }>(
|
||||
`SELECT layout_data FROM screen_layouts_v2
|
||||
WHERE screen_id = $1 AND company_code = $2
|
||||
ORDER BY layer_id ASC
|
||||
LIMIT 1`,
|
||||
[screenId, companyCode],
|
||||
);
|
||||
}
|
||||
|
||||
// 회사별 레이아웃이 없으면 공통(*) 레이아웃 조회
|
||||
if (!layout && companyCode !== "*") {
|
||||
layout = await queryOne<{ layout_data: any }>(
|
||||
`SELECT layout_data FROM screen_layouts_v2
|
||||
WHERE screen_id = $1 AND company_code = '*'`,
|
||||
WHERE screen_id = $1 AND company_code = '*'
|
||||
ORDER BY layer_id ASC
|
||||
LIMIT 1`,
|
||||
[screenId],
|
||||
);
|
||||
}
|
||||
@@ -5122,17 +5147,21 @@ export class ScreenManagementService {
|
||||
}
|
||||
|
||||
/**
|
||||
* V2 레이아웃 저장 (1 레코드 방식)
|
||||
* - screen_layouts_v2 테이블에 화면당 1개 레코드 저장
|
||||
* - layout_data JSON에 모든 컴포넌트 포함
|
||||
* V2 레이아웃 저장 (레이어별 저장)
|
||||
* - screen_layouts_v2 테이블에 화면당 레이어별 1개 레코드 저장
|
||||
* - layout_data JSON에 해당 레이어의 컴포넌트 포함
|
||||
*/
|
||||
async saveLayoutV2(
|
||||
screenId: number,
|
||||
layoutData: any,
|
||||
companyCode: string,
|
||||
): Promise<void> {
|
||||
const layerId = layoutData.layerId || 1;
|
||||
const layerName = layoutData.layerName || (layerId === 1 ? '기본 레이어' : `레이어 ${layerId}`);
|
||||
const conditionConfig = layoutData.conditionConfig || null;
|
||||
|
||||
console.log(`=== V2 레이아웃 저장 시작 ===`);
|
||||
console.log(`화면 ID: ${screenId}, 회사: ${companyCode}`);
|
||||
console.log(`화면 ID: ${screenId}, 회사: ${companyCode}, 레이어: ${layerId} (${layerName})`);
|
||||
console.log(`컴포넌트 수: ${layoutData.components?.length || 0}`);
|
||||
|
||||
// 권한 확인
|
||||
@@ -5151,22 +5180,173 @@ export class ScreenManagementService {
|
||||
throw new Error("이 화면의 레이아웃을 저장할 권한이 없습니다.");
|
||||
}
|
||||
|
||||
// 버전 정보 추가 (updatedAt은 DB 컬럼 updated_at으로 관리)
|
||||
// 저장할 layout_data에서 레이어 메타 정보 제거 (순수 레이아웃만 저장)
|
||||
const { layerId: _lid, layerName: _ln, conditionConfig: _cc, ...pureLayoutData } = layoutData;
|
||||
const dataToSave = {
|
||||
version: "2.0",
|
||||
...layoutData
|
||||
...pureLayoutData,
|
||||
};
|
||||
|
||||
// UPSERT (있으면 업데이트, 없으면 삽입)
|
||||
// UPSERT (레이어별 저장)
|
||||
await query(
|
||||
`INSERT INTO screen_layouts_v2 (screen_id, company_code, layout_data, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, NOW(), NOW())
|
||||
ON CONFLICT (screen_id, company_code)
|
||||
DO UPDATE SET layout_data = $3, updated_at = NOW()`,
|
||||
[screenId, companyCode, JSON.stringify(dataToSave)],
|
||||
`INSERT INTO screen_layouts_v2 (screen_id, company_code, layer_id, layer_name, condition_config, layout_data, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, NOW(), NOW())
|
||||
ON CONFLICT (screen_id, company_code, layer_id)
|
||||
DO UPDATE SET layout_data = $6, layer_name = $4, condition_config = $5, updated_at = NOW()`,
|
||||
[screenId, companyCode, layerId, layerName, conditionConfig ? JSON.stringify(conditionConfig) : null, JSON.stringify(dataToSave)],
|
||||
);
|
||||
|
||||
console.log(`V2 레이아웃 저장 완료`);
|
||||
console.log(`V2 레이아웃 저장 완료 (레이어 ${layerId})`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 화면의 모든 레이어 목록 조회
|
||||
* 레이어가 없으면 기본 레이어를 자동 생성
|
||||
*/
|
||||
async getScreenLayers(
|
||||
screenId: number,
|
||||
companyCode: string,
|
||||
): Promise<any[]> {
|
||||
let layers;
|
||||
|
||||
if (companyCode === "*") {
|
||||
layers = await query<any>(
|
||||
`SELECT layer_id, layer_name, condition_config,
|
||||
jsonb_array_length(COALESCE(layout_data->'components', '[]'::jsonb)) as component_count,
|
||||
updated_at
|
||||
FROM screen_layouts_v2
|
||||
WHERE screen_id = $1
|
||||
ORDER BY layer_id`,
|
||||
[screenId],
|
||||
);
|
||||
} else {
|
||||
layers = await query<any>(
|
||||
`SELECT layer_id, layer_name, condition_config,
|
||||
jsonb_array_length(COALESCE(layout_data->'components', '[]'::jsonb)) as component_count,
|
||||
updated_at
|
||||
FROM screen_layouts_v2
|
||||
WHERE screen_id = $1 AND company_code = $2
|
||||
ORDER BY layer_id`,
|
||||
[screenId, companyCode],
|
||||
);
|
||||
|
||||
// 회사별 레이어가 없으면 공통(*) 레이어 조회
|
||||
if (layers.length === 0 && companyCode !== "*") {
|
||||
layers = await query<any>(
|
||||
`SELECT layer_id, layer_name, condition_config,
|
||||
jsonb_array_length(COALESCE(layout_data->'components', '[]'::jsonb)) as component_count,
|
||||
updated_at
|
||||
FROM screen_layouts_v2
|
||||
WHERE screen_id = $1 AND company_code = '*'
|
||||
ORDER BY layer_id`,
|
||||
[screenId],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 레이어가 없으면 기본 레이어 자동 생성
|
||||
if (layers.length === 0) {
|
||||
const defaultLayout = JSON.stringify({ version: "2.0", components: [] });
|
||||
await query(
|
||||
`INSERT INTO screen_layouts_v2 (screen_id, company_code, layer_id, layer_name, layout_data, created_at, updated_at)
|
||||
VALUES ($1, $2, 1, '기본 레이어', $3, NOW(), NOW())
|
||||
ON CONFLICT (screen_id, company_code, layer_id) DO NOTHING`,
|
||||
[screenId, companyCode, defaultLayout],
|
||||
);
|
||||
console.log(`기본 레이어 자동 생성: screen_id=${screenId}, company_code=${companyCode}`);
|
||||
|
||||
// 다시 조회
|
||||
layers = await query<any>(
|
||||
`SELECT layer_id, layer_name, condition_config,
|
||||
jsonb_array_length(COALESCE(layout_data->'components', '[]'::jsonb)) as component_count,
|
||||
updated_at
|
||||
FROM screen_layouts_v2
|
||||
WHERE screen_id = $1 AND company_code = $2
|
||||
ORDER BY layer_id`,
|
||||
[screenId, companyCode],
|
||||
);
|
||||
}
|
||||
|
||||
return layers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 특정 레이어의 레이아웃 조회
|
||||
*/
|
||||
async getLayerLayout(
|
||||
screenId: number,
|
||||
layerId: number,
|
||||
companyCode: string,
|
||||
): Promise<any> {
|
||||
let layout = await queryOne<{ layout_data: any; layer_name: string; condition_config: any }>(
|
||||
`SELECT layout_data, layer_name, condition_config FROM screen_layouts_v2
|
||||
WHERE screen_id = $1 AND company_code = $2 AND layer_id = $3`,
|
||||
[screenId, companyCode, layerId],
|
||||
);
|
||||
|
||||
// 회사별 레이어가 없으면 공통(*) 조회
|
||||
if (!layout && companyCode !== "*") {
|
||||
layout = await queryOne<{ layout_data: any; layer_name: string; condition_config: any }>(
|
||||
`SELECT layout_data, layer_name, condition_config FROM screen_layouts_v2
|
||||
WHERE screen_id = $1 AND company_code = '*' AND layer_id = $2`,
|
||||
[screenId, layerId],
|
||||
);
|
||||
}
|
||||
|
||||
if (!layout) return null;
|
||||
|
||||
return {
|
||||
...layout.layout_data,
|
||||
layerId,
|
||||
layerName: layout.layer_name,
|
||||
conditionConfig: layout.condition_config,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 레이어 삭제
|
||||
*/
|
||||
async deleteLayer(
|
||||
screenId: number,
|
||||
layerId: number,
|
||||
companyCode: string,
|
||||
): Promise<void> {
|
||||
if (layerId === 1) {
|
||||
throw new Error("기본 레이어는 삭제할 수 없습니다.");
|
||||
}
|
||||
|
||||
await query(
|
||||
`DELETE FROM screen_layouts_v2
|
||||
WHERE screen_id = $1 AND company_code = $2 AND layer_id = $3`,
|
||||
[screenId, companyCode, layerId],
|
||||
);
|
||||
|
||||
console.log(`레이어 삭제 완료: screen_id=${screenId}, layer_id=${layerId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 레이어 조건 설정 업데이트
|
||||
*/
|
||||
async updateLayerCondition(
|
||||
screenId: number,
|
||||
layerId: number,
|
||||
companyCode: string,
|
||||
conditionConfig: any,
|
||||
layerName?: string,
|
||||
): Promise<void> {
|
||||
const setClauses = ['condition_config = $4', 'updated_at = NOW()'];
|
||||
const params: any[] = [screenId, companyCode, layerId, conditionConfig ? JSON.stringify(conditionConfig) : null];
|
||||
|
||||
if (layerName) {
|
||||
setClauses.push(`layer_name = $${params.length + 1}`);
|
||||
params.push(layerName);
|
||||
}
|
||||
|
||||
await query(
|
||||
`UPDATE screen_layouts_v2 SET ${setClauses.join(', ')}
|
||||
WHERE screen_id = $1 AND company_code = $2 AND layer_id = $3`,
|
||||
params,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user