@steedos-labs/plugin-workflow 3.0.0 → 3.0.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,246 @@
1
+ name: forms
2
+ label: 表单
3
+ description:
4
+ fields:
5
+ name:
6
+ label: 表单
7
+ help:
8
+ description:
9
+ state:
10
+ label: 表单状态
11
+ help:
12
+ options:
13
+ - label: 启用
14
+ value: enabled
15
+ - label: 停用
16
+ value: disabled
17
+ description:
18
+ description:
19
+ label: 表单描述
20
+ help:
21
+ description:
22
+ category:
23
+ label: 表单分类
24
+ help:
25
+ description:
26
+ is_valid:
27
+ label: 是否有效
28
+ help:
29
+ description:
30
+ instance_style:
31
+ label: 样式
32
+ help:
33
+ options:
34
+ - label: 表格
35
+ value: table
36
+ - label: 默认
37
+ value: default
38
+ description:
39
+ historys:
40
+ label: 历史版本
41
+ help:
42
+ description:
43
+ approve_on_create:
44
+ label: 是否同意审批
45
+ help:
46
+ description:
47
+ approve_on_modify:
48
+ label: 是否修改审批
49
+ help:
50
+ description:
51
+ approve_on_delete:
52
+ label: 是否删除审批
53
+ help:
54
+ description:
55
+ enable_workflow:
56
+ label: 是否启用工作流
57
+ help:
58
+ description:
59
+ enable_view_others:
60
+ label: 是否查看其它表单
61
+ help:
62
+ description:
63
+ current:
64
+ label: 当前版本
65
+ help:
66
+ description:
67
+ current.form_script:
68
+ label: 表单脚本
69
+ help:
70
+ description:
71
+ current.name_forumla:
72
+ label: 标题公式
73
+ help:
74
+ description:
75
+ current.fields:
76
+ label: 字段
77
+ help:
78
+ description:
79
+ current.fields.$.code:
80
+ label: Api 名称
81
+ help:
82
+ description:
83
+ current.fields.$.name:
84
+ label: 名称
85
+ help:
86
+ description:
87
+ current.fields.$.type:
88
+ label: 类型
89
+ help:
90
+ options:
91
+ - label: 勾选框
92
+ value: checkbox
93
+ - label: 日期时间
94
+ value: dateTime
95
+ - label: 日期
96
+ value: date
97
+ - label: 时间
98
+ value: time
99
+ - label: 电子邮件
100
+ value: email
101
+ - label: Group
102
+ value: group
103
+ - label: 输入框
104
+ value: input
105
+ - label: 多选框
106
+ value: multiSelect
107
+ - label: 数字框
108
+ value: number
109
+ - label: 密码
110
+ value: password
111
+ - label: 单选框
112
+ value: radio
113
+ - label: 分组
114
+ value: section
115
+ - label: 下拉框
116
+ value: select
117
+ - label: 表格
118
+ value: table
119
+ - label: 链接
120
+ value: url
121
+ - label: 选择用户
122
+ value: user
123
+ - label: Geolocation
124
+ value: geolocation
125
+ - label: 图片
126
+ value: image
127
+ - label: 附件
128
+ value: file
129
+ - label: 相关记录
130
+ value: lookup
131
+ - label: 文本
132
+ value: text
133
+ - label: 长文本
134
+ value: textarea
135
+ - label: 富文本
136
+ value: html
137
+ - label: 代码
138
+ value: code
139
+ - label: Markdown
140
+ value: markdown
141
+ - label: 选择框
142
+ value: select
143
+ - label: 颜色
144
+ value: color
145
+ - label: 复选框
146
+ value: boolean
147
+ - label: 开关
148
+ value: toggle
149
+ - label: 日期
150
+ value: date
151
+ - label: 日期时间
152
+ value: datetime
153
+ - label: 时间
154
+ value: time
155
+ - label: 数值
156
+ value: number
157
+ - label: 金额
158
+ value: currency
159
+ - label: 百分比
160
+ value: percent
161
+ - label: 密码
162
+ value: password
163
+ - label: 相关表关系
164
+ value: lookup
165
+ - label: 主/子表关系
166
+ value: master_detail
167
+ - label: 自动编号
168
+ value: autonumber
169
+ - label: 网址
170
+ value: url
171
+ - label: 邮件地址
172
+ value: email
173
+ - label: 地理位置
174
+ value: location
175
+ - label: 图片
176
+ value: image
177
+ - label: 附件
178
+ value: file
179
+ - label: 公式
180
+ value: formula
181
+ - label: 累计汇总
182
+ value: summary
183
+ description:
184
+ current.fields.$.is_required:
185
+ label: 必填
186
+ help:
187
+ description:
188
+ current.fields.$.is_wide:
189
+ label: 宽字段
190
+ help:
191
+ description:
192
+ current.fields.$.is_list_display:
193
+ label: 显示在列表内
194
+ help:
195
+ description:
196
+ current.fields.$.is_searchable:
197
+ label: 可搜索
198
+ help:
199
+ description:
200
+ current.fields.$.is_multiselect:
201
+ label: 可复选
202
+ help:
203
+ description:
204
+ current.fields.$.visibleOn:
205
+ label: 显隐的条件
206
+ listviews:
207
+ all:
208
+ label: 所有
209
+ actions:
210
+ standard_new:
211
+ label: 新建
212
+ standard_edit:
213
+ label: 编辑
214
+ standard_delete:
215
+ label: 删除
216
+ designForm:
217
+ label: 表单设计器
218
+ CustomLabels:
219
+ _forms_field_code_error: 字段名 %s 不能包含'.'
220
+ forms_field_current.fields:
221
+ forms_field_current.fields_$_code:
222
+ forms_field_current.fields_$_is_list_display:
223
+ forms_field_current.fields_$_is_multiselect:
224
+ forms_field_current.fields_$_is_required:
225
+ forms_field_current.fields_$_is_searchable:
226
+ forms_field_current.fields_$_is_wide:
227
+ forms_field_current.fields_$_name:
228
+ forms_field_current.fields_$_type:
229
+ forms_field_current.fields_$_type_options_checkbox:
230
+ forms_field_current.fields_$_type_options_date:
231
+ forms_field_current.fields_$_type_options_dateTime:
232
+ forms_field_current.fields_$_type_options_email:
233
+ forms_field_current.fields_$_type_options_geolocation:
234
+ forms_field_current.fields_$_type_options_group:
235
+ forms_field_current.fields_$_type_options_input:
236
+ forms_field_current.fields_$_type_options_multiSelect:
237
+ forms_field_current.fields_$_type_options_number:
238
+ forms_field_current.fields_$_type_options_password:
239
+ forms_field_current.fields_$_type_options_radio:
240
+ forms_field_current.fields_$_type_options_section:
241
+ forms_field_current.fields_$_type_options_select:
242
+ forms_field_current.fields_$_type_options_table:
243
+ forms_field_current.fields_$_type_options_url:
244
+ forms_field_current.fields_$_type_options_user:
245
+ forms_field_current.form_script:
246
+ forms_field_current.name_forumla:
@@ -209,3 +209,5 @@ listviews:
209
209
  actions:
210
210
  instance_new:
211
211
  label: New
212
+ CustomLabels:
213
+ instance_task_set_have_read_failed: Set as read failed
@@ -209,3 +209,5 @@ listviews:
209
209
  actions:
210
210
  instance_new:
211
211
  label: 新建
212
+ CustomLabels:
213
+ instance_task_set_have_read_failed: 设置为已读失败
@@ -209,6 +209,52 @@ fields:
209
209
  value: file
210
210
  - label: Lookup
211
211
  value: lookup
212
+ - label: Steedos Field
213
+ value: steedos-field
214
+ - label: Text
215
+ value: text
216
+ - label: Textarea
217
+ value: textarea
218
+ - label: Html
219
+ value: html
220
+ - label: Lookup
221
+ value: lookup
222
+ - label: Master Detail
223
+ value: master_detail
224
+ - label: Select
225
+ value: select
226
+ - label: Boolean
227
+ value: boolean
228
+ - label: Date
229
+ value: date
230
+ - label: Datetime
231
+ value: datetime
232
+ - label: Time
233
+ value: time
234
+ - label: Number
235
+ value: number
236
+ - label: Currency
237
+ value: currency
238
+ - label: Percent
239
+ value: percent
240
+ - label: Image
241
+ value: image
242
+ - label: File
243
+ value: file
244
+ - label: Code
245
+ value: code
246
+ - label: Markdown
247
+ value: markdown
248
+ - label: Color
249
+ value: color
250
+ - label: Toggle
251
+ value: toggle
252
+ - label: Password
253
+ value: password
254
+ - label: Url
255
+ value: url
256
+ - label: Email
257
+ value: email
212
258
  readonly: true
213
259
  name: current.fields.$.type
214
260
  filterable: true
@@ -615,7 +615,7 @@
615
615
  "id": "u:5df987ad9bc8",
616
616
  "className": "bg-white",
617
617
  "mode": "edit",
618
- "initApiAdaptor": "_.each(payload.data.current.fields, (item)=>{if(item.type =='table' || item.type == 'section'){ var _item = _.find(payload.data.current.fields, function(__item){return __item.code == item.code}); if(_item){_item.children = item.fields;} }}); return payload",
618
+ "initApiAdaptor": "_.each(payload.data.current.fields, (item)=>{if(item.type=='steedos-field'){item.type = item.config.type} if(item.type =='table' || item.type == 'section'){ var _item = _.find(payload.data.current.fields, function(__item){return __item.code == item.code}); if(_item){_item.children = item.fields;} }}); return payload",
619
619
  "fields": [
620
620
  "current",
621
621
  "current.fields",
@@ -630,7 +630,7 @@
630
630
  "current.fields.$.visibleOn"
631
631
  ],
632
632
  "fieldsExtend": "{\n \"current\": {\n \"label\": false\n },\n \"current.fields\": {\n \"label\": false\n }\n}",
633
- "formDataFilter": "if (!form.onEvent.change) {\n form.onEvent.change = { actions: [] }\n};\nform.onEvent.change.actions.push({\n actionType: \"custom\",\n script: \"console.log('onChange event.data', event.data)\"\n}, {\n \"actionType\": \"ajax\",\n \"outputVar\": \"responseResult\",\n \"args\": {\n \"options\": { \"silent\": false },\n \"api\": {\n \"url\": \"/api/workflow/updateFormFields\",\n \"method\": \"post\",\n \"requestAdaptor\": `\n console.log('api.data', api, 'context', context);\n var fields = (context.current.fields || []).map(function(item){\n // 有时编辑children子行记录时未同步到fields属性值中,造成保存数据不成功\n if(item.children){\n var newItem = _.clone(item);\n newItem.fields = newItem.children;\n delete newItem.children;\n return newItem;\n }\n else{\n return item;\n }\n });\n api.data = { \n formId: context.current.form, \n fields: fields\n };\n return api;\n `,\n \"adaptor\": \"\",\n \"messages\": {},\n \"dataType\": \"json\"\n }\n }\n})"
633
+ "formDataFilter": "if (!form.onEvent.change) {\n form.onEvent.change = { actions: [] }\n};\nform.onEvent.change.actions.push({\n actionType: \"custom\",\n script: \"console.log('onChange event.data', event.data)\"\n}, {\n \"actionType\": \"ajax\",\n \"outputVar\": \"responseResult\",\n \"args\": {\n \"options\": { \"silent\": false },\n \"api\": {\n \"url\": \"/api/workflow/updateFormFields\",\n \"method\": \"post\",\n \"requestAdaptor\": `\n console.log('api.data', api, 'context', context);\n var fields = (context.current.fields || []).map(function(item){\n if(item.config.type){item.type='steedos-field'} // 有时编辑children子行记录时未同步到fields属性值中,造成保存数据不成功\n if(item.children){\n var newItem = _.clone(item);\n newItem.fields = newItem.children;\n delete newItem.children;\n return newItem;\n }\n else{\n return item;\n }\n });\n api.data = { \n formId: context.current.form, \n fields: fields\n };\n return api;\n `,\n \"adaptor\": \"\",\n \"messages\": {},\n \"dataType\": \"json\"\n }\n }\n})"
634
634
  }
635
635
  ],
636
636
  "id": "u:827750209c5d",
@@ -85,46 +85,34 @@
85
85
  "visibleOn": "false"
86
86
  }
87
87
  },
88
+ {
89
+ "type": "button",
90
+ "label": "刷新",
91
+ "className": "steedos-workflow-reload-btn hidden",
92
+ "onEvent": {
93
+ "click": {
94
+ "actions": [
95
+ {
96
+ "componentId": "u:d6db0c84f150",
97
+ "groupType": "component",
98
+ "actionType": "rebuild"
99
+ }
100
+ ]
101
+ }
102
+ }
103
+ },
88
104
  {
89
105
  "type": "wrapper",
90
106
  "className": "steedos-instance-detail-wrapper m-0 p-0 flex-1 focus:outline-none lg:order-last sm:m-4 shadow sm:rounded",
91
107
  "body": [{
92
108
  "type": "service",
93
109
  "id": "u:d6db0c84f150",
94
- "body": [{
95
- "type": "wrapper",
96
- "className": "p-0 h-full",
97
- "body": [{
98
- "type": "steedos-instance-detail",
99
- "id": "u:40052b3812c1",
100
- "label": "Instance Detail",
101
- "instanceId": "${recordId}",
102
- "boxName": "${side_listview_id}",
103
- "hiddenOn": "!!!this.recordId"
104
- }],
105
- "visibleOn": "${window:InstanceDetailEnabledAmisRender != false}",
106
- "id": "u:829a40757f0a"
107
- },
108
- {
109
- "type": "wrapper",
110
- "className": "workflow-main flex w-full h-full no-columns instance-show instance-detail-service-wrapper",
111
- "visibleOn": "${window:InstanceDetailEnabledAmisRender == false}",
112
- "body": [{
113
- "type": "custom",
114
- "html": "<div className='workflow-main flex w-full h-full no-columns instance-show instance-detail-service-wrapper'></div>",
115
- "onMount": "try {\n Session.set(\"instance_change\", false);\n\n Session.set(\"instance_loading\", true);\n Session.set(\"judge\", null);\n Session.set(\"next_step_id\", null);\n Session.set(\"next_step_multiple\", null);\n Session.set(\"next_user_multiple\", null);\n let box;\n let recordId;\n if (props.data._inDrawer) {\n // 主表记录详细页面审批相关列表中点击名称字段右侧弹出drawer时,box写死,recordId取作用域中值\n box = \"outbox\";\n recordId = props.data.recordId;\n }\n else {\n box = props.data.side_listview_id;\n recordId = Session.get(\"record_id\");\n }\n Session.set(\"box\", box);\n Session.set(\"instanceId\", recordId);\n Session.set(\"instanceTaskId\", null);\n window.instanceDetailBlazeRenderView = Blaze.render(Blaze._getTemplate('instance_view_wrapper'), document.getElementsByClassName('instance-detail-service-wrapper')[0])\n} catch (error) {\n console.error(error)\n};\nif (window.instanceDetailBlazeRenderView) {\n window.instanceDetailBlazeRenderView.autorun(() => {\n if (FlowRouter.current().params.object_name == \"instances\") {\n console.debug('====onMount=', props);\n Session.set(\"instanceId\", Session.get(\"record_id\"));\n Session.set(\"instanceTaskId\", null)\n }\n }\n )\n};",
116
- "id": "u:a6e0685d1da3",
117
- "onUnmount": "if(window.instanceDetailBlazeRenderView){console.log('Blaze.remove===>instanceDetailBlazeRenderView');Blaze.remove(window.instanceDetailBlazeRenderView); window.instanceDetailBlazeRenderView = null;}"
118
- }],
119
- "id": "u:16cf880c925a",
120
- "style": {
121
- "position": "static",
122
- "display": "block"
123
- },
124
- "isFixedHeight": false,
125
- "isFixedWidth": false
126
- }
127
- ],
110
+ "schemaApi": {
111
+ "method": "get",
112
+ "url": "/api/health_check",
113
+ "adaptor": "return {data: {'type':'wrapper','className':'p-0 h-full','body':[{'type':'steedos-instance-detail','id':'u:40052b3812c1','label':'Instance Detail','instanceId':context.recordId,'boxName':context.side_listview_id}],'id':'u:829a40757f0a'}}"
114
+ },
115
+ "body": {},
128
116
  "className": "h-full",
129
117
  "dsType": "api"
130
118
  }],
@@ -123,8 +123,24 @@
123
123
  "headers": {
124
124
  "Authorization": "Bearer ${context.tenantId},${context.authToken}"
125
125
  },
126
- "requestAdaptor": "\nconst { recordId } = api.body;\napi.data = {\n query: `{instance_task:instance_tasks__findOne(id: \"${recordId}\"){_id,instance}}`\n};\n\nreturn api;",
127
- "adaptor": "\nlet boxName = api.body.side_listview_id === 'inbox' ? 'inbox' : 'outbox';\npayload.data = {\n instanceId: payload.data.instance_task.instance,\n boxName: boxName\n}\n; console.log('=======>payload===>', payload); \nreturn payload;"
126
+ "requestAdaptor": "\nconst { recordId } = api.body;\napi.data = {\n query: `{instance_task:instance_tasks__findOne(id: \"${recordId}\"){_id,instance,is_read}}`\n};\n\nreturn api;",
127
+ "adaptor": "\nlet boxName = api.body.side_listview_id === 'inbox' ? 'inbox' : 'outbox';\npayload.data = {\n instanceId: payload.data.instance_task.instance,\n boxName: boxName,\n isRead: payload.data.instance_task.is_read\n}\n; console.log('=======>payload===>', payload); \nreturn payload;"
128
+ },
129
+ "onEvent": {
130
+ "fetchInited": {
131
+ "actions": [
132
+ {
133
+ "actionType": "ajax",
134
+ "api": {
135
+ "url": "${context.rootUrl}/api/workflow/v2/set_have_read",
136
+ "method": "post",
137
+ "requestAdaptor": "api.data={instanceTaskId: context.recordId, isRead: context.isRead}\n\n\nreturn api;",
138
+ "adaptor": "return payload.success ? {data: {...payload}, status: 0} : {...payload, status: 1, msg: (t('CustomLabels.instance_task_set_have_read_failed') + ':' + payload.error)};"
139
+ },
140
+ "expression": "${!event.data.isRead}"
141
+ }
142
+ ]
143
+ }
128
144
  },
129
145
  "className": "h-full"
130
146
  }
@@ -0,0 +1,34 @@
1
+ const afterHook = (req, res, next) => {
2
+ res.on('finish', () => {
3
+ if (req.url.includes('/api/workflow/v2/approve/save')) {
4
+ const { approve } = req.body;
5
+ const ins_id = approve.instance;
6
+ broker.call('objectql.update', {
7
+ objectName: 'instances',
8
+ id: ins_id,
9
+ doc: {},
10
+ })
11
+ }else if(req.url.includes('/api/workflow/engine')){
12
+ var hashData = req.body;
13
+ const approve_from_client = hashData['Approvals'][0];
14
+ const ins_id = approve_from_client.instance;
15
+ broker.call('objectql.update', {
16
+ objectName: 'instances',
17
+ id: ins_id,
18
+ doc: {},
19
+ })
20
+ }else if(req.ulr.includes('/api/workflow/relocate')){
21
+ var hashData = req.body;
22
+ const instance_from_client = hashData['Instances'][0];
23
+ const ins_id = instance_from_client._id;
24
+ broker.call('objectql.update', {
25
+ objectName: 'instances',
26
+ id: ins_id,
27
+ doc: {},
28
+ })
29
+ }
30
+ });
31
+ next();
32
+ };
33
+
34
+ module.exports = afterHook;
@@ -0,0 +1,73 @@
1
+ /*
2
+ * @Author: 殷亮辉 yinlianghui@hotoa.com
3
+ * @Date: 2025-10-09 10:12:49
4
+ * @LastEditors: 殷亮辉 yinlianghui@hotoa.com
5
+ * @LastEditTime: 2025-10-29 09:38:43
6
+ */
7
+ const express = require("express");
8
+ const router = express.Router();
9
+ const { requireAuthentication } = require("@steedos/auth");
10
+ const objectql = require('@steedos/objectql')
11
+ // const { cc_do } = require('../methods/trace_approve_cc');
12
+ const { getCollection } = require('../utils/collection');
13
+ const { set_approve_have_read } = require('../methods/instance_approve');
14
+ const { cc_read } = require('../methods/trace_approve_cc');
15
+ // const InstanceManager = global.InstanceManager;
16
+ const InstanceManager = require('../manager/instance_manager');
17
+
18
+ router.post('/api/workflow/v2/set_have_read', requireAuthentication, async function (req, res) {
19
+ try {
20
+ let userSession = req.user;
21
+ if (!userSession) {
22
+ return res.status(200).send({
23
+ error: "unauthorized"
24
+ });
25
+ }
26
+ const userId = userSession.userId;
27
+ const { instanceTaskId, isRead } = req.body;
28
+ if (isRead){
29
+ return res.status(200).send({
30
+ success: true
31
+ });
32
+ }
33
+ const db = {
34
+ instances: await getCollection('instances'),
35
+ instance_tasks: await getCollection('instance_tasks')
36
+ };
37
+ const instance_task = await db.instance_tasks.findOne({
38
+ _id: instanceTaskId
39
+ }, {
40
+ fields: {
41
+ "instance": 1,
42
+ "is_read": 1,
43
+ "type": 1
44
+ }
45
+ });
46
+ if (instance_task.is_read){
47
+ return res.status(200).send({
48
+ success: true
49
+ });
50
+ }
51
+
52
+ const instanceId = instance_task.instance;
53
+ const ins = await db.instances.findOne({_id: instanceId});
54
+ var myApprove = InstanceManager.getCurrentApprove(ins, userId, instance_task.type);
55
+ if (instance_task.type == "cc") {
56
+ // 传阅
57
+ await cc_read(ins, myApprove, { userId });
58
+ }
59
+ else{
60
+ await set_approve_have_read(ins._id, myApprove.trace, myApprove._id, { userId });
61
+ }
62
+ return res.status(200).send({
63
+ success: true
64
+ });
65
+
66
+ } catch (error) {
67
+ console.error(error);
68
+ return res.status(200).send({
69
+ error: error.message
70
+ });
71
+ }
72
+ });
73
+ exports.default = router;
@@ -8,8 +8,9 @@ const {
8
8
  const UUFlowManager = require('../manager/uuflow_manager');
9
9
  const { getCollection } = require('../utils/collection');
10
10
  const approveManager = require('../manager/approve_manager');
11
+ const afterHook = require('./afterHook');
11
12
 
12
- router.post('/api/workflow/v2/approve/save', requireAuthentication, async function (req, res) {
13
+ router.post('/api/workflow/v2/approve/save', requireAuthentication, afterHook, async function (req, res) {
13
14
  try {
14
15
  const userSession = req.user;
15
16
  const { approve } = req.body;
@@ -15,7 +15,7 @@ const Fiber = function(fun){console.log('TODO Fiber...')}
15
15
  const { excuteTriggers } = require('../utils/trigger');
16
16
  const { getStep } = require('../uuflowManager');
17
17
  const UUFlowManager = require('../manager/uuflow_manager');
18
-
18
+ const afterHook = require('./afterHook');
19
19
  /**
20
20
  * 审批中提交申请单
21
21
  * body {
@@ -53,7 +53,7 @@ const UUFlowManager = require('../manager/uuflow_manager');
53
53
  ]
54
54
  * }
55
55
  */
56
- router.post('/api/workflow/engine', requireAuthentication, async function (req, res) {
56
+ router.post('/api/workflow/engine', requireAuthentication, afterHook, async function (req, res) {
57
57
  try {
58
58
  let userSession = req.user;
59
59
  const spaceId = userSession.spaceId;
@@ -80,7 +80,7 @@ router.post('/api/workflow/engine', requireAuthentication, async function (req,
80
80
  }
81
81
 
82
82
  try {
83
- const instance = UUFlowManager.workflow_engine(approve_from_client, userSession, userId);
83
+ const instance = await UUFlowManager.workflow_engine(approve_from_client, userSession, userId);
84
84
  // afterStepSubmit
85
85
  await excuteTriggers({ when: 'afterStepSubmit', userId, flowId, insId });
86
86
  // afterEnd
@@ -0,0 +1,61 @@
1
+ const express = require("express");
2
+ const router = express.Router();
3
+ const { requireAuthentication } = require("@steedos/auth");
4
+ const _ = require('lodash');
5
+ const { getCollection } = require('../utils/collection');
6
+ router.get('/api/workflow/flow/:flowId/version/:versionId', requireAuthentication, async function (req, res) {
7
+ try {
8
+ const { flowId, versionId } = req.params;
9
+ const flowsColl = await getCollection('flows');
10
+
11
+ const flow = await flowsColl.aggregate([
12
+ {
13
+ $match: {
14
+ "_id": flowId
15
+ }
16
+ },
17
+ {
18
+ $project: {
19
+ name: 1,
20
+ current_no: 1,
21
+ versionData: {
22
+ $cond: {
23
+ if: { $eq: ["$current._id", versionId] },
24
+ then: "$current",
25
+ else: {
26
+ $arrayElemAt: [
27
+ {
28
+ $filter: {
29
+ input: "$historys",
30
+ as: "history",
31
+ cond: { $eq: ["$$history._id", versionId] }
32
+ }
33
+ },
34
+ 0
35
+ ]
36
+ }
37
+ }
38
+ }
39
+ }
40
+ },
41
+ {
42
+ $match: {
43
+ "versionData": { $ne: null }
44
+ }
45
+ },
46
+ { $limit: 1 } // 限制只返回一个文档
47
+ ]).next();
48
+
49
+ if (flow) {
50
+ res.status(200).send(flow.versionData);
51
+ } else {
52
+ res.status(404).send({ message: "流程版本未找到" });
53
+ }
54
+ } catch (error) {
55
+ console.error('Unexpected error:', error);
56
+ res.status(500).send({
57
+ error: error.message
58
+ });
59
+ }
60
+ });
61
+ exports.default = router;