[v2.1] A 应用内审批地基:oapi 增 GET /tasks/by-biz——按单据查当前人待办(0/1/N 均 200 空数组不泄露,校 schId/bizType/assignee 三 match,IT 11 例)
build / build-push (push) Successful in 2m2s
build / build-push (push) Successful in 2m2s
This commit is contained in:
@@ -32,6 +32,7 @@ import java.util.List;
|
||||
* 应用内审批(2026-09 第三步,原"刻意不提供审批动作"立场由该方案修订:登录身份由调用方
|
||||
* 应用按其 CAS 会话保证并以 operatorId 传入,本端按任务办理人逐字复核,审计链不断):
|
||||
* - 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=分页办件
|
||||
* - POST /oapi/flow/task/{taskId}/approve?schId=&operatorId=&operatorName=(body: opinion/edgeName)
|
||||
* - POST /oapi/flow/task/{taskId}/reject?schId=&operatorId=&operatorName=(body: opinion 必填)
|
||||
@@ -158,6 +159,20 @@ public class OapiFlowController {
|
||||
return flowService.pageTodoTasks(schId, assigneeId, bizType, page, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按业务单据查当前人待办(v2.1 业务详情卡片数据源):与 tasks/todo 互补——那个是"人→单据",
|
||||
* 这个是"单据→我的待办"。0/1/N 三种计数都由 HTTP 200 + tasks 数组表达,办件不存在与
|
||||
* 他人待办同样出空(不用于探测)。operatorId 由子应用后端从 CAS 会话取,前端不可传。
|
||||
*/
|
||||
@GetMapping("/tasks/by-biz")
|
||||
public OapiModels.ByBizTasksVo tasksByBiz(
|
||||
@RequestParam("schId") Long schId,
|
||||
@RequestParam("bizType") String bizType,
|
||||
@RequestParam("bizId") String bizId,
|
||||
@RequestParam("operatorId") Integer operatorId) {
|
||||
return flowService.tasksByBiz(schId, bizType, bizId, operatorId);
|
||||
}
|
||||
|
||||
/** 办件分页(oapi):bizType/status 可选过滤 */
|
||||
@GetMapping("/instances")
|
||||
public BasePageResult<OapiModels.InstanceItemVo> instances(
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.yunhai.flowcenter.model;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -266,4 +267,45 @@ public final class OapiModels {
|
||||
/** 多出口节点办理时所选出口名(单出口可省) */
|
||||
private String edgeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按业务单据定位的当前人待办(tasks/by-biz 出参,v2.1 应用内审批卡片数据源)。
|
||||
* 无办件与"有办件但本人无待办"同为 tasks=[] + HTTP 200,出参不区分二者(防探测)。
|
||||
*/
|
||||
@Data
|
||||
public static class ByBizTasksVo {
|
||||
/** 办件 id;无办件时 null */
|
||||
private Long instanceId;
|
||||
private String bizType;
|
||||
private String bizId;
|
||||
/** 办件状态;无办件时 null */
|
||||
private Integer status;
|
||||
/**
|
||||
* 办件当前活跃节点——**节点 id** 逗号串,原文照抄 {@code flw_instance.current_nodes},
|
||||
* 与 {@code instances}/{@code progress} 同口径(不是节点名,渲染人读名称请取
|
||||
* {@code tasks[].nodeName})。无办件时 null。
|
||||
*/
|
||||
private String currentNodes;
|
||||
/** 当前人 PENDING 任务,按 taskId 升序;无待办为空数组 */
|
||||
private List<ByBizTaskVo> tasks = new ArrayList<>();
|
||||
}
|
||||
|
||||
/** by-biz 单条待办:只含办理该任务必需的定位信息,不透出他人办理人/意见 */
|
||||
@Data
|
||||
public static class ByBizTaskVo {
|
||||
private Long taskId;
|
||||
private String nodeId;
|
||||
private String nodeName;
|
||||
private String arriveTime;
|
||||
/** 出口列表;单出口节点为空数组(与 FlowQueryService.detail 的 outlets 口径一致) */
|
||||
private List<ByBizOutletVo> outlets = new ArrayList<>();
|
||||
}
|
||||
|
||||
/** by-biz 出口(多出口节点办理时须选其一,透传给 approve 的 edgeName) */
|
||||
@Data
|
||||
public static class ByBizOutletVo {
|
||||
private String name;
|
||||
private String toNodeId;
|
||||
private String toNodeName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,6 +349,79 @@ public class OapiFlowService {
|
||||
return toPageResult(p.getTotalElements(), items, page, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按业务单据查当前人待办(v2.1 应用内审批的业务详情卡片数据源):
|
||||
* 补齐 tasks/todo 缺的那一侧——todo 是"人 → 单据列表",本方法是"单据 → 我的待办",
|
||||
* 子应用据此判断"当前人是否该办件的待办人",不必再拉全量待办在前端过滤。
|
||||
* <p>治理口径:
|
||||
* <ul>
|
||||
* <li>只返回 {@code assigneeId=operatorId} 且 status=PENDING 的任务;他人待办与"办件不存在"
|
||||
* 一律出空数组(HTTP 200),出参不区分二者,避免用本端点探测别人的办件;</li>
|
||||
* <li>租户收敛在定位查询本身({@code findBySchIdAndBizTypeAndBizId}),bizType 不匹配即空;</li>
|
||||
* <li>{@code outlets} 只为当前人的任务计算,且出边 >1 才透出(与
|
||||
* {@code FlowQueryService.detail} 的办理面板口径一致——单出口无需选择,不给前端多余分支);</li>
|
||||
* <li>不透出他人办理人、意见或定义内部 id;taskId 是办理必需的运行时身份,保留。</li>
|
||||
* </ul>
|
||||
*/
|
||||
public OapiModels.ByBizTasksVo tasksByBiz(Long schId, String bizType, String bizId, Integer operatorId) {
|
||||
if (schId == null || schId <= 0 || operatorId == null || operatorId <= 0) {
|
||||
throw new BizException(ErrorCode.PARAM_ERROR, "缺少必要参数:schId/operatorId(均须为正)");
|
||||
}
|
||||
String cleanBizType = emptyToNull(bizType);
|
||||
String cleanBizId = emptyToNull(bizId);
|
||||
if (cleanBizType == null || cleanBizId == null) {
|
||||
throw new BizException(ErrorCode.PARAM_ERROR, "缺少必要参数:bizType/bizId");
|
||||
}
|
||||
OapiModels.ByBizTasksVo vo = new OapiModels.ByBizTasksVo();
|
||||
vo.setBizType(cleanBizType);
|
||||
vo.setBizId(cleanBizId);
|
||||
FlwInstance instance = instanceRepository
|
||||
.findBySchIdAndBizTypeAndBizId(schId, cleanBizType, cleanBizId).orElse(null);
|
||||
if (instance == null) {
|
||||
return vo;
|
||||
}
|
||||
vo.setInstanceId(instance.getId());
|
||||
vo.setStatus(instance.getStatus());
|
||||
vo.setCurrentNodes(instance.getCurrentNodes());
|
||||
List<FlwTask> mine = taskRepository
|
||||
.findByInstanceIdAndStatus(instance.getId(), FlwEnums.TaskStatus.PENDING.getCode()).stream()
|
||||
.filter(t -> operatorId.equals(t.getAssigneeId()))
|
||||
.sorted(java.util.Comparator.comparing(FlwTask::getId))
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
if (mine.isEmpty()) {
|
||||
return vo;
|
||||
}
|
||||
FlwDef def = defRepository.findById(instance.getDefId()).orElse(null);
|
||||
FlowDefModel model = def == null ? null : flowEngine.parseModel(def);
|
||||
for (FlwTask t : mine) {
|
||||
vo.getTasks().add(toByBizTaskVo(t, model));
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
private OapiModels.ByBizTaskVo toByBizTaskVo(FlwTask t, FlowDefModel model) {
|
||||
OapiModels.ByBizTaskVo vo = new OapiModels.ByBizTaskVo();
|
||||
vo.setTaskId(t.getId());
|
||||
vo.setNodeId(t.getNodeId());
|
||||
vo.setNodeName(t.getNodeName());
|
||||
vo.setArriveTime(t.getArriveTime() == null ? null : t.getArriveTime().toString());
|
||||
if (model == null) {
|
||||
return vo;
|
||||
}
|
||||
List<FlowDefModel.Edge> outs = model.outEdges(t.getNodeId());
|
||||
if (outs.size() > 1) {
|
||||
for (FlowDefModel.Edge e : outs) {
|
||||
FlowDefModel.Node to = model.node(e.to);
|
||||
OapiModels.ByBizOutletVo outlet = new OapiModels.ByBizOutletVo();
|
||||
outlet.setName(e.name);
|
||||
outlet.setToNodeId(e.to);
|
||||
outlet.setToNodeName(to == null || to.name == null || to.name.isEmpty() ? e.to : to.name);
|
||||
vo.getOutlets().add(outlet);
|
||||
}
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同意(oapi,应用内审批):operatorId 由调用方应用按其登录态传入,本端按任务办理人
|
||||
* 复核({@code task.assigneeId == operatorId},不满足→业务码拒绝,应用签名不替代"谁在办")。
|
||||
|
||||
Reference in New Issue
Block a user