interaqt 0.3.0 → 0.3.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/agent/.claude/agents/code-generation-handler.md +2 -0
- package/agent/.claude/agents/computation-generation-handler.md +1 -0
- package/agent/.claude/agents/implement-design-handler.md +4 -13
- package/agent/.claude/agents/requirements-analysis-handler.md +46 -14
- package/agent/agentspace/knowledge/generator/api-reference.md +3378 -0
- package/agent/agentspace/knowledge/generator/basic-interaction-generation.md +377 -0
- package/agent/agentspace/knowledge/generator/computation-analysis.md +307 -0
- package/agent/agentspace/knowledge/generator/computation-implementation.md +959 -0
- package/agent/agentspace/knowledge/generator/data-analysis.md +463 -0
- package/agent/agentspace/knowledge/generator/entity-relation-generation.md +395 -0
- package/agent/agentspace/knowledge/generator/permission-implementation.md +460 -0
- package/agent/agentspace/knowledge/generator/permission-test-implementation.md +870 -0
- package/agent/agentspace/knowledge/generator/test-implementation.md +674 -0
- package/agent/agentspace/knowledge/usage/00-mindset-shift.md +322 -0
- package/agent/agentspace/knowledge/usage/01-core-concepts.md +131 -0
- package/agent/agentspace/knowledge/usage/02-define-entities-properties.md +407 -0
- package/agent/agentspace/knowledge/usage/03-entity-relations.md +599 -0
- package/agent/agentspace/knowledge/usage/04-reactive-computations.md +2186 -0
- package/agent/agentspace/knowledge/usage/05-interactions.md +1411 -0
- package/agent/agentspace/knowledge/usage/06-attributive-permissions.md +10 -0
- package/agent/agentspace/knowledge/usage/07-payload-parameters.md +593 -0
- package/agent/agentspace/knowledge/usage/08-activities.md +863 -0
- package/agent/agentspace/knowledge/usage/09-filtered-entities.md +784 -0
- package/agent/agentspace/knowledge/usage/10-async-computations.md +734 -0
- package/agent/agentspace/knowledge/usage/11-global-dictionaries.md +942 -0
- package/agent/agentspace/knowledge/usage/12-data-querying.md +1033 -0
- package/agent/agentspace/knowledge/usage/13-testing.md +1201 -0
- package/agent/agentspace/knowledge/usage/14-api-reference.md +1606 -0
- package/agent/agentspace/knowledge/usage/15-entity-crud-patterns.md +1122 -0
- package/agent/agentspace/knowledge/usage/16-frontend-page-design-guide.md +485 -0
- package/agent/agentspace/knowledge/usage/17-performance-optimization.md +283 -0
- package/agent/agentspace/knowledge/usage/18-api-exports-reference.md +176 -0
- package/agent/agentspace/knowledge/usage/19-common-anti-patterns.md +563 -0
- package/agent/agentspace/knowledge/usage/README.md +148 -0
- package/package.json +1 -1
|
@@ -0,0 +1,863 @@
|
|
|
1
|
+
# How to Use Activities for Process Management
|
|
2
|
+
|
|
3
|
+
Activities are the core mechanism in interaqt for managing complex business processes. They allow you to define multi-step, multi-role business processes and control the execution order and conditions of processes through a state machine model.
|
|
4
|
+
|
|
5
|
+
## Understanding Activity Concepts
|
|
6
|
+
|
|
7
|
+
### What is an Activity
|
|
8
|
+
|
|
9
|
+
An activity is a stateful business process that contains multiple related interactions and state transitions. Activities manage process execution through a state machine model:
|
|
10
|
+
|
|
11
|
+
- **State**: The state of the activity at a particular moment
|
|
12
|
+
- **Transfer**: Transition from one state to another
|
|
13
|
+
- **Condition**: Conditions that control whether a transfer can be executed
|
|
14
|
+
- **Interaction**: User operations that trigger state transfers
|
|
15
|
+
|
|
16
|
+
### Activities vs Independent Interactions
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
// Independent interaction approach: each operation is independent
|
|
20
|
+
const SubmitOrder = Interaction.create({ name: 'SubmitOrder' });
|
|
21
|
+
const PayOrder = Interaction.create({ name: 'PayOrder' });
|
|
22
|
+
const ShipOrder = Interaction.create({ name: 'ShipOrder' });
|
|
23
|
+
const DeliverOrder = Interaction.create({ name: 'DeliverOrder' });
|
|
24
|
+
|
|
25
|
+
// Activity approach: organizing related operations into a process
|
|
26
|
+
const OrderProcessActivity = Activity.create({
|
|
27
|
+
name: 'OrderProcess',
|
|
28
|
+
interactions: [SubmitOrder, PayOrder, ShipOrder, DeliverOrder],
|
|
29
|
+
transfers: [
|
|
30
|
+
Transfer.create({
|
|
31
|
+
name: 'submitOrder',
|
|
32
|
+
source: SubmitOrder,
|
|
33
|
+
target: PayOrder
|
|
34
|
+
}),
|
|
35
|
+
Transfer.create({
|
|
36
|
+
name: 'payOrder',
|
|
37
|
+
source: PayOrder,
|
|
38
|
+
target: ShipOrder
|
|
39
|
+
}),
|
|
40
|
+
Transfer.create({
|
|
41
|
+
name: 'shipOrder',
|
|
42
|
+
source: ShipOrder,
|
|
43
|
+
target: DeliverOrder
|
|
44
|
+
})
|
|
45
|
+
]
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Creating Simple Activities
|
|
50
|
+
|
|
51
|
+
### Basic Activity Structure
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
import { Activity, Transfer, Condition, Interaction } from 'interaqt';
|
|
55
|
+
|
|
56
|
+
// Define related interactions
|
|
57
|
+
const CreatePost = Interaction.create({
|
|
58
|
+
name: 'CreatePost',
|
|
59
|
+
payload: Payload.create({
|
|
60
|
+
items: [
|
|
61
|
+
PayloadItem.create({ name: 'title' }),
|
|
62
|
+
PayloadItem.create({ name: 'content' })
|
|
63
|
+
]
|
|
64
|
+
})
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const SubmitForReview = Interaction.create({
|
|
68
|
+
name: 'SubmitForReview',
|
|
69
|
+
payload: Payload.create({
|
|
70
|
+
items: [
|
|
71
|
+
PayloadItem.create({ name: 'postId', base: Post, isRef: true })
|
|
72
|
+
]
|
|
73
|
+
})
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const ApprovePost = Interaction.create({
|
|
77
|
+
name: 'ApprovePost',
|
|
78
|
+
payload: Payload.create({
|
|
79
|
+
items: [
|
|
80
|
+
PayloadItem.create({ name: 'postId', base: Post, isRef: true }),
|
|
81
|
+
PayloadItem.create({ name: 'reviewerId', base: User, isRef: true })
|
|
82
|
+
]
|
|
83
|
+
})
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const PublishPost = Interaction.create({
|
|
87
|
+
name: 'PublishPost',
|
|
88
|
+
payload: Payload.create({
|
|
89
|
+
items: [
|
|
90
|
+
PayloadItem.create({ name: 'postId', base: Post, isRef: true })
|
|
91
|
+
]
|
|
92
|
+
})
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Create blog post activity
|
|
96
|
+
const BlogPostActivity = Activity.create({
|
|
97
|
+
name: 'BlogPostWorkflow',
|
|
98
|
+
interactions: [SubmitForReview, ApprovePost, PublishPost],
|
|
99
|
+
transfers: [
|
|
100
|
+
Transfer.create({
|
|
101
|
+
name: 'submitForReview',
|
|
102
|
+
source: SubmitForReview,
|
|
103
|
+
target: ApprovePost
|
|
104
|
+
}),
|
|
105
|
+
Transfer.create({
|
|
106
|
+
name: 'approvePost',
|
|
107
|
+
source: ApprovePost,
|
|
108
|
+
target: PublishPost
|
|
109
|
+
})
|
|
110
|
+
]
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Setting Initial State
|
|
115
|
+
|
|
116
|
+
```javascript
|
|
117
|
+
const LeaveRequestActivity = Activity.create({
|
|
118
|
+
name: 'LeaveRequestWorkflow',
|
|
119
|
+
initialState: 'draft', // State when activity starts
|
|
120
|
+
states: [
|
|
121
|
+
'draft',
|
|
122
|
+
'submitted',
|
|
123
|
+
'approved',
|
|
124
|
+
'rejected',
|
|
125
|
+
'cancelled'
|
|
126
|
+
],
|
|
127
|
+
// ... transfers definition
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Defining State Transfers
|
|
132
|
+
|
|
133
|
+
### Sequential Transfers
|
|
134
|
+
|
|
135
|
+
The simplest type of transfer, executed in predefined order:
|
|
136
|
+
|
|
137
|
+
```javascript
|
|
138
|
+
const SimpleWorkflow = Activity.create({
|
|
139
|
+
name: 'SimpleWorkflow',
|
|
140
|
+
initialState: 'step1',
|
|
141
|
+
states: ['step1', 'step2', 'step3', 'completed'],
|
|
142
|
+
transfers: [
|
|
143
|
+
Transfer.create({
|
|
144
|
+
from: 'step1',
|
|
145
|
+
to: 'step2',
|
|
146
|
+
interaction: CompleteStep1
|
|
147
|
+
}),
|
|
148
|
+
Transfer.create({
|
|
149
|
+
from: 'step2',
|
|
150
|
+
to: 'step3',
|
|
151
|
+
interaction: CompleteStep2
|
|
152
|
+
}),
|
|
153
|
+
Transfer.create({
|
|
154
|
+
from: 'step3',
|
|
155
|
+
to: 'completed',
|
|
156
|
+
interaction: CompleteStep3
|
|
157
|
+
})
|
|
158
|
+
]
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Conditional Transfers
|
|
163
|
+
|
|
164
|
+
Condition-based transfers that can only be executed when specific conditions are met:
|
|
165
|
+
|
|
166
|
+
```javascript
|
|
167
|
+
const ConditionalWorkflow = Activity.create({
|
|
168
|
+
name: 'ConditionalWorkflow',
|
|
169
|
+
initialState: 'submitted',
|
|
170
|
+
states: ['submitted', 'approved', 'rejected', 'revision_needed'],
|
|
171
|
+
transfers: [
|
|
172
|
+
Transfer.create({
|
|
173
|
+
from: 'submitted',
|
|
174
|
+
to: 'approved',
|
|
175
|
+
interaction: ReviewSubmission,
|
|
176
|
+
condition: Condition.create({
|
|
177
|
+
name: 'ApprovalCondition',
|
|
178
|
+
condition: async (context) => {
|
|
179
|
+
// Only score >= 80 can pass
|
|
180
|
+
return context.payload.score >= 80;
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
}),
|
|
184
|
+
Transfer.create({
|
|
185
|
+
from: 'submitted',
|
|
186
|
+
to: 'rejected',
|
|
187
|
+
interaction: ReviewSubmission,
|
|
188
|
+
condition: Condition.create({
|
|
189
|
+
name: 'RejectionCondition',
|
|
190
|
+
condition: async (context) => {
|
|
191
|
+
// Score < 60 is directly rejected
|
|
192
|
+
return context.payload.score < 60;
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
}),
|
|
196
|
+
Transfer.create({
|
|
197
|
+
from: 'submitted',
|
|
198
|
+
to: 'revision_needed',
|
|
199
|
+
interaction: ReviewSubmission,
|
|
200
|
+
condition: Condition.create({
|
|
201
|
+
name: 'RevisionCondition',
|
|
202
|
+
condition: async (context) => {
|
|
203
|
+
// Score between 60-79 needs revision
|
|
204
|
+
return context.payload.score >= 60 && context.payload.score < 80;
|
|
205
|
+
}
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
]
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Parallel Transfers
|
|
213
|
+
|
|
214
|
+
Allow transition from one state to multiple states:
|
|
215
|
+
|
|
216
|
+
```javascript
|
|
217
|
+
const ParallelWorkflow = Activity.create({
|
|
218
|
+
name: 'ParallelWorkflow',
|
|
219
|
+
initialState: 'submitted',
|
|
220
|
+
states: ['submitted', 'technical_review', 'business_review', 'approved', 'rejected'],
|
|
221
|
+
transfers: [
|
|
222
|
+
// After submission, enter both technical and business review
|
|
223
|
+
Transfer.create({
|
|
224
|
+
from: 'submitted',
|
|
225
|
+
to: 'technical_review',
|
|
226
|
+
interaction: StartTechnicalReview,
|
|
227
|
+
isParallel: true
|
|
228
|
+
}),
|
|
229
|
+
Transfer.create({
|
|
230
|
+
from: 'submitted',
|
|
231
|
+
to: 'business_review',
|
|
232
|
+
interaction: StartBusinessReview,
|
|
233
|
+
isParallel: true
|
|
234
|
+
}),
|
|
235
|
+
// Can only approve after both reviews are complete
|
|
236
|
+
Transfer.create({
|
|
237
|
+
from: ['technical_review', 'business_review'], // Multiple prerequisite states
|
|
238
|
+
to: 'approved',
|
|
239
|
+
interaction: FinalApproval,
|
|
240
|
+
condition: Condition.create({
|
|
241
|
+
name: 'BothReviewsComplete',
|
|
242
|
+
condition: async (context) => {
|
|
243
|
+
const activity = context.activity;
|
|
244
|
+
return activity.hasState('technical_review') &&
|
|
245
|
+
activity.hasState('business_review');
|
|
246
|
+
}
|
|
247
|
+
})
|
|
248
|
+
})
|
|
249
|
+
]
|
|
250
|
+
});
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Loop Transfers
|
|
254
|
+
|
|
255
|
+
Allow cyclic transitions between states:
|
|
256
|
+
|
|
257
|
+
```javascript
|
|
258
|
+
const IterativeWorkflow = Activity.create({
|
|
259
|
+
name: 'IterativeWorkflow',
|
|
260
|
+
initialState: 'draft',
|
|
261
|
+
states: ['draft', 'review', 'revision', 'approved'],
|
|
262
|
+
transfers: [
|
|
263
|
+
Transfer.create({
|
|
264
|
+
from: 'draft',
|
|
265
|
+
to: 'review',
|
|
266
|
+
interaction: SubmitForReview
|
|
267
|
+
}),
|
|
268
|
+
Transfer.create({
|
|
269
|
+
from: 'review',
|
|
270
|
+
to: 'approved',
|
|
271
|
+
interaction: ApproveDocument,
|
|
272
|
+
condition: Condition.create({
|
|
273
|
+
name: 'QualityCheck',
|
|
274
|
+
condition: async (context) => context.payload.quality === 'excellent'
|
|
275
|
+
})
|
|
276
|
+
}),
|
|
277
|
+
Transfer.create({
|
|
278
|
+
from: 'review',
|
|
279
|
+
to: 'revision',
|
|
280
|
+
interaction: RequestRevision,
|
|
281
|
+
condition: Condition.create({
|
|
282
|
+
name: 'NeedsImprovement',
|
|
283
|
+
condition: async (context) => context.payload.quality !== 'excellent'
|
|
284
|
+
})
|
|
285
|
+
}),
|
|
286
|
+
// Loop: resubmit for review after revision
|
|
287
|
+
Transfer.create({
|
|
288
|
+
from: 'revision',
|
|
289
|
+
to: 'review',
|
|
290
|
+
interaction: ResubmitAfterRevision
|
|
291
|
+
})
|
|
292
|
+
]
|
|
293
|
+
});
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Using Conditions to Control Processes
|
|
297
|
+
|
|
298
|
+
### Transfer Conditions
|
|
299
|
+
|
|
300
|
+
```javascript
|
|
301
|
+
const ConditionalTransfer = Transfer.create({
|
|
302
|
+
from: 'pending',
|
|
303
|
+
to: 'approved',
|
|
304
|
+
interaction: ProcessRequest,
|
|
305
|
+
condition: Condition.create({
|
|
306
|
+
name: 'AutoApprovalCondition',
|
|
307
|
+
condition: async (context) => {
|
|
308
|
+
const request = context.data;
|
|
309
|
+
const user = context.user;
|
|
310
|
+
|
|
311
|
+
// Auto-approval conditions:
|
|
312
|
+
// 1. Request amount < 1000
|
|
313
|
+
// 2. User credit rating >= A
|
|
314
|
+
// 3. User has good history
|
|
315
|
+
return request.amount < 1000 &&
|
|
316
|
+
user.creditRating >= 'A' &&
|
|
317
|
+
user.hasGoodHistory === true;
|
|
318
|
+
}
|
|
319
|
+
})
|
|
320
|
+
});
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Interaction Execution Conditions
|
|
324
|
+
|
|
325
|
+
```javascript
|
|
326
|
+
const ConditionalInteraction = Interaction.create({
|
|
327
|
+
name: 'ConditionalAction',
|
|
328
|
+
condition: Condition.create({
|
|
329
|
+
name: 'ExecutionCondition',
|
|
330
|
+
condition: async (context) => {
|
|
331
|
+
// Can only execute within specific time window
|
|
332
|
+
const now = new Date();
|
|
333
|
+
const hour = now.getHours();
|
|
334
|
+
return hour >= 9 && hour < 17; // Business hours
|
|
335
|
+
}
|
|
336
|
+
}),
|
|
337
|
+
// ... other configuration
|
|
338
|
+
});
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Data-based Conditions
|
|
342
|
+
|
|
343
|
+
```javascript
|
|
344
|
+
const DataBasedCondition = Condition.create({
|
|
345
|
+
name: 'DataBasedCondition',
|
|
346
|
+
condition: async (context) => {
|
|
347
|
+
const order = await context.findOne('Order', { id: context.payload.orderId });
|
|
348
|
+
const user = await context.findOne('User', { id: order.userId });
|
|
349
|
+
|
|
350
|
+
// Complex business logic judgment
|
|
351
|
+
if (order.totalAmount > 10000) {
|
|
352
|
+
// High-value orders need manager approval
|
|
353
|
+
return user.role === 'manager';
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (order.isInternational) {
|
|
357
|
+
// International orders need special permissions
|
|
358
|
+
return user.permissions.includes('international_orders');
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Regular orders can be processed by anyone
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### User-based Conditions
|
|
368
|
+
|
|
369
|
+
```javascript
|
|
370
|
+
const UserBasedCondition = Condition.create({
|
|
371
|
+
name: 'UserBasedCondition',
|
|
372
|
+
condition: async (context) => {
|
|
373
|
+
const user = context.user;
|
|
374
|
+
const targetUser = await context.findOne('User', { id: context.payload.targetUserId });
|
|
375
|
+
|
|
376
|
+
// Permission check
|
|
377
|
+
if (user.role === 'admin') {
|
|
378
|
+
return true; // Administrators can operate on any user
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (user.role === 'manager') {
|
|
382
|
+
// Managers can only operate on subordinates
|
|
383
|
+
return targetUser.managerId === user.id;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Regular users can only operate on themselves
|
|
387
|
+
return user.id === targetUser.id;
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## Implementing Complex Business Processes
|
|
393
|
+
|
|
394
|
+
### Multi-role Collaboration
|
|
395
|
+
|
|
396
|
+
```javascript
|
|
397
|
+
const MultiRoleWorkflow = Activity.create({
|
|
398
|
+
name: 'ProjectApprovalWorkflow',
|
|
399
|
+
initialState: 'draft',
|
|
400
|
+
states: [
|
|
401
|
+
'draft',
|
|
402
|
+
'tech_review',
|
|
403
|
+
'business_review',
|
|
404
|
+
'budget_review',
|
|
405
|
+
'final_approval',
|
|
406
|
+
'approved',
|
|
407
|
+
'rejected'
|
|
408
|
+
],
|
|
409
|
+
transfers: [
|
|
410
|
+
// Project manager submits for technical review
|
|
411
|
+
Transfer.create({
|
|
412
|
+
from: 'draft',
|
|
413
|
+
to: 'tech_review',
|
|
414
|
+
interaction: SubmitTechReview,
|
|
415
|
+
condition: Condition.create({
|
|
416
|
+
name: 'ProjectManagerOnly',
|
|
417
|
+
condition: (context) => context.user.role === 'project_manager'
|
|
418
|
+
})
|
|
419
|
+
}),
|
|
420
|
+
|
|
421
|
+
// Technical lead reviews
|
|
422
|
+
Transfer.create({
|
|
423
|
+
from: 'tech_review',
|
|
424
|
+
to: 'business_review',
|
|
425
|
+
interaction: ApproveTechReview,
|
|
426
|
+
condition: Condition.create({
|
|
427
|
+
name: 'TechLeadOnly',
|
|
428
|
+
condition: (context) => context.user.role === 'tech_lead'
|
|
429
|
+
})
|
|
430
|
+
}),
|
|
431
|
+
|
|
432
|
+
// Business lead reviews
|
|
433
|
+
Transfer.create({
|
|
434
|
+
from: 'business_review',
|
|
435
|
+
to: 'budget_review',
|
|
436
|
+
interaction: ApproveBusinessReview,
|
|
437
|
+
condition: Condition.create({
|
|
438
|
+
name: 'BusinessLeadOnly',
|
|
439
|
+
condition: (context) => context.user.role === 'business_lead'
|
|
440
|
+
})
|
|
441
|
+
}),
|
|
442
|
+
|
|
443
|
+
// Finance review (if budget exceeds threshold)
|
|
444
|
+
Transfer.create({
|
|
445
|
+
from: 'budget_review',
|
|
446
|
+
to: 'final_approval',
|
|
447
|
+
interaction: ApproveBudget,
|
|
448
|
+
condition: Condition.create({
|
|
449
|
+
name: 'FinanceApproval',
|
|
450
|
+
condition: async (context) => {
|
|
451
|
+
const project = context.data;
|
|
452
|
+
if (project.budget > 100000) {
|
|
453
|
+
return context.user.role === 'finance_manager';
|
|
454
|
+
}
|
|
455
|
+
return true; // Auto-pass for smaller budgets
|
|
456
|
+
}
|
|
457
|
+
})
|
|
458
|
+
}),
|
|
459
|
+
|
|
460
|
+
// Final approval
|
|
461
|
+
Transfer.create({
|
|
462
|
+
from: 'final_approval',
|
|
463
|
+
to: 'approved',
|
|
464
|
+
interaction: FinalApproval,
|
|
465
|
+
condition: Condition.create({
|
|
466
|
+
name: 'CEOApproval',
|
|
467
|
+
condition: (context) => context.user.role === 'ceo'
|
|
468
|
+
})
|
|
469
|
+
})
|
|
470
|
+
]
|
|
471
|
+
});
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### Branching and Merging
|
|
475
|
+
|
|
476
|
+
```javascript
|
|
477
|
+
const BranchingWorkflow = Activity.create({
|
|
478
|
+
name: 'BranchingWorkflow',
|
|
479
|
+
initialState: 'start',
|
|
480
|
+
states: [
|
|
481
|
+
'start',
|
|
482
|
+
'type_a_process',
|
|
483
|
+
'type_b_process',
|
|
484
|
+
'type_c_process',
|
|
485
|
+
'merge_point',
|
|
486
|
+
'final_step',
|
|
487
|
+
'completed'
|
|
488
|
+
],
|
|
489
|
+
transfers: [
|
|
490
|
+
// Branch based on type
|
|
491
|
+
Transfer.create({
|
|
492
|
+
from: 'start',
|
|
493
|
+
to: 'type_a_process',
|
|
494
|
+
interaction: ProcessTypeA,
|
|
495
|
+
condition: Condition.create({
|
|
496
|
+
name: 'IsTypeA',
|
|
497
|
+
condition: (context) => context.payload.type === 'A'
|
|
498
|
+
})
|
|
499
|
+
}),
|
|
500
|
+
Transfer.create({
|
|
501
|
+
from: 'start',
|
|
502
|
+
to: 'type_b_process',
|
|
503
|
+
interaction: ProcessTypeB,
|
|
504
|
+
condition: Condition.create({
|
|
505
|
+
name: 'IsTypeB',
|
|
506
|
+
condition: (context) => context.payload.type === 'B'
|
|
507
|
+
})
|
|
508
|
+
}),
|
|
509
|
+
Transfer.create({
|
|
510
|
+
from: 'start',
|
|
511
|
+
to: 'type_c_process',
|
|
512
|
+
interaction: ProcessTypeC,
|
|
513
|
+
condition: Condition.create({
|
|
514
|
+
name: 'IsTypeC',
|
|
515
|
+
condition: (context) => context.payload.type === 'C'
|
|
516
|
+
})
|
|
517
|
+
}),
|
|
518
|
+
|
|
519
|
+
// All branches converge to merge point
|
|
520
|
+
Transfer.create({
|
|
521
|
+
from: 'type_a_process',
|
|
522
|
+
to: 'merge_point',
|
|
523
|
+
interaction: CompleteTypeAProcess
|
|
524
|
+
}),
|
|
525
|
+
Transfer.create({
|
|
526
|
+
from: 'type_b_process',
|
|
527
|
+
to: 'merge_point',
|
|
528
|
+
interaction: CompleteTypeBProcess
|
|
529
|
+
}),
|
|
530
|
+
Transfer.create({
|
|
531
|
+
from: 'type_c_process',
|
|
532
|
+
to: 'merge_point',
|
|
533
|
+
interaction: CompleteTypeCProcess
|
|
534
|
+
}),
|
|
535
|
+
|
|
536
|
+
// Common process after merge
|
|
537
|
+
Transfer.create({
|
|
538
|
+
from: 'merge_point',
|
|
539
|
+
to: 'final_step',
|
|
540
|
+
interaction: ProcessFinalStep
|
|
541
|
+
}),
|
|
542
|
+
Transfer.create({
|
|
543
|
+
from: 'final_step',
|
|
544
|
+
to: 'completed',
|
|
545
|
+
interaction: CompleteWorkflow
|
|
546
|
+
})
|
|
547
|
+
]
|
|
548
|
+
});
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
### Timeout Handling
|
|
552
|
+
|
|
553
|
+
```javascript
|
|
554
|
+
const TimeoutWorkflow = Activity.create({
|
|
555
|
+
name: 'TimeoutWorkflow',
|
|
556
|
+
initialState: 'waiting_approval',
|
|
557
|
+
states: [
|
|
558
|
+
'waiting_approval',
|
|
559
|
+
'approved',
|
|
560
|
+
'rejected',
|
|
561
|
+
'auto_approved', // Auto-approved after timeout
|
|
562
|
+
'escalated' // Escalated for handling
|
|
563
|
+
],
|
|
564
|
+
transfers: [
|
|
565
|
+
Transfer.create({
|
|
566
|
+
from: 'waiting_approval',
|
|
567
|
+
to: 'approved',
|
|
568
|
+
interaction: ApproveRequest
|
|
569
|
+
}),
|
|
570
|
+
Transfer.create({
|
|
571
|
+
from: 'waiting_approval',
|
|
572
|
+
to: 'rejected',
|
|
573
|
+
interaction: RejectRequest
|
|
574
|
+
}),
|
|
575
|
+
|
|
576
|
+
// Timeout handling
|
|
577
|
+
Transfer.create({
|
|
578
|
+
from: 'waiting_approval',
|
|
579
|
+
to: 'auto_approved',
|
|
580
|
+
interaction: TimeoutAutoApproval,
|
|
581
|
+
condition: Condition.create({
|
|
582
|
+
name: 'TimeoutCondition',
|
|
583
|
+
condition: async (context) => {
|
|
584
|
+
const activity = context.activity;
|
|
585
|
+
const waitingTime = Date.now() - activity.enteredStateAt('waiting_approval');
|
|
586
|
+
const timeoutDuration = 24 * 60 * 60 * 1000; // 24 hours
|
|
587
|
+
|
|
588
|
+
return waitingTime > timeoutDuration;
|
|
589
|
+
}
|
|
590
|
+
})
|
|
591
|
+
}),
|
|
592
|
+
|
|
593
|
+
// Escalation handling
|
|
594
|
+
Transfer.create({
|
|
595
|
+
from: 'waiting_approval',
|
|
596
|
+
to: 'escalated',
|
|
597
|
+
interaction: EscalateRequest,
|
|
598
|
+
condition: Condition.create({
|
|
599
|
+
name: 'EscalationCondition',
|
|
600
|
+
condition: async (context) => {
|
|
601
|
+
const activity = context.activity;
|
|
602
|
+
const waitingTime = Date.now() - activity.enteredStateAt('waiting_approval');
|
|
603
|
+
const escalationTime = 12 * 60 * 60 * 1000; // Escalate after 12 hours
|
|
604
|
+
|
|
605
|
+
return waitingTime > escalationTime;
|
|
606
|
+
}
|
|
607
|
+
})
|
|
608
|
+
})
|
|
609
|
+
]
|
|
610
|
+
});
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
### Exception Handling
|
|
614
|
+
|
|
615
|
+
```javascript
|
|
616
|
+
const RobustWorkflow = Activity.create({
|
|
617
|
+
name: 'RobustWorkflow',
|
|
618
|
+
initialState: 'processing',
|
|
619
|
+
states: [
|
|
620
|
+
'processing',
|
|
621
|
+
'completed',
|
|
622
|
+
'failed',
|
|
623
|
+
'retry',
|
|
624
|
+
'manual_intervention',
|
|
625
|
+
'cancelled'
|
|
626
|
+
],
|
|
627
|
+
transfers: [
|
|
628
|
+
Transfer.create({
|
|
629
|
+
from: 'processing',
|
|
630
|
+
to: 'completed',
|
|
631
|
+
interaction: CompleteProcessing,
|
|
632
|
+
condition: Condition.create({
|
|
633
|
+
name: 'SuccessCondition',
|
|
634
|
+
condition: (context) => context.payload.success === true
|
|
635
|
+
})
|
|
636
|
+
}),
|
|
637
|
+
|
|
638
|
+
Transfer.create({
|
|
639
|
+
from: 'processing',
|
|
640
|
+
to: 'failed',
|
|
641
|
+
interaction: HandleFailure,
|
|
642
|
+
condition: Condition.create({
|
|
643
|
+
name: 'FailureCondition',
|
|
644
|
+
condition: (context) => context.payload.success === false
|
|
645
|
+
})
|
|
646
|
+
}),
|
|
647
|
+
|
|
648
|
+
// Retry after failure
|
|
649
|
+
Transfer.create({
|
|
650
|
+
from: 'failed',
|
|
651
|
+
to: 'retry',
|
|
652
|
+
interaction: RetryProcessing,
|
|
653
|
+
condition: Condition.create({
|
|
654
|
+
name: 'CanRetry',
|
|
655
|
+
condition: async (context) => {
|
|
656
|
+
const activity = context.activity;
|
|
657
|
+
const retryCount = activity.getMetadata('retryCount') || 0;
|
|
658
|
+
return retryCount < 3; // Maximum 3 retries
|
|
659
|
+
}
|
|
660
|
+
})
|
|
661
|
+
}),
|
|
662
|
+
|
|
663
|
+
// Manual intervention after retry failure
|
|
664
|
+
Transfer.create({
|
|
665
|
+
from: 'failed',
|
|
666
|
+
to: 'manual_intervention',
|
|
667
|
+
interaction: RequestManualIntervention,
|
|
668
|
+
condition: Condition.create({
|
|
669
|
+
name: 'NeedsManualIntervention',
|
|
670
|
+
condition: async (context) => {
|
|
671
|
+
const activity = context.activity;
|
|
672
|
+
const retryCount = activity.getMetadata('retryCount') || 0;
|
|
673
|
+
return retryCount >= 3;
|
|
674
|
+
}
|
|
675
|
+
})
|
|
676
|
+
}),
|
|
677
|
+
|
|
678
|
+
// Return to processing from retry state
|
|
679
|
+
Transfer.create({
|
|
680
|
+
from: 'retry',
|
|
681
|
+
to: 'processing',
|
|
682
|
+
interaction: RestartProcessing
|
|
683
|
+
}),
|
|
684
|
+
|
|
685
|
+
// Cancel workflow
|
|
686
|
+
Transfer.create({
|
|
687
|
+
from: ['processing', 'failed', 'retry'],
|
|
688
|
+
to: 'cancelled',
|
|
689
|
+
interaction: CancelWorkflow,
|
|
690
|
+
condition: Condition.create({
|
|
691
|
+
name: 'CanCancel',
|
|
692
|
+
condition: (context) => context.user.role === 'admin'
|
|
693
|
+
})
|
|
694
|
+
})
|
|
695
|
+
]
|
|
696
|
+
});
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
## Activity Instance Management
|
|
700
|
+
|
|
701
|
+
### Starting Activities
|
|
702
|
+
|
|
703
|
+
```javascript
|
|
704
|
+
// Start a new activity instance
|
|
705
|
+
const activityInstance = await controller.startActivity('LeaveRequestWorkflow', {
|
|
706
|
+
employeeId: 'user123',
|
|
707
|
+
startDate: '2024-01-15',
|
|
708
|
+
endDate: '2024-01-17',
|
|
709
|
+
reason: 'Personal leave'
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
console.log('Activity started:', activityInstance.id);
|
|
713
|
+
console.log('Current state:', activityInstance.currentState);
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
### Executing State Transfers
|
|
717
|
+
|
|
718
|
+
```javascript
|
|
719
|
+
// Execute interaction to trigger state transfer
|
|
720
|
+
const result = await controller.executeActivityInteraction(
|
|
721
|
+
activityInstance.id,
|
|
722
|
+
'SubmitLeaveRequest',
|
|
723
|
+
{
|
|
724
|
+
requestId: activityInstance.data.requestId
|
|
725
|
+
},
|
|
726
|
+
{
|
|
727
|
+
user: { id: 'user123', role: 'employee' }
|
|
728
|
+
}
|
|
729
|
+
);
|
|
730
|
+
|
|
731
|
+
console.log('New state:', result.newState);
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
### Querying Activity State
|
|
735
|
+
|
|
736
|
+
```javascript
|
|
737
|
+
// Get current state of activity instance
|
|
738
|
+
const currentActivity = await controller.getActivity(activityInstance.id);
|
|
739
|
+
console.log('Current state:', currentActivity.currentState);
|
|
740
|
+
console.log('State history:', currentActivity.stateHistory);
|
|
741
|
+
console.log('Available transitions:', currentActivity.availableTransitions);
|
|
742
|
+
|
|
743
|
+
// Check if specific transfer can be executed
|
|
744
|
+
const canApprove = await controller.canExecuteTransition(
|
|
745
|
+
activityInstance.id,
|
|
746
|
+
'ApproveLeaveRequest',
|
|
747
|
+
{ user: { id: 'manager123', role: 'manager' } }
|
|
748
|
+
);
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
### Activity Lifecycle Management
|
|
752
|
+
|
|
753
|
+
```javascript
|
|
754
|
+
// Pause activity
|
|
755
|
+
await controller.pauseActivity(activityInstance.id);
|
|
756
|
+
|
|
757
|
+
// Resume activity
|
|
758
|
+
await controller.resumeActivity(activityInstance.id);
|
|
759
|
+
|
|
760
|
+
// Terminate activity
|
|
761
|
+
await controller.terminateActivity(activityInstance.id, 'User cancelled');
|
|
762
|
+
|
|
763
|
+
// Query activity history
|
|
764
|
+
const history = await controller.getActivityHistory(activityInstance.id);
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
## Best Practices
|
|
768
|
+
|
|
769
|
+
### 1. State Design Principles
|
|
770
|
+
|
|
771
|
+
```javascript
|
|
772
|
+
// ✅ Clear state naming
|
|
773
|
+
const states = [
|
|
774
|
+
'draft',
|
|
775
|
+
'submitted',
|
|
776
|
+
'under_review',
|
|
777
|
+
'approved',
|
|
778
|
+
'rejected',
|
|
779
|
+
'published'
|
|
780
|
+
];
|
|
781
|
+
|
|
782
|
+
// ❌ Vague state naming
|
|
783
|
+
const states = [
|
|
784
|
+
'state1',
|
|
785
|
+
'processing',
|
|
786
|
+
'done',
|
|
787
|
+
'error'
|
|
788
|
+
];
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
### 2. Transfer Condition Design
|
|
792
|
+
|
|
793
|
+
```javascript
|
|
794
|
+
// ✅ Simple and clear conditions
|
|
795
|
+
const SimpleCondition = Condition.create({
|
|
796
|
+
name: 'IsManager',
|
|
797
|
+
condition: (context) => context.user.role === 'manager'
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
// ❌ Overly complex conditions
|
|
801
|
+
const ComplexCondition = Condition.create({
|
|
802
|
+
name: 'ComplexCheck',
|
|
803
|
+
condition: async (context) => {
|
|
804
|
+
// Avoid executing complex business logic in conditions
|
|
805
|
+
const result1 = await someComplexCalculation();
|
|
806
|
+
const result2 = await anotherComplexOperation();
|
|
807
|
+
return result1 && result2 && someOtherCondition();
|
|
808
|
+
}
|
|
809
|
+
});
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
### 3. Error Handling
|
|
813
|
+
|
|
814
|
+
```javascript
|
|
815
|
+
// ✅ Activities that include error handling
|
|
816
|
+
const RobustActivity = Activity.create({
|
|
817
|
+
name: 'RobustActivity',
|
|
818
|
+
states: ['processing', 'completed', 'failed', 'error'],
|
|
819
|
+
transfers: [
|
|
820
|
+
// Normal flow
|
|
821
|
+
Transfer.create({
|
|
822
|
+
from: 'processing',
|
|
823
|
+
to: 'completed',
|
|
824
|
+
interaction: CompleteTask
|
|
825
|
+
}),
|
|
826
|
+
// Error handling
|
|
827
|
+
Transfer.create({
|
|
828
|
+
from: 'processing',
|
|
829
|
+
to: 'error',
|
|
830
|
+
interaction: HandleError,
|
|
831
|
+
condition: Condition.create({
|
|
832
|
+
name: 'ErrorOccurred',
|
|
833
|
+
condition: (context) => context.payload.hasError === true
|
|
834
|
+
})
|
|
835
|
+
})
|
|
836
|
+
]
|
|
837
|
+
});
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
### 4. Performance Considerations
|
|
841
|
+
|
|
842
|
+
```javascript
|
|
843
|
+
// ✅ Efficient condition checking
|
|
844
|
+
const EfficientCondition = Condition.create({
|
|
845
|
+
name: 'EfficientCondition',
|
|
846
|
+
condition: async (context) => {
|
|
847
|
+
// Check simple conditions first
|
|
848
|
+
if (context.user.role === 'admin') {
|
|
849
|
+
return true;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// Then check conditions requiring database queries
|
|
853
|
+
const permission = await context.findOne('Permission', {
|
|
854
|
+
user: context.user.id,
|
|
855
|
+
resource: context.payload.resourceId
|
|
856
|
+
});
|
|
857
|
+
|
|
858
|
+
return !!permission;
|
|
859
|
+
}
|
|
860
|
+
});
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
The activity system provides interaqt with powerful process management capabilities. Through proper design of activities, states, and transfers, you can implement complex business process control while maintaining code clarity and maintainability.
|