- Integrated express-async-errors to automatically handle errors in async route handlers, enhancing the overall error management in the application. - Updated app.ts to include the express-async-errors import for global error handling. - Removed redundant logging statements in admin and user menu retrieval functions to streamline the code and improve readability. - Adjusted logging levels from info to debug for less critical logs, ensuring that important information is logged appropriately without cluttering the logs.
299 lines
8.1 KiB
TypeScript
299 lines
8.1 KiB
TypeScript
// @ts-nocheck
|
|
/**
|
|
* 플로우 단계 서비스
|
|
*/
|
|
|
|
import db from "../database/db";
|
|
import {
|
|
FlowStep,
|
|
CreateFlowStepRequest,
|
|
UpdateFlowStepRequest,
|
|
FlowConditionGroup,
|
|
} from "../types/flow";
|
|
import { FlowConditionParser } from "./flowConditionParser";
|
|
|
|
export class FlowStepService {
|
|
/**
|
|
* 플로우 단계 생성
|
|
*/
|
|
async create(request: CreateFlowStepRequest): Promise<FlowStep> {
|
|
// 조건 검증
|
|
if (request.conditionJson) {
|
|
FlowConditionParser.validateConditionGroup(request.conditionJson);
|
|
}
|
|
|
|
const query = `
|
|
INSERT INTO flow_step (
|
|
flow_definition_id, step_name, step_order, table_name, condition_json,
|
|
color, position_x, position_y, move_type, status_column, status_value,
|
|
target_table, field_mappings, required_fields,
|
|
integration_type, integration_config, display_config
|
|
)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
|
|
RETURNING *
|
|
`;
|
|
|
|
const result = await db.query(query, [
|
|
request.flowDefinitionId,
|
|
request.stepName,
|
|
request.stepOrder,
|
|
request.tableName || null,
|
|
request.conditionJson ? JSON.stringify(request.conditionJson) : null,
|
|
request.color || "#3B82F6",
|
|
request.positionX || 0,
|
|
request.positionY || 0,
|
|
request.moveType || null,
|
|
request.statusColumn || null,
|
|
request.statusValue || null,
|
|
request.targetTable || null,
|
|
request.fieldMappings ? JSON.stringify(request.fieldMappings) : null,
|
|
request.requiredFields ? JSON.stringify(request.requiredFields) : null,
|
|
request.integrationType || "internal",
|
|
request.integrationConfig
|
|
? JSON.stringify(request.integrationConfig)
|
|
: null,
|
|
request.displayConfig ? JSON.stringify(request.displayConfig) : null,
|
|
]);
|
|
|
|
return this.mapToFlowStep(result[0]);
|
|
}
|
|
|
|
/**
|
|
* 특정 플로우의 모든 단계 조회
|
|
*/
|
|
async findByFlowId(flowDefinitionId: number): Promise<FlowStep[]> {
|
|
const query = `
|
|
SELECT * FROM flow_step
|
|
WHERE flow_definition_id = $1
|
|
ORDER BY step_order ASC
|
|
`;
|
|
|
|
const result = await db.query(query, [flowDefinitionId]);
|
|
return result.map(this.mapToFlowStep);
|
|
}
|
|
|
|
/**
|
|
* 플로우 단계 단일 조회
|
|
*/
|
|
async findById(id: number): Promise<FlowStep | null> {
|
|
const query = "SELECT * FROM flow_step WHERE id = $1";
|
|
const result = await db.query(query, [id]);
|
|
|
|
if (result.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
return this.mapToFlowStep(result[0]);
|
|
}
|
|
|
|
/**
|
|
* 플로우 단계 수정
|
|
*/
|
|
async update(
|
|
id: number,
|
|
request: UpdateFlowStepRequest
|
|
): Promise<FlowStep | null> {
|
|
// 조건 검증
|
|
if (request.conditionJson) {
|
|
FlowConditionParser.validateConditionGroup(request.conditionJson);
|
|
}
|
|
|
|
const fields: string[] = [];
|
|
const params: any[] = [];
|
|
let paramIndex = 1;
|
|
|
|
if (request.stepName !== undefined) {
|
|
fields.push(`step_name = $${paramIndex}`);
|
|
params.push(request.stepName);
|
|
paramIndex++;
|
|
}
|
|
|
|
if (request.stepOrder !== undefined) {
|
|
fields.push(`step_order = $${paramIndex}`);
|
|
params.push(request.stepOrder);
|
|
paramIndex++;
|
|
}
|
|
|
|
if (request.tableName !== undefined) {
|
|
fields.push(`table_name = $${paramIndex}`);
|
|
params.push(request.tableName);
|
|
paramIndex++;
|
|
}
|
|
|
|
if (request.conditionJson !== undefined) {
|
|
fields.push(`condition_json = $${paramIndex}`);
|
|
params.push(
|
|
request.conditionJson ? JSON.stringify(request.conditionJson) : null
|
|
);
|
|
paramIndex++;
|
|
}
|
|
|
|
if (request.color !== undefined) {
|
|
fields.push(`color = $${paramIndex}`);
|
|
params.push(request.color);
|
|
paramIndex++;
|
|
}
|
|
|
|
if (request.positionX !== undefined) {
|
|
fields.push(`position_x = $${paramIndex}`);
|
|
params.push(request.positionX);
|
|
paramIndex++;
|
|
}
|
|
|
|
if (request.positionY !== undefined) {
|
|
fields.push(`position_y = $${paramIndex}`);
|
|
params.push(request.positionY);
|
|
paramIndex++;
|
|
}
|
|
|
|
// 하이브리드 플로우 필드
|
|
if (request.moveType !== undefined) {
|
|
fields.push(`move_type = $${paramIndex}`);
|
|
params.push(request.moveType);
|
|
paramIndex++;
|
|
}
|
|
|
|
if (request.statusColumn !== undefined) {
|
|
fields.push(`status_column = $${paramIndex}`);
|
|
params.push(request.statusColumn);
|
|
paramIndex++;
|
|
}
|
|
|
|
if (request.statusValue !== undefined) {
|
|
fields.push(`status_value = $${paramIndex}`);
|
|
params.push(request.statusValue);
|
|
paramIndex++;
|
|
}
|
|
|
|
if (request.targetTable !== undefined) {
|
|
fields.push(`target_table = $${paramIndex}`);
|
|
params.push(request.targetTable);
|
|
paramIndex++;
|
|
}
|
|
|
|
if (request.fieldMappings !== undefined) {
|
|
fields.push(`field_mappings = $${paramIndex}`);
|
|
params.push(
|
|
request.fieldMappings ? JSON.stringify(request.fieldMappings) : null
|
|
);
|
|
paramIndex++;
|
|
}
|
|
|
|
if (request.requiredFields !== undefined) {
|
|
fields.push(`required_fields = $${paramIndex}`);
|
|
params.push(
|
|
request.requiredFields ? JSON.stringify(request.requiredFields) : null
|
|
);
|
|
paramIndex++;
|
|
}
|
|
|
|
// 외부 연동 필드
|
|
if (request.integrationType !== undefined) {
|
|
fields.push(`integration_type = $${paramIndex}`);
|
|
params.push(request.integrationType);
|
|
paramIndex++;
|
|
}
|
|
|
|
if (request.integrationConfig !== undefined) {
|
|
fields.push(`integration_config = $${paramIndex}`);
|
|
params.push(
|
|
request.integrationConfig
|
|
? JSON.stringify(request.integrationConfig)
|
|
: null
|
|
);
|
|
paramIndex++;
|
|
}
|
|
|
|
// 표시 설정 (displayConfig)
|
|
if (request.displayConfig !== undefined) {
|
|
fields.push(`display_config = $${paramIndex}`);
|
|
params.push(
|
|
request.displayConfig ? JSON.stringify(request.displayConfig) : null
|
|
);
|
|
paramIndex++;
|
|
}
|
|
|
|
if (fields.length === 0) {
|
|
return this.findById(id);
|
|
}
|
|
|
|
fields.push(`updated_at = NOW()`);
|
|
|
|
const query = `
|
|
UPDATE flow_step
|
|
SET ${fields.join(", ")}
|
|
WHERE id = $${paramIndex}
|
|
RETURNING *
|
|
`;
|
|
params.push(id);
|
|
|
|
const result = await db.query(query, params);
|
|
|
|
if (result.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
return this.mapToFlowStep(result[0]);
|
|
}
|
|
|
|
/**
|
|
* 플로우 단계 삭제
|
|
*/
|
|
async delete(id: number): Promise<boolean> {
|
|
const query = "DELETE FROM flow_step WHERE id = $1 RETURNING id";
|
|
const result = await db.query(query, [id]);
|
|
return result.length > 0;
|
|
}
|
|
|
|
/**
|
|
* 단계 순서 재정렬
|
|
*/
|
|
async reorder(
|
|
flowDefinitionId: number,
|
|
stepOrders: { id: number; order: number }[]
|
|
): Promise<void> {
|
|
await db.transaction(async (client) => {
|
|
for (const { id, order } of stepOrders) {
|
|
await client.query(
|
|
"UPDATE flow_step SET step_order = $1, updated_at = NOW() WHERE id = $2 AND flow_definition_id = $3",
|
|
[order, id, flowDefinitionId]
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* DB 행을 FlowStep 객체로 변환
|
|
*/
|
|
private mapToFlowStep(row: any): FlowStep {
|
|
// JSONB 필드는 pg 라이브러리가 자동으로 파싱해줌
|
|
const displayConfig = row.display_config;
|
|
|
|
return {
|
|
id: row.id,
|
|
flowDefinitionId: row.flow_definition_id,
|
|
stepName: row.step_name,
|
|
stepOrder: row.step_order,
|
|
tableName: row.table_name || undefined,
|
|
conditionJson: row.condition_json as FlowConditionGroup | undefined,
|
|
color: row.color,
|
|
positionX: row.position_x,
|
|
positionY: row.position_y,
|
|
// 하이브리드 플로우 지원 필드
|
|
moveType: row.move_type || undefined,
|
|
statusColumn: row.status_column || undefined,
|
|
statusValue: row.status_value || undefined,
|
|
targetTable: row.target_table || undefined,
|
|
fieldMappings: row.field_mappings || undefined,
|
|
requiredFields: row.required_fields || undefined,
|
|
// 외부 연동 필드
|
|
integrationType: row.integration_type || "internal",
|
|
integrationConfig: row.integration_config || undefined,
|
|
// 표시 설정
|
|
displayConfig: displayConfig || undefined,
|
|
createdAt: row.created_at,
|
|
updatedAt: row.updated_at,
|
|
};
|
|
}
|
|
}
|