|
@@ -14,17 +14,17 @@ import {BpmnWork} from "@core-models/BpmnWork";
|
|
import {Logger} from "@util/Logger";
|
|
import {Logger} from "@util/Logger";
|
|
import {BpmnForm} from "@core-models/BpmnForm";
|
|
import {BpmnForm} from "@core-models/BpmnForm";
|
|
import BpmnModdle from 'bpmn-moddle';
|
|
import BpmnModdle from 'bpmn-moddle';
|
|
-import Serializer, { TypeResolver } from 'moddle-context-serializer';
|
|
|
|
|
|
+import Serializer, {TypeResolver} from 'moddle-context-serializer';
|
|
import * as elements from 'bpmn-elements';
|
|
import * as elements from 'bpmn-elements';
|
|
|
|
+
|
|
const camunda = require('camunda-bpmn-moddle/resources/camunda.json');
|
|
const camunda = require('camunda-bpmn-moddle/resources/camunda.json');
|
|
import {resolveExpression} from '@aircall/expression-parser';
|
|
import {resolveExpression} from '@aircall/expression-parser';
|
|
import {PrjPlanTask} from "@core-models/PrjPlanTask";
|
|
import {PrjPlanTask} from "@core-models/PrjPlanTask";
|
|
-import {IChecker} from "@src/utils/define";
|
|
|
|
|
|
+import {IChecker, WorkItemProcessType, WorkItemStatus} from "@src/utils/define";
|
|
import {PrjLogger} from "@src/utils/prj_logger";
|
|
import {PrjLogger} from "@src/utils/prj_logger";
|
|
import {PrjFile} from "@core-models/PrjFile";
|
|
import {PrjFile} from "@core-models/PrjFile";
|
|
import {executor} from "@util/Executor";
|
|
import {executor} from "@util/Executor";
|
|
import {Script} from 'vm';
|
|
import {Script} from 'vm';
|
|
-import {BpmnModel} from "@core-models/BpmnModel";
|
|
|
|
import {PrjTaskOutcome} from "@core-models/PrjTaskOutcome";
|
|
import {PrjTaskOutcome} from "@core-models/PrjTaskOutcome";
|
|
|
|
|
|
export interface IHandler {
|
|
export interface IHandler {
|
|
@@ -42,11 +42,16 @@ export interface IFlowArgs {
|
|
task_name?: string;
|
|
task_name?: string;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+export interface IScriptResult {
|
|
|
|
+ result: boolean;
|
|
|
|
+ message: string;
|
|
|
|
+}
|
|
|
|
+
|
|
interface IFiles {
|
|
interface IFiles {
|
|
id: string;
|
|
id: string;
|
|
name: string;
|
|
name: string;
|
|
}
|
|
}
|
|
-const kTypeResolver = Symbol.for('type resolver');
|
|
|
|
|
|
+
|
|
|
|
|
|
async function delay(ms) {
|
|
async function delay(ms) {
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
@@ -122,14 +127,15 @@ export class FlowEngine extends EventEmitter {
|
|
const listener = new EventEmitter();
|
|
const listener = new EventEmitter();
|
|
listener.on('flow.take', this.on_flow_take);
|
|
listener.on('flow.take', this.on_flow_take);
|
|
if (state) {
|
|
if (state) {
|
|
|
|
+ Logger.info(`recovering flow engine, id: ${this._id}`);
|
|
this.engine = this.engine.recover(state);
|
|
this.engine = this.engine.recover(state);
|
|
-
|
|
|
|
this.execution = await this.engine.resume({listener}, async (err, _execution) => {
|
|
this.execution = await this.engine.resume({listener}, async (err, _execution) => {
|
|
if (err) console.log(err);
|
|
if (err) console.log(err);
|
|
console.log('Execution completed with id', this.id);
|
|
console.log('Execution completed with id', this.id);
|
|
await this.complete();
|
|
await this.complete();
|
|
});
|
|
});
|
|
} else {
|
|
} else {
|
|
|
|
+ Logger.info(`creating flow engine, id: ${this._id}`);
|
|
this.execution = await this.engine.execute({listener}, async (err, _execution) => {
|
|
this.execution = await this.engine.execute({listener}, async (err, _execution) => {
|
|
if (err) console.log(err);
|
|
if (err) console.log(err);
|
|
console.log('Execution completed with id', this.id);
|
|
console.log('Execution completed with id', this.id);
|
|
@@ -183,7 +189,7 @@ export class FlowEngine extends EventEmitter {
|
|
camunda,
|
|
camunda,
|
|
},
|
|
},
|
|
services: {
|
|
services: {
|
|
- owner: async () => {
|
|
|
|
|
|
+ owner: async () => {
|
|
let owner = await AcsUserInfo.findOne({where: {id: this._owner}, raw: true});
|
|
let owner = await AcsUserInfo.findOne({where: {id: this._owner}, raw: true});
|
|
if (!owner) return;
|
|
if (!owner) return;
|
|
return {
|
|
return {
|
|
@@ -215,7 +221,7 @@ export class FlowEngine extends EventEmitter {
|
|
case_id: self._id,
|
|
case_id: self._id,
|
|
assigned_to: assigned_to,
|
|
assigned_to: assigned_to,
|
|
started_at: dayjs(),
|
|
started_at: dayjs(),
|
|
- process_type: 2,
|
|
|
|
|
|
+ process_type: WorkItemProcessType.to_read,
|
|
show_in_my_works: true,
|
|
show_in_my_works: true,
|
|
desc: detail
|
|
desc: detail
|
|
})
|
|
})
|
|
@@ -238,17 +244,40 @@ export class FlowEngine extends EventEmitter {
|
|
case_id: self._id,
|
|
case_id: self._id,
|
|
assigned_to: assigned_to,
|
|
assigned_to: assigned_to,
|
|
started_at: dayjs(),
|
|
started_at: dayjs(),
|
|
- process_type: 3,
|
|
|
|
|
|
+ process_type: WorkItemProcessType.to_read,
|
|
show_in_my_works: true,
|
|
show_in_my_works: true,
|
|
desc: detail
|
|
desc: detail
|
|
})
|
|
})
|
|
},
|
|
},
|
|
set_prj_phase: async (phase_id: string) => {
|
|
set_prj_phase: async (phase_id: string) => {
|
|
- await PrjInfo.update({phase_id: phase_id}, {
|
|
|
|
|
|
+ await PrjInfo.update({phase_id: phase_id}, {
|
|
where: {id: self._prj_id},
|
|
where: {id: self._prj_id},
|
|
returning: false
|
|
returning: false
|
|
})
|
|
})
|
|
},
|
|
},
|
|
|
|
+ // 获取交付物的数量
|
|
|
|
+ get_outcome_count: async () => {
|
|
|
|
+ let files = await PrjTaskOutcome.findAll({
|
|
|
|
+ where: {
|
|
|
|
+ task_id: self._task_id,
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ return files.length;
|
|
|
|
+ },
|
|
|
|
+ // 检查交付物是否都已上传
|
|
|
|
+ check_all_outcome_uploaded: async () => {
|
|
|
|
+ let files = await PrjTaskOutcome.findAll({
|
|
|
|
+ where: {
|
|
|
|
+ task_id: self._task_id,
|
|
|
|
+ uploaded: false
|
|
|
|
+ },
|
|
|
|
+ raw: true
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ if (files.length === 0) return true; // 全部已上传
|
|
|
|
+
|
|
|
|
+ return false;
|
|
|
|
+ },
|
|
// 归档任务交付物文档
|
|
// 归档任务交付物文档
|
|
place_on_outcome: async (suffix: string, category_id?: string, _memo?: string) => {
|
|
place_on_outcome: async (suffix: string, category_id?: string, _memo?: string) => {
|
|
console.log('$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$' + self._task_id);
|
|
console.log('$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$' + self._task_id);
|
|
@@ -265,7 +294,7 @@ export class FlowEngine extends EventEmitter {
|
|
Logger.error(files);
|
|
Logger.error(files);
|
|
for (let file of files) {
|
|
for (let file of files) {
|
|
let prj_file = await PrjFile.findOne({
|
|
let prj_file = await PrjFile.findOne({
|
|
- where: {id: file.object_name},raw: true
|
|
|
|
|
|
+ where: {id: file.object_name}, raw: true
|
|
});
|
|
});
|
|
Logger.error(prj_file);
|
|
Logger.error(prj_file);
|
|
if (!prj_file || !prj_file.uploaded) return;
|
|
if (!prj_file || !prj_file.uploaded) return;
|
|
@@ -318,44 +347,20 @@ export class FlowEngine extends EventEmitter {
|
|
// resolveExpression,
|
|
// resolveExpression,
|
|
// @ts-ignore
|
|
// @ts-ignore
|
|
bpmnActivityHandler: (bpmnActivity: any, _context) => {
|
|
bpmnActivityHandler: (bpmnActivity: any, _context) => {
|
|
- // if (bpmnActivity.type !== 'bpmn:UserTask' && bpmnActivity.type !== 'bpmn:ManualTask') return;
|
|
|
|
if (bpmnActivity.type === 'bpmn:Process') return;
|
|
if (bpmnActivity.type === 'bpmn:Process') return;
|
|
bpmnActivity.on('enter', this.on_enter);
|
|
bpmnActivity.on('enter', this.on_enter);
|
|
bpmnActivity.on('wait', this.on_wait);
|
|
bpmnActivity.on('wait', this.on_wait);
|
|
bpmnActivity.on('end', this.on_end);
|
|
bpmnActivity.on('end', this.on_end);
|
|
bpmnActivity.on('leave', this.on_leave);
|
|
bpmnActivity.on('leave', this.on_leave);
|
|
|
|
|
|
- // let extension_script;
|
|
|
|
- // if (bpmnActivity.behaviour.extensionElements?.values) {
|
|
|
|
- // for (const extn of bpmnActivity.behaviour.extensionElements.values) {
|
|
|
|
- // if (extn.$type === 'camunda:ExecutionListener') {
|
|
|
|
- // const event = extn.event;
|
|
|
|
- // const script = extn.script.values;
|
|
|
|
- // if (event === 'end') extension_script = script;
|
|
|
|
- // }
|
|
|
|
- // }
|
|
|
|
- // }
|
|
|
|
-
|
|
|
|
if (bpmnActivity.type !== 'bpmn:UserTask') return;
|
|
if (bpmnActivity.type !== 'bpmn:UserTask') return;
|
|
- // bpmnActivity.broker.subscribeTmp('execution', 'execute.iteration.batch', (routingKey, msg) => {
|
|
|
|
- // Logger.info('execute.iteration.batch');
|
|
|
|
- // Logger.info(msg);
|
|
|
|
- // });
|
|
|
|
|
|
+
|
|
bpmnActivity.broker.subscribeOnce('execution', 'execute.discard', self.on_execute_discard);
|
|
bpmnActivity.broker.subscribeOnce('execution', 'execute.discard', self.on_execute_discard);
|
|
bpmnActivity.broker.subscribeOnce('event', 'activity.wait', self.on_activity_wait);
|
|
bpmnActivity.broker.subscribeOnce('event', 'activity.wait', self.on_activity_wait);
|
|
- // bpmnActivity.broker.subscribeOnce('event', 'activity.discard', self.on_activity_discard);
|
|
|
|
|
|
|
|
bpmnActivity.broker.subscribeOnce('execution', 'execute.iteration.completed', self.on_loop_task_completed);
|
|
bpmnActivity.broker.subscribeOnce('execution', 'execute.iteration.completed', self.on_loop_task_completed);
|
|
return {
|
|
return {
|
|
activate: () => {
|
|
activate: () => {
|
|
- // bpmnActivity.on('end', async (elementApi, engineApi) => {
|
|
|
|
- // Logger.trace(`activity on end ${elementApi.id}`);
|
|
|
|
- // if (extension_script) {
|
|
|
|
- // console.log(extension_script);
|
|
|
|
- // }
|
|
|
|
- // },{consumerTag: `format-on-end_${IdGen.id()}`});
|
|
|
|
-
|
|
|
|
-
|
|
|
|
// @ts-ignore
|
|
// @ts-ignore
|
|
bpmnActivity.on('enter', async (_elementApi, _engineApi) => {
|
|
bpmnActivity.on('enter', async (_elementApi, _engineApi) => {
|
|
bpmnActivity.broker.publish('format', 'run.format.start', {endRoutingKey: 'run.format.complete'});
|
|
bpmnActivity.broker.publish('format', 'run.format.start', {endRoutingKey: 'run.format.complete'});
|
|
@@ -427,7 +432,7 @@ export class FlowEngine extends EventEmitter {
|
|
Logger.trace('execute.discard');
|
|
Logger.trace('execute.discard');
|
|
Logger.info(msg);
|
|
Logger.info(msg);
|
|
activity.broker.subscribeOnce('execution', 'execute.discard', this.on_execute_discard);
|
|
activity.broker.subscribeOnce('execution', 'execute.discard', this.on_execute_discard);
|
|
- await BpmnWork.update({completed_at: dayjs(), status: 3}, {
|
|
|
|
|
|
+ await BpmnWork.update({completed_at: dayjs(), status: WorkItemStatus.discarded}, {
|
|
where: {
|
|
where: {
|
|
id: msg.content.executionId
|
|
id: msg.content.executionId
|
|
},
|
|
},
|
|
@@ -481,7 +486,7 @@ export class FlowEngine extends EventEmitter {
|
|
await BpmnWork.update({
|
|
await BpmnWork.update({
|
|
completed_at: dayjs(),
|
|
completed_at: dayjs(),
|
|
handler: msg.content.output[msg.content.index].user_id,
|
|
handler: msg.content.output[msg.content.index].user_id,
|
|
- status: 2,
|
|
|
|
|
|
+ status: WorkItemStatus.completed,
|
|
},
|
|
},
|
|
{
|
|
{
|
|
returning: false,
|
|
returning: false,
|
|
@@ -544,7 +549,7 @@ export class FlowEngine extends EventEmitter {
|
|
}
|
|
}
|
|
|
|
|
|
// 等待前面的通知任务完成后,再生成新任务,这样时间线看起来是保持顺序的
|
|
// 等待前面的通知任务完成后,再生成新任务,这样时间线看起来是保持顺序的
|
|
- await delay(200);
|
|
|
|
|
|
+ await delay(300);
|
|
// 进入节点,创建此流程节点的工作项
|
|
// 进入节点,创建此流程节点的工作项
|
|
await BpmnWork.findOrCreate({
|
|
await BpmnWork.findOrCreate({
|
|
where: {id: activity.executionId},
|
|
where: {id: activity.executionId},
|
|
@@ -557,6 +562,7 @@ export class FlowEngine extends EventEmitter {
|
|
prj_id: this._prj_id,
|
|
prj_id: this._prj_id,
|
|
task_id: this._task_id ? this._task_id : null,
|
|
task_id: this._task_id ? this._task_id : null,
|
|
started_at: dayjs(),
|
|
started_at: dayjs(),
|
|
|
|
+ due_at: dayjs().add(handler.due_day ? handler.due_day : 1, 'day').format('YYYY-MM-DD 23:59:59'), // 起始时间加上过期时长(日)
|
|
completed_at: null,
|
|
completed_at: null,
|
|
case_id: this._id,
|
|
case_id: this._id,
|
|
process_type: process_type,
|
|
process_type: process_type,
|
|
@@ -589,8 +595,7 @@ export class FlowEngine extends EventEmitter {
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- on_end = async (activity, message) => {
|
|
|
|
- let self = this;
|
|
|
|
|
|
+ on_end = async (activity, _message) => {
|
|
if (activity.type !== 'bpmn:UserTask') return;
|
|
if (activity.type !== 'bpmn:UserTask') return;
|
|
console.log(`##### log state immediately in end ${activity.id} ${activity.executionId}`);
|
|
console.log(`##### log state immediately in end ${activity.id} ${activity.executionId}`);
|
|
|
|
|
|
@@ -644,7 +649,7 @@ export class FlowEngine extends EventEmitter {
|
|
await BpmnWork.update({
|
|
await BpmnWork.update({
|
|
completed_at: dayjs(),
|
|
completed_at: dayjs(),
|
|
handler: activity.content.output.user_id,
|
|
handler: activity.content.output.user_id,
|
|
- status: 2
|
|
|
|
|
|
+ status: WorkItemStatus.completed
|
|
}, {
|
|
}, {
|
|
where: {id: id},
|
|
where: {id: id},
|
|
returning: false
|
|
returning: false
|
|
@@ -805,7 +810,7 @@ export class FlowEngine extends EventEmitter {
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
case 'all':
|
|
case 'all':
|
|
- let all: any = await AcsUserRole.sequelize!.query<AcsUserInfo>(`
|
|
|
|
|
|
+ let all: any = await AcsUserRole.sequelize!.query(`
|
|
select
|
|
select
|
|
staff.id, staff.name
|
|
staff.id, staff.name
|
|
from ${AcsUserInfo.table_name} staff, ${AcsDomain.table_name} domain, ${AcsUserDomain.table_name} user_domain
|
|
from ${AcsUserInfo.table_name} staff, ${AcsDomain.table_name} domain, ${AcsUserDomain.table_name} user_domain
|
|
@@ -820,7 +825,7 @@ export class FlowEngine extends EventEmitter {
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
default:///其它标记,均通过角色获得
|
|
default:///其它标记,均通过角色获得
|
|
- let users = await AcsUserInfo.sequelize!.query<AcsUserInfo>(`
|
|
|
|
|
|
+ let users = await AcsUserInfo.sequelize!.query(`
|
|
select u.id, u.name from
|
|
select u.id, u.name from
|
|
${AcsUserInfo.table_name} u,
|
|
${AcsUserInfo.table_name} u,
|
|
${AcsUserRole.table_name} user_role
|
|
${AcsUserRole.table_name} user_role
|
|
@@ -878,4 +883,25 @@ export class FlowEngine extends EventEmitter {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ async exec_activity_script(type: 'start' | 'end', activity_id: string, message: any): Promise<IScriptResult | undefined> {
|
|
|
|
+ let activity = this.execution.getActivityById(activity_id);
|
|
|
|
+ if (activity.behaviour.extensionElements?.values) {
|
|
|
|
+ for (const extn of activity.behaviour.extensionElements.values) {
|
|
|
|
+ if (extn.$type.toLowerCase() === 'camunda:ExecutionListener'.toLowerCase()) {
|
|
|
|
+ if (!extn.script) continue;
|
|
|
|
+ const event = extn.event;
|
|
|
|
+ if (event === type) {
|
|
|
|
+ const script = extn.script.value;
|
|
|
|
+ let compile = new Script(script, {filename: `${extn.$type}/${activity.executionId}/on_${event}`});
|
|
|
|
+ return <IScriptResult>(await compile.runInNewContext({
|
|
|
|
+ ...message,
|
|
|
|
+ environment: activity.environment
|
|
|
|
+ }));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return undefined;
|
|
|
|
+ }
|
|
}
|
|
}
|