ERP-node/backend-node/src/services/flowDataMoveService.ts

182 lines
4.9 KiB
TypeScript
Raw Normal View History

2025-10-20 10:55:33 +09:00
/**
*
*
*/
import db from "../database/db";
import { FlowAuditLog } from "../types/flow";
import { FlowDefinitionService } from "./flowDefinitionService";
export class FlowDataMoveService {
private flowDefinitionService: FlowDefinitionService;
constructor() {
this.flowDefinitionService = new FlowDefinitionService();
}
/**
*
*/
async moveDataToStep(
flowId: number,
recordId: string,
toStepId: number,
userId: string,
note?: string
): Promise<void> {
await db.transaction(async (client) => {
// 1. 플로우 정의 조회
const flowDef = await this.flowDefinitionService.findById(flowId);
if (!flowDef) {
throw new Error(`Flow definition not found: ${flowId}`);
}
// 2. 현재 상태 조회
const currentStatusQuery = `
SELECT current_step_id, table_name
FROM flow_data_status
WHERE flow_definition_id = $1 AND record_id = $2
`;
const currentStatusResult = await client.query(currentStatusQuery, [
flowId,
recordId,
]);
const currentStatus =
currentStatusResult.rows.length > 0
? {
currentStepId: currentStatusResult.rows[0].current_step_id,
tableName: currentStatusResult.rows[0].table_name,
}
: null;
const fromStepId = currentStatus?.currentStepId || null;
// 3. flow_data_status 업데이트 또는 삽입
if (currentStatus) {
await client.query(
`
UPDATE flow_data_status
SET current_step_id = $1, updated_by = $2, updated_at = NOW()
WHERE flow_definition_id = $3 AND record_id = $4
`,
[toStepId, userId, flowId, recordId]
);
} else {
await client.query(
`
INSERT INTO flow_data_status
(flow_definition_id, table_name, record_id, current_step_id, updated_by)
VALUES ($1, $2, $3, $4, $5)
`,
[flowId, flowDef.tableName, recordId, toStepId, userId]
);
}
// 4. 오딧 로그 기록
await client.query(
`
INSERT INTO flow_audit_log
(flow_definition_id, table_name, record_id, from_step_id, to_step_id, changed_by, note)
VALUES ($1, $2, $3, $4, $5, $6, $7)
`,
[
flowId,
flowDef.tableName,
recordId,
fromStepId,
toStepId,
userId,
note || null,
]
);
});
}
/**
*
*/
async moveBatchData(
flowId: number,
recordIds: string[],
toStepId: number,
userId: string,
note?: string
): Promise<void> {
for (const recordId of recordIds) {
await this.moveDataToStep(flowId, recordId, toStepId, userId, note);
}
}
/**
*
*/
async getAuditLogs(
flowId: number,
recordId: string
): Promise<FlowAuditLog[]> {
const query = `
SELECT
fal.*,
fs_from.step_name as from_step_name,
fs_to.step_name as to_step_name
FROM flow_audit_log fal
LEFT JOIN flow_step fs_from ON fal.from_step_id = fs_from.id
LEFT JOIN flow_step fs_to ON fal.to_step_id = fs_to.id
WHERE fal.flow_definition_id = $1 AND fal.record_id = $2
ORDER BY fal.changed_at DESC
`;
const result = await db.query(query, [flowId, recordId]);
return result.map((row) => ({
id: row.id,
flowDefinitionId: row.flow_definition_id,
tableName: row.table_name,
recordId: row.record_id,
fromStepId: row.from_step_id,
toStepId: row.to_step_id,
changedBy: row.changed_by,
changedAt: row.changed_at,
note: row.note,
fromStepName: row.from_step_name,
toStepName: row.to_step_name,
}));
}
/**
*
*/
async getFlowAuditLogs(
flowId: number,
limit: number = 100
): Promise<FlowAuditLog[]> {
const query = `
SELECT
fal.*,
fs_from.step_name as from_step_name,
fs_to.step_name as to_step_name
FROM flow_audit_log fal
LEFT JOIN flow_step fs_from ON fal.from_step_id = fs_from.id
LEFT JOIN flow_step fs_to ON fal.to_step_id = fs_to.id
WHERE fal.flow_definition_id = $1
ORDER BY fal.changed_at DESC
LIMIT $2
`;
const result = await db.query(query, [flowId, limit]);
return result.map((row) => ({
id: row.id,
flowDefinitionId: row.flow_definition_id,
tableName: row.table_name,
recordId: row.record_id,
fromStepId: row.from_step_id,
toStepId: row.to_step_id,
changedBy: row.changed_by,
changedAt: row.changed_at,
note: row.note,
fromStepName: row.from_step_name,
toStepName: row.to_step_name,
}));
}
}