feat(p3.5): oapi 增 GET /tasks/done 与 /cc——已办/抄送按签名方应用过滤(P2-02 接缝)
build / build-push (push) Successful in 1m11s
build / build-push (push) Successful in 1m11s
- 两端点复用 FlowQueryService#pageDone/pageCc;过滤键=验签身份 appKey(请求属性 yh.app.auth → d.appKey),不收调用方 bizType——天然覆盖消费方新旧 bizType 存量、 防共享定义跨应用泄漏 - 契约测试 5 例(standalone 无 DB):appKey 接缝下传/bizType 不参与过滤/必参缺失 400 零查询 - 全量 262/262 绿(本地含 DB;Stage S5/P3.5)
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package com.yunhai.flowcenter.controller.oapi;
|
||||
|
||||
import com.yunhai.flowcenter.model.FlowModels;
|
||||
import com.yunhai.flowcenter.model.OapiModels;
|
||||
import com.yunhai.flowcenter.service.FlowQueryService;
|
||||
import com.yunhai.flowcenter.service.FlowRouterAdminService;
|
||||
import com.yunhai.flowcenter.service.OapiFlowService;
|
||||
import com.yunhai.platform.utils.model.BasePageResult;
|
||||
@@ -34,6 +36,8 @@ import java.util.List;
|
||||
* - GET /oapi/flow/tasks/todo?schId=&assigneeId=&bizType=&page=&size= → data=分页待办
|
||||
* - GET /oapi/flow/tasks/by-biz?schId=&bizType=&bizId=&operatorId= → data=当前人在该单据的待办(0/1/N)
|
||||
* - GET /oapi/flow/instances?schId=&bizType=&status=&page=&size= → data=分页办件
|
||||
* - GET /oapi/flow/tasks/done?schId=&assigneeId=&page=&size= → data=分页已办(过滤=签名 appKey→d.appKey,P3.5)
|
||||
* - GET /oapi/flow/cc?schId=&userId=&page=&size= → data=分页抄送我(过滤=签名 appKey→d.appKey,P3.5)
|
||||
* - POST /oapi/flow/task/{taskId}/approve?schId=&operatorId=&operatorName=(body: opinion/edgeName)
|
||||
* - POST /oapi/flow/task/{taskId}/reject?schId=&operatorId=&operatorName=(body: opinion 必填)
|
||||
*/
|
||||
@@ -44,6 +48,7 @@ public class OapiFlowController {
|
||||
|
||||
private final OapiFlowService flowService;
|
||||
private final FlowRouterAdminService routerAdminService;
|
||||
private final FlowQueryService queryService;
|
||||
|
||||
@GetMapping("/list")
|
||||
public List<OapiModels.FlowSummaryVo> list(@RequestParam("schId") Long schId) {
|
||||
@@ -173,6 +178,35 @@ public class OapiFlowController {
|
||||
return flowService.tasksByBiz(schId, bizType, bizId, operatorId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 我的已办(P3.5 应用内审批四页签):过滤接缝=<b>签名方应用身份</b>(d.appKey=验签 appKey),
|
||||
* 不收调用方 bizType 裸透传——办件挂本应用定义即含(天然覆盖新旧 bizType 存量),共享定义
|
||||
* 跨应用不泄漏(P2-02 裁决)。assigneeId 由子应用后端从 CAS 会话取。
|
||||
*/
|
||||
@GetMapping("/tasks/done")
|
||||
public BasePageResult<FlowModels.TaskItem> tasksDone(
|
||||
@RequestParam("schId") Long schId,
|
||||
@RequestParam("assigneeId") Integer assigneeId,
|
||||
@RequestParam(value = "page", required = false) Integer page,
|
||||
@RequestParam(value = "size", required = false) Integer size,
|
||||
@RequestAttribute(value = "yh.app.auth", required = false) String appKey) {
|
||||
return queryService.pageDone(schId, assigneeId, appKey, page, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 抄送我的(P3.5,NOTIFY 节点落库行,sent_time 倒序):过滤接缝同 tasks/done
|
||||
* (d.appKey=签名 appKey,不收 bizType)。userId 由子应用后端从 CAS 会话取。
|
||||
*/
|
||||
@GetMapping("/cc")
|
||||
public BasePageResult<FlowModels.CcItem> cc(
|
||||
@RequestParam("schId") Long schId,
|
||||
@RequestParam("userId") Integer userId,
|
||||
@RequestParam(value = "page", required = false) Integer page,
|
||||
@RequestParam(value = "size", required = false) Integer size,
|
||||
@RequestAttribute(value = "yh.app.auth", required = false) String appKey) {
|
||||
return queryService.pageCc(schId, userId, appKey, page, size);
|
||||
}
|
||||
|
||||
/** 办件分页(oapi):bizType/status 可选过滤 */
|
||||
@GetMapping("/instances")
|
||||
public BasePageResult<OapiModels.InstanceItemVo> instances(
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.yunhai.flowcenter.oapi;
|
||||
|
||||
import com.yunhai.flowcenter.controller.oapi.OapiFlowController;
|
||||
import com.yunhai.flowcenter.model.FlowModels;
|
||||
import com.yunhai.flowcenter.service.FlowQueryService;
|
||||
import com.yunhai.flowcenter.service.FlowRouterAdminService;
|
||||
import com.yunhai.flowcenter.service.OapiFlowService;
|
||||
import com.yunhai.platform.utils.model.BasePageResult;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* P3.5 新增 oapi 端点(/oapi/flow/tasks/done、/oapi/flow/cc)的控制器契约测试(standalone,无 DB)。
|
||||
* 核心断言=<b>过滤接缝</b>(P2-02 裁决):过滤键是验签身份 appKey(请求属性 yh.app.auth →
|
||||
* queryService 第三参,落 d.appKey 过滤),<b>不收调用方 bizType</b>——bizType 参数即使传了
|
||||
* 也不参与过滤(防裸透传/跨应用探查)。数据正确性由既有 pageDone/pageCc 的 IT 覆盖,
|
||||
* 本类只钉 HTTP 契约与接缝。
|
||||
*/
|
||||
@DisplayName("P3.5 oapi done/cc - 签名 appKey 过滤接缝契约")
|
||||
class OapiFlowP35DoneCcContractTest {
|
||||
|
||||
private FlowQueryService queryService;
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
OapiFlowService flowService = mock(OapiFlowService.class);
|
||||
FlowRouterAdminService routerAdminService = mock(FlowRouterAdminService.class);
|
||||
queryService = mock(FlowQueryService.class);
|
||||
OapiFlowController controller = new OapiFlowController(flowService, routerAdminService, queryService);
|
||||
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
|
||||
when(queryService.pageDone(any(), any(), any(), any(), any()))
|
||||
.thenReturn(emptyPage());
|
||||
when(queryService.pageCc(any(), any(), any(), any(), any()))
|
||||
.thenReturn(new BasePageResult<>(Collections.emptyList(), 0, 1, 10, 0, false));
|
||||
}
|
||||
|
||||
private static BasePageResult<FlowModels.TaskItem> emptyPage() {
|
||||
return new BasePageResult<>(Collections.emptyList(), 0, 1, 10, 0, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("done:验签 appKey 作为过滤键下传(d.appKey 接缝),分页参数透传")
|
||||
void donePassesSigningAppKeyAsFilter() throws Exception {
|
||||
mockMvc.perform(get("/oapi/flow/tasks/done")
|
||||
.param("schId", "3").param("assigneeId", "10006")
|
||||
.param("page", "2").param("size", "10")
|
||||
.requestAttr("yh.app.auth", "100008"))
|
||||
.andExpect(status().isOk());
|
||||
verify(queryService).pageDone(eq(3L), eq(10006), eq("100008"), eq(2), eq(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("done:调用方带 bizType 也不参与过滤(防裸透传)")
|
||||
void doneIgnoresCallerBizType() throws Exception {
|
||||
mockMvc.perform(get("/oapi/flow/tasks/done")
|
||||
.param("schId", "3").param("assigneeId", "10006")
|
||||
.param("bizType", "svc-hall")
|
||||
.requestAttr("yh.app.auth", "100008"))
|
||||
.andExpect(status().isOk());
|
||||
// 第三参(过滤键)必须是签名 appKey,不是 bizType
|
||||
verify(queryService).pageDone(eq(3L), eq(10006), eq("100008"), Mockito.isNull(), Mockito.isNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("done:缺 schId/assigneeId → 400,零查询")
|
||||
void doneMissingRequiredParams() throws Exception {
|
||||
mockMvc.perform(get("/oapi/flow/tasks/done").param("schId", "3"))
|
||||
.andExpect(status().is4xxClientError());
|
||||
verify(queryService, never()).pageDone(any(), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("cc:验签 appKey 过滤键 + userId 维度")
|
||||
void ccPassesSigningAppKeyAsFilter() throws Exception {
|
||||
mockMvc.perform(get("/oapi/flow/cc")
|
||||
.param("schId", "3").param("userId", "10006")
|
||||
.param("page", "1").param("size", "20")
|
||||
.requestAttr("yh.app.auth", "100008"))
|
||||
.andExpect(status().isOk());
|
||||
verify(queryService).pageCc(eq(3L), eq(10006), eq("100008"), eq(1), eq(20));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("cc:缺 userId → 400,零查询")
|
||||
void ccMissingUserId() throws Exception {
|
||||
mockMvc.perform(get("/oapi/flow/cc").param("schId", "3"))
|
||||
.andExpect(status().is4xxClientError());
|
||||
verify(queryService, never()).pageCc(any(), any(), any(), any(), any());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user