Просмотр исходного кода

feature:
1. 增加了检查任务和工作过期并设置状态的功能。
2. 增加了任务和工作即将过期和已过期提醒功能。

eyes4 5 месяцев назад
Родитель
Сommit
76efa30de9
32 измененных файлов с 576 добавлено и 375 удалено
  1. 4 1
      base-lib/src/core-models/BpmnWork.ts
  2. 35 0
      base-lib/src/core-models/Config.ts
  3. 40 1
      pmr-biz-manager/bpmn/任务审核流程.bpmn
  4. 32 1
      pmr-biz-manager/bpmn/立项流程.bpmn
  5. 27 0
      pmr-biz-manager/bpmn/计划创建审核流程.bpmn
  6. 30 2
      pmr-biz-manager/bpmn/计划变更审核流程.bpmn
  7. 146 161
      pmr-biz-manager/src/app.ts
  8. 69 43
      pmr-biz-manager/src/bpmn/flow_engine.ts
  9. 2 0
      pmr-biz-manager/src/models/Models.ts
  10. 2 1
      pmr-biz-manager/src/routes/api/prj/info/modify.ts
  11. 4 4
      pmr-biz-manager/src/routes/api/prj/outcome/add.ts
  12. 9 10
      pmr-biz-manager/src/routes/api/prj/outcome/modify.ts
  13. 6 7
      pmr-biz-manager/src/routes/api/prj/outcome/remove.ts
  14. 5 5
      pmr-biz-manager/src/routes/api/prj/outcome/remove_file.ts
  15. 4 7
      pmr-biz-manager/src/routes/api/prj/outcome/upload.ts
  16. 7 9
      pmr-biz-manager/src/routes/api/prj/plan/add_task.ts
  17. 6 7
      pmr-biz-manager/src/routes/api/prj/plan/get_tasks.ts
  18. 15 11
      pmr-biz-manager/src/routes/api/prj/plan/modify_task.ts
  19. 6 5
      pmr-biz-manager/src/routes/api/prj/plan/recover_task.ts
  20. 5 7
      pmr-biz-manager/src/routes/api/prj/plan/remove_task.ts
  21. 2 2
      pmr-biz-manager/src/routes/api/prj/plan/set_check_flow.ts
  22. 5 5
      pmr-biz-manager/src/routes/api/prj/plan/set_progress.ts
  23. 6 6
      pmr-biz-manager/src/routes/api/prj/plan/sort_task.ts
  24. 4 5
      pmr-biz-manager/src/routes/api/prj/work/detail.ts
  25. 7 5
      pmr-biz-manager/src/routes/api/prj/work/done_stat.ts
  26. 13 9
      pmr-biz-manager/src/routes/api/prj/work/stat.ts
  27. 10 18
      pmr-biz-manager/src/routes/api/prj/work/submit.ts
  28. 3 1
      pmr-biz-manager/src/routes/api/prj/work/undone_count.ts
  29. 7 5
      pmr-biz-manager/src/routes/api/prj/work/undone_stat.ts
  30. 23 11
      pmr-biz-manager/src/utils/bpmn_work_helper.ts
  31. 36 21
      pmr-biz-manager/src/utils/define.ts
  32. 6 5
      pmr-biz-manager/src/utils/plan_task_helper.ts

+ 4 - 1
base-lib/src/core-models/BpmnWork.ts

@@ -13,15 +13,18 @@ export class BpmnWork extends Model {
     declare id: string;
     declare execution_id: string;
     declare name: string;
+    declare assigned_to: string;
     declare activity_id: string;
     declare owner: string;
     declare prj_id: string;
     declare task_id: string;
     declare started_at: string;
     declare completed_at: string;
+    declare due_at: string;
     declare process_type: number;
     declare status: number;
     declare case_id: string;
+    declare show_in_my_works: boolean;
 
     static table_name = 'tb_bpmn_work';
 
@@ -134,7 +137,7 @@ export class BpmnWork extends Model {
 
     static indexes = []
 
-    static associate(sequelize: Sequelize) {}
+    static associate(_sequelize: Sequelize) {}
 
     static init_sqls = []
 }

+ 35 - 0
base-lib/src/core-models/Config.ts

@@ -0,0 +1,35 @@
+import {Model} from "sequelize";
+import { Sequelize, DataTypes }  from 'sequelize';
+
+/**
+ * 系统配置
+ */
+
+export class Config extends Model {
+    declare id: string;
+    declare config: any;
+
+    static table_name = 'tb_config';
+
+    static attributes = {
+        id: {
+            type: DataTypes.STRING,
+            primaryKey: true,
+            comment: '配置id',
+        },
+        config: {
+            type: DataTypes.JSONB,
+            comment: '配置信息',
+        }
+    }
+
+    static indexes = []
+
+    static associate(_sequelize: Sequelize) {}
+
+    static init_sqls = [
+        `insert into ${Config.table_name} (id, config) 
+        values('1', '{"remind": {"work": 2, "task": 2}}'::jsonb) ON CONFLICT DO NOTHING`,
+
+    ]
+}

+ 40 - 1
pmr-biz-manager/bpmn/任务审核流程.bpmn

@@ -49,6 +49,45 @@
           </camunda:script>
         </camunda:executionListener>
         <camunda:taskListener class="" event="assignment" />
+        <camunda:executionListener event="start">
+          <camunda:script scriptFormat="JavaScript">
+(async function () {
+    try {
+        let handlers = await this.environment.services.get_handlers('task_checker');
+        let outcome_count = await this.environment.services.get_outcome_count();
+        this.environment.Logger('work').error(handlers);
+        let result = handlers.length &gt; 0 ? true: false; 
+        if (!result &amp;&amp; outcome_count &gt; 0) {
+            return {
+                result: false,
+                message: '任务申请审核前,请先配置任务审核成员,否则无法执行审核任务。'
+            }
+        } 
+
+        let outcome_all_uploaded = await this.environment.services.check_all_outcome_uploaded();
+        this.environment.Logger('work').error(outcome_all_uploaded);
+        if (!outcome_all_uploaded) {
+            return {
+                result: false,
+                message: '任务审核前,请确保此任务的所有交付物文档都已上传。'
+            }
+        }
+        return {
+            result: true,
+            message: 'OK'
+        }
+        
+    } catch (e) {
+        this.environment.Logger('work').error(e);
+        return {
+            result: false,
+            message: 'fail'
+        }
+    }
+})();
+            
+          </camunda:script>
+        </camunda:executionListener>
       </bpmn2:extensionElements>
       <bpmn2:incoming>Flow_1t45qnv</bpmn2:incoming>
       <bpmn2:incoming>Flow_0gyvyyo</bpmn2:incoming>
@@ -408,7 +447,7 @@ this.environment.variables.pass = passed;
     <bpmn2:textAnnotation id="TextAnnotation_1vnw6c1">
       <bpmn2:text>所有人都审核通过才算通过</bpmn2:text>
     </bpmn2:textAnnotation>
-    <bpmn2:association id="Association_0p6rlsh" associationDirection="None" sourceRef="Activity_0gfds7w" targetRef="TextAnnotation_1vnw6c1" />
+    <bpmn2:association id="Association_0p6rlsh" sourceRef="Activity_0gfds7w" targetRef="TextAnnotation_1vnw6c1" />
   </bpmn2:process>
   <bpmn2:signal id="Signal_0hhmd7l" name="cancel_sign" />
   <bpmn2:message id="Message_0fh9t3e" name="msg_cancel" />

+ 32 - 1
pmr-biz-manager/bpmn/立项流程.bpmn

@@ -40,6 +40,37 @@
         this.environment.Logger('work').error(e);
     }
 })();           
+</camunda:script>
+        </camunda:executionListener>
+        <camunda:executionListener event="start">
+          <camunda:script scriptFormat="JavaScript">
+(async function () {
+    try {
+        let checkers = await this.environment.services.get_handlers('project_checker');
+         this.environment.Logger('work').error(checkers);
+
+        let result = checkers.length &gt; 0 ? true: false; 
+        if (!result) {
+            return {
+                result: false,
+                message: '立项申请前,请先配置审核组成员,否则无法执行立项审核任务。'
+            }
+        } else {
+            return {
+                result: true,
+                message: 'OK'
+            }
+        }
+        
+        
+    } catch (e) {
+        this.environment.Logger('work').error(e);
+        return {
+            result: false,
+            message: 'fail'
+        }
+    }
+})();
 </camunda:script>
         </camunda:executionListener>
       </bpmn2:extensionElements>
@@ -472,4 +503,4 @@ next();
       </bpmndi:BPMNEdge>
     </bpmndi:BPMNPlane>
   </bpmndi:BPMNDiagram>
-</bpmn2:definitions>
+</bpmn2:definitions>

+ 27 - 0
pmr-biz-manager/bpmn/计划创建审核流程.bpmn

@@ -44,6 +44,33 @@
 })();             
           </camunda:script>
         </camunda:executionListener>
+        <camunda:executionListener event="start">
+          <camunda:script scriptFormat="JavaScript">(async function () {
+    try {
+        let handlers = await this.environment.services.get_handlers('project_checker');
+        let result = handlers.length &gt; 0 ? true: false; 
+        if (!result) {
+            return {
+                result: false,
+                message: '计划申请前,请先配置审核组成员,否则无法执行计划审核任务。'
+            }
+        } else {
+            return {
+                result: true,
+                message: 'OK'
+            }
+        }
+        
+        
+    } catch (e) {
+        this.environment.Logger('work').error(e);
+        return {
+            result: false,
+            message: 'fail'
+        }
+    }
+})();</camunda:script>
+        </camunda:executionListener>
       </bpmn2:extensionElements>
       <bpmn2:incoming>Flow_1t45qnv</bpmn2:incoming>
       <bpmn2:incoming>Flow_0gyvyyo</bpmn2:incoming>

+ 30 - 2
pmr-biz-manager/bpmn/计划变更审核流程.bpmn

@@ -44,6 +44,33 @@
 })(); 
           </camunda:script>
         </camunda:executionListener>
+        <camunda:executionListener event="start">
+          <camunda:script scriptFormat="JavaScript">(async function () {
+    try {
+        let handlers = this.environment.services.get_handlers('project_checker');
+        let result = handlers.length &gt; 0 ? true: false; 
+        if (!result) {
+            return {
+                result: false,
+                message: '计划变更申请前,请先配置审核组成员,否则无法执行计划变更审核任务。'
+            }
+        } else {
+            return {
+                result: true,
+                message: 'OK'
+            }
+        }
+        
+        
+    } catch (e) {
+        this.environment.Logger('work').error(e);
+        return {
+            result: false,
+            message: 'fail'
+        }
+    }
+})();</camunda:script>
+        </camunda:executionListener>
       </bpmn2:extensionElements>
       <bpmn2:incoming>Flow_1t45qnv</bpmn2:incoming>
       <bpmn2:incoming>Flow_0gyvyyo</bpmn2:incoming>
@@ -54,7 +81,8 @@
     <bpmn2:sequenceFlow id="Flow_1rg4oob" sourceRef="task_request_plan_alter" targetRef="task_approve_plan_alter">
       <bpmn2:extensionElements>
         <camunda:properties>
-          <camunda:property name="prj_phase" value="apply_plan_alt" />
+          <camunda:property name="prj_phase" value="review_plan_alt" />
+          <camunda:property name="prj_phase_name" value="计划变更审核中" />
         </camunda:properties>
       </bpmn2:extensionElements>
     </bpmn2:sequenceFlow>
@@ -65,7 +93,6 @@
           <camunda:inputParameter name="process_type">${1}</camunda:inputParameter>
           <camunda:inputParameter name="target_type"> project-plan</camunda:inputParameter>
           <camunda:inputParameter name="prj_id">${environment.variables.prj_id}</camunda:inputParameter>
-          <camunda:inputParameter name="prj_phase">review_plan_alt</camunda:inputParameter>
           <camunda:inputParameter name="draft">${true}</camunda:inputParameter>
           <camunda:inputParameter name="reverse_target">${{"target": "project-plan", "draft": false}}</camunda:inputParameter>
         </camunda:inputOutput>
@@ -122,6 +149,7 @@
       <bpmn2:extensionElements>
         <camunda:properties>
           <camunda:property name="prj_phase" value="reject_plan_alt" />
+          <camunda:property name="prj_phase_name" value="计划变更被驳回" />
         </camunda:properties>
       </bpmn2:extensionElements>
     </bpmn2:sequenceFlow>

+ 146 - 161
pmr-biz-manager/src/app.ts

@@ -14,10 +14,12 @@ import {PrjFile} from "@core-models/PrjFile";
 import {FlowEngine} from "@src/bpmn/flow_engine";
 import {BpmnCase} from "@core-models/BpmnCase";
 import {bpmn_flow_on_end} from "@src/utils/bpmn_work_helper";
-import {OutcomeStatus} from "@src/utils/define";
+import {OutcomeStatus, TaskStatus, WorkItemProcessType, WorkItemStatus} from "@src/utils/define";
 import {prj_stat_monthly, task_delay_rate_daily, task_stat_daily} from "@src/utils/prj_stat";
-import {evaluate, executor} from "@util/Executor";
 import {PrjPlanTask} from "@core-models/PrjPlanTask";
+import {Op} from "sequelize";
+import {BpmnWork} from "@core-models/BpmnWork";
+import {Config} from "@core-models/Config";
 
 const schedule = require('node-schedule');
 
@@ -25,97 +27,7 @@ dayjs.extend(utc);
 dayjs.extend(timezone);
 dayjs.tz.guess();
 
-let json = `{
-    "type": "object",
-    "title": "条件",
-    "required": [
-        "pass"
-    ],
-    "ui:order": [
-        "pass",
-        "opinion"
-    ],
-    "properties": {
-        "memo": {
-            "type": "string",
-            "title": "申请理由",
-            "format": "textarea",
-            "default": "\${environment.output.task_request.memo}",
-            "readonly": true
-        },
-        "files": {
-            "type": "download",
-            "title": "附件",
-            "default": \${JSON.stringify(environment.output.task_request.files)}
-        },
-        "pass": {
-            "type": "boolean",
-            "title": "是否通过审核?",
-            "default": false
-        },
-        "opinion": {
-            "type": "string",
-            "title": "审核意见",
-            "format": "textarea",
-            "default": "",
-            "minRows": 6,
-            "minLength": 10,
-            "ui:options": {
-                "rows": 10,
-                "type": "textarea"
-            }
-        }
-    }
-}`
-
-
-let result = executor(json, {global: {
-    environment: {
-        output: {
-            task_request: {
-                memo: "memo text",
-                files: [
-                    {
-                        id: '1',
-                        name: 'file1'
-                    },
-                    {
-                        id: '2',
-                        name: 'file2'
-                    }
-                ]
-            }
-        }
-    },
-         get_files: async (files: string[]) => {
-            let result: any[] = [];
-           for (let file of files) {
-               let f = await PrjFile.findOne({where: {id: file}});
-               if (f) {
-                   result.push({id: file, filename: f.filename});
-               }
-           }
-           return result;
-        },
-}})
-console.log(result);
-// result = executor(`
-//                     ((num) => {
-//                      return new Promise((resolve) => {
-//                           setTimeout(function () {
-//                             return resolve(Math.round(num))
-//                         }, 30);
-//                      });
-//                      })(\${test})
-//                 `, {global: {test: 1.5}})
-//
-// result.then((r) => {
-//     console.log(r)
-// });
-
-// dayjs.tz("2014-06-01 12:00", "Asia/Shanghai")
 new ServiceApp().start(new MyRouter('@src/routes', guards), Models).then(async () => {
-    // await bpmn_flow_on_end('63dcaf1fe8000000', 'plan_alter', '63daded44f000000', 'admin');
     try {
         let oss = Oss.get_instance('pmr-doc');
         if (!await oss.bucket_exists(oss.bucket)) {
@@ -208,13 +120,13 @@ new ServiceApp().start(new MyRouter('@src/routes', guards), Models).then(async (
     let flow_cases = await BpmnCase.findAll({where: {completed_at: null}, raw: true});
     for (let flow of flow_cases) {
         let task = await PrjPlanTask.findOne({where: {id: flow.task_id}, raw: true});
-        if (!task) continue;
+        // if (!task) continue;
         await new FlowEngine(flow.model_id, flow.model, {
             prj_id: flow.prj_id,
             owner: flow.creator_id,
             id: flow.id,
             task_id: flow.task_id,
-            task_name: task.name
+            task_name: task?.name
         }, bpmn_flow_on_end).run(flow.state);
     }
 
@@ -225,78 +137,151 @@ new ServiceApp().start(new MyRouter('@src/routes', guards), Models).then(async (
         await task_delay_rate_daily();
     });
 
-    schedule.scheduleJob('0 * * * *', async function () {
-       FlowEngine.case_engine_map.forEach( (value, key) => {
-           console.log(key);
-           // console.log(value);
-       });
-    });
+    // 每天8:30检查任务和工作项是否有临近过期的,有的话生成提醒工作
+    schedule.scheduleJob('*/20 * * * * *', async function () {
+        let config = await Config.findOne({where: {id: '1'}});
+        let task_remind_day_in_advance = config?.config.remind?.task ? config.config.remind.task : 2;
+        let work_remind_day_in_advance = config?.config.remind?.work ? config.config.remind.work : 1;
 
-    // let bpmn = await BpmnModel.findOne({where: {id: 'task'}, raw: true});
-    // if (bpmn) {
-    //
-    //     let flow =  new FlowEngine('task', bpmn.content, {
-    //             prj_id: '63daded44f000000',
-    //             owner: 'admin',
-    //             task_id: '63e1c7d7c6800000'
-    //         },
-    //         bpmn_flow_on_end
-    //     );
-    //     await BpmnCase.create({
-    //         id: flow.id,
-    //         model_id: bpmn.id,
-    //         model: bpmn.content,
-    //         started_at: dayjs(),
-    //         creator_id: 'admin',
-    //         prj_id: '63daded44f000000',
-    //         // state: await flow.state(),
-    //     }, {});
-    //     await flow.run();
-    // }
-/*
-    console.log(`Reports are compact? ${report.compact}`);
-    const data = report.getReport();
-    console.log(data.header.cpus);
+        // 取即将过期的任务
+        let tasks = await PrjPlanTask.findAll({
+            where: {
+                end_at: {
+                    [Op.lte]: dayjs().add(task_remind_day_in_advance, 'day').format('YYYY-MM-DD 23:59:59'),
+                    [Op.gt]: dayjs()
+                }
+            },
+            raw: true
+        });
 
-    // 自动到MQTT服务上注册尚未注册的设备
-    const job = schedule.scheduleJob('* * * * * *', async function () {
-        // console.log(new Date());
-        const data = report.getReport();
-        try {
-            let cpus: string = '';
-            let totalUsage = 0;
-            let totalIdle = 0;
-            for (let i = 0; i < data.header.cpus.length; i++) {
-                let cpu = data.header.cpus[i];
-                totalUsage += (cpu.user + cpu.nice + cpu.sys + cpu.irq);
-                totalIdle += cpu.idle;
-            }
-            const totalTick = totalIdle + totalUsage;
-            const cpuUsage = totalUsage / totalTick * 100;
+        // 取即将过期的工作项
+        let works = await BpmnWork.findAll({
+            where: {
+                due_at: {
+                    [Op.lte]: dayjs().add(work_remind_day_in_advance, 'day').format('YYYY-MM-DD'),
+                    [Op.gt]: dayjs()
+                },
+                show_in_my_works: true,
+                process_type: WorkItemProcessType.todo,
+                status: {[Op.lt]: WorkItemStatus.completed}
+            },
+            raw: true
+        });
 
-            const totalMemory = data.resourceUsage.total_memory;
-            const usedMemory = data.resourceUsage.maxRss;
-            const memoryUsagePercent = (usedMemory / totalMemory) * 100;
-            console.log(`
-            time: ${data.header.dumpEventTime}
-            CPUs: ${cpuUsage.toFixed(2)} %
-            Mem: ${memoryUsagePercent.toFixed(2)} %  used ${formatBytes(usedMemory)}
-            
-            `);
+        for (let task of tasks) {
+            // 为每个即将过期的任务生成一条提醒工作
+            Logger.info(`任务 ${task.name} 即将过期`);
+            await BpmnWork.findOrCreate({
+                where: {id: `due_${task.id}_${dayjs().format('YYYYMMDD')}`},
+                defaults: {
+                    name: `任务 ${task.name} 即将过期`,
+                    prj_id: task.prj_id,
+                    task_id: task.id,
+                    case_id: null,
+                    assigned_to: task.handler_id,
+                    started_at: dayjs(),
+                    process_type: WorkItemProcessType.to_read,
+                    show_in_my_works: true,
+                    desc: `请注意:任务 ${task.name} 即将在<b>${dayjs(task.end_at).format('YYYY-MM-DD')}<span style="color: red;">过期</span></b>,请尽快完成该任务。`
+                }
+            });
+        }
 
-        } catch (e) {
-            Logger.error(e);
+        for (let work of works) {
+            // 为每个即将过期的工作项生成一条提醒工作
+            Logger.info(`工作 ${work.name} 即将过期`);
+            await BpmnWork.findOrCreate({
+                where: {id: `due_${work.id}_${dayjs().format('YYYYMMDD')}`},
+                defaults: {
+                    name: `工作 ${work.name} 即将过期`,
+                    prj_id: work.prj_id,
+                    task_id: work.task_id,
+                    case_id: work.case_id,
+                    assigned_to: work.assigned_to,
+                    started_at: dayjs(),
+                    process_type: WorkItemProcessType.to_read,
+                    show_in_my_works: true,
+                    desc: `请注意:您有一项工作"${work.name}" 即将在<b>${dayjs(work.due_at).format('YYYY-MM-DD')}<span style="color:red;">过期</span></b>,请尽快完成。`,
+                }
+            });
+        }
+
+        // 设置已过期的任务状态并返回这些任务
+        let expired_tasks = await PrjPlanTask.update({status: TaskStatus.expired}, {
+            where: {
+                end_at: {
+                    [Op.lt]: dayjs()
+                },
+                status: {
+                    [Op.lt]: TaskStatus.expired
+                }
+            },
+            returning: true
+        });
+        // 为已过期的任务生成一条提醒工作
+        for (let task of expired_tasks[1]) {
+            Logger.info(`任务 ${task.name} 已过期`);
+            await PrjPlanTask.findOrCreate({
+                where: {id: `expired_${task.id}`},
+                defaults: {
+                    name: `任务 ${task.name} 已过期`,
+                    prj_id: task.prj_id,
+                    task_id: task.id,
+                    case_id: null,
+                    assigned_to: task.handler_id,
+                    started_at: dayjs(),
+                    process_type: WorkItemProcessType.to_read,
+                    show_in_my_works: true,
+                    desc: `请注意:任务 <b>"${task.name}" <span style="color: red;">已超期</span></b>,请尽快完成该任务或调整任务计划。`
+                }
+            });
+        }
+
+        // 设置已过期的工作项状态
+        let expired_work = await BpmnWork.update({status: WorkItemStatus.expired}, {
+            where: {
+                due_at: {
+                    [Op.lt]: dayjs()
+                },
+                show_in_my_works: true,
+                process_type: WorkItemProcessType.todo,
+                status: WorkItemStatus.doing
+            },
+            returning: true
+        });
+        // 为已过期的工作项生成一条提醒工作
+        for (let work of expired_work[1]) {
+            Logger.info(`工作 ${work.name} 已过期`);
+            await BpmnWork.findOrCreate({
+                where: {id: `expired_${work.id}`},
+                defaults: {
+                    name: `工作 ${work.name} 已过期`,
+                    prj_id: work.prj_id,
+                    task_id: work.task_id,
+                    case_id: work.case_id,
+                    assigned_to: work.assigned_to,
+                    started_at: dayjs(),
+                    process_type: WorkItemProcessType.to_read,
+                    show_in_my_works: true,
+                    desc: `请注意:您的工作<b>"${work.name}" 已超期</span></b>,请尽快完成。`,
+                }
+            });
         }
     });
-*/
-});
+
+    schedule.scheduleJob('0 * * * *', async function () {
+        FlowEngine.case_engine_map.forEach((value, key) => {
+            console.log(key);
+            // console.log(value);
+        });
+    });
 
 
-// function formatBytes(bytes, decimals = 2) {
-//     if (bytes === 0) return '0 Bytes';
-//     const k = 1024;
-//     const dm = decimals < 0 ? 0 : decimals;
-//     const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
-//     const i = Math.floor(Math.log(bytes) / Math.log(k));
-//     return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
-// }
+    schedule.scheduleJob('0 * * * *', async function () {
+        FlowEngine.case_engine_map.forEach((value, key) => {
+            console.log(key);
+            // console.log(value);
+        });
+    });
+
+});

+ 69 - 43
pmr-biz-manager/src/bpmn/flow_engine.ts

@@ -14,17 +14,17 @@ import {BpmnWork} from "@core-models/BpmnWork";
 import {Logger} from "@util/Logger";
 import {BpmnForm} from "@core-models/BpmnForm";
 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';
+
 const camunda = require('camunda-bpmn-moddle/resources/camunda.json');
 import {resolveExpression} from '@aircall/expression-parser';
 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 {PrjFile} from "@core-models/PrjFile";
 import {executor} from "@util/Executor";
 import {Script} from 'vm';
-import {BpmnModel} from "@core-models/BpmnModel";
 import {PrjTaskOutcome} from "@core-models/PrjTaskOutcome";
 
 export interface IHandler {
@@ -42,11 +42,16 @@ export interface IFlowArgs {
     task_name?: string;
 }
 
+export interface IScriptResult {
+    result: boolean;
+    message: string;
+}
+
 interface IFiles {
     id: string;
     name: string;
 }
-const kTypeResolver = Symbol.for('type resolver');
+
 
 async function delay(ms) {
     return new Promise(resolve => setTimeout(resolve, ms));
@@ -122,14 +127,15 @@ export class FlowEngine extends EventEmitter {
         const listener = new EventEmitter();
         listener.on('flow.take', this.on_flow_take);
         if (state) {
+            Logger.info(`recovering flow engine, id: ${this._id}`);
             this.engine = this.engine.recover(state);
-
             this.execution = await this.engine.resume({listener}, async (err, _execution) => {
                 if (err) console.log(err);
                 console.log('Execution completed with id', this.id);
                 await this.complete();
             });
         } else {
+            Logger.info(`creating flow engine, id: ${this._id}`);
             this.execution = await this.engine.execute({listener}, async (err, _execution) => {
                 if (err) console.log(err);
                 console.log('Execution completed with id', this.id);
@@ -183,7 +189,7 @@ export class FlowEngine extends EventEmitter {
                 camunda,
             },
             services: {
-                owner: async () =>  {
+                owner: async () => {
                     let owner = await AcsUserInfo.findOne({where: {id: this._owner}, raw: true});
                     if (!owner) return;
                     return {
@@ -215,7 +221,7 @@ export class FlowEngine extends EventEmitter {
                         case_id: self._id,
                         assigned_to: assigned_to,
                         started_at: dayjs(),
-                        process_type: 2,
+                        process_type: WorkItemProcessType.to_read,
                         show_in_my_works: true,
                         desc: detail
                     })
@@ -238,17 +244,40 @@ export class FlowEngine extends EventEmitter {
                         case_id: self._id,
                         assigned_to: assigned_to,
                         started_at: dayjs(),
-                        process_type: 3,
+                        process_type: WorkItemProcessType.to_read,
                         show_in_my_works: true,
                         desc: detail
                     })
                 },
                 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},
                         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) => {
                     console.log('$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$' + self._task_id);
@@ -265,7 +294,7 @@ export class FlowEngine extends EventEmitter {
                     Logger.error(files);
                     for (let file of files) {
                         let prj_file = await PrjFile.findOne({
-                            where: {id: file.object_name},raw: true
+                            where: {id: file.object_name}, raw: true
                         });
                         Logger.error(prj_file);
                         if (!prj_file || !prj_file.uploaded) return;
@@ -318,44 +347,20 @@ export class FlowEngine extends EventEmitter {
                 // resolveExpression,
                 // @ts-ignore
                 bpmnActivityHandler: (bpmnActivity: any, _context) => {
-                    // if (bpmnActivity.type !== 'bpmn:UserTask' && bpmnActivity.type !== 'bpmn:ManualTask') return;
                     if (bpmnActivity.type === 'bpmn:Process') return;
                     bpmnActivity.on('enter', this.on_enter);
                     bpmnActivity.on('wait', this.on_wait);
                     bpmnActivity.on('end', this.on_end);
                     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;
-                    // 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('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);
                     return {
                         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
                             bpmnActivity.on('enter', async (_elementApi, _engineApi) => {
                                 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.info(msg);
         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: {
                 id: msg.content.executionId
             },
@@ -481,7 +486,7 @@ export class FlowEngine extends EventEmitter {
         await BpmnWork.update({
                 completed_at: dayjs(),
                 handler: msg.content.output[msg.content.index].user_id,
-                status: 2,
+                status: WorkItemStatus.completed,
             },
             {
                 returning: false,
@@ -544,7 +549,7 @@ export class FlowEngine extends EventEmitter {
         }
 
         // 等待前面的通知任务完成后,再生成新任务,这样时间线看起来是保持顺序的
-        await delay(200);
+        await delay(300);
         // 进入节点,创建此流程节点的工作项
         await BpmnWork.findOrCreate({
             where: {id: activity.executionId},
@@ -557,6 +562,7 @@ export class FlowEngine extends EventEmitter {
                 prj_id: this._prj_id,
                 task_id: this._task_id ? this._task_id : null,
                 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,
                 case_id: this._id,
                 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;
         console.log(`##### log state immediately in end ${activity.id} ${activity.executionId}`);
 
@@ -644,7 +649,7 @@ export class FlowEngine extends EventEmitter {
             await BpmnWork.update({
                 completed_at: dayjs(),
                 handler: activity.content.output.user_id,
-                status: 2
+                status: WorkItemStatus.completed
             }, {
                 where: {id: id},
                 returning: false
@@ -805,7 +810,7 @@ export class FlowEngine extends EventEmitter {
                 }
                 break;
             case 'all':
-                let all: any = await AcsUserRole.sequelize!.query<AcsUserInfo>(`
+                let all: any = await AcsUserRole.sequelize!.query(`
                     select 
                         staff.id, staff.name
                     from ${AcsUserInfo.table_name} staff,  ${AcsDomain.table_name} domain, ${AcsUserDomain.table_name} user_domain
@@ -820,7 +825,7 @@ export class FlowEngine extends EventEmitter {
                 }
                 break;
             default:///其它标记,均通过角色获得
-                let users = await AcsUserInfo.sequelize!.query<AcsUserInfo>(`
+                let users = await AcsUserInfo.sequelize!.query(`
                     select u.id, u.name from
                         ${AcsUserInfo.table_name} u,
                         ${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;
+    }
 }

+ 2 - 0
pmr-biz-manager/src/models/Models.ts

@@ -26,8 +26,10 @@ import {PrjLogs} from "@core-models/PrjLogs";
 import {ReportPrjStat} from "@core-models/ReportPrjStat";
 import {ReportTaskStat} from "@core-models/ReportTaskStat";
 import {ReportTaskDelayRate} from "@core-models/ReportTaskDelayRate";
+import {Config} from "@core-models/Config";
 
 export const Models: Array<IDataModelInfo> = [
+    {tag: 'acs', model: Config, timestamps: false},
     {tag: 'acs', model: AcsDomain, timestamps: false},
     {tag: 'acs', model: AcsUserInfo, timestamps: false},
     {tag: 'acs', model: AcsUserDomain, timestamps: false},

+ 2 - 1
pmr-biz-manager/src/routes/api/prj/info/modify.ts

@@ -16,6 +16,7 @@ import {BpmnCase} from "@core-models/BpmnCase";
 import {BpmnWork} from "@core-models/BpmnWork";
 import {FlowEngine} from "@src/bpmn/flow_engine";
 import {PrjPhaseDefine} from "@core-models/PrjPhaseDefine";
+import {WorkItemStatus} from "@src/utils/define";
 
 interface IData {
     /**
@@ -148,7 +149,7 @@ function modify(content: IRequest, params: IMethodParams, cached_data: ICachedDa
                 let [_count, flow_cases] = await BpmnCase.update({creator_id: data.leader_id},
                     {where: {prj_id: prj.id, task_id: {[Op.is]: null}, completed_at: {[Op.is]: null}}, transaction: t, returning: true});
                 await BpmnWork.update({assigned_to: data.leader_id},
-                    {where: {prj_id: prj.id, task_id: {[Op.is]: null}, status: {[Op.ne]: 2}}, transaction: t, returning: false});
+                    {where: {prj_id: prj.id, task_id: {[Op.is]: null}, status: {[Op.lt]: WorkItemStatus.completed}}, transaction: t, returning: false});
                 if (flow_cases && flow_cases.length > 0) {
                     for (let flow_case of flow_cases) {
                         let engine = FlowEngine.case_engine_map.get(flow_case.id);

+ 4 - 4
pmr-biz-manager/src/routes/api/prj/outcome/add.ts

@@ -69,7 +69,7 @@ function statusGuard(json: IRequest, cached_data: ICachedData): Promise<void> {
             if (phase.order_index === 70)
                 return reject(Resp.gen_err(Resp.InvalidFlow, '当前项目阶段不允许直接添加交付物,应在草稿中添加后重新提交计划变更审核。'));
         }
-        if (task.status >= TaskStatus.ApplyForReview) {
+        if (task.status >= TaskStatus.apply_for_review) {
             return reject(Resp.gen_err(Resp.InvalidFlow, '当前任务阶段不允许添加交付物。'));
         }
 
@@ -103,7 +103,7 @@ function add(json: IRequest, params: IMethodParams, cached_data: ICachedData): P
                 ...data
             }
             if (data.draft) {
-                value.change_marker = ChangeMarker.Added;
+                value.change_marker = ChangeMarker.added;
             }
             value = DataCURD.filter_null(value);
             let model = data.draft ? PrjTaskOutcomeDraft : PrjTaskOutcome;
@@ -111,9 +111,9 @@ function add(json: IRequest, params: IMethodParams, cached_data: ICachedData): P
             /// 如果计划任务是草稿,且还没有生成计划变更任务,则生成任务
             if (data.draft === true) {
                 let task = await PrjPlanTaskDraft.findOne({where: {id: data.task_id}, raw: true, transaction: t});
-                if (task && task.change_marker !== ChangeMarker.Added) {
+                if (task && task.change_marker !== ChangeMarker.added) {
                     await start_plan_alt_flow(task.prj_id, cached_data.user_id, t as Transaction);
-                    await PrjPlanTaskDraft.update({change_marker: ChangeMarker.Changed},
+                    await PrjPlanTaskDraft.update({change_marker: ChangeMarker.changed},
                         {where: {id: data.task_id}, transaction: t, returning: false});
                 }
             }

+ 9 - 10
pmr-biz-manager/src/routes/api/prj/outcome/modify.ts

@@ -12,8 +12,7 @@ import {PrjPlanTask} from "@core-models/PrjPlanTask";
 import {PrjInfo} from "@core-models/PrjInfo";
 import {PrjPhaseDefine} from "@core-models/PrjPhaseDefine";
 import {
-    is_project_task_outcome_modifiable,
-    is_project_task_progress_modifiable
+    is_project_task_outcome_modifiable
 } from "@src/utils/prj_premission_helper";
 
 interface IData {
@@ -74,8 +73,8 @@ function statusGuard(json: IRequest, cached_data: ICachedData): Promise<void> {
         let outcome = await outcome_model.findOne({where: {id: data.id}, raw: true});
         if (!outcome) return reject(Resp.gen_err(Resp.ResourceNotFound, "交付物不存在,id: " + data.id));
         if (data.draft) {
-            if ((<PrjTaskOutcomeDraft>outcome).change_marker !== ChangeMarker.Added) {
-                data.change_marker = ChangeMarker.Changed;
+            if ((<PrjTaskOutcomeDraft>outcome).change_marker !== ChangeMarker.added) {
+                data.change_marker = ChangeMarker.changed;
             }
         }
         if (!await is_project_task_outcome_modifiable(user, outcome.task_id, data.draft))
@@ -105,7 +104,7 @@ function statusGuard(json: IRequest, cached_data: ICachedData): Promise<void> {
             if (phase.order_index === 70)
                 return reject(Resp.gen_err(Resp.InvalidFlow, '当前项目阶段不允许直接修改交付物,应在草稿中修改后重新提交计划变更审核。'));
         }
-        if (task.status >= TaskStatus.ApplyForReview) {
+        if (task.status >= TaskStatus.apply_for_review) {
             return reject(Resp.gen_err(Resp.InvalidFlow, '当前任务阶段不允许修改交付物。'));
         }
 
@@ -141,15 +140,15 @@ async function before_respond(content: IRequest, params: IMethodParams, cached_d
         /// 如果是草稿,且还没有生成计划变更任务,则生成任务
         if (data.draft ) {
             let outcome = <PrjTaskOutcomeDraft>result;
-            if (outcome.change_marker !== ChangeMarker.Added) {
+            if (outcome.change_marker !== ChangeMarker.added) {
                 // 将交付物的状态改为已修改
-                await PrjTaskOutcomeDraft.update({change_marker: ChangeMarker.Changed},
-                        {where: {id: outcome.id}, transaction: t});
+                await PrjTaskOutcomeDraft.update({change_marker: ChangeMarker.changed},
+                        {where: {id: outcome.id}, transaction: t, returning: false});
             }
             let task = await PrjPlanTaskDraft.findOne({where: {id: result.task_id}, transaction: t});
-            if (task && task.change_marker !== ChangeMarker.Added){
+            if (task && task.change_marker !== ChangeMarker.added){
                 await start_plan_alt_flow(task.prj_id, cached_data.user_id, t as Transaction);
-                await PrjPlanTaskDraft.update({change_marker: ChangeMarker.Changed},
+                await PrjPlanTaskDraft.update({change_marker: ChangeMarker.changed},
                     {where: {id: result.task_id}, transaction: t, returning: false});
             }
         }

+ 6 - 7
pmr-biz-manager/src/routes/api/prj/outcome/remove.ts

@@ -11,8 +11,7 @@ import {ChangeMarker, TaskStatus} from "@src/utils/define";
 import {PrjPlanTask} from "@core-models/PrjPlanTask";
 import {Transaction} from "sequelize";
 import {
-    is_project_task_outcome_modifiable,
-    is_project_task_progress_modifiable
+    is_project_task_outcome_modifiable
 } from "@src/utils/prj_premission_helper";
 
 interface IData {
@@ -34,10 +33,10 @@ async function remove(json: IRequest, params: IMethodParams, cached_data: ICache
     try {
         if (data.draft) {
             let outcome = await PrjTaskOutcomeDraft.findOne({where: {id: data.id}, transaction: t});
-            if (outcome && outcome.change_marker === ChangeMarker.Added) {
+            if (outcome && outcome.change_marker === ChangeMarker.added) {
                 await PrjTaskOutcomeDraft.destroy({where: {id: data.id}, transaction: t});
             } else {
-                let outcome = await PrjTaskOutcomeDraft.update({change_marker: ChangeMarker.Deleted}, {
+                let outcome = await PrjTaskOutcomeDraft.update({change_marker: ChangeMarker.deleted}, {
                     where: {id: data.id},
                     transaction: t,
                     returning: true
@@ -45,9 +44,9 @@ async function remove(json: IRequest, params: IMethodParams, cached_data: ICache
                 Logger.trace(outcome);
                 if (!outcome[1][0]) throw Resp.gen_err(Resp.ResourceNotFound);
                 let task = await PrjPlanTaskDraft.findOne({where: {id: outcome[1][0].task_id}, transaction: t});
-                if (task && task.change_marker !== ChangeMarker.Added) {
+                if (task && task.change_marker !== ChangeMarker.added) {
                     await start_plan_alt_flow(task.prj_id, cached_data.user_id, t as Transaction);
-                    await PrjPlanTaskDraft.update({change_marker: ChangeMarker.Changed},
+                    await PrjPlanTaskDraft.update({change_marker: ChangeMarker.changed},
                         {where: {id: task.id}, transaction: t, returning: false});
                 }
             }
@@ -117,7 +116,7 @@ function statusGuard(json: IRequest, cached_data: ICachedData): Promise<void> {
             if (phase.order_index === 70)
                 return reject(Resp.gen_err(Resp.InvalidFlow, '当前项目阶段不允许直接删除交付物,应在草稿中修改后重新提交计划变更审核。'));
         }
-        if (task.status >= TaskStatus.ApplyForReview) {
+        if (task.status >= TaskStatus.apply_for_review) {
             return reject(Resp.gen_err(Resp.InvalidFlow, '当前任务阶段不允许删除交付物。'));
         }
 

+ 5 - 5
pmr-biz-manager/src/routes/api/prj/outcome/remove_file.ts

@@ -1,14 +1,14 @@
-import {ADMINISTRATOR, IApiProcessor, ICachedData, IMethodParams, IRequest} from "@core/Defined";
+import {IApiProcessor, ICachedData, IMethodParams, IRequest} from "@core/Defined";
 import {Resp} from "@util/Resp";
 import {Oss} from "@util/Oss";
 import {BizContractInfo} from "@core-models/BizContractInfo";
 import {PrjFile} from "@core-models/PrjFile";
-import {OutcomeStatus, PrjFileType, TaskStatus} from "@src/utils/define";
+import {OutcomeStatus, TaskStatus} from "@src/utils/define";
 import {PrjTaskOutcome} from "@core-models/PrjTaskOutcome";
 import {PrjPlanTask} from "@core-models/PrjPlanTask";
 import {PrjInfo} from "@core-models/PrjInfo";
 import {PrjPhaseDefine} from "@core-models/PrjPhaseDefine";
-import {is_project_task_progress_modifiable} from "../../../../utils/prj_premission_helper";
+import {is_project_task_progress_modifiable} from "@src/utils/prj_premission_helper";
 
 interface IData {
     /**
@@ -51,7 +51,7 @@ function statusGuard(json: IRequest, cached_data: ICachedData): Promise<void> {
             return reject(Resp.gen_err(Resp.InvalidFlow, '项目计划审核中,当前阶段不允许删除交付物。'));
 
 
-        if (task.status >= TaskStatus.ApplyForReview) {
+        if (task.status >= TaskStatus.apply_for_review) {
             return reject(Resp.gen_err(Resp.InvalidFlow, '当前任务阶段不允许删除交付物。'));
         }
 
@@ -70,7 +70,7 @@ function remove_file(json: IRequest, params: IMethodParams, cached_data: ICached
             let file = await PrjFile.findOne({where: {id: cached_data.object_name}, raw: true, transaction: t});
             if (!file) throw Resp.gen_err(Resp.ResourceNotFound, `交付物(id:${data.id})的文件(id ${cached_data.object_name})不存在。`);
 
-            if (file.uploaded === false) throw Resp.gen_err(Resp.ResourceNotFound, '附件尚未上传。')
+            if (!file.uploaded) throw Resp.gen_err(Resp.ResourceNotFound, '附件尚未上传。')
 
 
             let oss = Oss.get_instance('pmr-doc');

+ 4 - 7
pmr-biz-manager/src/routes/api/prj/outcome/upload.ts

@@ -1,18 +1,15 @@
-import {ADMINISTRATOR, IApiProcessor, ICachedData, IMethodParams, IRequest} from "@core/Defined";
+import {IApiProcessor, ICachedData, IMethodParams, IRequest} from "@core/Defined";
 import {Resp} from "@util/Resp";
 import {Oss} from "@util/Oss";
-import {IdGen} from "@util/IdGen";
 import {PrjTaskOutcome} from "@core-models/PrjTaskOutcome";
 import {PrjInfo} from "@core-models/PrjInfo";
 import {PrjPlanTask} from "@core-models/PrjPlanTask";
 import {QueryTypes} from "sequelize";
 import {PrjFile} from "@core-models/PrjFile";
 import dayjs from "dayjs";
-import {PrjPlanTaskDraft} from "@core-models/PrjPlanTaskDraft";
 import {PrjPhaseDefine} from "@core-models/PrjPhaseDefine";
 import {PrjFileType, TaskStatus} from "@src/utils/define";
 import {is_project_task_progress_modifiable} from "@src/utils/prj_premission_helper";
-import {PrjFileCategory} from "@core-models/PrjFileCategory";
 
 interface IData {
     /**
@@ -58,7 +55,7 @@ function statusGuard(json: IRequest, cached_data: ICachedData): Promise<void> {
             return reject(Resp.gen_err(Resp.InvalidFlow, '项目计划审核中,当前阶段不允许上传交付物。'));
 
 
-        if (task.status >= TaskStatus.ApplyForReview) {
+        if (task.status >= TaskStatus.apply_for_review) {
             return reject(Resp.gen_err(Resp.InvalidFlow, '当前任务阶段不允许上传交付物。'));
         }
 
@@ -86,7 +83,7 @@ function get_upload_url(json: IRequest, params: IMethodParams, cached_data: ICac
             await PrjTaskOutcome.update({object_name: object_name}, {where: {id: data.id}, transaction: t, returning: false});
 
             await PrjFile.upsert({
-                category_id: PrjFileType.Intermediate,
+                category_id: PrjFileType.intermediate,
                 prj_id: prj_id,
                 req_upload_time: dayjs(),
                 id: object_name,
@@ -108,7 +105,7 @@ function get_upload_url(json: IRequest, params: IMethodParams, cached_data: ICac
 }
 
 /// 检查是否存在
-function existsGuard(json: IRequest, cached_data: ICachedData): Promise<void> {
+function existsGuard(json: IRequest, _cached_data: ICachedData): Promise<void> {
     return new Promise<void>(async (resolve, reject) => {
         let data = <IData>json.data;
         let outcome = await PrjTaskOutcome.findOne({where: {id: data.id}, raw: true});

+ 7 - 9
pmr-biz-manager/src/routes/api/prj/plan/add_task.ts

@@ -1,8 +1,6 @@
 import {ADMINISTRATOR, IApiProcessor, ICachedData, IMethodParams, IRequest} from "@core/Defined";
 import {Resp} from "@util/Resp";
-import {WhereOptions} from "sequelize";
 import DataCURD from "@core/DataCURD";
-import {BizCustomer} from "@core-models/BizCustomer";
 import {IdGen} from "@util/IdGen";
 import dayjs from "dayjs";
 import {PrjInfo} from "@core-models/PrjInfo";
@@ -15,7 +13,7 @@ import {BpmnModel} from "@core-models/BpmnModel";
 import {bpmn_flow_on_end} from "@src/utils/bpmn_work_helper";
 import {PrjPlanTaskDraft} from "@core-models/PrjPlanTaskDraft";
 import {ChangeMarker} from "@src/utils/define";
-import {is_project_modifiable, is_project_task_addable} from "@src/utils/prj_premission_helper";
+import {is_project_task_addable} from "@src/utils/prj_premission_helper";
 import {PrjTaskOutcomeDraft} from "@core-models/PrjTaskOutcomeDraft";
 import {PrjTaskOutcome} from "@core-models/PrjTaskOutcome";
 
@@ -87,9 +85,9 @@ function guard(json: IRequest, cached_data: ICachedData): Promise<void> {
         let phase = await PrjPhaseDefine.findOne({where: {id: prj_info.phase_id}, raw: true});
         if (!phase) return reject(Resp.gen_err(Resp.ResourceNotFound, '项目状态信息出错,id: ' + prj_info.id));
         if (phase.order_index < 20) return reject(Resp.gen_err(Resp.InvalidFlow, '需要先对项目进行立项,立项后才可制订计划。'));
-        if (phase.order_index >= 30 && phase.order_index <= 40 && data.draft === false)
+        if (phase.order_index >= 30 && phase.order_index <= 40 && !data.draft)
             return reject(Resp.gen_err(Resp.InvalidFlow, '项目计划审核中,当前阶段不允许添加。'));
-        if (phase.order_index >= 60 && phase.order_index <= 70 && data.draft === false)
+        if (phase.order_index >= 60 && phase.order_index <= 70 && !data.draft)
             return reject(Resp.gen_err(Resp.InvalidFlow, '当前项目阶段不允许直接添加计划,应在草稿中修改后重新提交审核。'));
         if (phase.order_index >= 80) return reject(Resp.gen_err(Resp.InvalidFlow, '当前项目阶段不允许添加计划。'));
 
@@ -99,7 +97,7 @@ function guard(json: IRequest, cached_data: ICachedData): Promise<void> {
 
 // 如添加的是子任务,查找父任务是否存在
 // 如任务已经执行,且之前没有包含子任务,不能再作为主任务添加子任务
-function check_parent_guard(json: IRequest, cached_data: ICachedData): Promise<void> {
+function check_parent_guard(json: IRequest, _cached_data: ICachedData): Promise<void> {
     return new Promise<void>(async (resolve, reject) => {
         let data = <IData>json.data;
         if (data.task_pid === undefined) return resolve();
@@ -143,7 +141,7 @@ function add(json: IRequest, params: IMethodParams, cached_data: ICachedData): P
 
                 created_at: dayjs().format('YYYY-MM-DD HH:mm:ss'),
                 order_id: IdGen.id(),
-                change_marker: data.draft ? ChangeMarker.Added : undefined
+                change_marker: data.draft ? ChangeMarker.added : undefined
             }
             value = DataCURD.filter_null(value);
 
@@ -158,7 +156,7 @@ function add(json: IRequest, params: IMethodParams, cached_data: ICachedData): P
                 transaction: t,
                 raw: true
             });
-            if (!flow && data.draft === false) {
+            if (!flow && !data.draft) {
                 let bpmn = await BpmnModel.findOne({where: {id: 'plan'}, transaction: t});
                 if (!bpmn) throw Resp.gen_err(Resp.InternalServerError, '项目流程模型数据缺失!');
                 let flow = new FlowEngine(bpmn.id, bpmn.content, {
@@ -178,7 +176,7 @@ function add(json: IRequest, params: IMethodParams, cached_data: ICachedData): P
             }
 
             /// 如果计划任务是草稿,且还没有生成计划变更任务,则生成任务
-            if (data.draft === true) {
+            if (data.draft) {
                 await start_plan_alt_flow(data.prj_id, cached_data.user_id, t);
             }
 

+ 6 - 7
pmr-biz-manager/src/routes/api/prj/plan/get_tasks.ts

@@ -12,8 +12,7 @@ import {BpmnModel} from "@core-models/BpmnModel";
 import {PrjPlanTaskDraft} from "@core-models/PrjPlanTaskDraft";
 import {PrjTaskOutcome} from "@core-models/PrjTaskOutcome";
 import {PrjTaskOutcomeDraft} from "@core-models/PrjTaskOutcomeDraft";
-import {ChangeMarker} from "@src/utils/define";
-import {Logger} from "@util/Logger";
+import {WorkItemProcessType, WorkItemStatus} from "@src/utils/define";
 import {clone} from "@util/JsonToolkit";
 import {is_project_accessible} from "@src/utils/prj_premission_helper";
 
@@ -132,7 +131,7 @@ const ui_status = {
 
 function build_tasks(rows: Array<any>): Array<any> {
     const treeMap = {};
-    const root: Array<any> = new Array();
+    const root: Array<any> = [];
     rows.forEach((item) => {
         const {task_id, pid} = item;
         treeMap[task_id] = {...item, sub_tasks: treeMap[task_id]?.sub_tasks || []};
@@ -172,9 +171,10 @@ function statusGuard(json: IRequest, cached_data: ICachedData): Promise<void> {
         // if (phase.order_index >= 45 && data.draft === false) return reject(Resp.gen_err(Resp.InvalidFlow, '当前项目阶段不允许直接修改计划,应在草稿中修改后重新提交审核。'));
 
         // 项目执行阶段,可修改计划,但只能修改草稿,如草稿还没有生成,则自动生成,如果已经生成,则更新草稿中的进度和状态
-        if (phase.order_index >= 60 && phase.order_index <= 70 && data.draft === true) {
+        if (phase.order_index >= 60 && phase.order_index <= 70 && data.draft) {
             let t = await PrjPlanTaskDraft.sequelize!.transaction();
             try {
+                // @ts-ignore
                 let count = await PrjPlanTaskDraft.count({where: {prj_id: data.prj_id}, transaction: t});
                 if (count === 0) {
                     await PrjPlanTaskDraft.sequelize!.query(`
@@ -303,18 +303,17 @@ function get_tasks(json: IRequest, params: IMethodParams, cached_data: ICachedDa
                     prj_id: data.prj_id
                 }
             })
-            let works = await BpmnWork.sequelize!.query(`
+            result.works = await BpmnWork.sequelize!.query(`
                 select work.id, work.name, work.form 
                 from ${BpmnWork.table_name} work, ${BpmnCase.table_name} flow, ${BpmnModel.table_name} bpmn
                 where work.case_id = flow.id and flow.model_id = bpmn.id and 
-                    work.prj_id = :prj_id and work.status < 2 and work.process_type = 1 and
+                    work.prj_id = :prj_id and work.status < ${WorkItemStatus.completed} and work.process_type = ${WorkItemProcessType.todo} and
                     bpmn.id = :bpmn_id and work.assigned_to = :assigned_to
                 `, {
                 type: QueryTypes.SELECT,
                 raw: true,
                 replacements: {prj_id: data.prj_id, bpmn_id: data.draft? 'plan_alter' : 'plan', assigned_to: cached_data.user_id}
             });
-            result.works = works;
 
             if (!tasks) return resolve(result);
             tasks = DataCURD.filter_null(tasks);

+ 15 - 11
pmr-biz-manager/src/routes/api/prj/plan/modify_task.ts

@@ -1,8 +1,8 @@
 import {ADMINISTRATOR, IApiProcessor, ICachedData, IMethodParams, IRequest} from "@core/Defined";
 import DataCURD from "@core/DataCURD";
-import {Op, Transaction, WhereOptions} from "sequelize";
+import {Transaction, WhereOptions} from "sequelize";
 import {PrjInfo} from "@core-models/PrjInfo";
-import dayjs, {Dayjs} from "dayjs";
+import dayjs from "dayjs";
 import {Resp} from "@util/Resp";
 import {PrjPhaseDefine} from "@core-models/PrjPhaseDefine";
 import {PrjPlanTask} from "@core-models/PrjPlanTask";
@@ -10,7 +10,7 @@ import {
     get_outcome_max_plan_time,
     start_plan_alt_flow,
     update_parent_task_info
-} from "../../../../utils/plan_task_helper";
+} from "@src/utils/plan_task_helper";
 import {PrjPlanTaskDraft} from "@core-models/PrjPlanTaskDraft";
 import {Logger} from "@util/Logger";
 import {ChangeMarker, TaskStatus} from "@src/utils/define";
@@ -88,7 +88,7 @@ function guard(json: IRequest, cached_data: ICachedData): Promise<void> {
         if (cached_data.user_id !== prj_info.leader_id) {
             return reject(Resp.gen_err(Resp.Forbidden, '您不是项目负责人,不能修改任务。'));
         }
-        if (task.status === TaskStatus.Completed || task.status === TaskStatus.Cancelled) {
+        if (task.status === TaskStatus.completed || task.status === TaskStatus.cancelled) {
             return reject(Resp.gen_err(Resp.InvalidFlow, '任务已完成或已取消,不允许修改。'));
         }
         if (data.draft) return resolve();/// 草稿允许随意修改
@@ -96,16 +96,17 @@ function guard(json: IRequest, cached_data: ICachedData): Promise<void> {
         let phase = await PrjPhaseDefine.findOne({where: {id: prj_info.phase_id}, raw: true});
         if (!phase) return reject(Resp.gen_err(Resp.ResourceNotFound, '项目状态信息出错,id: ' + prj_info.id));
         if (phase.order_index < 20) return reject(Resp.gen_err(Resp.InvalidFlow, '需要先对项目进行立项,立项后才可制订计划。'));
-        if (phase.order_index >= 30 && phase.order_index <= 40 && data.draft === false)
+        if (phase.order_index >= 30 && phase.order_index <= 40 && !data.draft)
             return reject(Resp.gen_err(Resp.InvalidFlow, '项目计划审核中,当前阶段不允许修改。'));
-        if (phase.order_index >= 60 && phase.order_index <= 70 && data.draft === false) return reject(Resp.gen_err(Resp.InvalidFlow, '当前项目阶段不允许直接修改计划,应在草稿中修改后重新提交审核。'));
+        if (phase.order_index >= 60 && phase.order_index <= 70 && !data.draft)
+            return reject(Resp.gen_err(Resp.InvalidFlow, '当前项目阶段不允许直接修改计划,应在草稿中修改后重新提交审核。'));
         if (phase.order_index >= 80) return reject(Resp.gen_err(Resp.InvalidFlow, '当前项目阶段不允许修改计划。'));
 
         resolve();
     });
 }
 
-async function get_where(json: IRequest, params: IMethodParams, cached_data: ICachedData): Promise<WhereOptions> {
+async function get_where(json: IRequest, _params: IMethodParams, _cached_data: ICachedData): Promise<WhereOptions> {
     let data = <IData>json.data;
     return {id: data.task_id};
 }
@@ -154,10 +155,12 @@ function modify(json: IRequest, params: IMethodParams, cached_data: ICachedData)
 
             if (data.draft) {
                 Logger.trace(`[prj-plan-task-modify] draft task:  ${data.task_id}, change_marker: ${(<PrjPlanTaskDraft>task).change_marker}`);
-                if ((<PrjPlanTaskDraft>task).change_marker !== ChangeMarker.Added) {
-                    data.change_marker = ChangeMarker.Changed;
+                if ((<PrjPlanTaskDraft>task).change_marker !== ChangeMarker.added) {
+                    data.change_marker = ChangeMarker.changed;
                 }
             }
+
+
             let result = await DataCURD.internal_update_row(json, {
                 model: model,
                 where: get_where,
@@ -171,14 +174,15 @@ function modify(json: IRequest, params: IMethodParams, cached_data: ICachedData)
                     end_at: "end_at",
                     description: "description",
                     change_marker: "change_marker",
-                    updated_at: "updated_at"
+                    updated_at: "updated_at",
+                    status: "status"
                 }
             }, cached_data, t);
 
             await update_parent_task_info(data.draft, result.prj_id, result.pid, t as Transaction);
 
             /// 如果计划任务是草稿,且还没有生成计划变更任务,则生成任务
-            if (data.draft === true ) {
+            if (data.draft ) {
                 await start_plan_alt_flow(result.prj_id, cached_data.user_id, t);
             }
 

+ 6 - 5
pmr-biz-manager/src/routes/api/prj/plan/recover_task.ts

@@ -39,28 +39,29 @@ function statusGuard(json: IRequest, cached_data: ICachedData): Promise<void> {
         if (!await is_project_task_modifiable(PrjPlanTask, user, task.id))
             return reject(Resp.gen_err(Resp.Forbidden, '您没有权限恢复任务。'));
 
-        if (task.change_marker !== ChangeMarker.Deleted) {
+        if (task.change_marker !== ChangeMarker.deleted) {
             return reject(Resp.gen_err(Resp.InvalidFlow, '任务没有被删除,不用恢复。'));
         }
         let phase = await PrjPhaseDefine.findOne({where: {id: prj_info.phase_id}, raw: true});
         if (!phase) return reject(Resp.gen_err(Resp.ResourceNotFound, '项目状态信息出错,id: ' + prj_info.id));
 
 
-        if (phase.order_index >= 60 && phase.order_index <= 65) return reject(Resp.gen_err(Resp.InvalidFlow, '计划变更已提交审核,当前阶段不允许删除。'));
-        return resolve();/// 其它情况下草稿允许随意删除
+        if (phase.order_index >= 60 && phase.order_index <= 65)
+            return reject(Resp.gen_err(Resp.InvalidFlow, '计划变更已提交审核,当前阶段不允许删除。'));
+
 
         resolve();
     });
 }
 
-async function recover_task(json: IRequest, params: IMethodParams, cached_data: ICachedData): Promise<any> {
+async function recover_task(json: IRequest, _params: IMethodParams, _cached_data: ICachedData): Promise<any> {
     let data = <IData>json.data;
     let t = await PrjInfo.sequelize!.transaction();
     try {
         let task = await PrjPlanTaskDraft.findOne({where: {id: data.task_id}, raw: true, transaction: t});
         if (!task) throw Resp.gen_err(Resp.ResourceNotFound);
 
-        await PrjPlanTaskDraft.update({change_marker: ChangeMarker.NoChange}, {
+        await PrjPlanTaskDraft.update({change_marker: ChangeMarker.no_change}, {
             where: {id: data.task_id},
             transaction: t,
             returning: false

+ 5 - 7
pmr-biz-manager/src/routes/api/prj/plan/remove_task.ts

@@ -1,13 +1,11 @@
 import {ADMINISTRATOR, IApiProcessor, ICachedData, IMethodParams, IRequest} from "@core/Defined";
 import {PrjInfo} from "@core-models/PrjInfo";
-import {PrjMembers} from "@core-models/PrjMembers";
 import {Resp} from "@util/Resp";
 import {PrjPhaseDefine} from "@core-models/PrjPhaseDefine";
 import {PrjPlanTask} from "@core-models/PrjPlanTask";
 import {start_plan_alt_flow, update_parent_task_info} from "@src/utils/plan_task_helper";
 import {PrjPlanTaskDraft} from "@core-models/PrjPlanTaskDraft";
 import {PrjTaskOutcome} from "@core-models/PrjTaskOutcome";
-import {Op} from "sequelize";
 import {PrjTaskOutcomeDraft} from "@core-models/PrjTaskOutcomeDraft";
 import {ChangeMarker, TaskStatus} from "@src/utils/define";
 import {is_project_task_modifiable} from "@src/utils/prj_premission_helper";
@@ -43,7 +41,7 @@ function statusGuard(json: IRequest, cached_data: ICachedData): Promise<void> {
         if (!await is_project_task_modifiable(model, user, task.id))
             return reject(Resp.gen_err(Resp.Forbidden, '您没有权限删除任务。'));
 
-        if (task.status === TaskStatus.Completed || task.status === TaskStatus.Cancelled) {
+        if (task.status === TaskStatus.completed || task.status === TaskStatus.cancelled) {
             return reject(Resp.gen_err(Resp.InvalidFlow, '任务已完成或已取消,不允许删除。'));
         }
         let phase = await PrjPhaseDefine.findOne({where: {id: prj_info.phase_id}, raw: true});
@@ -62,9 +60,9 @@ function statusGuard(json: IRequest, cached_data: ICachedData): Promise<void> {
         }
 
         if (phase.order_index < 20) return reject(Resp.gen_err(Resp.InvalidFlow, '需要先对项目进行立项,立项后才可制订计划。'));
-        if (phase.order_index >= 30 && phase.order_index <= 40 && data.draft === false)
+        if (phase.order_index >= 30 && phase.order_index <= 40 && !data.draft)
             return reject(Resp.gen_err(Resp.InvalidFlow, '项目计划审核中,当前阶段不允许删除。'));
-        if (phase.order_index >= 60 && phase.order_index <= 70 && data.draft === false)
+        if (phase.order_index >= 60 && phase.order_index <= 70 && !data.draft)
             return reject(Resp.gen_err(Resp.InvalidFlow, '当前项目阶段不允许直接删除计划,应在草稿中删除后重新提交审核。'));
         if (phase.order_index >= 80) return reject(Resp.gen_err(Resp.InvalidFlow, '当前项目阶段不允许删除计划。'));
         resolve();
@@ -83,13 +81,13 @@ async function remove(json: IRequest, params: IMethodParams, cached_data: ICache
 
 
         if (data.draft) {
-            if ((<PrjPlanTaskDraft>task).change_marker! = ChangeMarker.Added) {
+            if ((<PrjPlanTaskDraft>task).change_marker === ChangeMarker.added) {
                 // 删除交付物记录
                 await PrjTaskOutcomeDraft.destroy({where: {task_id: data.task_id}, transaction: t});
                 // 在草稿中新增的条目可以直接删除
                 await model.destroy({where: {id: data.task_id}, transaction: t});
             } else {
-                await model.update({change_marker: ChangeMarker.Deleted}, {
+                await PrjTaskOutcomeDraft.update({change_marker: ChangeMarker.deleted}, {
                     where: {id: data.task_id},
                     transaction: t,
                     returning: false

+ 2 - 2
pmr-biz-manager/src/routes/api/prj/plan/set_check_flow.ts

@@ -59,7 +59,7 @@ function statusGuard(json: IRequest, cached_data: ICachedData): Promise<void> {
         if (!await is_project_task_modifiable(model, user, task.id))
             return reject(Resp.gen_err(Resp.Forbidden, '您没有权限设置任务审核流程。'));
 
-        if (task.status >= TaskStatus.ApplyForReview)
+        if (task.status >= TaskStatus.apply_for_review)
             return reject(Resp.gen_err(Resp.InvalidFlow, '计划任务项已提交审核,不允许修改。'));
 
         let phase = await PrjPhaseDefine.findOne({where: {id: prj_info.phase_id}, raw: true});
@@ -86,7 +86,7 @@ function set_check_flow(json: IRequest, params: IMethodParams, cached_data: ICac
                 if (task) {
                     await PrjPlanTaskDraft.update({
                         checkers: data.checkers,
-                        change_marker: task.change_marker === ChangeMarker.Added ? ChangeMarker.Added : ChangeMarker.Changed
+                        change_marker: task.change_marker === ChangeMarker.added ? ChangeMarker.added : ChangeMarker.changed
                     }, {where: {id: data.task_id}, transaction: t, returning: false});
                     await start_plan_alt_flow(task.prj_id, cached_data.user_id, t as Transaction);
                 }

+ 5 - 5
pmr-biz-manager/src/routes/api/prj/plan/set_progress.ts

@@ -32,12 +32,12 @@ function statusGuard(json: IRequest, cached_data: ICachedData): Promise<void> {
         if (!await is_project_task_progress_modifiable(user, task.id))
             return reject(Resp.gen_err(Resp.Forbidden, '您没有权限修改任务进度。'));
 
-        if (task.status === TaskStatus.ApplyForReview || task.status === TaskStatus.Reviewing) {
+        if (task.status === TaskStatus.apply_for_review || task.status === TaskStatus.reviewing) {
             return reject(Resp.gen_err(Resp.InvalidFlow, '计划任务项审核中,当前阶段不允许修改进度。'));
         }
 
         // 任务已完成,并且是有审核流程的,则不允许将已完成的任务回滚到未完成状态。
-        if (task.status === TaskStatus.DueDone || task.status === TaskStatus.Completed && task.checkers.length > 0) {
+        if (task.status === TaskStatus.due_done || task.status === TaskStatus.completed && task.checkers.length > 0) {
             return reject(Resp.gen_err(Resp.InvalidFlow, '计划任务项已完成,当前阶段不允许修改进度。'));
         }
 
@@ -68,14 +68,14 @@ function set_progress(json: IRequest, params: IMethodParams, cached_data: ICache
             let task = await PrjPlanTask.findOne({where: {id: data.task_id}, transaction: t, raw: true});
             if (!task) throw Resp.gen_err(Resp.ResourceNotFound, '任务不存在');
             let status = task.status;
-            if (data.progress > 0) status = TaskStatus.Doing;
-            else if (data.progress === 0) status = TaskStatus.New;
+            if (data.progress > 0) status = TaskStatus.doing;
+            else if (data.progress === 0) status = TaskStatus.new;
 
             if (data.progress === 100) {    // 如果还有一个审核流程,就不能直接设置任务完成度为100%,
                 if (task.checkers && task.checkers.length > 0)
                     data.progress = 99;
                 else
-                    status = TaskStatus.Completed;
+                    status = TaskStatus.completed;
 
             }
 

+ 6 - 6
pmr-biz-manager/src/routes/api/prj/plan/sort_task.ts

@@ -8,7 +8,7 @@ import {IdGen} from "@util/IdGen";
 import {start_plan_alt_flow, update_parent_task_info} from "@src/utils/plan_task_helper";
 import {PrjPlanTaskDraft} from "@core-models/PrjPlanTaskDraft";
 import {ChangeMarker} from "@src/utils/define";
-import {is_project_modifiable, is_project_task_modifiable} from "@src/utils/prj_premission_helper";
+import {is_project_modifiable} from "@src/utils/prj_premission_helper";
 
 interface IData {
     /**
@@ -48,9 +48,9 @@ function statusGuard(json: IRequest, cached_data: ICachedData): Promise<void> {
         if (!phase) return reject(Resp.gen_err(Resp.ResourceNotFound, '项目状态信息出错,id: ' + prj_info.id));
         // if (phase.order_index >= 30 && data.draft === false) return reject(Resp.gen_err(Resp.InvalidFlow, '当前项目阶段不允许直接修改计划,应在草稿中修改后重新提交审核。'));
         // if (phase.order_index < 20) return reject(Resp.gen_err(Resp.InvalidFlow, '需要先对项目进行立项,立项后才可制订计划。'));
-        if (phase.order_index >=30 && phase.order_index <= 40 && data.draft === false)
+        if (phase.order_index >=30 && phase.order_index <= 40 && !data.draft)
             return reject(Resp.gen_err(Resp.InvalidFlow, '项目计划审核中,当前阶段不允许修改。'));
-        if (phase.order_index >= 60 && phase.order_index <=70 && data.draft === false) return reject(Resp.gen_err(Resp.InvalidFlow, '当前项目阶段不允许直接修改计划,应在草稿中修改后重新提交审核。'));
+        if (phase.order_index >= 60 && phase.order_index <=70 && !data.draft) return reject(Resp.gen_err(Resp.InvalidFlow, '当前项目阶段不允许直接修改计划,应在草稿中修改后重新提交审核。'));
         if (phase.order_index >= 80) return reject(Resp.gen_err(Resp.InvalidFlow, '当前项目阶段不允许修改计划。'));
         resolve();
     });
@@ -80,8 +80,8 @@ function sort_task(json: IRequest, params: IMethodParams, cached_data: ICachedDa
                     update ${model.table_name} 
                     set order_id = :order_id, pid = :pid `;
                 if (data.draft) {
-                    if ((<PrjPlanTaskDraft>task).change_marker === ChangeMarker.NoChange) {
-                        sql += `, change_marker = ${ChangeMarker.Changed} `
+                    if ((<PrjPlanTaskDraft>task).change_marker === ChangeMarker.no_change) {
+                        sql += `, change_marker = ${ChangeMarker.changed} `
                     }
                 }
                 sql += `
@@ -102,7 +102,7 @@ function sort_task(json: IRequest, params: IMethodParams, cached_data: ICachedDa
             }
             await update_parent_task_info(data.draft, data.prj_id, pid, t);
             /// 如果计划任务是草稿,且还没有生成计划变更任务,则生成任务
-            if (data.draft === true ) {
+            if (data.draft ) {
                 await start_plan_alt_flow(data.prj_id, cached_data.user_id, t);
             }
             await t.commit();

+ 4 - 5
pmr-biz-manager/src/routes/api/prj/work/detail.ts

@@ -1,13 +1,12 @@
 import {IApiProcessor, ICachedData, IMethodParams, IRequest} from "@core/Defined";
 import {Resp} from "@util/Resp";
-import {QueryTypes, WhereOptions} from "sequelize";
-import {PrjPlanTask} from "@core-models/PrjPlanTask";
+import {QueryTypes} from "sequelize";
 import {PrjInfo} from "@core-models/PrjInfo";
 import {AcsUserInfo} from "@core-models/AcsUserInfo";
 import DataCURD from "@core/DataCURD";
-import {PrjTaskOutcome} from "@core-models/PrjTaskOutcome";
 import {BpmnWork} from "@core-models/BpmnWork";
 import dayjs from "dayjs";
+import {WorkItemProcessType, WorkItemStatus} from "@src/utils/define";
 
 interface IData  {
     /**
@@ -115,8 +114,8 @@ function detail(json: IRequest, params: IMethodParams, cached_data: ICachedData)
             `, {type: QueryTypes.SELECT, raw: true, replacements: {
                     work_id: data.id,
                 }, transaction: t});
-            await BpmnWork.update({status: 2, completed_at: dayjs(), handler: cached_data.user_id}, {
-                where: {id: data.id, process_type: 2},
+            await BpmnWork.update({status: WorkItemStatus.completed, completed_at: dayjs(), handler: cached_data.user_id}, {
+                where: {id: data.id, process_type: WorkItemProcessType.to_read},
                 returning: false,
                 transaction: t
             });

+ 7 - 5
pmr-biz-manager/src/routes/api/prj/work/done_stat.ts

@@ -2,7 +2,7 @@
 import {IApiProcessor, ICachedData, IMethodParams, IRequest} from "@core/Defined";
 import {Resp} from "@util/Resp";
 import {Op} from "sequelize";
-import {IStatInfo} from "@src/utils/define";
+import {IStatInfo, WorkItemStatus} from "@src/utils/define";
 import {BpmnWork} from "@core-models/BpmnWork";
 
 function stat(json: IRequest, params: IMethodParams, cached_data: ICachedData): Promise<any> {
@@ -11,24 +11,26 @@ function stat(json: IRequest, params: IMethodParams, cached_data: ICachedData):
             let result = new Array<IStatInfo>;
 
             /// 获取归属于自己的总工作项中已完成的数量,status=2表示已完成
+            // @ts-ignore
             let done_count = await BpmnWork.count({
                 where: {
                     assigned_to: cached_data.user_id,
                     show_in_my_works: true,
-                    status: 2
+                    status: WorkItemStatus.completed
                 }
             });
             result.push({
                 name: '总数',
-                value: done_count,
+                value: done_count as number,
                 unit: '个'
             });
 
             /// 获取本年度本人已完成的工作项总数
+            // @ts-ignore
             let this_year_done_count = await BpmnWork.count({
                 where: {
                     assigned_to: cached_data.user_id,
-                    status: 2,
+                    status: WorkItemStatus.completed,
                     show_in_my_works: true,
                     started_at: {
                         [Op.gte]: new Date(new Date().getFullYear() + '-01-01 00:00:00'),
@@ -38,7 +40,7 @@ function stat(json: IRequest, params: IMethodParams, cached_data: ICachedData):
             });
             result.push({
                 name: '本年度',
-                value: this_year_done_count,
+                value: this_year_done_count as number,
                 unit: '个'
             });
 

+ 13 - 9
pmr-biz-manager/src/routes/api/prj/work/stat.ts

@@ -1,7 +1,7 @@
 import {IApiProcessor, ICachedData, IMethodParams, IRequest} from "@core/Defined";
 import {Resp} from "@util/Resp";
 import {Op} from "sequelize";
-import {IStatInfo} from "@src/utils/define";
+import {IStatInfo, WorkItemStatus} from "@src/utils/define";
 import {BpmnWork} from "@core-models/BpmnWork";
 
 function stat(json: IRequest, params: IMethodParams, cached_data: ICachedData): Promise<any> {
@@ -9,29 +9,31 @@ function stat(json: IRequest, params: IMethodParams, cached_data: ICachedData):
         try {
             let result = new Array<IStatInfo>;
             /// 获取归属于自己的总工作项总数
+            // @ts-ignore
             let my_count = await BpmnWork.count({
                 where: {
                     assigned_to: cached_data.user_id,
                     show_in_my_works: true,
-                    status: {[Op.ne]: 3}
+                    status: {[Op.ne]: WorkItemStatus.discarded}
                 }
             });
             result.push({
                 name: '工作项总数',
-                value: my_count,
+                value: my_count as number,
                 unit: '个'
             });
             /// 获取归属于自己的总工作项中已完成的数量,status=2表示已完成
+            // @ts-ignore
             let done_count = await BpmnWork.count({
                 where: {
                     assigned_to: cached_data.user_id,
                     show_in_my_works: true,
-                    status: 2
+                    status: WorkItemStatus.completed
                 }
             });
             result.push({
                 name: '已完成',
-                value: done_count,
+                value: done_count as number,
                 unit: '个'
             });
             result.push({
@@ -41,11 +43,12 @@ function stat(json: IRequest, params: IMethodParams, cached_data: ICachedData):
             });
 
             /// 获取本年度本人工作项总数
+            // @ts-ignore
             let this_year_count = await BpmnWork.count({
                 where: {
                     assigned_to: cached_data.user_id,
                     show_in_my_works: true,
-                    status: {[Op.ne]: 3},
+                    status: {[Op.ne]: WorkItemStatus.discarded},
                     started_at: {
                         [Op.gte]: new Date(new Date().getFullYear() + '-01-01 00:00:00'),
                         [Op.lte]: new Date(new Date().getFullYear() + '-12-31 23:59:59')
@@ -54,14 +57,15 @@ function stat(json: IRequest, params: IMethodParams, cached_data: ICachedData):
             });
             result.push({
                 name: '本年度总数',
-                value: this_year_count,
+                value: this_year_count as number,
                 unit: '个'
             });
             /// 获取本年度本人已完成的工作项总数
+            // @ts-ignore
             let this_year_done_count = await BpmnWork.count({
                 where: {
                     assigned_to: cached_data.user_id,
-                    status: 2,
+                    status: WorkItemStatus.completed,
                     show_in_my_works: true,
                     started_at: {
                         [Op.gte]: new Date(new Date().getFullYear() + '-01-01 00:00:00'),
@@ -71,7 +75,7 @@ function stat(json: IRequest, params: IMethodParams, cached_data: ICachedData):
             });
             result.push({
                 name: '本年度已完成',
-                value: this_year_done_count,
+                value: this_year_done_count as number,
                 unit: '个'
             });
             result.push({

+ 10 - 18
pmr-biz-manager/src/routes/api/prj/work/submit.ts

@@ -1,8 +1,8 @@
 import {IApiProcessor, ICachedData, IMethodParams, IRequest} from "@core/Defined";
 import {Resp} from "@util/Resp";
 import {BpmnWork} from "@core-models/BpmnWork";
-import {FlowEngine} from "@src/bpmn/flow_engine";
-import {PrjLogger} from "../../../../utils/prj_logger";
+import {FlowEngine, IScriptResult} from "@src/bpmn/flow_engine";
+import {WorkItemStatus} from "@src/utils/define";
 
 interface IData  {
     /**
@@ -15,34 +15,26 @@ interface IData  {
     id: string;
 }
 
-
 function submit(json: IRequest, params: IMethodParams, cached_data: ICachedData): Promise<void> {
     return new Promise(async (resolve, reject) => {
-        // let t = await BpmnTask.sequelize!.transaction();
         try {
             let data = <IData>json.data;
             let engine = FlowEngine.execution_engine_map.get(data.id);
             if (engine) {
                 let work = await BpmnWork.findOne({where: {id: data.id}, raw: true});
-                if (work && work.status < 2) {
-                    // if (data.form_data && data.form_data.pass !== undefined) {
-                    //     data.form_data.break = !data.form_data.pass;
-                    // }
+
+                if (work && work.status < WorkItemStatus.completed) {
+                    // 获取此工作项对应的流程节点的起始脚本
+                    let result = <IScriptResult>await engine.exec_activity_script('start', work.activity_id, {});
+                    if (result && !result.result) {
+                        return reject(Resp.gen_err(Resp.InvalidFlow, result.message));
+                    }
+
                     await engine.signal(work.activity_id, data.id, cached_data.user_id, {...data.form_data});
-                    // await PrjLogger.log({
-                    //     creator_id: cached_data.user_id,
-                    //     creator_name: cached_data.user_name,
-                    //     content: work.name,
-                    //     prj_id: work.prj_id,
-                    //     task_id: work.task_id,
-                    // });
                 }
-
             }
-            // await t.commit();
             resolve();
         } catch (e) {
-            // await t.rollback();
             reject(Resp.throw_err(e));
         }
     })

+ 3 - 1
pmr-biz-manager/src/routes/api/prj/work/undone_count.ts

@@ -3,17 +3,19 @@ import {Resp} from "@util/Resp";
 import {Op} from "sequelize";
 
 import {BpmnWork} from "@core-models/BpmnWork";
+import {WorkItemStatus} from "@src/utils/define";
 
 function stat(json: IRequest, params: IMethodParams, cached_data: ICachedData): Promise<any> {
     return new Promise<any>(async (resolve, reject) => {
         try {
             /// 获取归属于自己的未完成工作项总数,status不等于2
+            // @ts-ignore
             let my_undone_count = await BpmnWork.count({
                 where: {
                     assigned_to: cached_data.user_id,
                     show_in_my_works: true,
                     status: {
-                        [Op.lt]: 2
+                        [Op.lt]: WorkItemStatus.completed
                     }
                 }
             });

+ 7 - 5
pmr-biz-manager/src/routes/api/prj/work/undone_stat.ts

@@ -1,7 +1,7 @@
 import {IApiProcessor, ICachedData, IMethodParams, IRequest} from "@core/Defined";
 import {Resp} from "@util/Resp";
 import {Op} from "sequelize";
-import {IStatInfo} from "@src/utils/define";
+import {IStatInfo, WorkItemStatus} from "@src/utils/define";
 import {BpmnWork} from "@core-models/BpmnWork";
 
 function stat(json: IRequest, params: IMethodParams, cached_data: ICachedData): Promise<any> {
@@ -9,31 +9,33 @@ function stat(json: IRequest, params: IMethodParams, cached_data: ICachedData):
         try {
             let result = new Array<IStatInfo>;
             /// 获取归属于自己的未完成工作项总数,status不等于2
+            // @ts-ignore
             let my_count = await BpmnWork.count({
                 where: {
                     assigned_to: cached_data.user_id,
                     show_in_my_works: true,
                     status: {
-                        [Op.lt]: 2
+                        [Op.lt]: WorkItemStatus.completed
                     }
                 }
             });
             result.push({
                 name: '未完成总数',
-                value: my_count,
+                value: my_count as number,
                 unit: '个'
             });
             /// 获取归属于自己的超期工作项数量,status=1表示超期
+            // @ts-ignore
             let due = await BpmnWork.count({
                 where: {
                     assigned_to: cached_data.user_id,
                     show_in_my_works: true,
-                    status: 1
+                    status: WorkItemStatus.expired
                 }
             });
             result.push({
                 name: '已超期',
-                value: due,
+                value: due as number,
                 unit: '个',
                 color: due > 0 ? 'red': undefined
             });

+ 23 - 11
pmr-biz-manager/src/utils/bpmn_work_helper.ts

@@ -69,8 +69,19 @@ export async function bpmn_flow_on_end(id: string, model_id: string, prj_id: str
                         created_at = B.created_at, updated_at = B.updated_at, description = B.description, order_id = B.order_id,
                         version = B.version, checkers = B.checkers
                     FROM ${PrjPlanTaskDraft.table_name} B
-                    WHERE B.change_marker = ${ChangeMarker.Changed} and ${PrjPlanTask.table_name}.id = B.id and B.prj_id = :prj_id;
+                    WHERE B.change_marker = ${ChangeMarker.changed} and ${PrjPlanTask.table_name}.id = B.id and B.prj_id = :prj_id;
                 `, {type: QueryTypes.UPDATE, transaction: t, replacements: {prj_id: prj_id}});
+                // 修改完计划后,某些超期任务由于结束时间发生变化,因此需要重新设置其状态
+                await PrjPlanTask.sequelize!.query(`
+                    UPDATE ${PrjPlanTask.table_name}
+                    SET status = CASE 
+                        WHEN (end_at IS NOT NULL AND end_at < CURRENT_TIMESTAMP) THEN ${TaskStatus.expired}
+                        WHEN progress > 0 THEN ${TaskStatus.doing}
+                        ELSE ${TaskStatus.new}
+                        END
+                    WHERE prj_id = :prj_id and status < ${TaskStatus.completed}
+                    `,
+                    {type: QueryTypes.UPDATE, transaction: t, replacements: {prj_id: prj_id}})
                 // 2. 新增
                 await PrjPlanTask.sequelize!.query(`
                     INSERT INTO ${PrjPlanTask.table_name} 
@@ -79,21 +90,21 @@ export async function bpmn_flow_on_end(id: string, model_id: string, prj_id: str
                     SELECT id, name, pid, prj_id, progress, status, handler_id, creator_id, begin_at, end_at, completed_at,
                         created_at, updated_at, description, order_id, version, checkers
                     FROM ${PrjPlanTaskDraft.table_name}
-                    WHERE change_marker = ${ChangeMarker.Added} and prj_id = :prj_id;
+                    WHERE change_marker = ${ChangeMarker.added} and prj_id = :prj_id;
                 `, {type: QueryTypes.INSERT, transaction: t, replacements: {prj_id: prj_id}});
                 // 3. 删除
                 // TODO: 清除已上传的交付物文件
                 await BpmnWork.sequelize!.query(`
                     DELETE FROM ${BpmnWork.table_name}
-                    WHERE task_id IN (SELECT id FROM ${PrjPlanTaskDraft.table_name} WHERE change_marker = ${ChangeMarker.Deleted} and prj_id = :prj_id);
+                    WHERE task_id IN (SELECT id FROM ${PrjPlanTaskDraft.table_name} WHERE change_marker = ${ChangeMarker.deleted} and prj_id = :prj_id);
                 `, {type: QueryTypes.DELETE, transaction: t, replacements: {prj_id: prj_id}});
                 await PrjTaskOutcome.sequelize!.query(`
                     DELETE FROM ${PrjTaskOutcome.table_name}
-                    WHERE task_id IN (SELECT id FROM ${PrjPlanTaskDraft.table_name} WHERE change_marker = ${ChangeMarker.Deleted} and prj_id = :prj_id);
+                    WHERE task_id IN (SELECT id FROM ${PrjPlanTaskDraft.table_name} WHERE change_marker = ${ChangeMarker.deleted} and prj_id = :prj_id);
                 `, {type: QueryTypes.DELETE, transaction: t, replacements: {prj_id: prj_id}});
                 await PrjPlanTask.sequelize!.query(`
                     DELETE FROM ${PrjPlanTask.table_name}
-                    WHERE id IN (SELECT id FROM ${PrjPlanTaskDraft.table_name} WHERE change_marker = ${ChangeMarker.Deleted} and prj_id = :prj_id);
+                    WHERE id IN (SELECT id FROM ${PrjPlanTaskDraft.table_name} WHERE change_marker = ${ChangeMarker.deleted} and prj_id = :prj_id);
                 `, {type: QueryTypes.DELETE, transaction: t, replacements: {prj_id: prj_id}});
 
                 /// 同步交付物记录
@@ -105,7 +116,7 @@ export async function bpmn_flow_on_end(id: string, model_id: string, prj_id: str
                         created_at = B.created_at, updated_at = B.updated_at, description = B.description, order_id = B.order_id,
                         version = B.version
                     FROM ${PrjTaskOutcomeDraft.table_name} B, ${PrjPlanTaskDraft.table_name} C
-                    WHERE B.change_marker = ${ChangeMarker.Changed} and 
+                    WHERE B.change_marker = ${ChangeMarker.changed} and 
                         ${PrjTaskOutcome.table_name}.id = B.id and 
                         B.task_id = C.id and C.prj_id = :prj_id;
                     `, {type: QueryTypes.UPDATE, transaction: t, replacements: {prj_id: prj_id}});
@@ -120,14 +131,14 @@ export async function bpmn_flow_on_end(id: string, model_id: string, prj_id: str
                         outcome.created_at, outcome.updated_at, outcome.description, outcome.order_id, 
                         outcome.uploaded, outcome.version
                     FROM ${PrjTaskOutcomeDraft.table_name} outcome, ${PrjPlanTaskDraft.table_name} task
-                    WHERE outcome.change_marker = ${ChangeMarker.Added} and outcome.task_id = task.id and task.prj_id = :prj_id;
+                    WHERE outcome.change_marker = ${ChangeMarker.added} and outcome.task_id = task.id and task.prj_id = :prj_id;
                 `, {type: QueryTypes.INSERT, transaction: t, replacements: {prj_id: prj_id}});
                 // 3. 删除
                 await PrjTaskOutcome.sequelize!.query(`
                     DELETE FROM ${PrjTaskOutcome.table_name}
                     WHERE id IN (
                         SELECT outcome.id FROM ${PrjTaskOutcomeDraft.table_name} outcome, ${PrjPlanTaskDraft.table_name} task
-                        WHERE outcome.change_marker = ${ChangeMarker.Deleted} and 
+                        WHERE outcome.change_marker = ${ChangeMarker.deleted} and 
                             outcome.task_id = task.id and 
                             task.prj_id = :prj_id);
                 `, {type: QueryTypes.DELETE, transaction: t, replacements: {prj_id: prj_id}});
@@ -156,7 +167,7 @@ export async function bpmn_flow_on_end(id: string, model_id: string, prj_id: str
                 try {
                     let task = await PrjPlanTask.update({
                         progress: 100,
-                        status: TaskStatus.Completed,
+                        status: TaskStatus.completed,
                         completed_at: dayjs().format('YYYY-MM-DD HH:mm:ss'),
                     }, {
                         where: {id: flow_case.task_id},
@@ -169,7 +180,8 @@ export async function bpmn_flow_on_end(id: string, model_id: string, prj_id: str
                         completed_at: dayjs().format('YYYY-MM-DD HH:mm:ss')
                     }, {
                         where: {task_id: flow_case.task_id},
-                        transaction: t
+                        transaction: t,
+                        returning: false
                     })
                     await PrjLogger.log({
                         creator_id: owner,
@@ -177,7 +189,7 @@ export async function bpmn_flow_on_end(id: string, model_id: string, prj_id: str
                         content: `任务审核通过,任务完成`,
                         prj_id: prj_id,
                         task_id: flow_case.task_id,
-                        t: t
+                        t: t as Transaction
                     })
                     await t.commit();
                 } catch (e) {

+ 36 - 21
pmr-biz-manager/src/utils/define.ts

@@ -1,40 +1,55 @@
 // 任务计划草稿中的记录变化情况
 export enum ChangeMarker {
-    NoChange = 0,
-    Changed = 1,
-    Added = 2,
-    Deleted = 3,
+    no_change = 0,
+    changed = 1,
+    added = 2,
+    deleted = 3,
 }
 
 
 // 计划任务状态
 export enum TaskStatus {
-    New = 0,                // 未开始
-    Doing = 10,             // 进行中
-    OutTime = 20,           // 超时
-    Rejected = 30,          // 审核不通过
-    ApplyForReview = 40,    // 申请审核
-    Reviewing = 50,         // 审核中
-    DueDone = 80,           // 已逾期完成
-    Cancelled = 90,         // 已取消
-    Completed = 100,        // 已完成
+    new = 0,                // 未开始
+    doing = 10,             // 进行中
+    expired = 20,           // 超时
+    rejected = 30,          // 审核不通过
+    apply_for_review = 40,    // 申请审核
+    reviewing = 50,         // 审核中
+    due_done = 80,           // 已逾期完成
+    cancelled = 90,         // 已取消
+    completed = 100,        // 已完成
+}
+
+// 工作项状态
+export enum WorkItemStatus {
+    doing = 0,
+    expired = 1,
+    completed = 2,
+    // 已废弃,某些工作项由于其它工作项完成,导致该工作项被废弃,如撤回立项时,立项的审批工作项被废弃
+    discarded = 3,
+}
+
+// 工作项的处理方式
+export enum WorkItemProcessType {
+    todo = 1,
+    to_read = 2,
 }
 
 // 交付物状态
 export enum OutcomeStatus {
     New = 0,
     Uploaded = 5,
-    ApplyForReview = 10,
-    Reviewing = 20,
-    Rejected = 30,
-    Completed = 100
+    // ApplyForReview = 10,
+    // Reviewing = 20,
+    // Rejected = 30,
+    // Completed = 100
 }
 
 export enum PrjFileType {
-    Contract = 'contract',
-    WeekReport = 'week_report',
-    Outcome = 'outcome',
-    Intermediate = 'intermediate'
+    contract = 'contract',
+    week_report = 'week_report',
+    outcome = 'outcome',
+    intermediate = 'intermediate'
 }
 
 // 任务审核流程

+ 6 - 5
pmr-biz-manager/src/utils/plan_task_helper.ts

@@ -1,4 +1,4 @@
-import {Op, QueryTypes, Transaction, WhereOptions} from "sequelize";
+import {Op, QueryTypes, WhereOptions} from "sequelize";
 import dayjs, {Dayjs} from "dayjs";
 import {PrjPlanTask} from "@core-models/PrjPlanTask";
 import {PrjInfo} from "@core-models/PrjInfo";
@@ -18,7 +18,7 @@ export async function update_parent_task_info(draft: boolean, prj_id: string, ta
 
     let model = draft ? PrjPlanTaskDraft : PrjPlanTask;
     let where: WhereOptions = {pid: task_pid, prj_id: prj_id, status: {[Op.ne]: 90}};  // 已废除的任务不参与计算
-    if (draft) where.change_marker = {[Op.ne]: ChangeMarker.Deleted};
+    if (draft) where.change_marker = {[Op.ne]: ChangeMarker.deleted};
     let child_tasks = await model.findAll({
         where: where,
         transaction: t as any
@@ -48,9 +48,10 @@ export async function update_parent_task_info(draft: boolean, prj_id: string, ta
         let task = await model.findOne({where: {id: task_pid}, raw: true, transaction: t});
         if (!task) return;
         let status = task.status;
-        if (progress > 0 && status === 0) status = TaskStatus.Doing;
-        else if (progress === 0) status = TaskStatus.New;
-        else if (progress === 100) status = TaskStatus.Completed;
+        if (progress > 0 && status === 0) status = TaskStatus.doing;
+        else if (progress === 0) status = TaskStatus.new;
+        else if (progress === 100) status = TaskStatus.completed;
+        // @ts-ignore
         let parent = await model.update({
             begin_at: min!,
             end_at: max!,