@steedos-labs/plugin-workflow 3.0.0-beta.9 → 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.
- package/main/default/client/instance.client.js +6 -6
- package/main/default/client/object_workflows.client.js +8 -7
- package/main/default/client/socket.client.js +46 -3
- package/main/default/manager/import.js +17 -1
- package/main/default/manager/instance_manager.js +20 -6
- package/main/default/manager/push_manager.js +20 -12
- package/main/default/manager/uuflowManagerForInitApproval.js +794 -0
- package/main/default/manager/uuflow_manager.js +53 -4
- package/main/default/manager/workflow_manager.js +1 -1
- package/main/default/methods/instance_approve.js +258 -0
- package/main/default/methods/trace_approve_cc.js +571 -0
- package/main/default/objectTranslations/flows.en/flows.en.objectTranslation.yml +19 -0
- package/main/default/objectTranslations/flows.zh-CN/flows.zh-CN.objectTranslation.yml +19 -0
- package/main/default/objectTranslations/forms.en/forms.en.objectTranslation.yml +191 -0
- package/main/default/objectTranslations/forms.zh-CN/forms.zh-CN.objectTranslation.yml +246 -0
- package/main/default/objectTranslations/instance_tasks.en/instance_tasks.en.objectTranslation.yml +213 -0
- package/main/default/objectTranslations/instance_tasks.zh-CN/instance_tasks.zh-CN.objectTranslation.yml +213 -0
- package/main/default/objectTranslations/instances.en/instances.en.objectTranslation.yml +212 -0
- package/main/default/objectTranslations/instances.zh-CN/instances.zh-CN.objectTranslation.yml +209 -0
- package/main/default/objects/categories.object.yml +1 -0
- package/main/default/objects/flows/buttons/del.button.yml +7 -10
- package/main/default/objects/flows/buttons/design_form_layout.button.js +5 -2
- package/main/default/objects/flows/buttons/distributeAdmin.button.yml +5 -5
- package/main/default/objects/flows/buttons/newexport.button.yml +1 -1
- package/main/default/objects/flows/buttons/newimport.button.yml +2 -1
- package/main/default/objects/flows/flows.object.yml +12 -4
- package/main/default/objects/forms/forms.object.yml +85 -0
- package/main/default/objects/instance_tasks/buttons/instance_new.button.yml +3 -5
- package/main/default/objects/instances/buttons/instance_cc.button.yml +7 -7
- package/main/default/objects/instances/buttons/instance_delete.button.yml +2 -2
- package/main/default/objects/instances/buttons/instance_delete_many.button.yml +6 -6
- package/main/default/objects/instances/buttons/instance_distribute.button.yml +14 -13
- package/main/default/objects/instances/buttons/instance_flow_chart.button.yml +1 -1
- package/main/default/objects/instances/buttons/instance_forward.button.yml +8 -8
- package/main/default/objects/instances/buttons/instance_new.button.yml +3 -5
- package/main/default/objects/instances/buttons/instance_reassign.button.yml +1 -16
- package/main/default/objects/instances/buttons/instance_related.button.yml +4 -4
- package/main/default/objects/instances/buttons/instance_relocate.button.yml +9 -12
- package/main/default/objects/instances/buttons/instance_retrieve.button.yml +106 -2
- package/main/default/objects/instances/buttons/instance_save.button.yml +3 -9
- package/main/default/objects/instances/buttons/instance_submit.button.yml +1 -1
- package/main/default/objects/instances/buttons/instance_terminate.button.yml +7 -10
- package/main/default/objects/instances/listviews/monitor.listview.yml +0 -1
- package/main/default/pages/flowdetail.page.amis.json +11 -11
- package/main/default/pages/instance_detail.page.amis.json +25 -37
- package/main/default/pages/instance_tasks_detail.page.amis.json +21 -5
- package/main/default/pages/instance_tasks_list.page.amis.json +147 -110
- package/main/default/pages/instances_list.page.amis.json +146 -110
- package/main/default/routes/afterHook.js +34 -0
- package/main/default/routes/am.router.js +49 -2
- package/main/default/routes/api_cc.router.js +5 -12
- package/main/default/routes/api_flow_permission.router.js +7 -2
- package/main/default/routes/api_get_object_workflows.router.js +79 -22
- package/main/default/routes/api_have_read.router.js +73 -0
- package/main/default/routes/api_object_workflow_drafts.router.js +18 -19
- package/main/default/routes/api_workflow_approve_save.router.js +2 -1
- package/main/default/routes/api_workflow_chart.router.js +682 -0
- package/main/default/routes/api_workflow_engine.router.js +4 -4
- package/main/default/routes/api_workflow_flow_version.router.js +61 -0
- package/main/default/routes/api_workflow_form_version.router.js +61 -0
- package/main/default/routes/api_workflow_instance_return.router.js +164 -167
- package/main/default/routes/api_workflow_next_step_users.router.js +13 -8
- package/main/default/routes/api_workflow_reassign.router.js +200 -196
- package/main/default/routes/api_workflow_relocate.router.js +4 -3
- package/main/default/routes/api_workflow_retrieve.router.js +246 -237
- package/main/default/routes/export.router.js +5 -4
- package/main/default/routes/flow_form_design.ejs +33 -8
- package/main/default/routes/flow_form_design.router.js +4 -3
- package/main/default/routes/import.router.js +6 -7
- package/main/default/services/flows.service.js +1 -1
- package/main/default/translations/en.translation.yml +5 -0
- package/main/default/translations/zh-CN.translation.yml +2 -1
- package/main/default/triggers/amis_form_design.trigger.js +27 -5
- package/main/default/triggers/instances.trigger.js +9 -7
- package/main/default/utils/designerManager.js +12 -5
- package/package.json +4 -4
- package/package.service.js +21 -7
- package/public/workflow/index.css +4 -0
- package/src/instance_record_queue.js +1 -3
- package/src/rests/api_workflow_instance_batch_remove.js +4 -3
- package/src/webhook_queue.js +283 -0
- package/main/default/manager/index.js +0 -23
|
@@ -34,8 +34,8 @@ router.get('/api/workflow/form_design', auth.requireAuthentication, async functi
|
|
|
34
34
|
// userId: userSession.userId,
|
|
35
35
|
// authToken: userSession.authToken
|
|
36
36
|
// }
|
|
37
|
-
let locale = "zh-CN";
|
|
38
|
-
if (req.query.locale
|
|
37
|
+
let locale = userSession.language || process.env.STEEDOS_DEFAULT_LANGUAGE || "zh-CN";
|
|
38
|
+
if (req.query.locale?.startsWith('en')) {
|
|
39
39
|
locale = "en-US";
|
|
40
40
|
} else if (req.query.locale == "zh-cn") {
|
|
41
41
|
locale = "zh-CN";
|
|
@@ -56,7 +56,8 @@ router.get('/api/workflow/form_design', auth.requireAuthentication, async functi
|
|
|
56
56
|
authToken: userSession.authToken,
|
|
57
57
|
userSession: userSession,
|
|
58
58
|
id: flow.form,
|
|
59
|
-
useOpenAPI: process.env.STEEDOS_PUBLIC_USE_OPEN_API
|
|
59
|
+
useOpenAPI: process.env.STEEDOS_PUBLIC_USE_OPEN_API,
|
|
60
|
+
locale
|
|
60
61
|
}
|
|
61
62
|
const options = {}
|
|
62
63
|
ejs.renderFile(filename, data, options, function(err, str){
|
|
@@ -138,12 +138,12 @@ router.post('/api/workflow/import/form', requireAuthentication, async function (
|
|
|
138
138
|
} catch (e) {
|
|
139
139
|
console.log(e)
|
|
140
140
|
if (e.reason && e.details) {
|
|
141
|
-
|
|
141
|
+
fail[filename] = {
|
|
142
142
|
reason: e.reason,
|
|
143
143
|
details: e.details
|
|
144
144
|
};
|
|
145
145
|
} else {
|
|
146
|
-
|
|
146
|
+
fail[filename] = e.reason || e.message;
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
}
|
|
@@ -152,16 +152,15 @@ router.post('/api/workflow/import/form', requireAuthentication, async function (
|
|
|
152
152
|
} else {
|
|
153
153
|
res.statusCode = 500;
|
|
154
154
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
success: success
|
|
155
|
+
res.end(JSON.stringify({
|
|
156
|
+
status: res.statusCode ? 0 : 1,
|
|
157
|
+
msg: fail
|
|
159
158
|
}));
|
|
160
159
|
} catch (error) {
|
|
161
160
|
console.log(error)
|
|
162
161
|
msg = "无效的JSON文件";
|
|
163
162
|
res.statusCode = 500;
|
|
164
|
-
|
|
163
|
+
res.end(msg);
|
|
165
164
|
}
|
|
166
165
|
});
|
|
167
166
|
} catch (e) {
|
|
@@ -56,6 +56,9 @@ const AmisInputTypes = [
|
|
|
56
56
|
|
|
57
57
|
// getInstanceFormSchema() 在用户创建的表单中获取到type为form name为instanceForm的表单,其他的表单不要
|
|
58
58
|
function getInstanceFormSchema(amis_schema) {
|
|
59
|
+
if(!amis_schema){
|
|
60
|
+
return ;
|
|
61
|
+
}
|
|
59
62
|
|
|
60
63
|
if (amis_schema.type === 'form' && amis_schema.name === 'instanceForm' || amis_schema.type === 'steedos-flow-form') {
|
|
61
64
|
const proper_schema = amis_schema;
|
|
@@ -94,7 +97,7 @@ function getFormInputFields(formSchema) {
|
|
|
94
97
|
let flag = true;
|
|
95
98
|
// 对符合条件的formSchema做解析处理
|
|
96
99
|
const field = getInputFiled(bodyItem);
|
|
97
|
-
console.log('getFormInputFields field', field);
|
|
100
|
+
// console.log('getFormInputFields field', field);
|
|
98
101
|
if (field) {
|
|
99
102
|
//进入if说明是表单项
|
|
100
103
|
_.each(inputFields, (item) => {
|
|
@@ -108,7 +111,7 @@ function getFormInputFields(formSchema) {
|
|
|
108
111
|
flag = false;
|
|
109
112
|
}
|
|
110
113
|
})
|
|
111
|
-
console.log('getFormInputFields flag', flag);
|
|
114
|
+
// console.log('getFormInputFields flag', flag);
|
|
112
115
|
if (flag) {
|
|
113
116
|
inputFields.push(field);
|
|
114
117
|
}
|
|
@@ -157,8 +160,8 @@ function transformFormFields(amisField) {
|
|
|
157
160
|
|
|
158
161
|
|
|
159
162
|
if(amisField.type === 'steedos-field'){
|
|
160
|
-
|
|
161
|
-
_id:
|
|
163
|
+
const sfield = {
|
|
164
|
+
_id: amisField.config.name,
|
|
162
165
|
code: amisField.config.name,
|
|
163
166
|
name: amisField.config.label,
|
|
164
167
|
is_wide: amisField.config.is_wide,
|
|
@@ -170,6 +173,17 @@ function transformFormFields(amisField) {
|
|
|
170
173
|
type: 'steedos-field',
|
|
171
174
|
config: amisField.config
|
|
172
175
|
}
|
|
176
|
+
|
|
177
|
+
let tempConfig = amisField.config;
|
|
178
|
+
|
|
179
|
+
if (tempConfig.reference_to === "organizations") {
|
|
180
|
+
sfield.type = 'group'
|
|
181
|
+
sfield._type = 'steedos-field'
|
|
182
|
+
}else if(tempConfig.reference_to === "space_users" || tempConfig.reference_to === "users"){
|
|
183
|
+
sfield.type = 'user'
|
|
184
|
+
sfield._type = 'steedos-field'
|
|
185
|
+
}
|
|
186
|
+
return sfield;
|
|
173
187
|
};
|
|
174
188
|
|
|
175
189
|
let formFieldsItem = {
|
|
@@ -451,7 +465,7 @@ function transformFormFields(amisField) {
|
|
|
451
465
|
|
|
452
466
|
if (tempConfig.reference_to === "organizations") {
|
|
453
467
|
steedosField.type = 'group'
|
|
454
|
-
}else if(tempConfig.reference_to === "space_users"){
|
|
468
|
+
}else if(tempConfig.reference_to === "space_users" || tempConfig.reference_to === "user"){
|
|
455
469
|
// && tempConfig.reference_to_field === 'user'
|
|
456
470
|
steedosField.type = 'user'
|
|
457
471
|
}else{
|
|
@@ -520,6 +534,14 @@ module.exports = {
|
|
|
520
534
|
|
|
521
535
|
// 数据库更新操作:将forms表current的fields字段更新为最终数组
|
|
522
536
|
form.current.fields = getFinalFormFields(inputFields);
|
|
537
|
+
form.current.amis_schema = this.doc.amis_schema;
|
|
538
|
+
form.current.style = formSchema.style;
|
|
539
|
+
form.current.mode = formSchema.mode;
|
|
540
|
+
form.current.wizard_mode = formSchema.wizard_mode;
|
|
541
|
+
form.style = formSchema.style;
|
|
542
|
+
form.mode = formSchema.mode;
|
|
543
|
+
form.wizard_mode = formSchema.wizard_mode;
|
|
544
|
+
form.description = formSchema.description;
|
|
523
545
|
|
|
524
546
|
// 以下为将解析字段存储的功能,为将解析字段存储至数据库forms表的功能
|
|
525
547
|
let updatedForms = [];
|
|
@@ -22,13 +22,15 @@ module.exports = {
|
|
|
22
22
|
},
|
|
23
23
|
|
|
24
24
|
afterDelete: async function () {
|
|
25
|
-
const {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
const { previousDoc } = this;
|
|
26
|
+
if(previousDoc){
|
|
27
|
+
let recordIds = previousDoc.record_ids;
|
|
28
|
+
if (recordIds && recordIds.length == 1) {
|
|
29
|
+
let recordObjName = recordIds[0].o;
|
|
30
|
+
let recordId = recordIds[0].ids[0];
|
|
31
|
+
if (recordObjName && recordId) {
|
|
32
|
+
await objectql.getObject(recordObjName).update(recordId, { "instances": null, "instance_state": null, "locked": false })
|
|
33
|
+
}
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
36
|
},
|
|
@@ -134,7 +134,7 @@ async function updateForm(formId, form, forms, flows, currentUserId) {
|
|
|
134
134
|
let insCount = await instancesCollection.countDocuments({
|
|
135
135
|
space: spaceId,
|
|
136
136
|
form: formId,
|
|
137
|
-
|
|
137
|
+
form_version: form['current']['id'] || form['current']['_id']
|
|
138
138
|
});
|
|
139
139
|
if (insCount > 0) {
|
|
140
140
|
pass = true;
|
|
@@ -144,7 +144,7 @@ async function updateForm(formId, form, forms, flows, currentUserId) {
|
|
|
144
144
|
let recordsCount = await recordsCollection.countDocuments({
|
|
145
145
|
space: spaceId,
|
|
146
146
|
form: formId,
|
|
147
|
-
|
|
147
|
+
form_version: form['current']['id'] || form['current']['_id']
|
|
148
148
|
});
|
|
149
149
|
if (recordsCount > 0) {
|
|
150
150
|
pass = true;
|
|
@@ -221,6 +221,10 @@ async function updateForm(formId, form, forms, flows, currentUserId) {
|
|
|
221
221
|
current.fields = _formatFieldsID(form["current"]["fields"]);
|
|
222
222
|
current.form_script = form["current"]["form_script"];
|
|
223
223
|
current.name_forumla = form["current"]["name_forumla"];
|
|
224
|
+
current.style = form["current"]["style"];
|
|
225
|
+
current.mode = form["current"]["mode"];
|
|
226
|
+
current.wizard_mode = form["current"]["wizard_mode"];
|
|
227
|
+
current.amis_schema = form["current"]["amis_schema"]
|
|
224
228
|
|
|
225
229
|
formUpdateObj.$set = {
|
|
226
230
|
'current': current,
|
|
@@ -228,6 +232,9 @@ async function updateForm(formId, form, forms, flows, currentUserId) {
|
|
|
228
232
|
'modified': now,
|
|
229
233
|
'modified_by': currentUserId,
|
|
230
234
|
'is_valid': form["is_valid"],
|
|
235
|
+
'style': form["style"],
|
|
236
|
+
'mode': form["mode"],
|
|
237
|
+
'wizard_mode': form["wizard_mode"],
|
|
231
238
|
'description': form["description"],
|
|
232
239
|
'help_text': form["help_text"],
|
|
233
240
|
'error_message': form["error_message"],
|
|
@@ -293,7 +300,7 @@ async function isSpaceAdmin(spaceId, userId, roles) {
|
|
|
293
300
|
}
|
|
294
301
|
}
|
|
295
302
|
|
|
296
|
-
async function makeSteps(userId, fields = []) {
|
|
303
|
+
async function makeSteps(userId, fields = [], language) {
|
|
297
304
|
let blank_ayy = [];
|
|
298
305
|
let stepEnd = _makeNewID();
|
|
299
306
|
let steps = [];
|
|
@@ -303,7 +310,7 @@ async function makeSteps(userId, fields = []) {
|
|
|
303
310
|
start_step.approver_roles = blank_ayy;
|
|
304
311
|
start_step.approver_users = blank_ayy;
|
|
305
312
|
start_step.fields_modifiable = blank_ayy;
|
|
306
|
-
start_step.name = '开始';
|
|
313
|
+
start_step.name = language === 'zh-CN' ? '开始' : 'Start';
|
|
307
314
|
start_step.step_type = "start";
|
|
308
315
|
start_step.posx = -1;
|
|
309
316
|
start_step.posy = -1;
|
|
@@ -336,7 +343,7 @@ async function makeSteps(userId, fields = []) {
|
|
|
336
343
|
end_step.approver_users = blank_ayy;
|
|
337
344
|
end_step.fields_modifiable = blank_ayy;
|
|
338
345
|
end_step.lines = blank_ayy;
|
|
339
|
-
end_step.name = '结束';
|
|
346
|
+
end_step.name = language === 'zh-CN' ? '结束' : 'End';
|
|
340
347
|
end_step.step_type = "end";
|
|
341
348
|
end_step.posx = -1;
|
|
342
349
|
end_step.posy = -1;
|
package/package.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steedos-labs/plugin-workflow",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.1-beta.1",
|
|
4
4
|
"main": "package.service.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"build": "tsc && webpack --config webpack.config.js",
|
|
8
7
|
"build:watch": "tsc --watch",
|
|
9
|
-
"release": "
|
|
8
|
+
"release": "npm publish --registry https://registry.npmjs.org && open https://npmmirror.com/sync/@steedos-labs/plugin-workflow && cnpm sync @steedos-labs/plugin-workflow"
|
|
10
9
|
},
|
|
11
10
|
"dependencies": {
|
|
12
11
|
"graphql-parse-resolve-info": "^4.12.3",
|
|
13
12
|
"he": "1.2.0",
|
|
14
13
|
"lodash": "^4.17.21",
|
|
15
14
|
"node-schedule": "^2.0.0",
|
|
16
|
-
"formidable": "2.0.1"
|
|
15
|
+
"formidable": "2.0.1",
|
|
16
|
+
"jszip": "3.10.1"
|
|
17
17
|
},
|
|
18
18
|
"private": false,
|
|
19
19
|
"publishConfig": {
|
package/package.service.js
CHANGED
|
@@ -5,6 +5,7 @@ const packageLoader = require('@steedos/service-package-loader');
|
|
|
5
5
|
|
|
6
6
|
const rests = require('./src/rests');
|
|
7
7
|
const InstanceRecordQueue = require('./src/instance_record_queue')
|
|
8
|
+
const WebhookQueue = require('./src/webhook_queue')
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* @typedef {import('moleculer').Context} Context Moleculer's Context
|
|
@@ -20,7 +21,7 @@ module.exports = {
|
|
|
20
21
|
$package: {
|
|
21
22
|
name: serviceName,
|
|
22
23
|
path: __dirname,
|
|
23
|
-
isPackage:
|
|
24
|
+
isPackage: false
|
|
24
25
|
}
|
|
25
26
|
},
|
|
26
27
|
/**
|
|
@@ -124,12 +125,25 @@ module.exports = {
|
|
|
124
125
|
* Service started lifecycle event handler
|
|
125
126
|
*/
|
|
126
127
|
async started(ctx) {
|
|
127
|
-
if((process.env.STEEDOS_CRON_ENABLED === true || process.env.STEEDOS_CRON_ENABLED === 'true')
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
128
|
+
if((process.env.STEEDOS_CRON_ENABLED === true || process.env.STEEDOS_CRON_ENABLED === 'true')){
|
|
129
|
+
|
|
130
|
+
if (process.env.STEEDOS_CRON_INSTANCERECORDQUEUE_INTERVAL) {
|
|
131
|
+
InstanceRecordQueue.Configure({
|
|
132
|
+
sendInterval: process.env.STEEDOS_CRON_INSTANCERECORDQUEUE_INTERVAL,
|
|
133
|
+
sendBatchSize: 10,
|
|
134
|
+
keepDocs: true
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// webhooks 流程触发器 队列
|
|
139
|
+
if (process.env.STEEDOS_CRON_WEBHOOKQUEUE_INTERVAL) {
|
|
140
|
+
await WebhookQueue.Configure({
|
|
141
|
+
sendInterval: process.env.STEEDOS_CRON_WEBHOOKQUEUE_INTERVAL,
|
|
142
|
+
sendBatchSize: 10,
|
|
143
|
+
keepDocs: false
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
|
|
133
147
|
}
|
|
134
148
|
},
|
|
135
149
|
|
|
@@ -926,7 +926,7 @@ InstanceRecordQueue.syncRelatedObjectsValue = async function (mainRecordId, rela
|
|
|
926
926
|
}
|
|
927
927
|
}
|
|
928
928
|
//清理申请单上被删除子表记录对应的相关表记录
|
|
929
|
-
for (
|
|
929
|
+
for (let [tableCode, tableIds] of Object.entries(tableMap)) {
|
|
930
930
|
tableIds = _.compact(tableIds);
|
|
931
931
|
var docsForRemove = await objectFind(relatedObject.object_name, { filters: [[relatedObject.foreign_key, '=', mainRecordId], ['_table._code', '=', tableCode]] });
|
|
932
932
|
for (const doc of docsForRemove) {
|
|
@@ -1025,10 +1025,8 @@ InstanceRecordQueue.sendDoc = async function (doc) {
|
|
|
1025
1025
|
|
|
1026
1026
|
// 同步新附件
|
|
1027
1027
|
await InstanceRecordQueue.syncAttach(sync_attachment, insId, record.space, record._id, objectName);
|
|
1028
|
-
console.log(`1031 ===>`)
|
|
1029
1028
|
// 执行公式
|
|
1030
1029
|
await runQuoted(objectName, record._id);
|
|
1031
|
-
console.log(`runQuoted===>`)
|
|
1032
1030
|
} catch (error) {
|
|
1033
1031
|
console.error(error);
|
|
1034
1032
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* @Author: 孙浩林 sunhaolin@steedos.com
|
|
3
3
|
* @Date: 2023-11-04 18:16:15
|
|
4
|
-
* @LastEditors:
|
|
5
|
-
* @LastEditTime:
|
|
4
|
+
* @LastEditors: 殷亮辉 yinlianghui@hotoa.com
|
|
5
|
+
* @LastEditTime: 2025-09-01 16:23:40
|
|
6
6
|
* @FilePath: /steedos-platform-2.3/services/service-workflow/src/rests/api_workflow_instance_batch_remove.js
|
|
7
7
|
* @Description: 草稿箱列表删除按钮调用此接口批量删除草稿
|
|
8
8
|
*/
|
|
@@ -22,6 +22,7 @@ module.exports = {
|
|
|
22
22
|
|
|
23
23
|
const insObj = this.getObject('instances')
|
|
24
24
|
const insTaskObj = this.getObject('instance_tasks')
|
|
25
|
+
const lang = userSession.locale === 'zh-cn' ? 'zh-CN' : 'en';
|
|
25
26
|
|
|
26
27
|
// 先检查是否符合删除条件:1、状态为草稿,2、提交人人为当前用户。
|
|
27
28
|
const insDocs = await insObj.find({
|
|
@@ -62,7 +63,7 @@ module.exports = {
|
|
|
62
63
|
|
|
63
64
|
return {
|
|
64
65
|
'status': 0, // 返回 0,表示当前接口正确返回,否则按错误请求处理
|
|
65
|
-
'msg': '草稿已删除',
|
|
66
|
+
'msg': t('instance.draft_deleted_success', {}, lang),//'草稿已删除',
|
|
66
67
|
'data': {
|
|
67
68
|
}
|
|
68
69
|
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @Author: 孙浩林 sunhaolin@steedos.com
|
|
3
|
+
* @Date: 2025-10-09 15:07:29
|
|
4
|
+
* @LastEditors: 孙浩林 sunhaolin@steedos.com
|
|
5
|
+
* @LastEditTime: 2025-10-10 11:43:04
|
|
6
|
+
* @FilePath: /steedos-plugins/steedos-packages/plugin-workflow/src/webhook_queue.js
|
|
7
|
+
* @Description:
|
|
8
|
+
*/
|
|
9
|
+
const _ = require("lodash");
|
|
10
|
+
const axios = require('axios');
|
|
11
|
+
|
|
12
|
+
const { getCollection } = require("../main/default/utils/collection");
|
|
13
|
+
|
|
14
|
+
const WebhookQueue = {};
|
|
15
|
+
|
|
16
|
+
WebhookQueue.send = async function (options) {
|
|
17
|
+
var currentUser = '<SERVER>'
|
|
18
|
+
var webhook = _.extend({
|
|
19
|
+
createdAt: new Date(),
|
|
20
|
+
createdBy: currentUser
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
webhook.webhook = _.pick(options, ['instance', 'current_approve', 'payload_url', 'content_type', 'action', 'from_user', 'to_users']);
|
|
24
|
+
|
|
25
|
+
webhook.sent = false;
|
|
26
|
+
webhook.sending = 0;
|
|
27
|
+
|
|
28
|
+
return await WebhookQueue.collection.insert(webhook);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
var isConfigured = false;
|
|
32
|
+
var sendWorker = async function (task, interval) {
|
|
33
|
+
|
|
34
|
+
if (WebhookQueue.debug) {
|
|
35
|
+
console.log('WebhookQueue: Send worker started, using interval: ' + interval);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return setInterval(async function () {
|
|
39
|
+
try {
|
|
40
|
+
await task();
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (WebhookQueue.debug) {
|
|
43
|
+
console.log('WebhookQueue: Error while sending: ' + error.message);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}, interval);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/*
|
|
50
|
+
options: {
|
|
51
|
+
// Controls the sending interval
|
|
52
|
+
sendInterval: Match.Optional(Number),
|
|
53
|
+
// Controls the sending batch size per interval
|
|
54
|
+
sendBatchSize: Match.Optional(Number),
|
|
55
|
+
// Allow optional keeping notifications in collection
|
|
56
|
+
keepWebhooks: Match.Optional(Boolean)
|
|
57
|
+
}
|
|
58
|
+
*/
|
|
59
|
+
WebhookQueue.Configure = async function (options) {
|
|
60
|
+
WebhookQueue.collection = await getCollection('_webhook_queue');
|
|
61
|
+
|
|
62
|
+
var self = this;
|
|
63
|
+
options = _.extend({
|
|
64
|
+
sendTimeout: 60000, // Timeout period for webhook send
|
|
65
|
+
}, options);
|
|
66
|
+
|
|
67
|
+
// Block multiple calls
|
|
68
|
+
if (isConfigured) {
|
|
69
|
+
throw new Error('WebhookQueue.Configure should not be called more than once!');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
isConfigured = true;
|
|
73
|
+
|
|
74
|
+
// Add debug info
|
|
75
|
+
if (WebhookQueue.debug) {
|
|
76
|
+
console.log('WebhookQueue.Configure', options);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
self.sendWebhook = async function (webhook) {
|
|
80
|
+
if (WebhookQueue.debug) {
|
|
81
|
+
console.log("sendWebhook");
|
|
82
|
+
console.log(webhook);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const response = await axios({
|
|
87
|
+
method: 'post',
|
|
88
|
+
url: webhook.webhook.payload_url,
|
|
89
|
+
headers: {
|
|
90
|
+
"Content-Type": webhook.webhook.content_type
|
|
91
|
+
},
|
|
92
|
+
data: {
|
|
93
|
+
instance: webhook.webhook.instance,
|
|
94
|
+
current_approve: webhook.webhook.current_approve,
|
|
95
|
+
action: webhook.webhook.action,
|
|
96
|
+
from_user: webhook.webhook.from_user,
|
|
97
|
+
to_users: webhook.webhook.to_users
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (!options.keepWebhooks) {
|
|
102
|
+
// Pr. Default we will remove webhooks
|
|
103
|
+
await WebhookQueue.collection.remove({
|
|
104
|
+
_id: webhook._id,
|
|
105
|
+
errMsg: {
|
|
106
|
+
$exists: false
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
} else {
|
|
110
|
+
// Update the webhook
|
|
111
|
+
await WebhookQueue.collection.updateOne({
|
|
112
|
+
_id: webhook._id
|
|
113
|
+
}, {
|
|
114
|
+
$set: {
|
|
115
|
+
// Mark as sent
|
|
116
|
+
sent: true,
|
|
117
|
+
// Set the sent date
|
|
118
|
+
sentAt: new Date(),
|
|
119
|
+
// Not being sent anymore
|
|
120
|
+
sending: 0
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
}
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error(error);
|
|
127
|
+
await WebhookQueue.collection.updateOne({
|
|
128
|
+
_id: webhook._id
|
|
129
|
+
}, {
|
|
130
|
+
$set: {
|
|
131
|
+
// error message
|
|
132
|
+
errMsg: error
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Universal send function
|
|
140
|
+
var _querySend = async function (options) {
|
|
141
|
+
|
|
142
|
+
if (self.sendWebhook) {
|
|
143
|
+
await self.sendWebhook(options);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
webhook: [options._id]
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
self.serverSend = async function (options) {
|
|
152
|
+
options = options || {};
|
|
153
|
+
return await _querySend(options);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
// This interval will allow only one webhook to be sent at a time, it
|
|
158
|
+
// will check for new webhooks at every `options.sendInterval`
|
|
159
|
+
// (default interval is 15000 ms)
|
|
160
|
+
//
|
|
161
|
+
// It looks in webhooks collection to see if theres any pending
|
|
162
|
+
// webhooks, if so it will try to reserve the pending webhook.
|
|
163
|
+
// If successfully reserved the send is started.
|
|
164
|
+
//
|
|
165
|
+
// If webhook.query is type string, it's assumed to be a json string
|
|
166
|
+
// version of the query selector. Making it able to carry `$` properties in
|
|
167
|
+
// the mongo collection.
|
|
168
|
+
//
|
|
169
|
+
// Pr. default webhooks are removed from the collection after send have
|
|
170
|
+
// completed. Setting `options.keepWebhooks` will update and keep the
|
|
171
|
+
// webhook eg. if needed for historical reasons.
|
|
172
|
+
//
|
|
173
|
+
// After the send have completed a "send" event will be emitted with a
|
|
174
|
+
// status object containing webhook id and the send result object.
|
|
175
|
+
//
|
|
176
|
+
var isSendingWebhook = false;
|
|
177
|
+
|
|
178
|
+
if (options.sendInterval !== null) {
|
|
179
|
+
|
|
180
|
+
// This will require index since we sort webhooks by createdAt
|
|
181
|
+
WebhookQueue.collection.createIndex({
|
|
182
|
+
createdAt: 1
|
|
183
|
+
});
|
|
184
|
+
WebhookQueue.collection.createIndex({
|
|
185
|
+
sent: 1
|
|
186
|
+
});
|
|
187
|
+
WebhookQueue.collection.createIndex({
|
|
188
|
+
sending: 1
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
var sendWebhook = async function (webhook) {
|
|
193
|
+
// Reserve webhook
|
|
194
|
+
var now = +new Date();
|
|
195
|
+
var timeoutAt = now + options.sendTimeout;
|
|
196
|
+
var reserved = await WebhookQueue.collection.updateOne({
|
|
197
|
+
_id: webhook._id,
|
|
198
|
+
sent: false, // xxx: need to make sure this is set on create
|
|
199
|
+
sending: {
|
|
200
|
+
$lt: now
|
|
201
|
+
}
|
|
202
|
+
}, {
|
|
203
|
+
$set: {
|
|
204
|
+
sending: timeoutAt,
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Make sure we only handle webhooks reserved by this
|
|
209
|
+
// instance
|
|
210
|
+
if (reserved) {
|
|
211
|
+
|
|
212
|
+
// Send the webhook
|
|
213
|
+
await WebhookQueue.serverSend(webhook);
|
|
214
|
+
|
|
215
|
+
} // Else could not reserve
|
|
216
|
+
}; // EO sendWebhook
|
|
217
|
+
|
|
218
|
+
await sendWorker(async function () {
|
|
219
|
+
|
|
220
|
+
if (isSendingWebhook) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
// Set send fence
|
|
224
|
+
isSendingWebhook = true;
|
|
225
|
+
|
|
226
|
+
var batchSize = options.sendBatchSize || 1;
|
|
227
|
+
|
|
228
|
+
var now = +new Date();
|
|
229
|
+
|
|
230
|
+
// Find webhooks that are not being or already sent
|
|
231
|
+
var pendingWebhooks = await WebhookQueue.collection.find({
|
|
232
|
+
$and: [
|
|
233
|
+
// Message is not sent
|
|
234
|
+
{
|
|
235
|
+
sent: false
|
|
236
|
+
},
|
|
237
|
+
// And not being sent by other instances
|
|
238
|
+
{
|
|
239
|
+
sending: {
|
|
240
|
+
$lt: now
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
// And no error
|
|
244
|
+
{
|
|
245
|
+
errMsg: {
|
|
246
|
+
$exists: false
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
]
|
|
250
|
+
}).sort({
|
|
251
|
+
createdAt: 1
|
|
252
|
+
}).limit(batchSize).toArray();
|
|
253
|
+
|
|
254
|
+
for (const webhook of pendingWebhooks) {
|
|
255
|
+
try {
|
|
256
|
+
await sendWebhook(webhook);
|
|
257
|
+
} catch (error) {
|
|
258
|
+
console.error(error.stack);
|
|
259
|
+
console.log('WebhookQueue: Could not send doc id: "' + webhook._id + '", Error: ' + error.message);
|
|
260
|
+
await WebhookQueue.collection.updateOne({
|
|
261
|
+
_id: webhook._id
|
|
262
|
+
}, {
|
|
263
|
+
$set: {
|
|
264
|
+
// error message
|
|
265
|
+
errMsg: error.message
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}; // EO forEach
|
|
270
|
+
|
|
271
|
+
// Remove the send fence
|
|
272
|
+
isSendingWebhook = false;
|
|
273
|
+
}, options.sendInterval || 15000); // Default every 15th sec
|
|
274
|
+
|
|
275
|
+
} else {
|
|
276
|
+
if (WebhookQueue.debug) {
|
|
277
|
+
console.log('WebhookQueue: Send server is disabled');
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
module.exports = WebhookQueue;
|