feat(p3.5): 审批代理增 Done/Cc 两端点(中心 done/cc 中转)
build / build-push (push) Failing after 10s

- SvcApprovalClient.doneTasks/ccMine:/oapi/flow/tasks/done、/oapi/flow/cc(中心按
  签名方应用过滤,出站不传 bizType——P2-02 接缝,双值双查不适用)
- SvcApprovalController GET Done/Cc:requireOperator+身份字段入参拒绝,信封透传
- 测试 +3(client 拼装 2+controller 透传与拒绝 1),全量 166/166 绿(Stage S5/P3.5)
This commit is contained in:
chenxing
2026-10-20 00:16:20 +08:00
parent 05d2143ce1
commit 6086c4374c
4 changed files with 103 additions and 0 deletions
@@ -62,6 +62,28 @@ public class SvcApprovalController {
return approvalClient.todoTasks(op.schId, op.userId, page, size);
}
/** 我已审批(P3.5):中心 done,过滤=签名方应用身份(中心侧接缝),信封透传 */
@GetMapping("Done")
public Map<String, Object> done(BoaUser user,
@RequestParam(value = "page", required = false) Integer page,
@RequestParam(value = "size", required = false) Integer size,
@RequestParam(required = false) Map<String, String> rawParams) {
Operator op = requireOperator(user);
rejectIdentityParams(rawParams);
return approvalClient.doneTasks(op.schId, op.userId, page, size);
}
/** 抄送我的(P3.5):中心 cc,过滤=签名方应用身份(中心侧接缝),信封透传 */
@GetMapping("Cc")
public Map<String, Object> cc(BoaUser user,
@RequestParam(value = "page", required = false) Integer page,
@RequestParam(value = "size", required = false) Integer size,
@RequestParam(required = false) Map<String, String> rawParams) {
Operator op = requireOperator(user);
rejectIdentityParams(rawParams);
return approvalClient.ccMine(op.schId, op.userId, page, size);
}
/**
* 当前人在该办件上的待办(详情卡片数据源,0/1/N 均 200 空 tasks)。
* 先按会话学校定位办件行再过 Guard——跨校行在此即"办件不存在"。
@@ -93,6 +93,42 @@ public class SvcApprovalClient {
return first;
}
/**
* 我的已办(P3.5):{@code /oapi/flow/tasks/done}。中心按<b>签名方应用身份</b>过滤
* (d.appKey=本应用 appKey,由 oapi 端从验签层取),出站不传 bizType——天然覆盖新旧
* bizType 存量(P2-02 接缝),双值双查在此端点不适用(过滤键不是 bizType)。
*/
public Map<String, Object> doneTasks(Integer schId, Integer assigneeId, Integer page, Integer size) {
requireEnabled();
Map<String, String> query = new LinkedHashMap<>();
query.put("schId", String.valueOf(schId));
query.put("assigneeId", String.valueOf(assigneeId));
if (page != null) {
query.put("page", String.valueOf(page));
}
if (size != null) {
query.put("size", String.valueOf(size));
}
return openCall.get(TARGET, "/oapi/flow/tasks/done", query, Map.class);
}
/**
* 抄送我的(P3.5):{@code /oapi/flow/cc}。过滤接缝同 {@link #doneTasks}(签名 appKey)。
*/
public Map<String, Object> ccMine(Integer schId, Integer userId, Integer page, Integer size) {
requireEnabled();
Map<String, String> query = new LinkedHashMap<>();
query.put("schId", String.valueOf(schId));
query.put("userId", String.valueOf(userId));
if (page != null) {
query.put("page", String.valueOf(page));
}
if (size != null) {
query.put("size", String.valueOf(size));
}
return openCall.get(TARGET, "/oapi/flow/cc", query, Map.class);
}
/** 同意(opinion 可空;多出口 edgeName 透传) */
public void approve(Long taskId, Integer schId, Integer operatorId, String operatorName,
String opinion, String edgeName) {
@@ -195,6 +195,26 @@ class SvcApprovalControllerTest {
.andExpect(jsonPath("$.data.total").value(0));
}
@Test
@DisplayName("Done/Cc(P3.5):会话身份出站,信封透传,身份字段入参拒绝")
void doneAndCcEndpoints() throws Exception {
BoaUserContext.set(user(10006, 3));
Map<String, Object> data = new LinkedHashMap<>();
data.put("total", 0);
data.put("content", new java.util.ArrayList<>());
when(approvalClient.doneTasks(3, 10006, 1, 10)).thenReturn(data);
when(approvalClient.ccMine(3, 10006, 1, 10)).thenReturn(data);
mockMvc.perform(get("/api/apply/approval/Done"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0));
mockMvc.perform(get("/api/apply/approval/Cc"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(0));
mockMvc.perform(get("/api/apply/approval/Done").param("assigneeId", "1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(2));
}
@Test
@DisplayName("My:{total,list} → FlowPage{total,content,pageIndex,pageSize,totalPages,hasNext} 显式映射")
void myEnvelopeMapping() throws Exception {
@@ -170,6 +170,31 @@ class SvcApprovalClientTest {
assertTrue(((List<?>) out.get("tasks")).isEmpty());
}
@Test
@DisplayName("doneTasks:/oapi/flow/tasks/done,无 bizType 参数(中心按签名 appKey 过滤)")
void doneTasksAssembly() {
client.doneTasks(3, 10006, 2, 10);
@SuppressWarnings("unchecked")
ArgumentCaptor<Map<String, String>> q = ArgumentCaptor.forClass(Map.class);
verify(openCall).get(eq(TARGET), eq("/oapi/flow/tasks/done"), q.capture(), eq(Map.class));
assertEquals("3", q.getValue().get("schId"));
assertEquals("10006", q.getValue().get("assigneeId"));
assertEquals("2", q.getValue().get("page"));
assertEquals("10", q.getValue().get("size"));
assertTrue(!q.getValue().containsKey("bizType"), "P2-02 接缝:不传 bizType");
}
@Test
@DisplayName("ccMine:/oapi/flow/cc,userId 维度,无 bizType 参数")
void ccMineAssembly() {
client.ccMine(3, 10006, 1, 10);
@SuppressWarnings("unchecked")
ArgumentCaptor<Map<String, String>> q = ArgumentCaptor.forClass(Map.class);
verify(openCall).get(eq(TARGET), eq("/oapi/flow/cc"), q.capture(), eq(Map.class));
assertEquals("10006", q.getValue().get("userId"));
assertTrue(!q.getValue().containsKey("bizType"));
}
@Test
@DisplayName("approve:taskId 入路径、身份入查询串、opinion/edgeName 入 body 且 trim")
void approveAssembly() {