플로우 분기처리 구현
This commit is contained in:
@@ -56,7 +56,7 @@ import todoRoutes from "./routes/todoRoutes"; // To-Do 관리
|
||||
import bookingRoutes from "./routes/bookingRoutes"; // 예약 요청 관리
|
||||
import mapDataRoutes from "./routes/mapDataRoutes"; // 지도 데이터 관리
|
||||
import yardLayoutRoutes from "./routes/yardLayoutRoutes"; // 야드 관리 3D
|
||||
import materialRoutes from "./routes/materialRoutes"; // 자재 관리
|
||||
//import materialRoutes from "./routes/materialRoutes"; // 자재 관리
|
||||
import flowRoutes from "./routes/flowRoutes"; // 플로우 관리
|
||||
import { BatchSchedulerService } from "./services/batchSchedulerService";
|
||||
// import collectionRoutes from "./routes/collectionRoutes"; // 임시 주석
|
||||
@@ -208,7 +208,7 @@ app.use("/api/todos", todoRoutes); // To-Do 관리
|
||||
app.use("/api/bookings", bookingRoutes); // 예약 요청 관리
|
||||
app.use("/api/map-data", mapDataRoutes); // 지도 데이터 조회
|
||||
app.use("/api/yard-layouts", yardLayoutRoutes); // 야드 관리 3D
|
||||
app.use("/api/materials", materialRoutes); // 자재 관리
|
||||
// app.use("/api/materials", materialRoutes); // 자재 관리 (임시 주석)
|
||||
app.use("/api/flow", flowRoutes); // 플로우 관리
|
||||
// app.use("/api/collections", collectionRoutes); // 임시 주석
|
||||
// app.use("/api/batch", batchRoutes); // 임시 주석
|
||||
|
||||
@@ -573,28 +573,46 @@ export class FlowController {
|
||||
*/
|
||||
moveBatchData = async (req: Request, res: Response): Promise<void> => {
|
||||
try {
|
||||
const { flowId, recordIds, toStepId, note } = req.body;
|
||||
const { flowId, fromStepId, toStepId, dataIds } = req.body;
|
||||
const userId = (req as any).user?.userId || "system";
|
||||
|
||||
if (!flowId || !recordIds || !Array.isArray(recordIds) || !toStepId) {
|
||||
if (
|
||||
!flowId ||
|
||||
!fromStepId ||
|
||||
!toStepId ||
|
||||
!dataIds ||
|
||||
!Array.isArray(dataIds)
|
||||
) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: "flowId, recordIds (array), and toStepId are required",
|
||||
message:
|
||||
"flowId, fromStepId, toStepId, and dataIds (array) are required",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await this.flowDataMoveService.moveBatchData(
|
||||
const result = await this.flowDataMoveService.moveBatchData(
|
||||
flowId,
|
||||
recordIds,
|
||||
fromStepId,
|
||||
toStepId,
|
||||
userId,
|
||||
note
|
||||
dataIds,
|
||||
userId
|
||||
);
|
||||
|
||||
const successCount = result.results.filter((r) => r.success).length;
|
||||
const failureCount = result.results.filter((r) => !r.success).length;
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: `${recordIds.length} records moved successfully`,
|
||||
success: result.success,
|
||||
message: result.success
|
||||
? `${successCount}건의 데이터를 성공적으로 이동했습니다`
|
||||
: `${successCount}건 성공, ${failureCount}건 실패`,
|
||||
data: {
|
||||
successCount,
|
||||
failureCount,
|
||||
total: dataIds.length,
|
||||
},
|
||||
results: result.results,
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error("Error moving batch data:", error);
|
||||
|
||||
@@ -33,12 +33,14 @@ export class FlowConditionParser {
|
||||
|
||||
switch (condition.operator) {
|
||||
case "equals":
|
||||
case "=":
|
||||
conditions.push(`${column} = $${paramIndex}`);
|
||||
params.push(condition.value);
|
||||
paramIndex++;
|
||||
break;
|
||||
|
||||
case "not_equals":
|
||||
case "!=":
|
||||
conditions.push(`${column} != $${paramIndex}`);
|
||||
params.push(condition.value);
|
||||
paramIndex++;
|
||||
@@ -65,24 +67,28 @@ export class FlowConditionParser {
|
||||
break;
|
||||
|
||||
case "greater_than":
|
||||
case ">":
|
||||
conditions.push(`${column} > $${paramIndex}`);
|
||||
params.push(condition.value);
|
||||
paramIndex++;
|
||||
break;
|
||||
|
||||
case "less_than":
|
||||
case "<":
|
||||
conditions.push(`${column} < $${paramIndex}`);
|
||||
params.push(condition.value);
|
||||
paramIndex++;
|
||||
break;
|
||||
|
||||
case "greater_than_or_equal":
|
||||
case ">=":
|
||||
conditions.push(`${column} >= $${paramIndex}`);
|
||||
params.push(condition.value);
|
||||
paramIndex++;
|
||||
break;
|
||||
|
||||
case "less_than_or_equal":
|
||||
case "<=":
|
||||
conditions.push(`${column} <= $${paramIndex}`);
|
||||
params.push(condition.value);
|
||||
paramIndex++;
|
||||
@@ -165,13 +171,19 @@ export class FlowConditionParser {
|
||||
|
||||
const validOperators = [
|
||||
"equals",
|
||||
"=",
|
||||
"not_equals",
|
||||
"!=",
|
||||
"in",
|
||||
"not_in",
|
||||
"greater_than",
|
||||
">",
|
||||
"less_than",
|
||||
"<",
|
||||
"greater_than_or_equal",
|
||||
">=",
|
||||
"less_than_or_equal",
|
||||
"<=",
|
||||
"is_null",
|
||||
"is_not_null",
|
||||
"like",
|
||||
|
||||
@@ -1,118 +1,360 @@
|
||||
/**
|
||||
* 플로우 데이터 이동 서비스
|
||||
* 데이터의 플로우 단계 이동 및 오딧 로그 관리
|
||||
* 플로우 데이터 이동 서비스 (하이브리드 방식 지원)
|
||||
* - 상태 변경 방식: 같은 테이블 내에서 상태 컬럼 업데이트
|
||||
* - 테이블 이동 방식: 다른 테이블로 데이터 복사 및 매핑
|
||||
* - 하이브리드 방식: 두 가지 모두 수행
|
||||
*/
|
||||
|
||||
import db from "../database/db";
|
||||
import { FlowAuditLog } from "../types/flow";
|
||||
import { FlowDefinitionService } from "./flowDefinitionService";
|
||||
import { FlowStepService } from "./flowStepService";
|
||||
|
||||
export class FlowDataMoveService {
|
||||
private flowDefinitionService: FlowDefinitionService;
|
||||
private flowStepService: FlowStepService;
|
||||
|
||||
constructor() {
|
||||
this.flowDefinitionService = new FlowDefinitionService();
|
||||
this.flowStepService = new FlowStepService();
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터를 다음 플로우 단계로 이동
|
||||
* 데이터를 다음 플로우 단계로 이동 (하이브리드 지원)
|
||||
*/
|
||||
async moveDataToStep(
|
||||
flowId: number,
|
||||
recordId: string,
|
||||
fromStepId: number,
|
||||
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}`);
|
||||
}
|
||||
dataId: any,
|
||||
userId: string = "system",
|
||||
additionalData?: Record<string, any>
|
||||
): Promise<{ success: boolean; targetDataId?: any; message?: string }> {
|
||||
return await db.transaction(async (client) => {
|
||||
try {
|
||||
// 1. 단계 정보 조회
|
||||
const fromStep = await this.flowStepService.findById(fromStepId);
|
||||
const toStep = await this.flowStepService.findById(toStepId);
|
||||
|
||||
// 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;
|
||||
if (!fromStep || !toStep) {
|
||||
throw new Error("유효하지 않은 단계입니다");
|
||||
}
|
||||
|
||||
// 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]
|
||||
);
|
||||
}
|
||||
let targetDataId = dataId;
|
||||
let sourceTable = fromStep.tableName;
|
||||
let targetTable = toStep.tableName || fromStep.tableName;
|
||||
|
||||
// 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)
|
||||
`,
|
||||
[
|
||||
// 2. 이동 방식에 따라 처리
|
||||
switch (toStep.moveType || "status") {
|
||||
case "status":
|
||||
// 상태 변경 방식
|
||||
await this.moveByStatusChange(
|
||||
client,
|
||||
fromStep,
|
||||
toStep,
|
||||
dataId,
|
||||
additionalData
|
||||
);
|
||||
break;
|
||||
|
||||
case "table":
|
||||
// 테이블 이동 방식
|
||||
targetDataId = await this.moveByTableTransfer(
|
||||
client,
|
||||
fromStep,
|
||||
toStep,
|
||||
dataId,
|
||||
additionalData
|
||||
);
|
||||
targetTable = toStep.targetTable || toStep.tableName;
|
||||
break;
|
||||
|
||||
case "both":
|
||||
// 하이브리드 방식: 둘 다 수행
|
||||
await this.moveByStatusChange(
|
||||
client,
|
||||
fromStep,
|
||||
toStep,
|
||||
dataId,
|
||||
additionalData
|
||||
);
|
||||
targetDataId = await this.moveByTableTransfer(
|
||||
client,
|
||||
fromStep,
|
||||
toStep,
|
||||
dataId,
|
||||
additionalData
|
||||
);
|
||||
targetTable = toStep.targetTable || toStep.tableName;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`지원하지 않는 이동 방식: ${toStep.moveType}`);
|
||||
}
|
||||
|
||||
// 3. 매핑 테이블 업데이트 (테이블 이동 방식일 때)
|
||||
if (toStep.moveType === "table" || toStep.moveType === "both") {
|
||||
await this.updateDataMapping(
|
||||
client,
|
||||
flowId,
|
||||
toStepId,
|
||||
fromStepId,
|
||||
dataId,
|
||||
targetDataId
|
||||
);
|
||||
}
|
||||
|
||||
// 4. 감사 로그 기록
|
||||
await this.logDataMove(client, {
|
||||
flowId,
|
||||
flowDef.tableName,
|
||||
recordId,
|
||||
fromStepId,
|
||||
toStepId,
|
||||
moveType: toStep.moveType || "status",
|
||||
sourceTable,
|
||||
targetTable,
|
||||
sourceDataId: String(dataId),
|
||||
targetDataId: String(targetDataId),
|
||||
statusFrom: fromStep.statusValue,
|
||||
statusTo: toStep.statusValue,
|
||||
userId,
|
||||
note || null,
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
targetDataId,
|
||||
message: "데이터가 성공적으로 이동되었습니다",
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.error("데이터 이동 실패:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 상태 변경 방식으로 데이터 이동
|
||||
*/
|
||||
private async moveByStatusChange(
|
||||
client: any,
|
||||
fromStep: any,
|
||||
toStep: any,
|
||||
dataId: any,
|
||||
additionalData?: Record<string, any>
|
||||
): Promise<void> {
|
||||
const statusColumn = toStep.statusColumn || "flow_status";
|
||||
const tableName = fromStep.tableName;
|
||||
|
||||
// 추가 필드 업데이트 준비
|
||||
const updates: string[] = [`${statusColumn} = $2`, `updated_at = NOW()`];
|
||||
const values: any[] = [dataId, toStep.statusValue];
|
||||
let paramIndex = 3;
|
||||
|
||||
// 추가 데이터가 있으면 함께 업데이트
|
||||
if (additionalData) {
|
||||
for (const [key, value] of Object.entries(additionalData)) {
|
||||
updates.push(`${key} = $${paramIndex}`);
|
||||
values.push(value);
|
||||
paramIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
const updateQuery = `
|
||||
UPDATE ${tableName}
|
||||
SET ${updates.join(", ")}
|
||||
WHERE id = $1
|
||||
`;
|
||||
|
||||
const result = await client.query(updateQuery, values);
|
||||
|
||||
if (result.rowCount === 0) {
|
||||
throw new Error(`데이터를 찾을 수 없습니다: ${dataId}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 테이블 이동 방식으로 데이터 이동
|
||||
*/
|
||||
private async moveByTableTransfer(
|
||||
client: any,
|
||||
fromStep: any,
|
||||
toStep: any,
|
||||
dataId: any,
|
||||
additionalData?: Record<string, any>
|
||||
): Promise<any> {
|
||||
const sourceTable = fromStep.tableName;
|
||||
const targetTable = toStep.targetTable || toStep.tableName;
|
||||
const fieldMappings = toStep.fieldMappings || {};
|
||||
|
||||
// 1. 소스 데이터 조회
|
||||
const selectQuery = `SELECT * FROM ${sourceTable} WHERE id = $1`;
|
||||
const sourceResult = await client.query(selectQuery, [dataId]);
|
||||
|
||||
if (sourceResult.length === 0) {
|
||||
throw new Error(`소스 데이터를 찾을 수 없습니다: ${dataId}`);
|
||||
}
|
||||
|
||||
const sourceData = sourceResult[0];
|
||||
|
||||
// 2. 필드 매핑 적용
|
||||
const mappedData: Record<string, any> = {};
|
||||
|
||||
// 매핑 정의가 있으면 적용
|
||||
for (const [sourceField, targetField] of Object.entries(fieldMappings)) {
|
||||
if (sourceData[sourceField] !== undefined) {
|
||||
mappedData[targetField as string] = sourceData[sourceField];
|
||||
}
|
||||
}
|
||||
|
||||
// 추가 데이터 병합
|
||||
if (additionalData) {
|
||||
Object.assign(mappedData, additionalData);
|
||||
}
|
||||
|
||||
// 3. 타겟 테이블에 데이터 삽입
|
||||
if (Object.keys(mappedData).length === 0) {
|
||||
throw new Error("매핑할 데이터가 없습니다");
|
||||
}
|
||||
|
||||
const columns = Object.keys(mappedData);
|
||||
const values = Object.values(mappedData);
|
||||
const placeholders = columns.map((_, i) => `$${i + 1}`).join(", ");
|
||||
|
||||
const insertQuery = `
|
||||
INSERT INTO ${targetTable} (${columns.join(", ")})
|
||||
VALUES (${placeholders})
|
||||
RETURNING id
|
||||
`;
|
||||
|
||||
const insertResult = await client.query(insertQuery, values);
|
||||
return insertResult[0].id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터 매핑 테이블 업데이트
|
||||
*/
|
||||
private async updateDataMapping(
|
||||
client: any,
|
||||
flowId: number,
|
||||
currentStepId: number,
|
||||
prevStepId: number,
|
||||
sourceDataId: any,
|
||||
targetDataId: any
|
||||
): Promise<void> {
|
||||
// 기존 매핑 조회
|
||||
const selectQuery = `
|
||||
SELECT id, step_data_map
|
||||
FROM flow_data_mapping
|
||||
WHERE flow_definition_id = $1
|
||||
AND step_data_map->$2 = $3
|
||||
`;
|
||||
const mappingResult = await client.query(selectQuery, [
|
||||
flowId,
|
||||
String(prevStepId),
|
||||
JSON.stringify(String(sourceDataId)),
|
||||
]);
|
||||
|
||||
const stepDataMap: Record<string, string> =
|
||||
mappingResult.length > 0 ? mappingResult[0].step_data_map : {};
|
||||
|
||||
// 새 단계 데이터 추가
|
||||
stepDataMap[String(currentStepId)] = String(targetDataId);
|
||||
|
||||
if (mappingResult.length > 0) {
|
||||
// 기존 매핑 업데이트
|
||||
const updateQuery = `
|
||||
UPDATE flow_data_mapping
|
||||
SET current_step_id = $1,
|
||||
step_data_map = $2,
|
||||
updated_at = NOW()
|
||||
WHERE id = $3
|
||||
`;
|
||||
await client.query(updateQuery, [
|
||||
currentStepId,
|
||||
JSON.stringify(stepDataMap),
|
||||
mappingResult[0].id,
|
||||
]);
|
||||
} else {
|
||||
// 새 매핑 생성
|
||||
const insertQuery = `
|
||||
INSERT INTO flow_data_mapping
|
||||
(flow_definition_id, current_step_id, step_data_map)
|
||||
VALUES ($1, $2, $3)
|
||||
`;
|
||||
await client.query(insertQuery, [
|
||||
flowId,
|
||||
currentStepId,
|
||||
JSON.stringify(stepDataMap),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 감사 로그 기록
|
||||
*/
|
||||
private async logDataMove(client: any, params: any): Promise<void> {
|
||||
const query = `
|
||||
INSERT INTO flow_audit_log (
|
||||
flow_definition_id, from_step_id, to_step_id,
|
||||
move_type, source_table, target_table,
|
||||
source_data_id, target_data_id,
|
||||
status_from, status_to,
|
||||
changed_by, note
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
||||
`;
|
||||
|
||||
await client.query(query, [
|
||||
params.flowId,
|
||||
params.fromStepId,
|
||||
params.toStepId,
|
||||
params.moveType,
|
||||
params.sourceTable,
|
||||
params.targetTable,
|
||||
params.sourceDataId,
|
||||
params.targetDataId,
|
||||
params.statusFrom,
|
||||
params.statusTo,
|
||||
params.userId,
|
||||
params.note || null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 여러 데이터를 동시에 다음 단계로 이동
|
||||
*/
|
||||
async moveBatchData(
|
||||
flowId: number,
|
||||
recordIds: string[],
|
||||
fromStepId: number,
|
||||
toStepId: number,
|
||||
userId: string,
|
||||
note?: string
|
||||
): Promise<void> {
|
||||
for (const recordId of recordIds) {
|
||||
await this.moveDataToStep(flowId, recordId, toStepId, userId, note);
|
||||
dataIds: any[],
|
||||
userId: string = "system"
|
||||
): Promise<{ success: boolean; results: any[] }> {
|
||||
const results = [];
|
||||
|
||||
for (const dataId of dataIds) {
|
||||
try {
|
||||
const result = await this.moveDataToStep(
|
||||
flowId,
|
||||
fromStepId,
|
||||
toStepId,
|
||||
dataId,
|
||||
userId
|
||||
);
|
||||
results.push({ dataId, ...result });
|
||||
} catch (error: any) {
|
||||
results.push({ dataId, success: false, message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: results.every((r) => r.success),
|
||||
results,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 데이터의 플로우 이력 조회
|
||||
*/
|
||||
async getAuditLogs(
|
||||
flowId: number,
|
||||
recordId: string
|
||||
): Promise<FlowAuditLog[]> {
|
||||
async getAuditLogs(flowId: number, dataId: string): Promise<FlowAuditLog[]> {
|
||||
const query = `
|
||||
SELECT
|
||||
fal.*,
|
||||
@@ -121,17 +363,18 @@ export class FlowDataMoveService {
|
||||
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
|
||||
WHERE fal.flow_definition_id = $1
|
||||
AND (fal.source_data_id = $2 OR fal.target_data_id = $2)
|
||||
ORDER BY fal.changed_at DESC
|
||||
`;
|
||||
|
||||
const result = await db.query(query, [flowId, recordId]);
|
||||
const result = await db.query(query, [flowId, dataId]);
|
||||
|
||||
return result.map((row) => ({
|
||||
id: row.id,
|
||||
flowDefinitionId: row.flow_definition_id,
|
||||
tableName: row.table_name,
|
||||
recordId: row.record_id,
|
||||
tableName: row.table_name || row.source_table,
|
||||
recordId: row.record_id || row.source_data_id,
|
||||
fromStepId: row.from_step_id,
|
||||
toStepId: row.to_step_id,
|
||||
changedBy: row.changed_by,
|
||||
@@ -139,6 +382,13 @@ export class FlowDataMoveService {
|
||||
note: row.note,
|
||||
fromStepName: row.from_step_name,
|
||||
toStepName: row.to_step_name,
|
||||
moveType: row.move_type,
|
||||
sourceTable: row.source_table,
|
||||
targetTable: row.target_table,
|
||||
sourceDataId: row.source_data_id,
|
||||
targetDataId: row.target_data_id,
|
||||
statusFrom: row.status_from,
|
||||
statusTo: row.status_to,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -167,8 +417,8 @@ export class FlowDataMoveService {
|
||||
return result.map((row) => ({
|
||||
id: row.id,
|
||||
flowDefinitionId: row.flow_definition_id,
|
||||
tableName: row.table_name,
|
||||
recordId: row.record_id,
|
||||
tableName: row.table_name || row.source_table,
|
||||
recordId: row.record_id || row.source_data_id,
|
||||
fromStepId: row.from_step_id,
|
||||
toStepId: row.to_step_id,
|
||||
changedBy: row.changed_by,
|
||||
@@ -176,6 +426,13 @@ export class FlowDataMoveService {
|
||||
note: row.note,
|
||||
fromStepName: row.from_step_name,
|
||||
toStepName: row.to_step_name,
|
||||
moveType: row.move_type,
|
||||
sourceTable: row.source_table,
|
||||
targetTable: row.target_table,
|
||||
sourceDataId: row.source_data_id,
|
||||
targetDataId: row.target_data_id,
|
||||
statusFrom: row.status_from,
|
||||
statusTo: row.status_to,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,6 +195,13 @@ export class FlowStepService {
|
||||
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,
|
||||
createdAt: row.created_at,
|
||||
updatedAt: row.updated_at,
|
||||
};
|
||||
|
||||
@@ -31,13 +31,19 @@ export interface UpdateFlowDefinitionRequest {
|
||||
// 조건 연산자
|
||||
export type ConditionOperator =
|
||||
| "equals"
|
||||
| "="
|
||||
| "not_equals"
|
||||
| "!="
|
||||
| "in"
|
||||
| "not_in"
|
||||
| "greater_than"
|
||||
| ">"
|
||||
| "less_than"
|
||||
| "<"
|
||||
| "greater_than_or_equal"
|
||||
| ">="
|
||||
| "less_than_or_equal"
|
||||
| "<="
|
||||
| "is_null"
|
||||
| "is_not_null"
|
||||
| "like"
|
||||
@@ -67,6 +73,13 @@ export interface FlowStep {
|
||||
color: string;
|
||||
positionX: number;
|
||||
positionY: number;
|
||||
// 하이브리드 플로우 지원 필드
|
||||
moveType?: "status" | "table" | "both"; // 데이터 이동 방식
|
||||
statusColumn?: string; // 상태 컬럼명 (상태 변경 방식)
|
||||
statusValue?: string; // 이 단계의 상태값
|
||||
targetTable?: string; // 타겟 테이블명 (테이블 이동 방식)
|
||||
fieldMappings?: Record<string, string>; // 필드 매핑 정보
|
||||
requiredFields?: string[]; // 필수 입력 필드
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
@@ -81,6 +94,13 @@ export interface CreateFlowStepRequest {
|
||||
color?: string;
|
||||
positionX?: number;
|
||||
positionY?: number;
|
||||
// 하이브리드 플로우 지원 필드
|
||||
moveType?: "status" | "table" | "both";
|
||||
statusColumn?: string;
|
||||
statusValue?: string;
|
||||
targetTable?: string;
|
||||
fieldMappings?: Record<string, string>;
|
||||
requiredFields?: string[];
|
||||
}
|
||||
|
||||
// 플로우 단계 수정 요청
|
||||
@@ -92,6 +112,13 @@ export interface UpdateFlowStepRequest {
|
||||
color?: string;
|
||||
positionX?: number;
|
||||
positionY?: number;
|
||||
// 하이브리드 플로우 지원 필드
|
||||
moveType?: "status" | "table" | "both";
|
||||
statusColumn?: string;
|
||||
statusValue?: string;
|
||||
targetTable?: string;
|
||||
fieldMappings?: Record<string, string>;
|
||||
requiredFields?: string[];
|
||||
}
|
||||
|
||||
// 플로우 단계 연결
|
||||
@@ -134,6 +161,14 @@ export interface FlowAuditLog {
|
||||
changedBy?: string;
|
||||
changedAt: Date;
|
||||
note?: string;
|
||||
// 하이브리드 플로우 지원 필드
|
||||
moveType?: "status" | "table" | "both";
|
||||
sourceTable?: string;
|
||||
targetTable?: string;
|
||||
sourceDataId?: string;
|
||||
targetDataId?: string;
|
||||
statusFrom?: string;
|
||||
statusTo?: string;
|
||||
// 조인 필드
|
||||
fromStepName?: string;
|
||||
toStepName?: string;
|
||||
|
||||
Reference in New Issue
Block a user