@steedos-labs/plugin-workflow 3.0.55 → 3.0.57
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/designer/dist/amis-renderer/amis-renderer.css +1 -1
- package/designer/dist/amis-renderer/amis-renderer.js +1 -1
- package/designer/dist/assets/index-DIAKZCeb.css +1 -0
- package/designer/dist/assets/{index-CBj3S4BT.js → index-DqalQY4s.js} +173 -173
- package/designer/dist/index.html +2 -2
- package/main/default/manager/uuflow_manager.js +149 -45
- package/main/default/objects/categories/buttons/badge_recalc.button.yml +44 -0
- package/main/default/objects/instance_tasks/listviews/inbox.listview.yml +1 -1
- package/main/default/objects/instance_tasks/listviews/outbox.listview.yml +1 -2
- package/main/default/objects/instances/buttons/instance_related.button.yml +3 -59
- package/main/default/objects/instances/listviews/completed.listview.yml +1 -2
- package/main/default/objects/instances/listviews/draft.listview.yml +1 -2
- package/main/default/objects/instances/listviews/monitor.listview.yml +1 -1
- package/main/default/objects/instances/listviews/pending.listview.yml +1 -2
- package/main/default/pages/page_instance_print.page.amis.json +20 -45
- package/main/default/routes/api_workflow_ai_form_design.router.js +9 -3
- package/main/default/routes/api_workflow_ai_form_design_stream.router.js +9 -3
- package/main/default/routes/api_workflow_instance_forward.router.js +15 -1
- package/main/default/routes/api_workflow_instance_permissions.router.js +2 -2
- package/main/default/routes/api_workflow_nav.router.js +7 -1
- package/main/default/services/instance.service.js +1 -1
- package/main/default/utils/business_hours.js +210 -0
- package/main/default/utils/business_timeout.js +211 -0
- package/package.json +1 -1
- package/package.service.js +9 -1
- package/public/amis-renderer/amis-renderer.css +1 -1
- package/public/amis-renderer/amis-renderer.js +1 -1
- package/public/workflow/index.css +7 -2
- package/src/rests/badgeRecalcConsole.js +593 -0
- package/src/rests/badgeRecalcExecute.js +308 -0
- package/src/rests/index.js +2 -0
- package/src/timeout_auto_submit.js +81 -0
- package/designer/dist/assets/index-BNulYl_s.css +0 -1
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Business timeout calculation utilities.
|
|
4
|
+
* Ported from steedos-platform packages/core/src/holidays/business_timeout.ts
|
|
5
|
+
*
|
|
6
|
+
* Uses @steedos/objectql (already a project dependency) to access holidays and
|
|
7
|
+
* business_hours objects, instead of @steedos/core.
|
|
8
|
+
*/
|
|
9
|
+
const moment = require('moment');
|
|
10
|
+
const _ = require('lodash');
|
|
11
|
+
const { getObject } = require('@steedos/objectql');
|
|
12
|
+
const {
|
|
13
|
+
BusinessHoursCheckedType,
|
|
14
|
+
getBusinessHoursPerDay,
|
|
15
|
+
computeIsBusinessDate,
|
|
16
|
+
computeNextBusinessDate
|
|
17
|
+
} = require('./business_hours');
|
|
18
|
+
|
|
19
|
+
// Simple in-memory cache for holidays and business hours with TTL
|
|
20
|
+
const CACHE_TTL_MS = 10 * 60 * 1000; // 10 minutes
|
|
21
|
+
const _holidaysCache = {}; // key: `${spaceId}-${year}`, value: { values: [...], timestamp }
|
|
22
|
+
const _businessHoursCache = {}; // key: spaceId, value: { records: [...], timestamp }
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 从数据库中取出指定工作区内的节假日数据,支持缓存
|
|
26
|
+
* @param {string} spaceId
|
|
27
|
+
* @param {Array<number>} years
|
|
28
|
+
*/
|
|
29
|
+
async function getHolidays(spaceId, years) {
|
|
30
|
+
const holidaysFields = ["name", "type", "date", "adjusted_to", "space"];
|
|
31
|
+
for (const year of years) {
|
|
32
|
+
const cacheKey = `${spaceId}-${year}`;
|
|
33
|
+
const cached = _holidaysCache[cacheKey];
|
|
34
|
+
if (!cached || (Date.now() - cached.timestamp > CACHE_TTL_MS)) {
|
|
35
|
+
const yearStartDate = moment.utc(`${year}-01-01T00:00:00Z`).toDate();
|
|
36
|
+
const yearEndDate = moment.utc(`${year}-12-31T23:59:59Z`).toDate();
|
|
37
|
+
const objectHolidays = getObject("holidays");
|
|
38
|
+
const holidaysRecords = await objectHolidays.find({
|
|
39
|
+
filters: [["space", "=", spaceId], ["date", "between", [yearStartDate, yearEndDate]]],
|
|
40
|
+
fields: holidaysFields
|
|
41
|
+
});
|
|
42
|
+
_holidaysCache[cacheKey] = {
|
|
43
|
+
space: spaceId,
|
|
44
|
+
year: year,
|
|
45
|
+
values: (holidaysRecords && holidaysRecords.length) ? holidaysRecords : [],
|
|
46
|
+
timestamp: Date.now()
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const result = [];
|
|
51
|
+
for (const year of years) {
|
|
52
|
+
const cacheKey = `${spaceId}-${year}`;
|
|
53
|
+
if (_holidaysCache[cacheKey] && _holidaysCache[cacheKey].values) {
|
|
54
|
+
result.push(..._holidaysCache[cacheKey].values);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 从数据库中取出指定工作区内的默认工作时间数据,支持缓存
|
|
62
|
+
* @param {string} spaceId
|
|
63
|
+
*/
|
|
64
|
+
async function getDefaultBusinessHours(spaceId) {
|
|
65
|
+
const cached = _businessHoursCache[spaceId];
|
|
66
|
+
if (cached && (Date.now() - cached.timestamp <= CACHE_TTL_MS)) {
|
|
67
|
+
return cached.records;
|
|
68
|
+
}
|
|
69
|
+
const objectBusinessHours = getObject("business_hours");
|
|
70
|
+
const records = await objectBusinessHours.find({
|
|
71
|
+
filters: [["space", "=", spaceId], ["is_default", "=", true]],
|
|
72
|
+
fields: ["name", "start", "end", "lunch_start", "lunch_end", "utc_offset", "working_days", "space"]
|
|
73
|
+
});
|
|
74
|
+
if (records && records.length) {
|
|
75
|
+
_businessHoursCache[spaceId] = { records, timestamp: Date.now() };
|
|
76
|
+
}
|
|
77
|
+
return records || [];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 计算以某个时间点开始,超时几个小时后,超时时间点是什么时候 (纯计算,不查数据库)
|
|
82
|
+
* @param {Date} start
|
|
83
|
+
* @param {number} timeoutHours
|
|
84
|
+
* @param {Array} holidays
|
|
85
|
+
* @param {object} businessHours
|
|
86
|
+
* @param {number} digitsForHours
|
|
87
|
+
* @returns {Date}
|
|
88
|
+
*/
|
|
89
|
+
function computeTimeoutDateWithoutHolidays(start, timeoutHours, holidays, businessHours, digitsForHours = 2) {
|
|
90
|
+
if (timeoutHours <= 0) {
|
|
91
|
+
return start;
|
|
92
|
+
}
|
|
93
|
+
const utcOffset = businessHours.utc_offset;
|
|
94
|
+
const businessHoursPerDay = getBusinessHoursPerDay(businessHours, digitsForHours);
|
|
95
|
+
const startBhct = computeIsBusinessDate(start, holidays, businessHours, digitsForHours);
|
|
96
|
+
let offsetMinutes = 0;
|
|
97
|
+
let startMoment = moment.utc(start);
|
|
98
|
+
let startClosingMoment = moment.utc(start);
|
|
99
|
+
if (startBhct <= 0) {
|
|
100
|
+
if (startBhct === BusinessHoursCheckedType.offLunch) {
|
|
101
|
+
startMoment.hours(businessHoursPerDay.lunchEndValue.hours - utcOffset);
|
|
102
|
+
startMoment.minutes(businessHoursPerDay.lunchEndValue.minutes);
|
|
103
|
+
start = startMoment.toDate();
|
|
104
|
+
} else if (startBhct === BusinessHoursCheckedType.offAm) {
|
|
105
|
+
startMoment.hours(businessHoursPerDay.startValue.hours - utcOffset);
|
|
106
|
+
startMoment.minutes(businessHoursPerDay.startValue.minutes);
|
|
107
|
+
start = startMoment.toDate();
|
|
108
|
+
startClosingMoment = moment.utc(start);
|
|
109
|
+
startClosingMoment.hours(businessHoursPerDay.lunchStartValue.hours - utcOffset);
|
|
110
|
+
startClosingMoment.minutes(businessHoursPerDay.lunchStartValue.minutes);
|
|
111
|
+
offsetMinutes = startClosingMoment.diff(startMoment, 'minute');
|
|
112
|
+
startMoment.hours(businessHoursPerDay.lunchEndValue.hours - utcOffset);
|
|
113
|
+
startMoment.minutes(businessHoursPerDay.lunchEndValue.minutes);
|
|
114
|
+
start = startMoment.toDate();
|
|
115
|
+
} else {
|
|
116
|
+
const nextBusinessDate = computeNextBusinessDate(start, holidays, businessHours, digitsForHours);
|
|
117
|
+
start = nextBusinessDate.start;
|
|
118
|
+
startMoment = moment.utc(start);
|
|
119
|
+
startClosingMoment = moment.utc(start);
|
|
120
|
+
startClosingMoment.hours(businessHoursPerDay.lunchStartValue.hours - utcOffset);
|
|
121
|
+
startClosingMoment.minutes(businessHoursPerDay.lunchStartValue.minutes);
|
|
122
|
+
offsetMinutes = startClosingMoment.diff(startMoment, 'minute');
|
|
123
|
+
startMoment.hours(businessHoursPerDay.lunchEndValue.hours - utcOffset);
|
|
124
|
+
startMoment.minutes(businessHoursPerDay.lunchEndValue.minutes);
|
|
125
|
+
start = startMoment.toDate();
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
if (startBhct === BusinessHoursCheckedType.onAm) {
|
|
129
|
+
startClosingMoment.hours(businessHoursPerDay.lunchStartValue.hours - utcOffset);
|
|
130
|
+
startClosingMoment.minutes(businessHoursPerDay.lunchStartValue.minutes);
|
|
131
|
+
offsetMinutes = startClosingMoment.diff(startMoment, 'minute');
|
|
132
|
+
startMoment.hours(businessHoursPerDay.lunchEndValue.hours - utcOffset);
|
|
133
|
+
startMoment.minutes(businessHoursPerDay.lunchEndValue.minutes);
|
|
134
|
+
start = startMoment.toDate();
|
|
135
|
+
} else {
|
|
136
|
+
startMoment = moment.utc(start);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
startClosingMoment.hours(businessHoursPerDay.endValue.hours - utcOffset);
|
|
140
|
+
startClosingMoment.minutes(businessHoursPerDay.endValue.minutes);
|
|
141
|
+
offsetMinutes += startClosingMoment.diff(startMoment, 'minute');
|
|
142
|
+
const timeoutMinutes = timeoutHours * 60;
|
|
143
|
+
if (timeoutMinutes <= offsetMinutes) {
|
|
144
|
+
let subMinutes = offsetMinutes - timeoutMinutes;
|
|
145
|
+
if (subMinutes > 0) {
|
|
146
|
+
if (subMinutes >= businessHoursPerDay.computedPmMinutes) {
|
|
147
|
+
subMinutes += businessHoursPerDay.computedLunchMinutes;
|
|
148
|
+
}
|
|
149
|
+
startClosingMoment.subtract(subMinutes, 'm');
|
|
150
|
+
}
|
|
151
|
+
return startClosingMoment.toDate();
|
|
152
|
+
} else {
|
|
153
|
+
let nextMoment = startClosingMoment;
|
|
154
|
+
for (let i = 0; offsetMinutes < timeoutMinutes; i++) {
|
|
155
|
+
const nextBusinessDate = computeNextBusinessDate(nextMoment.toDate(), holidays, businessHours, digitsForHours);
|
|
156
|
+
if (nextBusinessDate) {
|
|
157
|
+
nextMoment = moment.utc(nextBusinessDate.end);
|
|
158
|
+
offsetMinutes += businessHoursPerDay.computedMinutes;
|
|
159
|
+
} else {
|
|
160
|
+
throw new Error("computeTimeoutDateWithoutHolidays:Maximum number of calls, The number of days in holidays may exceed 365.");
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (offsetMinutes > timeoutMinutes) {
|
|
164
|
+
let subMinutes = offsetMinutes - timeoutMinutes;
|
|
165
|
+
if (subMinutes >= businessHoursPerDay.computedPmMinutes) {
|
|
166
|
+
subMinutes += businessHoursPerDay.computedLunchMinutes;
|
|
167
|
+
}
|
|
168
|
+
nextMoment.subtract(subMinutes, 'm');
|
|
169
|
+
return nextMoment.toDate();
|
|
170
|
+
}
|
|
171
|
+
return nextMoment.toDate();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 计算在某个工作区下的节假日、工作时间数据基础上,以某个时间点开始,超时几个小时后,超时时间点是什么时候
|
|
177
|
+
* @param {Date} start
|
|
178
|
+
* @param {number} timeoutHours
|
|
179
|
+
* @param {string} spaceId
|
|
180
|
+
* @param {number} digitsForHours
|
|
181
|
+
* @returns {Promise<Date>}
|
|
182
|
+
*/
|
|
183
|
+
async function getTimeoutDateWithoutHolidays(start, timeoutHours, spaceId, digitsForHours = 2) {
|
|
184
|
+
let enableHolidays = false;
|
|
185
|
+
try {
|
|
186
|
+
const { getSteedosConfig } = require('@steedos/objectql');
|
|
187
|
+
const config = getSteedosConfig();
|
|
188
|
+
enableHolidays = !!config.enable_holidays;
|
|
189
|
+
} catch (e) {
|
|
190
|
+
// If config is not available, treat as holidays not enabled
|
|
191
|
+
}
|
|
192
|
+
if (!enableHolidays) {
|
|
193
|
+
return moment(start).add(timeoutHours, 'h').toDate();
|
|
194
|
+
}
|
|
195
|
+
const defaultBusinessHoursRecords = await getDefaultBusinessHours(spaceId);
|
|
196
|
+
const defaultBusinessHoursRecord = defaultBusinessHoursRecords && defaultBusinessHoursRecords[0];
|
|
197
|
+
if (!defaultBusinessHoursRecord) {
|
|
198
|
+
// 未配置工作时间,直接按未开启enable_holidays处理
|
|
199
|
+
return moment(start).add(timeoutHours, 'h').toDate();
|
|
200
|
+
}
|
|
201
|
+
const startUTCYear = start.getUTCFullYear();
|
|
202
|
+
const holidaysRecords = await getHolidays(spaceId, [startUTCYear, startUTCYear + 1]);
|
|
203
|
+
return computeTimeoutDateWithoutHolidays(start, timeoutHours, holidaysRecords, defaultBusinessHoursRecord, digitsForHours);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
module.exports = {
|
|
207
|
+
getHolidays,
|
|
208
|
+
getDefaultBusinessHours,
|
|
209
|
+
getTimeoutDateWithoutHolidays,
|
|
210
|
+
computeTimeoutDateWithoutHolidays
|
|
211
|
+
};
|
package/package.json
CHANGED
package/package.service.js
CHANGED
|
@@ -6,6 +6,7 @@ const packageLoader = require('@steedos/service-package-loader');
|
|
|
6
6
|
const rests = require('./src/rests');
|
|
7
7
|
const InstanceRecordQueue = require('./src/instance_record_queue')
|
|
8
8
|
const WebhookQueue = require('./src/webhook_queue')
|
|
9
|
+
const TimeoutAutoSubmit = require('./src/timeout_auto_submit')
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @typedef {import('moleculer').Context} Context Moleculer's Context
|
|
@@ -158,6 +159,13 @@ module.exports = {
|
|
|
158
159
|
})
|
|
159
160
|
}
|
|
160
161
|
|
|
162
|
+
// 超时自动提交
|
|
163
|
+
if (process.env.STEEDOS_CRON_TIMEOUT_AUTO_SUBMIT_INTERVAL) {
|
|
164
|
+
TimeoutAutoSubmit.Configure({
|
|
165
|
+
sendInterval: process.env.STEEDOS_CRON_TIMEOUT_AUTO_SUBMIT_INTERVAL
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
|
|
161
169
|
}
|
|
162
170
|
},
|
|
163
171
|
|
|
@@ -165,6 +173,6 @@ module.exports = {
|
|
|
165
173
|
* Service stopped lifecycle event handler
|
|
166
174
|
*/
|
|
167
175
|
async stopped() {
|
|
168
|
-
|
|
176
|
+
TimeoutAutoSubmit.Stop();
|
|
169
177
|
}
|
|
170
178
|
};
|