package com.sinata.appointment.service;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.sinata.appointment.constants.ApprovalNodeCode;
|
import com.sinata.appointment.constants.ApprovalTaskStatus;
|
import com.sinata.appointment.dto.ApprovalFlowGroupVo;
|
import com.sinata.appointment.dto.ApprovalFlowNodeVo;
|
import com.sinata.appointment.dto.ApprovalFlowTaskUserVo;
|
import com.sinata.appointment.dto.MiniVisitedUserVo;
|
import com.sinata.appointment.entity.AppointmentOrder;
|
import com.sinata.appointment.entity.ApprovalInstance;
|
import com.sinata.appointment.entity.ApprovalTask;
|
import com.sinata.appointment.entity.BizApprovalNodeConfig;
|
import com.sinata.appointment.entity.MpStaffGroup;
|
import com.sinata.appointment.entity.MpUser;
|
import com.sinata.appointment.entity.SysDept;
|
import com.sinata.appointment.mapper.AppointmentOrderMapper;
|
import com.sinata.appointment.mapper.ApprovalInstanceMapper;
|
import com.sinata.appointment.mapper.ApprovalTaskMapper;
|
import com.sinata.appointment.mapper.BizApprovalNodeConfigMapper;
|
import com.sinata.appointment.mapper.MpStaffGroupMapper;
|
import com.sinata.appointment.mapper.MpUserMapper;
|
import com.sinata.appointment.mapper.SysDeptMapper;
|
import com.sinata.appointment.util.UuidAssert;
|
import java.util.ArrayList;
|
import java.util.LinkedHashMap;
|
import java.util.LinkedHashSet;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Objects;
|
import java.util.Set;
|
import java.util.stream.Collectors;
|
import lombok.RequiredArgsConstructor;
|
import org.springframework.stereotype.Service;
|
import org.springframework.util.StringUtils;
|
|
@Service
|
@RequiredArgsConstructor
|
public class ApprovalFlowViewApplicationService {
|
|
private static final TypeReference<List<String>> STRING_LIST_TYPE = new TypeReference<>() {};
|
|
private final ApprovalInstanceMapper approvalInstanceMapper;
|
private final ApprovalTaskMapper approvalTaskMapper;
|
private final BizApprovalNodeConfigMapper bizApprovalNodeConfigMapper;
|
private final AppointmentOrderMapper appointmentOrderMapper;
|
private final MpUserMapper mpUserMapper;
|
private final SysDeptMapper sysDeptMapper;
|
private final MpStaffGroupMapper mpStaffGroupMapper;
|
private final ObjectMapper objectMapper;
|
|
public List<ApprovalFlowNodeVo> treeByOrderId(Long orderId) {
|
return treeByOrderId(orderId, null);
|
}
|
|
public List<ApprovalFlowNodeVo> treeByOrderId(Long orderId, String orderUuid) {
|
if (orderId == null || orderId <= 0) {
|
return List.of();
|
}
|
ApprovalInstance inst = loadLatestInstance(orderId);
|
if (inst == null) {
|
return List.of();
|
}
|
AppointmentOrder order = appointmentOrderMapper.selectById(orderId);
|
if (order != null && StringUtils.hasText(orderUuid)) {
|
UuidAssert.match(order, orderUuid);
|
}
|
List<ApprovalTask> tasks =
|
approvalTaskMapper.selectList(
|
new LambdaQueryWrapper<ApprovalTask>()
|
.eq(ApprovalTask::getDelFlag, 0)
|
.eq(ApprovalTask::getInstanceId, inst.getId())
|
.orderByAsc(ApprovalTask::getCreateTime)
|
.orderByAsc(ApprovalTask::getId));
|
if (tasks.isEmpty()) {
|
return List.of();
|
}
|
|
Map<String, String> nodeNameMap = loadNodeNameMap();
|
Map<Long, MpUser> userMap = loadUserMap(tasks);
|
Map<Long, String> deptNameMap = loadDeptNameMap(tasks);
|
Map<Long, String> staffGroupNameMap = loadStaffGroupNameMap(tasks);
|
List<String> localUnitNames = parseStringList(order == null ? null : order.getPassLocalUnitNames());
|
|
LinkedHashMap<String, LinkedHashMap<String, List<ApprovalTask>>> byNode = new LinkedHashMap<>();
|
for (ApprovalTask t : tasks) {
|
byNode.computeIfAbsent(t.getNodeCode(), k -> new LinkedHashMap<>())
|
.computeIfAbsent(t.getGroupKey(), k -> new ArrayList<>())
|
.add(t);
|
}
|
|
List<ApprovalFlowNodeVo> out = new ArrayList<>();
|
int tmpIdx = 1;
|
for (Map.Entry<String, LinkedHashMap<String, List<ApprovalTask>>> nodeEntry : byNode.entrySet()) {
|
String nodeCode = nodeEntry.getKey();
|
ApprovalFlowNodeVo nodeVo = new ApprovalFlowNodeVo();
|
nodeVo.setNodeCode(nodeCode);
|
nodeVo.setNodeName(nodeNameMap.getOrDefault(nodeCode, nodeCode));
|
|
for (Map.Entry<String, List<ApprovalTask>> groupEntry : nodeEntry.getValue().entrySet()) {
|
String groupKey = groupEntry.getKey();
|
List<ApprovalTask> groupTasks = groupEntry.getValue();
|
ApprovalFlowGroupVo g = new ApprovalFlowGroupVo();
|
g.setGroupKey(groupKey);
|
GroupMeta meta = resolveGroupMeta(nodeCode, groupKey, deptNameMap, staffGroupNameMap, localUnitNames, tmpIdx);
|
if ("TEMP_GROUP".equals(meta.groupType())) {
|
tmpIdx++;
|
}
|
g.setGroupType(meta.groupType());
|
g.setGroupName(meta.groupName());
|
g.setGroupStatus(calcStatus(groupTasks));
|
|
for (ApprovalTask t : groupTasks) {
|
ApprovalFlowTaskUserVo u = new ApprovalFlowTaskUserVo();
|
u.setTaskId(t.getId());
|
u.setUserId(t.getAssigneeUserId());
|
MpUser mp = userMap.get(t.getAssigneeUserId());
|
u.setUserName(resolveUserName(mp));
|
u.setAvatarUrl(mp == null ? null : mp.getAvatarUrl());
|
u.setTaskStatus(t.getTaskStatus());
|
u.setComment(t.getComment());
|
u.setActionTime(t.getUpdateTime());
|
u.setNoticeUsers(loadNoticeUsers(t.getNoticeIds(), userMap, deptNameMap));
|
g.getUsers().add(u);
|
}
|
nodeVo.getGroups().add(g);
|
}
|
nodeVo.setNodeStatus(calcNodeStatus(nodeVo.getGroups()));
|
out.add(nodeVo);
|
}
|
return out;
|
}
|
|
private ApprovalInstance loadLatestInstance(Long orderId) {
|
List<ApprovalInstance> list =
|
approvalInstanceMapper.selectList(
|
new LambdaQueryWrapper<ApprovalInstance>()
|
.eq(ApprovalInstance::getDelFlag, 0)
|
.eq(ApprovalInstance::getOrderId, orderId)
|
.orderByDesc(ApprovalInstance::getId));
|
return list.isEmpty() ? null : list.get(0);
|
}
|
|
private Map<String, String> loadNodeNameMap() {
|
List<BizApprovalNodeConfig> rows =
|
bizApprovalNodeConfigMapper.selectList(
|
new LambdaQueryWrapper<BizApprovalNodeConfig>()
|
.eq(BizApprovalNodeConfig::getDelFlag, 0)
|
.orderByAsc(BizApprovalNodeConfig::getSortOrder));
|
Map<String, String> map = new LinkedHashMap<>();
|
for (BizApprovalNodeConfig row : rows) {
|
map.put(row.getNodeCode(), row.getNodeName());
|
}
|
return map;
|
}
|
|
private Map<Long, MpUser> loadUserMap(List<ApprovalTask> tasks) {
|
Set<Long> ids = new LinkedHashSet<>();
|
for (ApprovalTask t : tasks) {
|
if (t.getAssigneeUserId() != null && t.getAssigneeUserId() > 0) {
|
ids.add(t.getAssigneeUserId());
|
}
|
}
|
if (ids.isEmpty()) {
|
return Map.of();
|
}
|
List<MpUser> rows =
|
mpUserMapper.selectList(
|
new LambdaQueryWrapper<MpUser>()
|
.eq(MpUser::getDelFlag, 0)
|
.in(MpUser::getId, ids));
|
Map<Long, MpUser> out = new LinkedHashMap<>();
|
for (MpUser row : rows) {
|
out.put(row.getId(), row);
|
}
|
return out;
|
}
|
|
private Map<Long, String> loadDeptNameMap(List<ApprovalTask> tasks) {
|
Set<Long> deptIds = new LinkedHashSet<>();
|
for (ApprovalTask t : tasks) {
|
Long deptId = parseTailId(t.getGroupKey(), ":DEPT:");
|
if (deptId != null && deptId > 0) {
|
deptIds.add(deptId);
|
}
|
}
|
if (deptIds.isEmpty()) {
|
return Map.of();
|
}
|
List<SysDept> rows =
|
sysDeptMapper.selectList(
|
new LambdaQueryWrapper<SysDept>()
|
.eq(SysDept::getDelFlag, 0)
|
.in(SysDept::getId, deptIds));
|
Map<Long, String> out = new LinkedHashMap<>();
|
for (SysDept row : rows) {
|
out.put(row.getId(), row.getDeptName());
|
}
|
return out;
|
}
|
|
private Map<Long, String> loadStaffGroupNameMap(List<ApprovalTask> tasks) {
|
Set<Long> ids = new LinkedHashSet<>();
|
for (ApprovalTask t : tasks) {
|
Long gid = parseTailId(t.getGroupKey(), ":GROUP:");
|
if (gid != null && gid > 0) {
|
ids.add(gid);
|
}
|
}
|
if (ids.isEmpty()) {
|
return Map.of();
|
}
|
List<MpStaffGroup> rows =
|
mpStaffGroupMapper.selectList(
|
new LambdaQueryWrapper<MpStaffGroup>()
|
.eq(MpStaffGroup::getDelFlag, 0)
|
.in(MpStaffGroup::getId, ids));
|
Map<Long, String> out = new LinkedHashMap<>();
|
for (MpStaffGroup row : rows) {
|
out.put(row.getId(), row.getGroupName());
|
}
|
return out;
|
}
|
|
private GroupMeta resolveGroupMeta(
|
String nodeCode,
|
String groupKey,
|
Map<Long, String> deptNameMap,
|
Map<Long, String> staffGroupNameMap,
|
List<String> localUnitNames,
|
int tmpIdx) {
|
if (!ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER.equals(nodeCode)) {
|
return new GroupMeta("DEFAULT", "审批人");
|
}
|
Long deptId = parseTailId(groupKey, ":DEPT:");
|
if (deptId != null) {
|
String name = deptNameMap.getOrDefault(deptId, "部门#" + deptId);
|
return new GroupMeta("DEPT", name + "审批组");
|
}
|
Long gid = parseTailId(groupKey, ":GROUP:");
|
if (gid != null) {
|
String name = staffGroupNameMap.getOrDefault(gid, "分组#" + gid);
|
return new GroupMeta("STAFF_GROUP", name + "审批组");
|
}
|
if (groupKey != null && groupKey.contains(":TMP:")) {
|
String unitName =
|
localUnitNames.isEmpty()
|
? null
|
: (localUnitNames.size() == 1
|
? localUnitNames.get(0)
|
: localUnitNames.get(Math.min(tmpIdx - 1, localUnitNames.size() - 1)));
|
String label = StringUtils.hasText(unitName) ? unitName + "临时负责组" : "临时负责组";
|
return new GroupMeta("TEMP_GROUP", label);
|
}
|
return new GroupMeta("DEFAULT", "审批组");
|
}
|
|
private static Long parseTailId(String groupKey, String token) {
|
if (!StringUtils.hasText(groupKey) || !groupKey.contains(token)) {
|
return null;
|
}
|
int idx = groupKey.lastIndexOf(token);
|
if (idx < 0) {
|
return null;
|
}
|
String raw = groupKey.substring(idx + token.length());
|
if (!StringUtils.hasText(raw)) {
|
return null;
|
}
|
try {
|
return Long.parseLong(raw.trim());
|
} catch (Exception ex) {
|
return null;
|
}
|
}
|
|
private List<String> parseStringList(String json) {
|
if (!StringUtils.hasText(json)) {
|
return List.of();
|
}
|
try {
|
List<String> rows = objectMapper.readValue(json, STRING_LIST_TYPE);
|
if (rows == null) {
|
return List.of();
|
}
|
List<String> out = new ArrayList<>();
|
for (String row : rows) {
|
if (StringUtils.hasText(row)) {
|
out.add(row.trim());
|
}
|
}
|
return out;
|
} catch (Exception ex) {
|
return List.of();
|
}
|
}
|
|
private List<MiniVisitedUserVo> loadNoticeUsers(
|
String noticeIds, Map<Long, MpUser> existingUserMap, Map<Long, String> existingDeptNameMap) {
|
List<Long> ids = parseCommaSeparatedIds(noticeIds);
|
if (ids.isEmpty()) {
|
return List.of();
|
}
|
Map<Long, MpUser> userMap = existingUserMap;
|
LinkedHashSet<Long> missingIds = ids.stream()
|
.filter(id -> !existingUserMap.containsKey(id))
|
.collect(Collectors.toCollection(LinkedHashSet::new));
|
if (!missingIds.isEmpty()) {
|
List<MpUser> extraUsers =
|
mpUserMapper.selectList(
|
new LambdaQueryWrapper<MpUser>()
|
.eq(MpUser::getDelFlag, 0)
|
.in(MpUser::getId, missingIds));
|
userMap = new LinkedHashMap<>(existingUserMap);
|
for (MpUser extraUser : extraUsers) {
|
userMap.put(extraUser.getId(), extraUser);
|
}
|
}
|
|
Map<Long, String> deptNameMap = existingDeptNameMap;
|
LinkedHashSet<Long> missingDeptIds = ids.stream()
|
.map(userMap::get)
|
.filter(Objects::nonNull)
|
.map(MpUser::getDeptId)
|
.filter(Objects::nonNull)
|
.filter(id -> id > 0 && !existingDeptNameMap.containsKey(id))
|
.collect(Collectors.toCollection(LinkedHashSet::new));
|
if (!missingDeptIds.isEmpty()) {
|
deptNameMap = new LinkedHashMap<>(existingDeptNameMap);
|
List<SysDept> depts =
|
sysDeptMapper.selectList(
|
new LambdaQueryWrapper<SysDept>()
|
.eq(SysDept::getDelFlag, 0)
|
.in(SysDept::getId, missingDeptIds));
|
for (SysDept dept : depts) {
|
deptNameMap.put(dept.getId(), dept.getDeptName());
|
}
|
}
|
|
List<MiniVisitedUserVo> result = new ArrayList<>();
|
for (Long id : ids) {
|
MpUser user = userMap.get(id);
|
if (user == null) {
|
continue;
|
}
|
MiniVisitedUserVo vo = new MiniVisitedUserVo();
|
vo.setId(user.getId());
|
vo.setRealName(resolveUserName(user));
|
vo.setDeptName(user.getDeptId() == null ? null : deptNameMap.get(user.getDeptId()));
|
result.add(vo);
|
}
|
return result;
|
}
|
|
private List<Long> parseCommaSeparatedIds(String raw) {
|
if (!StringUtils.hasText(raw)) {
|
return List.of();
|
}
|
LinkedHashSet<Long> out = new LinkedHashSet<>();
|
for (String part : raw.split(",")) {
|
if (!StringUtils.hasText(part)) {
|
continue;
|
}
|
try {
|
long id = Long.parseLong(part.trim());
|
if (id > 0) {
|
out.add(id);
|
}
|
} catch (NumberFormatException ignored) {
|
}
|
}
|
return new ArrayList<>(out);
|
}
|
|
private String calcNodeStatus(List<ApprovalFlowGroupVo> groups) {
|
if (groups.isEmpty()) {
|
return ApprovalTaskStatus.WAITING;
|
}
|
boolean hasRejected = false;
|
boolean hasCancelled = false;
|
boolean hasPending = false;
|
boolean hasWaiting = false;
|
for (ApprovalFlowGroupVo g : groups) {
|
String s = g.getGroupStatus();
|
if (ApprovalTaskStatus.REJECTED.equals(s)) {
|
hasRejected = true;
|
} else if (ApprovalTaskStatus.CANCELLED.equals(s)) {
|
hasCancelled = true;
|
} else if (ApprovalTaskStatus.PENDING.equals(s)) {
|
hasPending = true;
|
} else if (ApprovalTaskStatus.WAITING.equals(s)) {
|
hasWaiting = true;
|
}
|
}
|
if (hasRejected) {
|
return ApprovalTaskStatus.REJECTED;
|
}
|
if (hasCancelled) {
|
return ApprovalTaskStatus.CANCELLED;
|
}
|
if (hasPending) {
|
return ApprovalTaskStatus.PENDING;
|
}
|
if (hasWaiting) {
|
return ApprovalTaskStatus.WAITING;
|
}
|
return ApprovalTaskStatus.APPROVED;
|
}
|
|
private String calcStatus(List<ApprovalTask> tasks) {
|
if (tasks == null || tasks.isEmpty()) {
|
return ApprovalTaskStatus.WAITING;
|
}
|
boolean hasRejected = false;
|
boolean hasCancelled = false;
|
boolean hasPending = false;
|
boolean hasWaiting = false;
|
for (ApprovalTask t : tasks) {
|
if (ApprovalTaskStatus.REJECTED.equals(t.getTaskStatus())) {
|
hasRejected = true;
|
} else if (ApprovalTaskStatus.CANCELLED.equals(t.getTaskStatus())) {
|
hasCancelled = true;
|
} else if (ApprovalTaskStatus.PENDING.equals(t.getTaskStatus())) {
|
hasPending = true;
|
} else if (ApprovalTaskStatus.WAITING.equals(t.getTaskStatus())) {
|
hasWaiting = true;
|
}
|
}
|
if (hasRejected) {
|
return ApprovalTaskStatus.REJECTED;
|
}
|
if (hasCancelled) {
|
return ApprovalTaskStatus.CANCELLED;
|
}
|
if (hasPending) {
|
return ApprovalTaskStatus.PENDING;
|
}
|
if (hasWaiting) {
|
return ApprovalTaskStatus.WAITING;
|
}
|
return ApprovalTaskStatus.APPROVED;
|
}
|
|
private static String resolveUserName(MpUser user) {
|
if (user == null) {
|
return "";
|
}
|
if (StringUtils.hasText(user.getNickName())) {
|
return user.getNickName();
|
}
|
if (StringUtils.hasText(user.getStaffNo())) {
|
return user.getStaffNo();
|
}
|
return String.valueOf(Objects.requireNonNullElse(user.getId(), 0L));
|
}
|
|
private record GroupMeta(String groupType, String groupName) {}
|
}
|