Spring Boot 流程引擎服务:流程定义/实例/任务、审批人解析、 回调分发、oapi 接口及单元测试。 保留远程 .gitea/workflows/build.yml 与 docker/Dockerfile, 新增 .gitignore 排除 target/ 构建产物。
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
package com.yunhai.flowcenter.service;
|
||||
|
||||
import com.yunhai.flowcenter.engine.FlowEngine;
|
||||
import com.yunhai.flowcenter.entity.FlwBranch;
|
||||
import com.yunhai.flowcenter.entity.FlwDef;
|
||||
import com.yunhai.flowcenter.entity.FlwInstance;
|
||||
import com.yunhai.flowcenter.entity.FlwTask;
|
||||
import com.yunhai.flowcenter.enums.FlwEnums;
|
||||
import com.yunhai.flowcenter.model.FlowModels;
|
||||
import com.yunhai.flowcenter.repository.FlwBranchRepository;
|
||||
import com.yunhai.flowcenter.repository.FlwDefRepository;
|
||||
import com.yunhai.flowcenter.repository.FlwInstanceRepository;
|
||||
import com.yunhai.flowcenter.repository.FlwTaskRepository;
|
||||
import com.yunhai.platform.apistd.BizException;
|
||||
import com.yunhai.platform.utils.exception.ErrorCode;
|
||||
import com.yunhai.platform.utils.model.BasePageResult;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 用户面查询(我的办件/待办/已办/办件详情)。
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class FlowQueryService {
|
||||
|
||||
private final FlwInstanceRepository instanceRepository;
|
||||
private final FlwTaskRepository taskRepository;
|
||||
private final FlwBranchRepository branchRepository;
|
||||
private final FlwDefRepository defRepository;
|
||||
private final FlowEngine flowEngine;
|
||||
|
||||
public BasePageResult<FlowModels.InstanceItem> pageMy(Long schId, Integer submitterId,
|
||||
Integer status, Integer page, Integer size) {
|
||||
Page<FlwInstance> p = instanceRepository.pageMy(schId, submitterId, status,
|
||||
PageRequest.of(normPage(page), normSize(size)));
|
||||
return toResult(p.getTotalElements(), p.getContent().stream().map(this::toInstanceItem)
|
||||
.collect(Collectors.toList()), page, size);
|
||||
}
|
||||
|
||||
public BasePageResult<FlowModels.TaskItem> pageTodo(Long schId, Integer assigneeId,
|
||||
Integer page, Integer size) {
|
||||
Page<FlwTask> p = taskRepository.pageTodo(schId, assigneeId,
|
||||
PageRequest.of(normPage(page), normSize(size)));
|
||||
List<FlowModels.TaskItem> items = p.getContent().stream()
|
||||
.map(this::toTaskItem).collect(Collectors.toList());
|
||||
fillInstanceInfo(items);
|
||||
return toResult(p.getTotalElements(), items, page, size);
|
||||
}
|
||||
|
||||
public BasePageResult<FlowModels.TaskItem> pageDone(Long schId, Integer assigneeId,
|
||||
Integer page, Integer size) {
|
||||
Page<FlwTask> p = taskRepository.pageDone(schId, assigneeId,
|
||||
PageRequest.of(normPage(page), normSize(size)));
|
||||
List<FlowModels.TaskItem> items = p.getContent().stream()
|
||||
.map(this::toTaskItem).collect(Collectors.toList());
|
||||
fillInstanceInfo(items);
|
||||
return toResult(p.getTotalElements(), items, page, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 办件详情:快照 + 任务轨迹 + 分支进度 + 本人待办标记(办理页/详情页共用数据源)。
|
||||
* viewerId 必须由控制器从 BoaUser.getId() 传入(CAS 会话 LoginUser.userId 是登录名字符串,
|
||||
* 不是数字主键,不能直接转 Integer——2026-09-12 全链测试修复)。
|
||||
*/
|
||||
public FlowModels.InstanceDetail detail(Long schId, Long instanceId, Integer viewerId) {
|
||||
FlwInstance instance = instanceRepository.findById(instanceId)
|
||||
.orElseThrow(() -> new BizException(ErrorCode.PARAM_ERROR, "办件不存在"));
|
||||
if (!instance.getSchId().equals(schId)) {
|
||||
throw new BizException(ErrorCode.FORBIDDEN, "办件不属于该学校");
|
||||
}
|
||||
FlowModels.InstanceDetail detail = new FlowModels.InstanceDetail();
|
||||
detail.setInstanceId(instance.getId());
|
||||
detail.setSchId(instance.getSchId());
|
||||
detail.setDefCode(instance.getDefCode());
|
||||
detail.setDefVersion(instance.getDefVersion());
|
||||
detail.setBizType(instance.getBizType());
|
||||
detail.setBizId(instance.getBizId());
|
||||
detail.setBizTitle(instance.getBizTitle());
|
||||
detail.setBizUrl(instance.getBizUrl());
|
||||
detail.setBizSnapshot(instance.getBizSnapshot());
|
||||
detail.setFormDataJson(instance.getFormDataJson());
|
||||
detail.setStatus(instance.getStatus());
|
||||
detail.setCurrentNodes(instance.getCurrentNodes());
|
||||
detail.setSubmitterId(instance.getSubmitterId());
|
||||
detail.setSubmitterName(instance.getSubmitterName());
|
||||
detail.setSubmitTime(str(instance.getSubmitTime()));
|
||||
detail.setFinishTime(str(instance.getFinishTime()));
|
||||
detail.setVersion(instance.getVersion());
|
||||
List<FlwTask> tasks = taskRepository.findByInstanceIdOrderByIdAsc(instanceId);
|
||||
List<Map<String, Object>> trace = new ArrayList<>();
|
||||
for (FlwTask t : tasks) {
|
||||
Map<String, Object> row = new HashMap<>();
|
||||
row.put("taskId", t.getId());
|
||||
row.put("nodeId", t.getNodeId());
|
||||
row.put("nodeName", t.getNodeName());
|
||||
row.put("assigneeId", t.getAssigneeId());
|
||||
row.put("assigneeName", t.getAssigneeName());
|
||||
row.put("status", t.getStatus());
|
||||
row.put("opinion", t.getOpinion());
|
||||
row.put("arriveTime", str(t.getArriveTime()));
|
||||
row.put("handleTime", str(t.getHandleTime()));
|
||||
row.put("deadline", str(t.getDeadline()));
|
||||
row.put("urgeCount", t.getUrgeCount());
|
||||
trace.add(row);
|
||||
}
|
||||
detail.setTrace(trace);
|
||||
List<Map<String, Object>> branches = new ArrayList<>();
|
||||
for (FlwBranch b : branchRepository.findByInstanceId(instanceId)) {
|
||||
Map<String, Object> row = new HashMap<>();
|
||||
row.put("branchKey", b.getBranchKey());
|
||||
row.put("status", b.getStatus());
|
||||
branches.add(row);
|
||||
}
|
||||
detail.setBranches(branches);
|
||||
List<Long> myPending = new ArrayList<>();
|
||||
if (viewerId != null) {
|
||||
for (FlwTask t : tasks) {
|
||||
if (viewerId.equals(t.getAssigneeId())
|
||||
&& t.getStatus() == FlwEnums.TaskStatus.PENDING.getCode()) {
|
||||
myPending.add(t.getId());
|
||||
}
|
||||
}
|
||||
detail.setMyPendingTask(!myPending.isEmpty());
|
||||
detail.setMyPendingTaskIds(myPending);
|
||||
}
|
||||
FlwDef def = defRepository.findById(instance.getDefId()).orElse(null);
|
||||
detail.setNodeSummary(def == null ? "" : FlowEngine.nodeSummary(flowEngine.parseModel(def)));
|
||||
return detail;
|
||||
}
|
||||
|
||||
/** 任务定位办件详情(办理页 /task/{taskId} 数据源:UserMsg jumpPath 只带 taskId) */
|
||||
public FlowModels.InstanceDetail taskDetail(Long schId, Long taskId, Integer viewerId) {
|
||||
FlwTask task = taskRepository.findById(taskId)
|
||||
.orElseThrow(() -> new BizException(ErrorCode.PARAM_ERROR, "任务不存在:" + taskId));
|
||||
if (!task.getSchId().equals(schId)) {
|
||||
throw new BizException(ErrorCode.FORBIDDEN, "任务不属于该学校");
|
||||
}
|
||||
return detail(schId, task.getInstanceId(), viewerId);
|
||||
}
|
||||
|
||||
private FlowModels.InstanceItem toInstanceItem(FlwInstance instance) {
|
||||
FlowModels.InstanceItem item = new FlowModels.InstanceItem();
|
||||
item.setInstanceId(instance.getId());
|
||||
item.setBizType(instance.getBizType());
|
||||
item.setBizId(instance.getBizId());
|
||||
item.setBizTitle(instance.getBizTitle());
|
||||
item.setStatus(instance.getStatus());
|
||||
item.setCurrentNodes(instance.getCurrentNodes());
|
||||
item.setSubmitterName(instance.getSubmitterName());
|
||||
item.setSubmitTime(str(instance.getSubmitTime()));
|
||||
item.setFinishTime(str(instance.getFinishTime()));
|
||||
item.setDefVersion(instance.getDefVersion());
|
||||
return item;
|
||||
}
|
||||
|
||||
private FlowModels.TaskItem toTaskItem(FlwTask t) {
|
||||
FlowModels.TaskItem item = new FlowModels.TaskItem();
|
||||
item.setTaskId(t.getId());
|
||||
item.setInstanceId(t.getInstanceId());
|
||||
item.setNodeId(t.getNodeId());
|
||||
item.setNodeName(t.getNodeName());
|
||||
item.setAssigneeName(t.getAssigneeName());
|
||||
item.setStatus(t.getStatus());
|
||||
item.setOpinion(t.getOpinion());
|
||||
item.setArriveTime(str(t.getArriveTime()));
|
||||
item.setHandleTime(str(t.getHandleTime()));
|
||||
item.setDeadline(str(t.getDeadline()));
|
||||
item.setUrgeCount(t.getUrgeCount());
|
||||
return item;
|
||||
}
|
||||
|
||||
private void fillInstanceInfo(List<FlowModels.TaskItem> items) {
|
||||
for (FlowModels.TaskItem item : items) {
|
||||
FlwInstance instance = instanceRepository.findById(item.getInstanceId()).orElse(null);
|
||||
if (instance != null) {
|
||||
item.setBizTitle(instance.getBizTitle());
|
||||
item.setBizType(instance.getBizType());
|
||||
item.setBizId(instance.getBizId());
|
||||
item.setInstanceStatus(instance.getStatus());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String str(LocalDateTime t) {
|
||||
return t == null ? null : t.toString();
|
||||
}
|
||||
|
||||
private static int normPage(Integer page) {
|
||||
return page == null || page < 1 ? 0 : page - 1;
|
||||
}
|
||||
|
||||
private static int normSize(Integer size) {
|
||||
return size == null || size < 1 || size > 100 ? 10 : size;
|
||||
}
|
||||
|
||||
private static <T> BasePageResult<T> toResult(long total, List<T> content, Integer page, Integer size) {
|
||||
int pageIndex = page == null || page < 1 ? 1 : page;
|
||||
int pageSize = size == null || size < 1 || size > 100 ? 10 : size;
|
||||
int totalPages = (int) Math.ceil(total / (double) pageSize);
|
||||
return new BasePageResult<>(content, (int) total, pageIndex, pageSize, totalPages,
|
||||
pageIndex < totalPages);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user