package com.sinata.appointment.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.sinata.appointment.config.MiniStpUtil; import com.sinata.appointment.constants.AppointmentBizConstants; import com.sinata.appointment.constants.ApprovalInstanceStatus; import com.sinata.appointment.constants.ApprovalNodeCode; import com.sinata.appointment.constants.ApprovalTaskStatus; import com.sinata.appointment.constants.BizErrorCode; import com.sinata.appointment.dto.MiniApprovalTodoVo; import com.sinata.appointment.dto.MiniApprovalActionRequest; import com.sinata.appointment.dto.MiniHandlerApprovalActionRequest; import com.sinata.appointment.dto.MiniHandlerApprovalTodoVo; import com.sinata.appointment.entity.AppointmentOrder; import com.sinata.appointment.entity.AppointmentVehicle; import com.sinata.appointment.entity.AppointmentVisitor; import com.sinata.appointment.entity.ApprovalInstance; import com.sinata.appointment.entity.ApprovalTask; import com.sinata.appointment.entity.BizApprovalNodeConfig; import com.sinata.appointment.entity.BizVisitReason; import com.sinata.appointment.entity.MpPassAccessArea; import com.sinata.appointment.entity.MpPassLocalUnit; import com.sinata.appointment.entity.MpVisitor; import com.sinata.appointment.exception.BizException; import com.sinata.appointment.mapper.AppointmentOrderMapper; import com.sinata.appointment.mapper.AppointmentVehicleMapper; import com.sinata.appointment.mapper.AppointmentVisitorMapper; import com.sinata.appointment.mapper.ApprovalInstanceMapper; import com.sinata.appointment.mapper.ApprovalTaskMapper; import com.sinata.appointment.mapper.BizApprovalNodeConfigMapper; import com.sinata.appointment.mapper.BizVisitReasonMapper; import com.sinata.appointment.mapper.MpVisitorMapper; import com.sinata.appointment.mapper.MpPassAccessAreaMapper; import com.sinata.appointment.mapper.MpPassLocalUnitMapper; import java.time.LocalDate; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Stream; import java.util.stream.Collectors; import java.util.LinkedHashMap; import lombok.extern.slf4j.Slf4j; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import com.sinata.appointment.util.UuidAssert; @Slf4j @Service @RequiredArgsConstructor public class MiniHandlerApprovalApplicationService { private final ApprovalTaskMapper approvalTaskMapper; private final ApprovalInstanceMapper approvalInstanceMapper; private final AppointmentOrderMapper appointmentOrderMapper; private final AppointmentVisitorMapper appointmentVisitorMapper; private final AppointmentVehicleMapper appointmentVehicleMapper; private final MpVisitorMapper mpVisitorMapper; private final BizVisitReasonMapper bizVisitReasonMapper; private final ApprovalEngineService approvalEngineService; private final IntegrationWechatService integrationWechatService; private final PostApprovalOrchestrationService postApprovalOrchestrationService; private final BizApprovalNodeConfigMapper bizApprovalNodeConfigMapper; private final MpPassAccessAreaMapper mpPassAccessAreaMapper; private final MpPassLocalUnitMapper mpPassLocalUnitMapper; private final ObjectMapper objectMapper; public IPage pageMyTodoAll(long page, long size) { return pageMyTodoAll(page, size, null, null); } public IPage pageMyTodoAll( long page, long size, String visitedUserNameKeyword, LocalDate visitDate) { long uid = MiniStpUtil.getLoginIdAsLong(); List tasks = approvalTaskMapper.selectList( new LambdaQueryWrapper() .eq(ApprovalTask::getAssigneeUserId, uid) .eq(ApprovalTask::getTaskStatus, ApprovalTaskStatus.PENDING) .orderByDesc(ApprovalTask::getCreateTime) .orderByDesc(ApprovalTask::getId)); if (tasks.isEmpty()) { return new Page<>(page, size); } List instanceIds = tasks.stream() .map(ApprovalTask::getInstanceId) .filter(Objects::nonNull) .toList(); Map instanceMap = loadInstanceMap(instanceIds); List orderIds = instanceMap.values().stream() .map(ApprovalInstance::getOrderId) .filter(Objects::nonNull) .toList(); Map orderMap = loadOrderMap(orderIds); Map visitorDisplayMap = loadVisitorDisplayMap(orderIds); Map plateTextMap = loadPlateTextMap(orderIds); Map reasonNameMap = loadReasonNameMap(orderMap.values()); Map nodeNameMap = loadNodeNameMap(); List rowsAll = tasks.stream() .filter(t -> matchTodoFilters(t, instanceMap, orderMap, visitorDisplayMap, visitedUserNameKeyword, visitDate)) .map(t -> toMiniTodoVo(t, instanceMap, orderMap, nodeNameMap, visitorDisplayMap, plateTextMap, reasonNameMap)) .toList(); return slicePage(page, size, rowsAll); } public IPage pageMyTodo(long page, long size) { return pageMyTodo(page, size, null, null); } public IPage pageMyTodo( long page, long size, String visitedUserNameKeyword, LocalDate visitDate) { long uid = MiniStpUtil.getLoginIdAsLong(); List tasks = approvalTaskMapper.selectList( new LambdaQueryWrapper() .eq(ApprovalTask::getAssigneeUserId, uid) .eq(ApprovalTask::getNodeCode, ApprovalNodeCode.LEVEL1_HANDLER) .eq(ApprovalTask::getTaskStatus, ApprovalTaskStatus.PENDING) .orderByDesc(ApprovalTask::getCreateTime) .orderByDesc(ApprovalTask::getId)); if (tasks.isEmpty()) { return new Page<>(page, size); } List instanceIds = tasks.stream() .map(ApprovalTask::getInstanceId) .filter(Objects::nonNull) .toList(); Map instanceMap = loadInstanceMap(instanceIds); List orderIds = instanceMap.values().stream() .map(ApprovalInstance::getOrderId) .filter(Objects::nonNull) .toList(); Map orderMap = loadOrderMap(orderIds); Map visitorDisplayMap = loadVisitorDisplayMap(orderIds); List rowsAll = tasks.stream() .filter(t -> matchTodoFilters(t, instanceMap, orderMap, visitorDisplayMap, visitedUserNameKeyword, visitDate)) .map(t -> toTodoVo(t, instanceMap, orderMap)) .toList(); return slicePage(page, size, rowsAll); } @Transactional(rollbackFor = Exception.class) public void handleAction(MiniHandlerApprovalActionRequest req) { long uid = MiniStpUtil.getLoginIdAsLong(); ApprovalTask task = approvalTaskMapper.selectById(req.getTaskId()); if (task == null || (task.getDelFlag() != null && task.getDelFlag() == 1)) { throw new BizException(BizErrorCode.APPROVAL_TASK_NOT_FOUND, "审批任务不存在"); } UuidAssert.match(task, req.getTaskUuid()); if (!ApprovalNodeCode.LEVEL1_HANDLER.equals(task.getNodeCode())) { throw new BizException(BizErrorCode.AUTH_FORBIDDEN, "仅支持经办人一级审批"); } if (!ApprovalTaskStatus.PENDING.equals(task.getTaskStatus())) { throw new BizException(BizErrorCode.APPROVAL_CONFLICT, "任务已处理"); } if (!Objects.equals(task.getAssigneeUserId(), uid)) { throw new BizException(BizErrorCode.AUTH_FORBIDDEN, "无权处理该任务"); } String comment = req.getComment() == null ? null : req.getComment().trim(); ApprovalInstance inst = approvalInstanceMapper.selectById(task.getInstanceId()); if (inst == null || (inst.getDelFlag() != null && inst.getDelFlag() == 1)) { throw new BizException(BizErrorCode.ORDER_NOT_FOUND, "审批实例不存在"); } AppointmentOrder order = appointmentOrderMapper.selectById(inst.getOrderId()); if (order == null || (order.getDelFlag() != null && order.getDelFlag() == 1)) { throw new BizException(BizErrorCode.ORDER_NOT_FOUND, "预约单不存在"); } if (Boolean.FALSE.equals(req.getApprove())) { if (!StringUtils.hasText(comment)) { throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "审批意见不能为空"); } validateRejectCommentLength(comment); task.setTaskStatus(ApprovalTaskStatus.REJECTED); task.setComment(comment); approvalTaskMapper.updateById(task); approvalEngineService.rejectInstance(inst, comment); postApprovalOrchestrationService.onOrderRejected(order, comment); return; } fillOrderApprovalExtraFields(order, req); appointmentOrderMapper.updateById(order); task.setTaskStatus(ApprovalTaskStatus.APPROVED); task.setComment(comment); approvalTaskMapper.updateById(task); approvalEngineService.markGroupSkippedExcept(task); approvalEngineService.rebuildAfterHandlerApproved(task, inst, order); ApprovalInstance latest = approvalInstanceMapper.selectById(inst.getId()); if (ApprovalInstanceStatus.APPROVED.equals(latest.getStatus())) { AppointmentOrder fresh = appointmentOrderMapper.selectById(order.getId()); postApprovalOrchestrationService.onOrderFullyApproved(fresh); return; } if (ApprovalInstanceStatus.IN_PROGRESS.equals(latest.getStatus())) { try { integrationWechatService.notifyCurrentApprovers(order, latest.getId()); } catch (Exception ex) { log.warn("notify current approvers failed, orderId={}", order.getId(), ex); } } } @Transactional(rollbackFor = Exception.class) public void handleOtherAction(MiniApprovalActionRequest req) { long uid = MiniStpUtil.getLoginIdAsLong(); ApprovalTask task = approvalTaskMapper.selectById(req.getTaskId()); if (task == null || (task.getDelFlag() != null && task.getDelFlag() == 1)) { throw new BizException(BizErrorCode.APPROVAL_TASK_NOT_FOUND, "审批任务不存在"); } UuidAssert.match(task, req.getTaskUuid()); if (ApprovalNodeCode.LEVEL1_HANDLER.equals(task.getNodeCode())) { throw new BizException(BizErrorCode.AUTH_FORBIDDEN, "经办人一级请走专用审批接口"); } if (!ApprovalTaskStatus.PENDING.equals(task.getTaskStatus())) { throw new BizException(BizErrorCode.APPROVAL_CONFLICT, "任务已处理"); } if (!Objects.equals(task.getAssigneeUserId(), uid)) { throw new BizException(BizErrorCode.AUTH_FORBIDDEN, "无权处理该任务"); } ApprovalInstance inst = approvalInstanceMapper.selectById(task.getInstanceId()); if (inst == null || (inst.getDelFlag() != null && inst.getDelFlag() == 1)) { throw new BizException(BizErrorCode.ORDER_NOT_FOUND, "审批实例不存在"); } AppointmentOrder order = appointmentOrderMapper.selectById(inst.getOrderId()); if (order == null || (order.getDelFlag() != null && order.getDelFlag() == 1)) { throw new BizException(BizErrorCode.ORDER_NOT_FOUND, "预约单不存在"); } String comment = req.getComment() == null ? null : req.getComment().trim(); appendNoticeIdsIfNeeded(task, req.getNoticeIds()); if (Boolean.FALSE.equals(req.getApprove())) { if (StringUtils.hasText(comment)) { validateRejectCommentLength(comment); } String reason = StringUtils.hasText(comment) ? comment : "拒绝"; task.setTaskStatus(ApprovalTaskStatus.REJECTED); task.setComment(reason); approvalTaskMapper.updateById(task); approvalEngineService.rejectInstance(inst, reason); postApprovalOrchestrationService.onOrderRejected(order, reason); return; } task.setTaskStatus(ApprovalTaskStatus.APPROVED); task.setComment(comment); approvalTaskMapper.updateById(task); approvalEngineService.markGroupSkippedExcept(task); if (approvalEngineService.isCurrentNodeComplete(inst)) { approvalEngineService.advanceOrComplete(inst, order); ApprovalInstance latest = approvalInstanceMapper.selectById(inst.getId()); if (ApprovalInstanceStatus.APPROVED.equals(latest.getStatus())) { AppointmentOrder fresh = appointmentOrderMapper.selectById(order.getId()); postApprovalOrchestrationService.onOrderFullyApproved(fresh); return; } if (ApprovalInstanceStatus.IN_PROGRESS.equals(latest.getStatus())) { try { integrationWechatService.notifyCurrentApprovers(order, latest.getId()); } catch (Exception ex) { log.warn("notify current approvers failed, orderId={}", order.getId(), ex); } } } } private void appendNoticeIdsIfNeeded(ApprovalTask task, List noticeIds) { if (!ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER.equals(task.getNodeCode())) { return; } List normalized = normalizeIds(noticeIds); if (normalized.isEmpty()) { return; } LinkedHashSet merged = new LinkedHashSet<>(parseNoticeIds(task.getNoticeIds())); merged.addAll(normalized); task.setNoticeIds(merged.stream().map(String::valueOf).collect(Collectors.joining(","))); } private List parseNoticeIds(String raw) { if (!StringUtils.hasText(raw)) { return List.of(); } return Stream.of(raw.split(",")) .filter(StringUtils::hasText) .map(String::trim) .map(value -> { try { return Long.parseLong(value); } catch (NumberFormatException ex) { return null; } }) .filter(Objects::nonNull) .filter(id -> id > 0) .distinct() .toList(); } private Map loadOrderMap(List orderIds) { if (orderIds.isEmpty()) { return Map.of(); } List rows = appointmentOrderMapper.selectList( new LambdaQueryWrapper() .eq(AppointmentOrder::getDelFlag, 0) .in(AppointmentOrder::getId, orderIds)); return rows.stream().collect(Collectors.toMap(AppointmentOrder::getId, x -> x, (a, b) -> a)); } private Map loadInstanceMap(List instanceIds) { if (instanceIds.isEmpty()) { return Map.of(); } List rows = approvalInstanceMapper.selectList( new LambdaQueryWrapper() .eq(ApprovalInstance::getDelFlag, 0) .in(ApprovalInstance::getId, instanceIds)); return rows.stream().collect(Collectors.toMap(ApprovalInstance::getId, x -> x, (a, b) -> a)); } private MiniHandlerApprovalTodoVo toTodoVo( ApprovalTask task, Map instanceMap, Map orderMap) { MiniHandlerApprovalTodoVo vo = new MiniHandlerApprovalTodoVo(); vo.setTaskId(task.getId()); vo.setTaskUuid(task.getUuid()); vo.setCreateTime(task.getCreateTime()); ApprovalInstance inst = instanceMap.get(task.getInstanceId()); if (inst == null) { return vo; } AppointmentOrder order = orderMap.get(inst.getOrderId()); if (order == null) { return vo; } vo.setOrderId(order.getId()); vo.setOrderUuid(order.getUuid()); vo.setOrderNo(order.getOrderNo()); vo.setVisitedUserName(order.getVisitedUserName()); vo.setVisitStartDate(order.getVisitStartDate()); vo.setVisitEndDate(order.getVisitEndDate()); vo.setVisitReasonCode(order.getVisitReasonCode()); return vo; } private Map loadNodeNameMap() { List rows = bizApprovalNodeConfigMapper.selectList( new LambdaQueryWrapper() .eq(BizApprovalNodeConfig::getDelFlag, 0) .orderByAsc(BizApprovalNodeConfig::getSortOrder)); Map out = new java.util.LinkedHashMap<>(); for (BizApprovalNodeConfig row : rows) { out.put(row.getNodeCode(), row.getNodeName()); } return out; } private MiniApprovalTodoVo toMiniTodoVo( ApprovalTask task, Map instanceMap, Map orderMap, Map nodeNameMap, Map visitorDisplayMap, Map plateTextMap, Map reasonNameMap) { MiniApprovalTodoVo vo = new MiniApprovalTodoVo(); vo.setTaskId(task.getId()); vo.setTaskUuid(task.getUuid()); vo.setCreateTime(task.getCreateTime()); vo.setNodeCode(task.getNodeCode()); vo.setNodeName(nodeNameMap.getOrDefault(task.getNodeCode(), task.getNodeCode())); ApprovalInstance inst = instanceMap.get(task.getInstanceId()); if (inst == null) { return vo; } AppointmentOrder order = orderMap.get(inst.getOrderId()); if (order == null) { return vo; } vo.setOrderId(order.getId()); vo.setOrderUuid(order.getUuid()); vo.setOrderNo(order.getOrderNo()); vo.setVisitedUserName(order.getVisitedUserName()); vo.setVisitorDisplayText(visitorDisplayMap.get(order.getId())); vo.setPlateNoText(plateTextMap.get(order.getId())); vo.setVisitStartDate(order.getVisitStartDate()); vo.setVisitEndDate(order.getVisitEndDate()); vo.setVisitReasonCode(order.getVisitReasonCode()); vo.setVisitReasonText(buildVisitReasonText(order, reasonNameMap)); vo.setVisitReasonOther(order.getVisitReasonOther()); vo.setVisitTimeText(buildVisitTimeText(order)); return vo; } private void fillOrderApprovalExtraFields(AppointmentOrder order, MiniHandlerApprovalActionRequest req) { List areaIds = normalizeIds(req.getPassAccessAreaIds()); if (areaIds.isEmpty()) { throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "通行区域不能为空"); } List areas = mpPassAccessAreaMapper.selectList( new LambdaQueryWrapper() .eq(MpPassAccessArea::getDelFlag, 0) .in(MpPassAccessArea::getId, areaIds)); Map areaNameMap = areas.stream() .collect(Collectors.toMap(MpPassAccessArea::getId, MpPassAccessArea::getAreaName, (a, b) -> a)); List areaNames = new ArrayList<>(); for (Long id : areaIds) { String name = areaNameMap.get(id); if (!StringUtils.hasText(name)) { throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "通行区域不存在: " + id); } areaNames.add(name); } boolean involve = Boolean.TRUE.equals(req.getInvolveOtherLocalUnit()); List unitIds = normalizeIds(req.getPassLocalUnitIds()); List unitNames = List.of(); if (involve) { if (unitIds.isEmpty()) { throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "涉及其他属地单位时,单位列表不能为空"); } List units = mpPassLocalUnitMapper.selectList( new LambdaQueryWrapper() .eq(MpPassLocalUnit::getDelFlag, 0) .in(MpPassLocalUnit::getId, unitIds)); Map unitNameMap = units.stream() .collect(Collectors.toMap(MpPassLocalUnit::getId, MpPassLocalUnit::getUnitName, (a, b) -> a)); List names = new ArrayList<>(); for (Long id : unitIds) { String name = unitNameMap.get(id); if (!StringUtils.hasText(name)) { throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "属地单位不存在: " + id); } names.add(name); } unitNames = names; } else { unitIds = List.of(); } order.setPassAccessAreaIds(writeJson(areaIds)); order.setPassAccessAreaNames(writeJson(areaNames)); order.setInvolveOtherLocalUnit(involve); order.setPassLocalUnitIds(writeJson(unitIds)); order.setPassLocalUnitNames(writeJson(unitNames)); } private static List normalizeIds(List ids) { if (ids == null || ids.isEmpty()) { return List.of(); } LinkedHashSet out = new LinkedHashSet<>(); for (Long id : ids) { if (id != null && id > 0) { out.add(id); } } return out.stream().toList(); } private void validateRejectCommentLength(String comment) { if (comment.length() > 30) { throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "驳回原因最多30个字"); } } private String writeJson(Object value) { try { return objectMapper.writeValueAsString(value); } catch (JsonProcessingException ex) { throw new BizException(BizErrorCode.AUTH_FORBIDDEN, "数据序列化失败"); } } private boolean matchTodoFilters( ApprovalTask task, Map instanceMap, Map orderMap, Map visitorDisplayMap, String visitedUserNameKeyword, LocalDate visitDate) { ApprovalInstance inst = instanceMap.get(task.getInstanceId()); if (inst == null) { return false; } AppointmentOrder order = orderMap.get(inst.getOrderId()); if (order == null) { return false; } if (StringUtils.hasText(visitedUserNameKeyword)) { String keyword = visitedUserNameKeyword.trim(); String visitorDisplayText = visitorDisplayMap.get(order.getId()); if (!StringUtils.hasText(visitorDisplayText) || !visitorDisplayText.contains(keyword)) { return false; } } if (visitDate != null) { if (order.getVisitStartDate() == null || order.getVisitEndDate() == null) { return false; } if (visitDate.isBefore(order.getVisitStartDate()) || visitDate.isAfter(order.getVisitEndDate())) { return false; } } return true; } private Map loadVisitorDisplayMap(List orderIds) { if (orderIds == null || orderIds.isEmpty()) { return Map.of(); } List rows = appointmentVisitorMapper.selectList( new LambdaQueryWrapper() .eq(AppointmentVisitor::getDelFlag, 0) .in(AppointmentVisitor::getOrderId, orderIds) .orderByAsc(AppointmentVisitor::getId)); Set visitorIds = rows.stream() .map(AppointmentVisitor::getMpVisitorId) .filter(Objects::nonNull) .collect(Collectors.toCollection(LinkedHashSet::new)); Map companyMap = loadVisitorCompanyMap(visitorIds); Map> grouped = new LinkedHashMap<>(); for (AppointmentVisitor row : rows) { if (row.getOrderId() == null) { continue; } String name = trimToNull(row.getName()); if (!StringUtils.hasText(name)) { continue; } String company = companyMap.get(row.getMpVisitorId()); String display = StringUtils.hasText(company) ? name + "(" + company + ")" : name; grouped.computeIfAbsent(row.getOrderId(), key -> new ArrayList<>()).add(display); } Map result = new LinkedHashMap<>(); for (Map.Entry> entry : grouped.entrySet()) { String joined = entry.getValue().stream().distinct().collect(Collectors.joining(",")); if (StringUtils.hasText(joined)) { result.put(entry.getKey(), joined); } } return result; } private Map loadVisitorCompanyMap(Set visitorIds) { if (visitorIds == null || visitorIds.isEmpty()) { return Map.of(); } List rows = mpVisitorMapper.selectList( new LambdaQueryWrapper() .eq(MpVisitor::getDelFlag, 0) .in(MpVisitor::getId, visitorIds)); Map result = new LinkedHashMap<>(); for (MpVisitor row : rows) { result.put(row.getId(), trimToNull(row.getVisitorCompany())); } return result; } private Map loadPlateTextMap(List orderIds) { if (orderIds == null || orderIds.isEmpty()) { return Map.of(); } List rows = appointmentVehicleMapper.selectList( new LambdaQueryWrapper() .eq(AppointmentVehicle::getDelFlag, 0) .in(AppointmentVehicle::getOrderId, orderIds) .orderByAsc(AppointmentVehicle::getId)); Map> grouped = new LinkedHashMap<>(); for (AppointmentVehicle row : rows) { if (row.getOrderId() == null) { continue; } String plateNo = trimToNull(row.getPlateNo()); if (!StringUtils.hasText(plateNo)) { continue; } grouped.computeIfAbsent(row.getOrderId(), key -> new ArrayList<>()).add(plateNo); } Map result = new LinkedHashMap<>(); for (Map.Entry> entry : grouped.entrySet()) { String joined = entry.getValue().stream().distinct().collect(Collectors.joining(",")); if (StringUtils.hasText(joined)) { result.put(entry.getKey(), joined); } } return result; } private Map loadReasonNameMap(java.util.Collection orders) { Set codes = orders.stream() .map(AppointmentOrder::getVisitReasonCode) .filter(StringUtils::hasText) .map(String::trim) .filter(code -> !"OTHER".equalsIgnoreCase(code)) .collect(Collectors.toCollection(LinkedHashSet::new)); if (codes.isEmpty()) { return Map.of(); } List rows = bizVisitReasonMapper.selectList( new LambdaQueryWrapper() .eq(BizVisitReason::getDelFlag, 0) .in(BizVisitReason::getReasonCode, codes)); return rows.stream().collect(Collectors.toMap(BizVisitReason::getReasonCode, BizVisitReason::getReasonName, (a, b) -> a)); } private String buildVisitReasonText(AppointmentOrder order, Map reasonNameMap) { String reasonCode = trimToNull(order.getVisitReasonCode()); if (AppointmentBizConstants.VISIT_REASON_OTHER.equalsIgnoreCase(reasonCode)) { return "其他"; } String reasonName = reasonNameMap.get(reasonCode); return StringUtils.hasText(reasonName) ? reasonName : reasonCode; } private String buildVisitTimeText(AppointmentOrder order) { if ("SINGLE".equals(order.getVisitTimeMode())) { String slotLabel = switch (trimToNull(order.getVisitTimeSlot())) { case "ALL_DAY" -> "00:00-23:59"; case "AM" -> "00:00-11:59"; case "PM" -> "12:00-23:59"; default -> ""; }; if (order.getVisitStartDate() == null) { return slotLabel; } return StringUtils.hasText(slotLabel) ? order.getVisitStartDate() + " " + slotLabel : order.getVisitStartDate().toString(); } if (order.getVisitStartDate() == null && order.getVisitEndDate() == null) { return "-"; } if (Objects.equals(order.getVisitStartDate(), order.getVisitEndDate()) || order.getVisitEndDate() == null) { return String.valueOf(order.getVisitStartDate()); } return order.getVisitStartDate() + " 至 " + order.getVisitEndDate(); } private String trimToNull(String text) { if (text == null) { return null; } String t = text.trim(); return t.isEmpty() ? null : t; } private IPage slicePage(long page, long size, List rowsAll) { long total = rowsAll == null ? 0 : rowsAll.size(); long current = Math.max(1L, page); long pageSize = Math.max(1L, size); int from = (int) Math.min(total, (current - 1) * pageSize); int to = (int) Math.min(total, from + pageSize); List records = from >= to ? List.of() : rowsAll.subList(from, to); Page out = new Page<>(current, pageSize); out.setTotal(total); out.setRecords(records); return out; } }