1+ package com .codingapi .flow .service ;
2+
3+ import com .codingapi .flow .action .IFlowAction ;
4+ import com .codingapi .flow .action .actions .CustomAction ;
5+ import com .codingapi .flow .builder .ActionBuilder ;
6+ import com .codingapi .flow .builder .FormFieldPermissionsBuilder ;
7+ import com .codingapi .flow .builder .NodeStrategyBuilder ;
8+ import com .codingapi .flow .context .GatewayContext ;
9+ import com .codingapi .flow .factory .MyFlowServiceFactory ;
10+ import com .codingapi .flow .form .DataType ;
11+ import com .codingapi .flow .form .FlowForm ;
12+ import com .codingapi .flow .form .FlowFormBuilder ;
13+ import com .codingapi .flow .form .permission .PermissionType ;
14+ import com .codingapi .flow .node .nodes .ApprovalNode ;
15+ import com .codingapi .flow .node .nodes .EndNode ;
16+ import com .codingapi .flow .node .nodes .StartNode ;
17+ import com .codingapi .flow .pojo .body .FlowAdviceBody ;
18+ import com .codingapi .flow .pojo .request .FlowActionRequest ;
19+ import com .codingapi .flow .pojo .request .FlowCreateRequest ;
20+ import com .codingapi .flow .pojo .request .FlowDetailRequest ;
21+ import com .codingapi .flow .pojo .response .FlowContent ;
22+ import com .codingapi .flow .record .FlowRecord ;
23+ import com .codingapi .flow .script .factory .FlowGroovyScriptFactory ;
24+ import com .codingapi .flow .strategy .node .FormFieldPermissionStrategy ;
25+ import com .codingapi .flow .strategy .node .OperatorLoadStrategy ;
26+ import com .codingapi .flow .strategy .node .RecordMergeStrategy ;
27+ import com .codingapi .flow .user .User ;
28+ import com .codingapi .flow .workflow .Workflow ;
29+ import com .codingapi .flow .workflow .WorkflowBuilder ;
30+ import org .junit .jupiter .api .Test ;
31+
32+ import java .util .List ;
33+ import java .util .Map ;
34+
35+ import static org .junit .jupiter .api .Assertions .assertEquals ;
36+ import static org .junit .jupiter .api .Assertions .assertTrue ;
37+
38+ /**
39+ * 合并待办详情中,各合并记录的发起人(createdOperator)应保持各自流程的发起人,
40+ * 而不是全部等于当前打开记录(record)的发起人。
41+ */
42+ public class FlowMergeDetailInitiatorTest {
43+
44+ private final MyFlowServiceFactory factory = new MyFlowServiceFactory ();
45+
46+ @ Test
47+ void detail_should_preserve_each_merge_record_creator () {
48+ // given 两个不同发起人 + 一个审批人
49+ User initiator1 = new User (1 , "initiator1" );
50+ User initiator2 = new User (2 , "initiator2" );
51+ User boss = new User (3 , "boss" );
52+ factory .userGateway .save (initiator1 );
53+ factory .userGateway .save (initiator2 );
54+ factory .userGateway .save (boss );
55+ GatewayContext .getInstance ().setFlowOperatorGateway (factory .userGateway );
56+
57+ FlowForm form = FlowFormBuilder .builder ()
58+ .name ("请假流程" )
59+ .code ("leave" )
60+ .addField ("请假人" , "name" , DataType .STRING )
61+ .addField ("请假天数" , "days" , DataType .INTEGER )
62+ .addField ("请假事由" , "reason" , DataType .STRING )
63+ .build ();
64+
65+ StartNode startNode = StartNode
66+ .builder ()
67+ .strategies (NodeStrategyBuilder .builder ()
68+ .addStrategy (new FormFieldPermissionStrategy (FormFieldPermissionsBuilder .builder ()
69+ .addPermission ("leave" , "name" , PermissionType .WRITE )
70+ .addPermission ("leave" , "days" , PermissionType .WRITE )
71+ .addPermission ("leave" , "reason" , PermissionType .WRITE )
72+ .build ()))
73+ .build ())
74+ .actions (ActionBuilder .builder ()
75+ .addAction (CustomAction .defaultAction ())
76+ .build ())
77+ .build ();
78+
79+ ApprovalNode bossNode = ApprovalNode .builder ()
80+ .name ("经理审批" )
81+ .strategies (NodeStrategyBuilder .builder ()
82+ .addStrategy (new FormFieldPermissionStrategy (FormFieldPermissionsBuilder .builder ()
83+ .addPermission ("leave" , "name" , PermissionType .WRITE )
84+ .addPermission ("leave" , "days" , PermissionType .WRITE )
85+ .addPermission ("leave" , "reason" , PermissionType .WRITE )
86+ .build ()))
87+ .addStrategy (new OperatorLoadStrategy (
88+ FlowGroovyScriptFactory .createOperatorLoadScript ("def run(request){return [3]}" ).getKey ()))
89+ .addStrategy (new RecordMergeStrategy (true ))
90+ .build ())
91+ .build ();
92+
93+ EndNode endNode = EndNode .builder ().build ();
94+ Workflow workflow = WorkflowBuilder .builder ()
95+ .title ("请假流程" )
96+ .code ("leave" )
97+ .createdOperator (initiator1 )
98+ .form (form )
99+ .addNode (startNode )
100+ .addNode (bossNode )
101+ .addNode (endNode )
102+ .build ();
103+ factory .workflowService .saveWorkflow (workflow );
104+
105+ Map <String , Object > data = Map .of ("name" , "lorne" , "days" , 1 , "reason" , "leave" );
106+
107+ // when 两个不同发起人先后发起并提交,汇聚到审批人待办(合并为一条)
108+ for (User initiator : List .of (initiator1 , initiator2 )) {
109+ List <IFlowAction > startActions = startNode .actionManager ().getActions ();
110+ FlowCreateRequest createRequest = new FlowCreateRequest ();
111+ createRequest .setWorkCode (workflow .getCode ());
112+ createRequest .setFormData (data );
113+ createRequest .setActionId (startActions .get (0 ).id ());
114+ createRequest .setOperatorId (initiator .getUserId ());
115+ factory .flowService .create (createRequest );
116+
117+ FlowRecord todoRecord = factory .flowRecordRepository .findTodoByOperator (initiator .getUserId ()).get (0 );
118+ FlowActionRequest actionRequest = new FlowActionRequest ();
119+ actionRequest .setFormData (data );
120+ actionRequest .setRecordId (todoRecord .getId ());
121+ actionRequest .setAdvice (new FlowAdviceBody (startActions .get (0 ).id (), "同意" , initiator .getUserId ()));
122+ factory .flowService .action (actionRequest );
123+ }
124+
125+ // 审批人待办汇聚为一条合并待办(cnt=2),但对应两条流程记录
126+ List <FlowRecord > bossTodoRecords = factory .flowRecordRepository .findTodoByOperator (boss .getUserId ());
127+ assertEquals (2 , bossTodoRecords .size ());
128+ assertEquals (2 , factory .flowTodoRecordRepository .findAll ().get (0 ).getMargeCount ());
129+
130+ // when 打开合并待办详情(传入其中一条流程记录 id)
131+ FlowRecord opened = bossTodoRecords .get (0 );
132+ assertEquals (2 , factory .flowRecordService .getMergeRecord (opened .getTodoKey ()).size (),
133+ "详情查询前合并关系应保持原始两条" );
134+ FlowContent detail = factory .flowService .detail (new FlowDetailRequest (opened .getId (), boss .getUserId ()));
135+
136+ // then 详情应恰好包含两条合并记录,且无重复
137+ List <FlowContent .Body > todos = detail .getTodos ();
138+ assertEquals (2 , todos .size (), "详情应返回两条合并记录,且不得因查询详情而增多" );
139+ assertEquals (2 , todos .stream ().map (FlowContent .Body ::getRecordId ).distinct ().count (),
140+ "详情返回的合并记录不得重复" );
141+
142+ // then 每条合并记录的发起人应为各自流程自身的发起人(getMergeRecord 中的真实 creator)
143+ List <FlowRecord > mergeRecords = factory .flowRecordService .getMergeRecord (opened .getTodoKey ());
144+ Map <Long , Long > recordToCreator = mergeRecords .stream ()
145+ .collect (java .util .stream .Collectors .toMap (FlowRecord ::getId , FlowRecord ::getCreateOperatorId ));
146+ assertTrue (recordToCreator .values ().stream ().distinct ().count () == 2 ,
147+ "前置条件:两条合并记录应来自不同发起人" );
148+ for (FlowContent .Body body : todos ) {
149+ assertEquals (recordToCreator .get (body .getRecordId ()), body .getCreatedOperator ().getId (),
150+ "合并记录 " + body .getRecordId () + " 的发起人应为其流程自身的发起人" );
151+ }
152+
153+ // then 查询详情不得改写入库合并状态(计数仍为 2)
154+ assertEquals (2 , factory .flowTodoRecordRepository .findAll ().get (0 ).getMargeCount (),
155+ "查询详情不得改变合并计数" );
156+ assertEquals (2 , factory .flowTodoMergeRepository .findAll ().size (),
157+ "查询详情不得新增合并关系" );
158+ }
159+ }
0 commit comments