@steedos-labs/plugin-workflow 3.0.36 → 3.0.37
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-BVsRCpLO.css +1 -0
- package/designer/dist/assets/index-DkVFo_s4.js +757 -0
- package/designer/dist/index.html +2 -2
- package/main/default/manager/uuflow_manager.js +401 -0
- package/main/default/objects/instances/buttons/instance_save.button.yml +1 -1
- package/main/default/objects/workflow_designer_backups.object.yml +62 -0
- package/main/default/routes/api_workflow_instance_return.router.js +7 -0
- package/main/default/utils/designerManager.js +2 -0
- package/package.json +1 -1
- package/public/amis-renderer/amis-renderer.css +1 -1
- package/public/amis-renderer/amis-renderer.js +1 -1
- package/src/rests/approvalCommentsConsole.js +19 -5
- package/designer/dist/assets/index-CzP9-MzW.css +0 -1
- package/designer/dist/assets/index-r2EtJdFh.js +0 -757
package/designer/dist/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/api/workflow/designer-v2/vite.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>designer</title>
|
|
8
|
-
<script type="module" crossorigin src="/api/workflow/designer-v2/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/api/workflow/designer-v2/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/api/workflow/designer-v2/assets/index-DkVFo_s4.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/api/workflow/designer-v2/assets/index-BVsRCpLO.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|
|
@@ -1496,6 +1496,41 @@ UUFlowManager.workflow_engine = async function (approve_from_client, current_use
|
|
|
1496
1496
|
}
|
|
1497
1497
|
console.log(`[workflow/engine] [workflow_engine] update instance(second part) took ${Date.now() - t8}ms`);
|
|
1498
1498
|
|
|
1499
|
+
// Skip Processed: Update processed_user_ids based on step type and judge
|
|
1500
|
+
const t8_skip = Date.now();
|
|
1501
|
+
if (step_type === 'sign' && judge === 'approved') {
|
|
1502
|
+
// Sign node approval - add current user to processed set
|
|
1503
|
+
await instancesCollection.updateOne(
|
|
1504
|
+
{ _id: instance_id },
|
|
1505
|
+
{ $addToSet: { processed_user_ids: current_user } }
|
|
1506
|
+
);
|
|
1507
|
+
} else if (step_type === 'sign' && judge === 'rejected') {
|
|
1508
|
+
// Sign node rejection - reset processed set to applicant only
|
|
1509
|
+
await instancesCollection.updateOne(
|
|
1510
|
+
{ _id: instance_id },
|
|
1511
|
+
{ $set: { processed_user_ids: [updatedInstance.applicant] } }
|
|
1512
|
+
);
|
|
1513
|
+
} else if (step_type === 'counterSign') {
|
|
1514
|
+
// Check if the counterSign trace is now fully finished
|
|
1515
|
+
const csInstance = await UUFlowManager.getInstance(instance_id);
|
|
1516
|
+
const csTrace = _.find(csInstance.traces, t => t._id === trace_id);
|
|
1517
|
+
if (csTrace && csTrace.is_finished) {
|
|
1518
|
+
// All finished - add all non-cc/distribute approved users to processed set
|
|
1519
|
+
const approvedUsers = csTrace.approves
|
|
1520
|
+
.filter(a => isNormalApprove(a) && a.is_finished && a.judge !== 'skipped')
|
|
1521
|
+
.map(a => a.user);
|
|
1522
|
+
if (approvedUsers.length > 0) {
|
|
1523
|
+
await instancesCollection.updateOne(
|
|
1524
|
+
{ _id: instance_id },
|
|
1525
|
+
{ $addToSet: { processed_user_ids: { $each: approvedUsers } } }
|
|
1526
|
+
);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
// Skip Processed: Check if next step should be auto-skipped
|
|
1531
|
+
await UUFlowManager.handleSkipProcessed(instance_id, flow);
|
|
1532
|
+
console.log(`[workflow/engine] [workflow_engine] handleSkipProcessed took ${Date.now() - t8_skip}ms`);
|
|
1533
|
+
|
|
1499
1534
|
const t9 = Date.now();
|
|
1500
1535
|
// Get final updated instance
|
|
1501
1536
|
const finalInstance = await UUFlowManager.getInstance(instance_id);
|
|
@@ -1549,6 +1584,360 @@ UUFlowManager.workflow_engine = async function (approve_from_client, current_use
|
|
|
1549
1584
|
return finalInstance;
|
|
1550
1585
|
};
|
|
1551
1586
|
|
|
1587
|
+
// Skip processed constants and helpers
|
|
1588
|
+
const SKIP_DESCRIPTION = '滑步跳过:已在之前步骤处理过';
|
|
1589
|
+
|
|
1590
|
+
function isNormalApprove(approve) {
|
|
1591
|
+
return !approve.type || approve.type === 'draft' || approve.type === 'reassign';
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
/**
|
|
1595
|
+
* Handle skip processed (滑步) logic for workflow instances.
|
|
1596
|
+
* When a new trace is created for the next step, check if the handlers
|
|
1597
|
+
* should be automatically skipped because they are already in the processed_user_ids set.
|
|
1598
|
+
*
|
|
1599
|
+
* @param {String} instance_id - Instance ID
|
|
1600
|
+
* @param {Object} flow - Flow object
|
|
1601
|
+
* @param {Number} maxDepth - Maximum recursion depth to prevent infinite loops.
|
|
1602
|
+
* Default 20 is generous since typical workflows have fewer than 20 sequential steps.
|
|
1603
|
+
* This prevents infinite loops caused by circular flow definitions.
|
|
1604
|
+
*/
|
|
1605
|
+
UUFlowManager.handleSkipProcessed = async function (instance_id, flow, maxDepth = 20) {
|
|
1606
|
+
if (maxDepth <= 0) {
|
|
1607
|
+
console.warn('[workflow/engine] [handleSkipProcessed] Max recursion depth reached');
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
const instancesCollection = await getCollection('instances');
|
|
1612
|
+
const instance = await UUFlowManager.getInstance(instance_id);
|
|
1613
|
+
|
|
1614
|
+
// Only process pending instances
|
|
1615
|
+
if (instance.state !== 'pending') return;
|
|
1616
|
+
|
|
1617
|
+
const processedUserIds = instance.processed_user_ids || [];
|
|
1618
|
+
if (processedUserIds.length === 0) return;
|
|
1619
|
+
|
|
1620
|
+
// Get the last trace (the one just created for the next step)
|
|
1621
|
+
const lastTrace = _.last(instance.traces);
|
|
1622
|
+
if (!lastTrace || lastTrace.is_finished) return;
|
|
1623
|
+
|
|
1624
|
+
// Get the step definition for this trace
|
|
1625
|
+
let step;
|
|
1626
|
+
try {
|
|
1627
|
+
step = UUFlowManager.getStep(instance, flow, lastTrace.step);
|
|
1628
|
+
} catch (e) {
|
|
1629
|
+
return;
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
// Only skip for sign and counterSign nodes
|
|
1633
|
+
if (!['sign', 'counterSign'].includes(step.step_type)) return;
|
|
1634
|
+
|
|
1635
|
+
// Check if skip_enabled (default true)
|
|
1636
|
+
if (step.skip_enabled === false) return;
|
|
1637
|
+
|
|
1638
|
+
// Get non-cc/distribute approves
|
|
1639
|
+
const normalApproves = lastTrace.approves.filter(isNormalApprove);
|
|
1640
|
+
|
|
1641
|
+
if (normalApproves.length === 0) return;
|
|
1642
|
+
|
|
1643
|
+
// Check which users should be skipped
|
|
1644
|
+
const shouldSkipApproves = normalApproves.filter(a => processedUserIds.includes(a.user));
|
|
1645
|
+
|
|
1646
|
+
if (shouldSkipApproves.length === 0) return;
|
|
1647
|
+
|
|
1648
|
+
const now = new Date();
|
|
1649
|
+
const traceIdx = instance.traces.length - 1;
|
|
1650
|
+
|
|
1651
|
+
if (shouldSkipApproves.length === normalApproves.length) {
|
|
1652
|
+
// All users should be skipped - skip entire node
|
|
1653
|
+
console.log(`[workflow/engine] [handleSkipProcessed] Skipping entire step "${step.name}" - all handlers already processed`);
|
|
1654
|
+
|
|
1655
|
+
// Determine the next step from this skipped step
|
|
1656
|
+
// For sign: use "approved" lines; for counterSign: use "submitted" lines
|
|
1657
|
+
const lineState = step.step_type === 'sign' ? 'approved' : 'submitted';
|
|
1658
|
+
const lines = (step.lines || []).filter(l => l.state === lineState);
|
|
1659
|
+
|
|
1660
|
+
if (lines.length === 0) {
|
|
1661
|
+
console.warn('[workflow/engine] [handleSkipProcessed] No outgoing lines found for skipped step');
|
|
1662
|
+
return;
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
const next_step_id = lines[0].to_step;
|
|
1666
|
+
let next_step;
|
|
1667
|
+
try {
|
|
1668
|
+
next_step = UUFlowManager.getStep(instance, flow, next_step_id);
|
|
1669
|
+
} catch (e) {
|
|
1670
|
+
return;
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
// Mark current trace as finished with all approves as skipped
|
|
1674
|
+
const setObj = {};
|
|
1675
|
+
setObj[`traces.${traceIdx}.is_finished`] = true;
|
|
1676
|
+
setObj[`traces.${traceIdx}.finish_date`] = now;
|
|
1677
|
+
setObj[`traces.${traceIdx}.judge`] = 'skipped';
|
|
1678
|
+
|
|
1679
|
+
for (let h = 0; h < lastTrace.approves.length; h++) {
|
|
1680
|
+
const approve = lastTrace.approves[h];
|
|
1681
|
+
if (isNormalApprove(approve)) {
|
|
1682
|
+
setObj[`traces.${traceIdx}.approves.${h}.is_finished`] = true;
|
|
1683
|
+
setObj[`traces.${traceIdx}.approves.${h}.finish_date`] = now;
|
|
1684
|
+
setObj[`traces.${traceIdx}.approves.${h}.judge`] = 'skipped';
|
|
1685
|
+
setObj[`traces.${traceIdx}.approves.${h}.is_read`] = true;
|
|
1686
|
+
setObj[`traces.${traceIdx}.approves.${h}.read_date`] = now;
|
|
1687
|
+
setObj[`traces.${traceIdx}.approves.${h}.cost_time`] = 0;
|
|
1688
|
+
setObj[`traces.${traceIdx}.approves.${h}.description`] = SKIP_DESCRIPTION;
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
// Collect skipped user IDs to add to processed set
|
|
1693
|
+
const skippedUserIds = shouldSkipApproves.map(a => a.user);
|
|
1694
|
+
const skippedHandlers = shouldSkipApproves.map(a => a.handler);
|
|
1695
|
+
|
|
1696
|
+
if (next_step.step_type === 'end') {
|
|
1697
|
+
// Flow ends
|
|
1698
|
+
const newEndTrace = {
|
|
1699
|
+
_id: _makeNewID(),
|
|
1700
|
+
instance: instance_id,
|
|
1701
|
+
previous_trace_ids: [lastTrace._id],
|
|
1702
|
+
is_finished: true,
|
|
1703
|
+
step: next_step_id,
|
|
1704
|
+
name: next_step.name,
|
|
1705
|
+
start_date: now,
|
|
1706
|
+
finish_date: now
|
|
1707
|
+
};
|
|
1708
|
+
|
|
1709
|
+
setObj.state = 'completed';
|
|
1710
|
+
setObj.finish_date = now;
|
|
1711
|
+
setObj.inbox_users = [];
|
|
1712
|
+
setObj.current_step_name = next_step.name;
|
|
1713
|
+
setObj.final_decision = 'approved';
|
|
1714
|
+
setObj.current_step_auto_submit = false;
|
|
1715
|
+
setObj.modified = now;
|
|
1716
|
+
|
|
1717
|
+
// Split into two updates to avoid MongoDB conflict on 'traces' path
|
|
1718
|
+
// 1) $set trace sub-paths (traces.X.Y)
|
|
1719
|
+
const traceSetObj1 = {};
|
|
1720
|
+
const otherSetObj1 = {};
|
|
1721
|
+
for (const key of Object.keys(setObj)) {
|
|
1722
|
+
if (key.startsWith('traces.')) {
|
|
1723
|
+
traceSetObj1[key] = setObj[key];
|
|
1724
|
+
} else {
|
|
1725
|
+
otherSetObj1[key] = setObj[key];
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
if (Object.keys(traceSetObj1).length > 0) {
|
|
1730
|
+
await instancesCollection.updateOne(
|
|
1731
|
+
{ _id: instance_id },
|
|
1732
|
+
{ $set: traceSetObj1 }
|
|
1733
|
+
);
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
// 2) $push new trace + $set non-trace fields + $addToSet
|
|
1737
|
+
await instancesCollection.updateOne(
|
|
1738
|
+
{ _id: instance_id },
|
|
1739
|
+
{
|
|
1740
|
+
$set: otherSetObj1,
|
|
1741
|
+
$push: { traces: newEndTrace },
|
|
1742
|
+
$addToSet: {
|
|
1743
|
+
processed_user_ids: { $each: skippedUserIds },
|
|
1744
|
+
outbox_users: { $each: skippedHandlers }
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
);
|
|
1748
|
+
|
|
1749
|
+
// Update instance tasks for skipped approves
|
|
1750
|
+
for (const approve of shouldSkipApproves) {
|
|
1751
|
+
await update_instance_tasks(instance_id, lastTrace._id, approve._id);
|
|
1752
|
+
}
|
|
1753
|
+
} else {
|
|
1754
|
+
// Create new trace for the next step
|
|
1755
|
+
const space_id = instance.space;
|
|
1756
|
+
const next_step_users = await HandlersManager.getHandlers(instance_id, next_step_id, instance.applicant);
|
|
1757
|
+
|
|
1758
|
+
if (!next_step_users || next_step_users.length === 0) {
|
|
1759
|
+
console.warn('[workflow/engine] [handleSkipProcessed] No handlers found for next step after skip');
|
|
1760
|
+
return;
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
// For non-counterSign steps, use only the first handler
|
|
1764
|
+
let usersForTrace = next_step_users.slice();
|
|
1765
|
+
if (next_step.step_type !== 'counterSign' && usersForTrace.length > 1) {
|
|
1766
|
+
usersForTrace = [usersForTrace[0]];
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
const newTrace = {
|
|
1770
|
+
_id: _makeNewID(),
|
|
1771
|
+
instance: instance_id,
|
|
1772
|
+
previous_trace_ids: [lastTrace._id],
|
|
1773
|
+
is_finished: false,
|
|
1774
|
+
step: next_step_id,
|
|
1775
|
+
name: next_step.name,
|
|
1776
|
+
start_date: now,
|
|
1777
|
+
due_date: UUFlowManager.getDueDate(next_step.timeout_hours, space_id),
|
|
1778
|
+
approves: []
|
|
1779
|
+
};
|
|
1780
|
+
|
|
1781
|
+
const usersCollection = await getCollection('users');
|
|
1782
|
+
const spaceUsersCollection = await getCollection('space_users');
|
|
1783
|
+
const updatedValues = await UUFlowManager.getUpdatedValues(instance);
|
|
1784
|
+
|
|
1785
|
+
const traceUserIds = [];
|
|
1786
|
+
for (let idx = 0; idx < usersForTrace.length; idx++) {
|
|
1787
|
+
const next_step_user_id = usersForTrace[idx];
|
|
1788
|
+
const user_info = await usersCollection.findOne(
|
|
1789
|
+
{ _id: next_step_user_id },
|
|
1790
|
+
{ projection: { name: 1 } }
|
|
1791
|
+
);
|
|
1792
|
+
|
|
1793
|
+
if (!user_info) continue;
|
|
1794
|
+
|
|
1795
|
+
const newApprove = {
|
|
1796
|
+
_id: _makeNewID(),
|
|
1797
|
+
instance: instance_id,
|
|
1798
|
+
trace: newTrace._id,
|
|
1799
|
+
is_finished: false,
|
|
1800
|
+
user: next_step_user_id,
|
|
1801
|
+
user_name: user_info.name
|
|
1802
|
+
};
|
|
1803
|
+
|
|
1804
|
+
let handler_id = next_step_user_id;
|
|
1805
|
+
let handler_info = user_info;
|
|
1806
|
+
const agent = await UUFlowManager.getAgent(space_id, next_step_user_id);
|
|
1807
|
+
|
|
1808
|
+
if (agent) {
|
|
1809
|
+
usersForTrace[idx] = agent;
|
|
1810
|
+
handler_id = agent;
|
|
1811
|
+
handler_info = await usersCollection.findOne(
|
|
1812
|
+
{ _id: agent },
|
|
1813
|
+
{ projection: { name: 1 } }
|
|
1814
|
+
);
|
|
1815
|
+
newApprove.agent = agent;
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
newApprove.handler = handler_id;
|
|
1819
|
+
newApprove.handler_name = handler_info.name;
|
|
1820
|
+
|
|
1821
|
+
const next_step_space_user = await spaceUsersCollection.findOne({
|
|
1822
|
+
space: space_id,
|
|
1823
|
+
user: handler_id
|
|
1824
|
+
});
|
|
1825
|
+
|
|
1826
|
+
if (next_step_space_user) {
|
|
1827
|
+
const org_info = await UUFlowManager.getSpaceUserOrgInfo(next_step_space_user);
|
|
1828
|
+
newApprove.handler_organization = org_info.organization;
|
|
1829
|
+
newApprove.handler_organization_name = org_info.organization_name;
|
|
1830
|
+
newApprove.handler_organization_fullname = org_info.organization_fullname;
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
newApprove.start_date = now;
|
|
1834
|
+
newApprove.due_date = newTrace.due_date;
|
|
1835
|
+
newApprove.is_read = false;
|
|
1836
|
+
newApprove.is_error = false;
|
|
1837
|
+
newApprove.values = {};
|
|
1838
|
+
|
|
1839
|
+
UUFlowManager.setRemindInfo(updatedValues, newApprove);
|
|
1840
|
+
newTrace.approves.push(newApprove);
|
|
1841
|
+
traceUserIds.push(handler_id);
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
if (newTrace.approves.length === 0) {
|
|
1845
|
+
console.warn('[workflow/engine] [handleSkipProcessed] No valid approves created for next step');
|
|
1846
|
+
return;
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
setObj.inbox_users = traceUserIds;
|
|
1850
|
+
setObj.current_step_name = next_step.name;
|
|
1851
|
+
setObj.modified = now;
|
|
1852
|
+
|
|
1853
|
+
// Split into two updates to avoid MongoDB conflict on 'traces' path
|
|
1854
|
+
// 1) $set trace sub-paths (traces.X.Y)
|
|
1855
|
+
const traceSetObj2 = {};
|
|
1856
|
+
const otherSetObj2 = {};
|
|
1857
|
+
for (const key of Object.keys(setObj)) {
|
|
1858
|
+
if (key.startsWith('traces.')) {
|
|
1859
|
+
traceSetObj2[key] = setObj[key];
|
|
1860
|
+
} else {
|
|
1861
|
+
otherSetObj2[key] = setObj[key];
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
if (Object.keys(traceSetObj2).length > 0) {
|
|
1866
|
+
await instancesCollection.updateOne(
|
|
1867
|
+
{ _id: instance_id },
|
|
1868
|
+
{ $set: traceSetObj2 }
|
|
1869
|
+
);
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
// 2) $push new trace + $set non-trace fields + $addToSet
|
|
1873
|
+
await instancesCollection.updateOne(
|
|
1874
|
+
{ _id: instance_id },
|
|
1875
|
+
{
|
|
1876
|
+
$set: otherSetObj2,
|
|
1877
|
+
$push: { traces: newTrace },
|
|
1878
|
+
$addToSet: {
|
|
1879
|
+
processed_user_ids: { $each: skippedUserIds },
|
|
1880
|
+
outbox_users: { $each: skippedHandlers }
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
);
|
|
1884
|
+
|
|
1885
|
+
// Update instance tasks for skipped approves
|
|
1886
|
+
for (const approve of shouldSkipApproves) {
|
|
1887
|
+
await update_instance_tasks(instance_id, lastTrace._id, approve._id);
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
// Insert tasks for new trace
|
|
1891
|
+
const approveIds = newTrace.approves.map(a => a._id);
|
|
1892
|
+
await insert_many_instance_tasks(instance_id, newTrace._id, approveIds);
|
|
1893
|
+
|
|
1894
|
+
// Recursively check if the new trace should also be skipped
|
|
1895
|
+
await UUFlowManager.handleSkipProcessed(instance_id, flow, maxDepth - 1);
|
|
1896
|
+
}
|
|
1897
|
+
} else {
|
|
1898
|
+
// Partial skip (counterSign) - mark skipped users' approves as finished
|
|
1899
|
+
console.log(`[workflow/engine] [handleSkipProcessed] Partial skip in counterSign step "${step.name}" - ${shouldSkipApproves.length}/${normalApproves.length} handlers already processed`);
|
|
1900
|
+
|
|
1901
|
+
const setObj = {};
|
|
1902
|
+
|
|
1903
|
+
for (let h = 0; h < lastTrace.approves.length; h++) {
|
|
1904
|
+
const approve = lastTrace.approves[h];
|
|
1905
|
+
if (processedUserIds.includes(approve.user) && isNormalApprove(approve)) {
|
|
1906
|
+
setObj[`traces.${traceIdx}.approves.${h}.is_finished`] = true;
|
|
1907
|
+
setObj[`traces.${traceIdx}.approves.${h}.finish_date`] = now;
|
|
1908
|
+
setObj[`traces.${traceIdx}.approves.${h}.judge`] = 'skipped';
|
|
1909
|
+
setObj[`traces.${traceIdx}.approves.${h}.is_read`] = true;
|
|
1910
|
+
setObj[`traces.${traceIdx}.approves.${h}.read_date`] = now;
|
|
1911
|
+
setObj[`traces.${traceIdx}.approves.${h}.cost_time`] = 0;
|
|
1912
|
+
setObj[`traces.${traceIdx}.approves.${h}.description`] = SKIP_DESCRIPTION;
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
// Remove skipped users from inbox_users
|
|
1917
|
+
const skippedHandlers = shouldSkipApproves.map(a => a.handler);
|
|
1918
|
+
const newInboxUsers = (instance.inbox_users || []).filter(u => !skippedHandlers.includes(u));
|
|
1919
|
+
setObj.inbox_users = newInboxUsers;
|
|
1920
|
+
|
|
1921
|
+
const skippedUserIds = shouldSkipApproves.map(a => a.user);
|
|
1922
|
+
|
|
1923
|
+
await instancesCollection.updateOne(
|
|
1924
|
+
{ _id: instance_id },
|
|
1925
|
+
{
|
|
1926
|
+
$set: setObj,
|
|
1927
|
+
$addToSet: {
|
|
1928
|
+
processed_user_ids: { $each: skippedUserIds },
|
|
1929
|
+
outbox_users: { $each: skippedHandlers }
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
);
|
|
1933
|
+
|
|
1934
|
+
// Update instance tasks for skipped approves
|
|
1935
|
+
for (const approve of shouldSkipApproves) {
|
|
1936
|
+
await update_instance_tasks(instance_id, lastTrace._id, approve._id);
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
};
|
|
1940
|
+
|
|
1552
1941
|
/**
|
|
1553
1942
|
* Handle workflow engine for start/submit/condition steps
|
|
1554
1943
|
* @param {String} instance_id - Instance ID
|
|
@@ -3155,6 +3544,18 @@ UUFlowManager.submit_instance = async function (instance_from_client, user_info)
|
|
|
3155
3544
|
await insert_many_instance_tasks(instance_id, nextTrace._id, approveIds);
|
|
3156
3545
|
}
|
|
3157
3546
|
|
|
3547
|
+
// Skip Processed: Initialize processed_user_ids with applicant
|
|
3548
|
+
const instancesCollectionForSkip = await getCollection('instances');
|
|
3549
|
+
await instancesCollectionForSkip.updateOne(
|
|
3550
|
+
{ _id: instance_id },
|
|
3551
|
+
{ $set: { processed_user_ids: [applicant_id] } }
|
|
3552
|
+
);
|
|
3553
|
+
|
|
3554
|
+
// Skip Processed: Check if next step should be auto-skipped
|
|
3555
|
+
if (next_step.step_type !== "end") {
|
|
3556
|
+
await UUFlowManager.handleSkipProcessed(instance_id, flow);
|
|
3557
|
+
}
|
|
3558
|
+
|
|
3158
3559
|
if (next_step.step_type !== "end") {
|
|
3159
3560
|
const finalInstance = await UUFlowManager.getInstance(instance_id);
|
|
3160
3561
|
// 发送短消息给申请人
|
|
@@ -17,7 +17,7 @@ amis_schema: |-
|
|
|
17
17
|
"url": "/api/workflow/v2/instance/save",
|
|
18
18
|
"method": "post",
|
|
19
19
|
"sendOn": "",
|
|
20
|
-
"requestAdaptor": "var _SteedosUI$getRef$get, _approveValues$next_s;\nconst formValues = context._scoped.getComponentById(\"instance_form\").getValues();const _formValues = JSON.parse(JSON.stringify(formValues)); if(_formValues){delete _formValues.__applicant} \nconst approveValues = (_SteedosUI$getRef$get = context._scoped.getComponentById(\"instance_approval\")) === null || _SteedosUI$getRef$get === void 0 ? void 0 : _SteedosUI$getRef$get.getValues();\nlet nextUsers = approveValues === null || approveValues === void 0 ? void 0 : approveValues.next_users;\nif (_.isString(nextUsers)) {\n nextUsers = [approveValues.next_users];\n}\nconst instance = context.record;\nconst body = {\n instance: {\n _id: instance._id,\n applicant: formValues.applicant.user,\n submitter: formValues.submitter,\n traces: [{\n _id: instance.trace._id,\n step: instance.step._id,\n approves: [{\n _id: instance.approve._id,\n next_steps: [{\n step: approveValues === null || approveValues === void 0 || (_approveValues$next_s = approveValues.next_step) === null || _approveValues$next_s === void 0 ? void 0 : _approveValues$next_s._id,\n users: nextUsers\n }],\n description: approveValues === null || approveValues === void 0 ? void 0 : approveValues.suggestion,\n values: _formValues\n }]\n }]\n }\n};\napi.data = body;\nreturn api;",
|
|
20
|
+
"requestAdaptor": "var _SteedosUI$getRef$get, _approveValues$next_s;\nconst formValues = context._scoped.getComponentById(\"instance_form\").getValues();const _formValues = JSON.parse(JSON.stringify(formValues)); if(_formValues){delete _formValues.__applicant} \nconst approveValues = (_SteedosUI$getRef$get = context._scoped.getComponentById(\"instance_approval\")) === null || _SteedosUI$getRef$get === void 0 ? void 0 : _SteedosUI$getRef$get.getValues();\nlet nextUsers = approveValues === null || approveValues === void 0 ? void 0 : approveValues.next_users;\nif (_.isString(nextUsers)) {\n nextUsers = [approveValues.next_users];\n}\nconst instance = context.record;\nconst body = {\n instance: {\n _id: instance._id,\n applicant: formValues.__applicant || formValues.applicant.user,\n submitter: formValues.submitter,\n traces: [{\n _id: instance.trace._id,\n step: instance.step._id,\n approves: [{\n _id: instance.approve._id,\n next_steps: [{\n step: approveValues === null || approveValues === void 0 || (_approveValues$next_s = approveValues.next_step) === null || _approveValues$next_s === void 0 ? void 0 : _approveValues$next_s._id,\n users: nextUsers\n }],\n description: approveValues === null || approveValues === void 0 ? void 0 : approveValues.suggestion,\n values: _formValues\n }]\n }]\n }\n};\napi.data = body;\nreturn api;",
|
|
21
21
|
"adaptor": "window.SteedosWorkflow.Instance.changed = false; if (payload.instance == \"upgraded\") { window.setTimeout(function(){ $('.steedos-workflow-reload-btn').trigger('click'); }, 2000); return {...payload, status: 1, msg: t('instance_action_instance_save_msg_upgraded')}; } \n return payload.instance != false ? {data: payload, status: 0, msg: t('instance_action_instance_save_msg_success')} : {...payload, status: 1, msg: t('instance_action_instance_save_msg_failed')};",
|
|
22
22
|
"data": {
|
|
23
23
|
"&": "$$"
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
name: workflow_designer_backups
|
|
2
|
+
label: Designer Backups
|
|
3
|
+
icon: backup
|
|
4
|
+
hidden: true
|
|
5
|
+
enable_dataloader: false
|
|
6
|
+
fields:
|
|
7
|
+
name:
|
|
8
|
+
type: text
|
|
9
|
+
label: Name
|
|
10
|
+
required: true
|
|
11
|
+
name: name
|
|
12
|
+
form_id:
|
|
13
|
+
type: text
|
|
14
|
+
label: Form ID
|
|
15
|
+
required: true
|
|
16
|
+
name: form_id
|
|
17
|
+
index: true
|
|
18
|
+
flow_id:
|
|
19
|
+
type: text
|
|
20
|
+
label: Flow ID
|
|
21
|
+
name: flow_id
|
|
22
|
+
backup_type:
|
|
23
|
+
type: select
|
|
24
|
+
label: Backup Type
|
|
25
|
+
options:
|
|
26
|
+
- label: AI Upgrade
|
|
27
|
+
value: ai_upgrade
|
|
28
|
+
- label: Import
|
|
29
|
+
value: import
|
|
30
|
+
name: backup_type
|
|
31
|
+
form_snapshot:
|
|
32
|
+
type: textarea
|
|
33
|
+
label: Form Snapshot
|
|
34
|
+
name: form_snapshot
|
|
35
|
+
is_wide: true
|
|
36
|
+
flow_snapshot:
|
|
37
|
+
type: textarea
|
|
38
|
+
label: Flow Snapshot
|
|
39
|
+
name: flow_snapshot
|
|
40
|
+
is_wide: true
|
|
41
|
+
list_views:
|
|
42
|
+
all:
|
|
43
|
+
label: All
|
|
44
|
+
columns:
|
|
45
|
+
- name
|
|
46
|
+
- form_id
|
|
47
|
+
- backup_type
|
|
48
|
+
- created
|
|
49
|
+
sort:
|
|
50
|
+
- - created
|
|
51
|
+
- desc
|
|
52
|
+
permission_set:
|
|
53
|
+
user:
|
|
54
|
+
allowCreate: true
|
|
55
|
+
allowRead: true
|
|
56
|
+
allowEdit: true
|
|
57
|
+
allowDelete: true
|
|
58
|
+
admin:
|
|
59
|
+
allowCreate: true
|
|
60
|
+
allowRead: true
|
|
61
|
+
allowEdit: true
|
|
62
|
+
allowDelete: true
|
|
@@ -171,6 +171,13 @@ router.post('/api/workflow/v2/instance/return', requireAuthentication, async fun
|
|
|
171
171
|
});
|
|
172
172
|
// 生成新记录
|
|
173
173
|
await insert_instance_tasks(instance_id, newTrace._id, newTrace.approves[0]._id)
|
|
174
|
+
|
|
175
|
+
// Skip Processed: Reset processed_user_ids to applicant only on return/rejection
|
|
176
|
+
await instancesCollection.updateOne(
|
|
177
|
+
{ _id: instance_id },
|
|
178
|
+
{ $set: { processed_user_ids: [ins.applicant] } }
|
|
179
|
+
);
|
|
180
|
+
|
|
174
181
|
if (r && b) {
|
|
175
182
|
await pushManager.send_message_to_specifyUser("current_user", current_user);
|
|
176
183
|
instance = await UUFlowManager.getInstance(instance_id);
|
|
@@ -267,6 +267,8 @@ async function updateForm(formId, form, forms, flows, currentUserId) {
|
|
|
267
267
|
current.viewMode = form["current"]["viewMode"];
|
|
268
268
|
current.tableColumns = form["current"]["tableColumns"];
|
|
269
269
|
|
|
270
|
+
current.events = form["current"]["events"];
|
|
271
|
+
|
|
270
272
|
formUpdateObj.$set = {
|
|
271
273
|
'current': current,
|
|
272
274
|
'name': form["name"],
|