package com.sinata.appointment.service;
|
|
import com.alibaba.excel.EasyExcel;
|
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.type.TypeReference;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.sinata.appointment.constants.AppointmentOrderStatus;
|
import com.sinata.appointment.constants.ApprovalNodeCode;
|
import com.sinata.appointment.constants.ApprovalTaskStatus;
|
import com.sinata.appointment.dto.AppointmentAdminDetailVisitorVo;
|
import com.sinata.appointment.dto.AppointmentAdminDetailVo;
|
import com.sinata.appointment.dto.AppointmentAdminExportRow;
|
import com.sinata.appointment.dto.AppointmentAdminListVo;
|
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.BizVisitReason;
|
import com.sinata.appointment.entity.MpUser;
|
import com.sinata.appointment.entity.MpVisitor;
|
import com.sinata.appointment.entity.SysDept;
|
import com.sinata.appointment.exception.BizException;
|
import com.sinata.appointment.constants.BizErrorCode;
|
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.BizVisitReasonMapper;
|
import com.sinata.appointment.mapper.MpUserMapper;
|
import com.sinata.appointment.mapper.MpVisitorMapper;
|
import com.sinata.appointment.mapper.SysDeptMapper;
|
import com.sinata.appointment.util.MobileMaskUtil;
|
import com.sinata.appointment.util.SensitiveDataUtil;
|
import com.sinata.appointment.util.UuidAssert;
|
import java.io.ByteArrayOutputStream;
|
import java.time.LocalDate;
|
import java.time.LocalDateTime;
|
import java.time.format.DateTimeFormatter;
|
import java.util.ArrayList;
|
import java.util.Collection;
|
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 AppointmentApplicationService {
|
|
private static final String DISPLAY_STATUS_PENDING_VISIT = "PENDING_VISIT";
|
private static final String DISPLAY_STATUS_EFFECTIVE = "EFFECTIVE";
|
private static final String DISPLAY_STATUS_INVALID = "INVALID";
|
private static final TypeReference<List<String>> STRING_LIST_TYPE = new TypeReference<>() {};
|
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
private final AppointmentOrderMapper appointmentOrderMapper;
|
private final AppointmentVehicleMapper appointmentVehicleMapper;
|
private final AppointmentVisitorMapper appointmentVisitorMapper;
|
private final ApprovalInstanceMapper approvalInstanceMapper;
|
private final ApprovalTaskMapper approvalTaskMapper;
|
private final BizVisitReasonMapper bizVisitReasonMapper;
|
private final MpUserMapper mpUserMapper;
|
private final MpVisitorMapper mpVisitorMapper;
|
private final SysDeptMapper sysDeptMapper;
|
private final ObjectMapper objectMapper;
|
|
public IPage<AppointmentAdminListVo> pageOrders(
|
long page,
|
long size,
|
String visitedUserName,
|
Long visitedDeptId,
|
String accessArea,
|
String localUnit,
|
LocalDate visitDateStart,
|
LocalDate visitDateEnd,
|
String displayStatus,
|
String approvalStatus,
|
String visitorKeyword,
|
String visitReasonCode) {
|
List<AppointmentAdminListVo> all =
|
listAdminOrders(
|
visitedUserName,
|
visitedDeptId,
|
accessArea,
|
localUnit,
|
visitDateStart,
|
visitDateEnd,
|
displayStatus,
|
approvalStatus,
|
visitorKeyword,
|
visitReasonCode);
|
return slicePage(page, size, all);
|
}
|
|
public byte[] exportExcel(
|
String visitedUserName,
|
Long visitedDeptId,
|
String accessArea,
|
String localUnit,
|
LocalDate visitDateStart,
|
LocalDate visitDateEnd,
|
String displayStatus,
|
String approvalStatus,
|
String visitorKeyword,
|
String visitReasonCode) {
|
Set<Long> visitorOrderIds = findOrderIdsByVisitorKeyword(visitorKeyword);
|
if (visitorOrderIds != null && visitorOrderIds.isEmpty()) {
|
return writeExportRows(List.of());
|
}
|
|
List<AppointmentOrder> orders =
|
appointmentOrderMapper.selectList(
|
buildOrderQuery(
|
visitedUserName,
|
visitedDeptId,
|
accessArea,
|
localUnit,
|
visitDateStart,
|
visitDateEnd,
|
visitReasonCode,
|
visitorOrderIds));
|
if (orders.isEmpty()) {
|
return writeExportRows(List.of());
|
}
|
|
List<Long> orderIds = orders.stream().map(AppointmentOrder::getId).filter(Objects::nonNull).toList();
|
Map<Long, List<AppointmentVisitor>> visitorRowsMap = loadVisitorRows(orderIds);
|
Map<Long, ApprovalInstance> approvalInstanceMap = loadApprovalInstanceMap(orderIds);
|
Map<Long, String> deptNameMap = loadDeptNameMap(orders);
|
Map<String, String> reasonNameMap = loadReasonNameMap(orders);
|
|
List<AppointmentOrder> filteredOrders = new ArrayList<>();
|
for (AppointmentOrder order : orders) {
|
AppointmentAdminListVo vo =
|
toAdminListVo(
|
order,
|
deptNameMap.get(order.getVisitedDeptId()),
|
reasonNameMap,
|
visitorRowsMap.get(order.getId()),
|
approvalInstanceMap.get(order.getId()));
|
if (matchesDisplayStatus(vo, displayStatus) && matchesApprovalStatus(vo, approvalStatus)) {
|
filteredOrders.add(order);
|
}
|
}
|
|
List<AppointmentAdminExportRow> rows = buildExportRows(filteredOrders, visitorRowsMap, approvalInstanceMap, reasonNameMap);
|
return writeExportRows(rows);
|
}
|
|
private byte[] writeExportRows(List<AppointmentAdminExportRow> rows) {
|
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
|
EasyExcel.write(bos, AppointmentAdminExportRow.class).sheet("预约记录").doWrite(rows);
|
return bos.toByteArray();
|
} catch (Exception e) {
|
return new byte[0];
|
}
|
}
|
|
public AppointmentOrder getById(Long id, String uuid) {
|
AppointmentOrder o = appointmentOrderMapper.selectById(id);
|
if (o == null || o.getDelFlag() != null && o.getDelFlag() == 1) {
|
throw new BizException(BizErrorCode.ORDER_NOT_FOUND, "预约单不存在");
|
}
|
UuidAssert.match(o, uuid);
|
return o;
|
}
|
|
public AppointmentAdminDetailVo detail(Long id, String uuid) {
|
AppointmentOrder order = getById(id, uuid);
|
String deptName = loadDeptNameMap(List.of(order)).get(order.getVisitedDeptId());
|
Map<String, String> reasonNameMap = loadReasonNameMap(List.of(order));
|
List<AppointmentVisitor> visitorRows = listVisitors(id);
|
Map<Long, MpVisitor> visitorMap = loadVisitorMap(visitorRows);
|
Map<Long, String> plateMap = loadVisitorPlateMap(id);
|
ApprovalInstance approvalInstance = loadLatestInstance(id);
|
|
AppointmentAdminDetailVo vo = new AppointmentAdminDetailVo();
|
vo.setId(order.getId());
|
vo.setUuid(order.getUuid());
|
vo.setOrderNo(order.getOrderNo());
|
vo.setVisitTypeText(buildVisitTypeText(order));
|
vo.setVisitedUserName(defaultText(trimToNull(order.getVisitedUserName())));
|
vo.setVisitedDeptName(defaultText(trimToNull(deptName)));
|
vo.setAccessAreaText(defaultText(buildJsonNameText(order.getPassAccessAreaNames())));
|
vo.setLocalUnitText(defaultText(buildJsonNameText(order.getPassLocalUnitNames())));
|
vo.setPlateNoText(defaultText(joinUniqueTexts(visitorRows, AppointmentVisitor::getPlateNo)));
|
vo.setVisitReasonText(defaultText(buildVisitReasonText(order, reasonNameMap)));
|
vo.setVisitStartText(defaultText(resolveVisitStartText(order)));
|
vo.setVisitEndText(defaultText(resolveVisitEndText(order)));
|
String displayStatusCode = resolveDisplayStatus(order);
|
vo.setDisplayStatusCode(displayStatusCode);
|
vo.setDisplayStatusText(resolveDisplayStatusText(displayStatusCode));
|
String approvalStatusCode = resolveApprovalStatusCode(order, approvalInstance);
|
vo.setApprovalStatusCode(approvalStatusCode);
|
vo.setApprovalStatusText(resolveApprovalStatusText(approvalStatusCode));
|
vo.setVisitors(buildDetailVisitors(visitorRows, visitorMap, plateMap));
|
return vo;
|
}
|
|
public List<AppointmentVisitor> listVisitors(Long orderId) {
|
return appointmentVisitorMapper.selectList(
|
new LambdaQueryWrapper<AppointmentVisitor>()
|
.eq(AppointmentVisitor::getOrderId, orderId)
|
.eq(AppointmentVisitor::getDelFlag, 0)
|
.orderByAsc(AppointmentVisitor::getId));
|
}
|
|
private List<AppointmentAdminListVo> listAdminOrders(
|
String visitedUserName,
|
Long visitedDeptId,
|
String accessArea,
|
String localUnit,
|
LocalDate visitDateStart,
|
LocalDate visitDateEnd,
|
String displayStatus,
|
String approvalStatus,
|
String visitorKeyword,
|
String visitReasonCode) {
|
Set<Long> visitorOrderIds = findOrderIdsByVisitorKeyword(visitorKeyword);
|
if (visitorOrderIds != null && visitorOrderIds.isEmpty()) {
|
return List.of();
|
}
|
|
List<AppointmentOrder> orders =
|
appointmentOrderMapper.selectList(
|
buildOrderQuery(
|
visitedUserName,
|
visitedDeptId,
|
accessArea,
|
localUnit,
|
visitDateStart,
|
visitDateEnd,
|
visitReasonCode,
|
visitorOrderIds));
|
if (orders.isEmpty()) {
|
return List.of();
|
}
|
|
List<Long> orderIds = orders.stream().map(AppointmentOrder::getId).filter(Objects::nonNull).toList();
|
Map<Long, List<AppointmentVisitor>> visitorRowsMap = loadVisitorRows(orderIds);
|
Map<Long, ApprovalInstance> approvalInstanceMap = loadApprovalInstanceMap(orderIds);
|
Map<Long, String> deptNameMap = loadDeptNameMap(orders);
|
Map<String, String> reasonNameMap = loadReasonNameMap(orders);
|
|
List<AppointmentAdminListVo> result = new ArrayList<>();
|
for (AppointmentOrder order : orders) {
|
AppointmentAdminListVo vo =
|
toAdminListVo(
|
order,
|
deptNameMap.get(order.getVisitedDeptId()),
|
reasonNameMap,
|
visitorRowsMap.get(order.getId()),
|
approvalInstanceMap.get(order.getId()));
|
if (matchesDisplayStatus(vo, displayStatus) && matchesApprovalStatus(vo, approvalStatus)) {
|
result.add(vo);
|
}
|
}
|
return result;
|
}
|
|
private LambdaQueryWrapper<AppointmentOrder> buildOrderQuery(
|
String visitedUserName,
|
Long visitedDeptId,
|
String accessArea,
|
String localUnit,
|
LocalDate visitDateStart,
|
LocalDate visitDateEnd,
|
String visitReasonCode,
|
Set<Long> visitorOrderIds) {
|
LambdaQueryWrapper<AppointmentOrder> q = new LambdaQueryWrapper<>();
|
q.eq(AppointmentOrder::getDelFlag, 0);
|
q.like(StringUtils.hasText(visitedUserName), AppointmentOrder::getVisitedUserName, trim(visitedUserName));
|
q.eq(visitedDeptId != null, AppointmentOrder::getVisitedDeptId, visitedDeptId);
|
q.like(StringUtils.hasText(accessArea), AppointmentOrder::getPassAccessAreaNames, trim(accessArea));
|
q.like(StringUtils.hasText(localUnit), AppointmentOrder::getPassLocalUnitNames, trim(localUnit));
|
q.eq(StringUtils.hasText(visitReasonCode), AppointmentOrder::getVisitReasonCode, trim(visitReasonCode));
|
q.le(visitDateEnd != null, AppointmentOrder::getVisitStartDate, visitDateEnd);
|
q.ge(visitDateStart != null, AppointmentOrder::getVisitEndDate, visitDateStart);
|
if (visitorOrderIds != null) {
|
q.in(AppointmentOrder::getId, visitorOrderIds);
|
}
|
q.orderByDesc(AppointmentOrder::getCreateTime).orderByDesc(AppointmentOrder::getId);
|
return q;
|
}
|
|
private Set<Long> findOrderIdsByVisitorKeyword(String visitorKeyword) {
|
String keyword = trimToNull(visitorKeyword);
|
if (!StringUtils.hasText(keyword)) {
|
return null;
|
}
|
List<AppointmentVisitor> rows =
|
appointmentVisitorMapper.selectList(
|
new LambdaQueryWrapper<AppointmentVisitor>()
|
.eq(AppointmentVisitor::getDelFlag, 0)
|
.like(AppointmentVisitor::getName, keyword)
|
.select(AppointmentVisitor::getOrderId));
|
return rows.stream()
|
.map(AppointmentVisitor::getOrderId)
|
.filter(Objects::nonNull)
|
.collect(Collectors.toCollection(LinkedHashSet::new));
|
}
|
|
private Map<Long, MpVisitor> loadVisitorMap(List<AppointmentVisitor> rows) {
|
if (rows == null || rows.isEmpty()) {
|
return Map.of();
|
}
|
Set<Long> visitorIds =
|
rows.stream()
|
.map(AppointmentVisitor::getMpVisitorId)
|
.filter(Objects::nonNull)
|
.collect(Collectors.toCollection(LinkedHashSet::new));
|
if (visitorIds.isEmpty()) {
|
return Map.of();
|
}
|
List<MpVisitor> visitors =
|
mpVisitorMapper.selectList(
|
new LambdaQueryWrapper<MpVisitor>()
|
.eq(MpVisitor::getDelFlag, 0)
|
.in(MpVisitor::getId, visitorIds));
|
return visitors.stream().collect(Collectors.toMap(MpVisitor::getId, v -> v, (a, b) -> a, LinkedHashMap::new));
|
}
|
|
private Map<Long, String> loadVisitorPlateMap(Long orderId) {
|
List<AppointmentVehicle> rows =
|
appointmentVehicleMapper.selectList(
|
new LambdaQueryWrapper<AppointmentVehicle>()
|
.eq(AppointmentVehicle::getDelFlag, 0)
|
.eq(AppointmentVehicle::getOrderId, orderId)
|
.orderByAsc(AppointmentVehicle::getId));
|
if (rows.isEmpty()) {
|
return Map.of();
|
}
|
Map<Long, List<String>> plates = new LinkedHashMap<>();
|
for (AppointmentVehicle row : rows) {
|
if (row.getDriverVisitorId() == null || !StringUtils.hasText(row.getPlateNo())) {
|
continue;
|
}
|
plates.computeIfAbsent(row.getDriverVisitorId(), key -> new ArrayList<>());
|
plates.get(row.getDriverVisitorId()).add(row.getPlateNo().trim());
|
}
|
Map<Long, String> result = new LinkedHashMap<>();
|
for (Map.Entry<Long, List<String>> entry : plates.entrySet()) {
|
String joined =
|
entry.getValue().stream()
|
.filter(StringUtils::hasText)
|
.map(String::trim)
|
.distinct()
|
.collect(Collectors.joining("、"));
|
result.put(entry.getKey(), joined);
|
}
|
return result;
|
}
|
|
private Map<Long, List<AppointmentVisitor>> loadVisitorRows(List<Long> orderIds) {
|
if (orderIds == null || orderIds.isEmpty()) {
|
return Map.of();
|
}
|
List<AppointmentVisitor> rows =
|
appointmentVisitorMapper.selectList(
|
new LambdaQueryWrapper<AppointmentVisitor>()
|
.eq(AppointmentVisitor::getDelFlag, 0)
|
.in(AppointmentVisitor::getOrderId, orderIds)
|
.orderByAsc(AppointmentVisitor::getId));
|
return rows.stream()
|
.collect(Collectors.groupingBy(AppointmentVisitor::getOrderId, LinkedHashMap::new, Collectors.toList()));
|
}
|
|
private Map<Long, ApprovalInstance> loadApprovalInstanceMap(List<Long> orderIds) {
|
if (orderIds == null || orderIds.isEmpty()) {
|
return Map.of();
|
}
|
List<ApprovalInstance> rows =
|
approvalInstanceMapper.selectList(
|
new LambdaQueryWrapper<ApprovalInstance>()
|
.eq(ApprovalInstance::getDelFlag, 0)
|
.in(ApprovalInstance::getOrderId, orderIds)
|
.orderByDesc(ApprovalInstance::getId));
|
Map<Long, ApprovalInstance> result = new LinkedHashMap<>();
|
for (ApprovalInstance row : rows) {
|
result.putIfAbsent(row.getOrderId(), row);
|
}
|
return result;
|
}
|
|
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<Long, String> loadDeptNameMap(List<AppointmentOrder> orders) {
|
Set<Long> deptIds =
|
orders.stream()
|
.map(AppointmentOrder::getVisitedDeptId)
|
.filter(Objects::nonNull)
|
.collect(Collectors.toCollection(LinkedHashSet::new));
|
if (deptIds.isEmpty()) {
|
return Map.of();
|
}
|
List<SysDept> rows =
|
sysDeptMapper.selectList(
|
new LambdaQueryWrapper<SysDept>()
|
.eq(SysDept::getDelFlag, 0)
|
.in(SysDept::getId, deptIds));
|
return rows.stream()
|
.collect(Collectors.toMap(SysDept::getId, SysDept::getDeptName, (a, b) -> a, LinkedHashMap::new));
|
}
|
|
private Map<String, String> loadReasonNameMap(List<AppointmentOrder> orders) {
|
Set<String> codes =
|
orders.stream()
|
.map(AppointmentOrder::getVisitReasonCode)
|
.filter(StringUtils::hasText)
|
.map(String::trim)
|
.collect(Collectors.toCollection(LinkedHashSet::new));
|
if (codes.isEmpty()) {
|
return Map.of();
|
}
|
List<BizVisitReason> rows =
|
bizVisitReasonMapper.selectList(
|
new LambdaQueryWrapper<BizVisitReason>()
|
.eq(BizVisitReason::getDelFlag, 0)
|
.in(BizVisitReason::getReasonCode, codes));
|
return rows.stream()
|
.collect(
|
Collectors.toMap(
|
BizVisitReason::getReasonCode,
|
BizVisitReason::getReasonName,
|
(a, b) -> a,
|
LinkedHashMap::new));
|
}
|
|
private AppointmentAdminListVo toAdminListVo(
|
AppointmentOrder order,
|
String deptName,
|
Map<String, String> reasonNameMap,
|
List<AppointmentVisitor> visitorRows,
|
ApprovalInstance approvalInstance) {
|
AppointmentAdminListVo vo = new AppointmentAdminListVo();
|
vo.setId(order.getId());
|
vo.setUuid(order.getUuid());
|
vo.setOrderNo(order.getOrderNo());
|
vo.setVisitTypeText(buildVisitTypeText(order));
|
vo.setVisitedUserName(defaultText(trimToNull(order.getVisitedUserName())));
|
vo.setVisitedDeptName(defaultText(trimToNull(deptName)));
|
vo.setAccessAreaText(defaultText(buildJsonNameText(order.getPassAccessAreaNames())));
|
vo.setLocalUnitText(defaultText(buildJsonNameText(order.getPassLocalUnitNames())));
|
vo.setPlateNoText(defaultText(joinUniqueTexts(visitorRows, AppointmentVisitor::getPlateNo)));
|
vo.setVisitReasonText(defaultText(buildVisitReasonText(order, reasonNameMap)));
|
vo.setVisitTimeText(defaultText(buildVisitTimeText(order)));
|
vo.setVisitorNamesText(defaultText(joinUniqueTexts(visitorRows, AppointmentVisitor::getName)));
|
vo.setStatus(order.getStatus());
|
vo.setCreateTime(order.getCreateTime());
|
|
String displayStatusCode = resolveDisplayStatus(order);
|
vo.setDisplayStatusCode(displayStatusCode);
|
vo.setDisplayStatusText(resolveDisplayStatusText(displayStatusCode));
|
|
String approvalStatusCode = resolveApprovalStatusCode(order, approvalInstance);
|
vo.setApprovalStatusCode(approvalStatusCode);
|
vo.setApprovalStatusText(resolveApprovalStatusText(approvalStatusCode));
|
return vo;
|
}
|
|
private List<AppointmentAdminExportRow> buildExportRows(
|
List<AppointmentOrder> orders,
|
Map<Long, List<AppointmentVisitor>> visitorRowsMap,
|
Map<Long, ApprovalInstance> approvalInstanceMap,
|
Map<String, String> reasonNameMap) {
|
if (orders == null || orders.isEmpty()) {
|
return List.of();
|
}
|
List<Long> orderIds = orders.stream().map(AppointmentOrder::getId).filter(Objects::nonNull).toList();
|
List<AppointmentVisitor> allVisitorRows =
|
orderIds.isEmpty()
|
? List.of()
|
: visitorRowsMap.values().stream().flatMap(Collection::stream).toList();
|
Map<Long, MpVisitor> visitorMap = loadVisitorMap(allVisitorRows);
|
Map<Long, Map<Long, String>> visitorPlateMap = loadVisitorPlateMap(orderIds);
|
Map<Long, Map<String, String>> approvalNameMap = loadApprovalNodeApproverTextMap(approvalInstanceMap.values());
|
Map<Long, MpUser> submitterMap = loadSubmitterMap(orders);
|
|
List<AppointmentAdminExportRow> result = new ArrayList<>();
|
for (AppointmentOrder order : orders) {
|
AppointmentAdminListVo summary =
|
toAdminListVo(
|
order,
|
null,
|
reasonNameMap,
|
visitorRowsMap.get(order.getId()),
|
approvalInstanceMap.get(order.getId()));
|
Map<String, String> nodeApproverNames =
|
approvalNameMap.getOrDefault(order.getId(), Map.of());
|
MpUser submitter = submitterMap.get(order.getSubmitterId());
|
List<AppointmentVisitor> visitors = visitorRowsMap.get(order.getId());
|
if (visitors == null || visitors.isEmpty()) {
|
result.add(buildExportRow(summary, order, null, null, Map.of(), nodeApproverNames, submitter));
|
continue;
|
}
|
Map<Long, String> orderPlateMap = visitorPlateMap.getOrDefault(order.getId(), Map.of());
|
for (AppointmentVisitor visitorRow : visitors) {
|
MpVisitor visitor = visitorMap.get(visitorRow.getMpVisitorId());
|
result.add(buildExportRow(summary, order, visitorRow, visitor, orderPlateMap, nodeApproverNames, submitter));
|
}
|
}
|
return result;
|
}
|
|
private AppointmentAdminExportRow buildExportRow(
|
AppointmentAdminListVo summary,
|
AppointmentOrder order,
|
AppointmentVisitor visitorRow,
|
MpVisitor visitor,
|
Map<Long, String> orderPlateMap,
|
Map<String, String> nodeApproverNames,
|
MpUser submitter) {
|
AppointmentAdminExportRow row = new AppointmentAdminExportRow();
|
row.setVisitTypeText(summary.getVisitTypeText());
|
row.setVisitedUserName(summary.getVisitedUserName());
|
row.setAccessAreaText(summary.getAccessAreaText());
|
row.setLocalUnitText(summary.getLocalUnitText());
|
row.setPlateNoText(
|
defaultText(
|
firstNonBlank(
|
visitorRow == null ? null : visitorRow.getPlateNo(),
|
visitorRow == null ? null : orderPlateMap.get(visitorRow.getMpVisitorId()),
|
summary.getPlateNoText())));
|
row.setVisitReasonText(summary.getVisitReasonText());
|
row.setVisitTimeText(summary.getVisitTimeText());
|
row.setDisplayStatusText(summary.getDisplayStatusText());
|
row.setVisitorLabel(defaultText(visitorRow == null ? null : trimToNull(visitorRow.getName())));
|
row.setVisitorName(
|
defaultText(
|
firstNonBlank(
|
visitorRow == null ? null : visitorRow.getName(),
|
visitor == null ? null : visitor.getVisitorName(),
|
null)));
|
row.setIdCardText(defaultText(resolveIdCardMasked(visitorRow, visitor)));
|
row.setMobileText(
|
defaultText(
|
MobileMaskUtil.mask(
|
firstNonBlank(
|
visitorRow == null ? null : visitorRow.getMobile(),
|
visitor == null ? null : visitor.getVisitorMobile(),
|
null))));
|
row.setCompanyName(defaultText(visitor == null ? null : trimToNull(visitor.getVisitorCompany())));
|
row.setHandlerApproverNames(defaultText(nodeApproverNames.get(ApprovalNodeCode.LEVEL1_HANDLER)));
|
row.setDeptLeaderApproverNames(defaultText(nodeApproverNames.get(ApprovalNodeCode.LEVEL2_DEPT_LEADER)));
|
row.setVisitDeptApproverNames(defaultText(nodeApproverNames.get(ApprovalNodeCode.LEVEL4_VISIT_DEPT)));
|
row.setVisitDeptLeaderApproverNames(defaultText(nodeApproverNames.get(ApprovalNodeCode.LEVEL5_VISIT_DEPT_LEADER)));
|
row.setLocalDeptLeaderApproverNames(defaultText(nodeApproverNames.get(ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER)));
|
row.setSubmitterMobile(defaultText(MobileMaskUtil.mask(submitter == null ? null : trimToNull(submitter.getMobile()))));
|
row.setSubmitTimeText(defaultText(formatDateTime(order.getCreateTime())));
|
return row;
|
}
|
|
private Map<Long, MpUser> loadSubmitterMap(List<AppointmentOrder> orders) {
|
Set<Long> submitterIds =
|
orders.stream()
|
.map(AppointmentOrder::getSubmitterId)
|
.filter(Objects::nonNull)
|
.collect(Collectors.toCollection(LinkedHashSet::new));
|
if (submitterIds.isEmpty()) {
|
return Map.of();
|
}
|
List<MpUser> rows =
|
mpUserMapper.selectList(
|
new LambdaQueryWrapper<MpUser>()
|
.eq(MpUser::getDelFlag, 0)
|
.in(MpUser::getId, submitterIds));
|
return rows.stream().collect(Collectors.toMap(MpUser::getId, u -> u, (a, b) -> a, LinkedHashMap::new));
|
}
|
|
private Map<Long, Map<String, String>> loadApprovalNodeApproverTextMap(Collection<ApprovalInstance> instances) {
|
List<ApprovalInstance> instanceList =
|
instances == null ? List.of() : instances.stream().filter(Objects::nonNull).toList();
|
if (instanceList.isEmpty()) {
|
return Map.of();
|
}
|
List<Long> instanceIds = instanceList.stream().map(ApprovalInstance::getId).filter(Objects::nonNull).toList();
|
if (instanceIds.isEmpty()) {
|
return Map.of();
|
}
|
List<ApprovalTask> tasks =
|
approvalTaskMapper.selectList(
|
new LambdaQueryWrapper<ApprovalTask>()
|
.eq(ApprovalTask::getDelFlag, 0)
|
.in(ApprovalTask::getInstanceId, instanceIds)
|
.ne(ApprovalTask::getTaskStatus, ApprovalTaskStatus.SKIPPED)
|
.orderByAsc(ApprovalTask::getId));
|
if (tasks.isEmpty()) {
|
return Map.of();
|
}
|
Set<Long> userIds =
|
tasks.stream()
|
.map(ApprovalTask::getAssigneeUserId)
|
.filter(Objects::nonNull)
|
.collect(Collectors.toCollection(LinkedHashSet::new));
|
Map<Long, String> userNameMap = loadMpUserNameMap(userIds);
|
Map<Long, Long> orderIdByInstanceId =
|
instanceList.stream()
|
.collect(Collectors.toMap(ApprovalInstance::getId, ApprovalInstance::getOrderId, (a, b) -> a, LinkedHashMap::new));
|
Map<Long, Map<String, LinkedHashSet<String>>> grouped = new LinkedHashMap<>();
|
for (ApprovalTask task : tasks) {
|
Long orderId = orderIdByInstanceId.get(task.getInstanceId());
|
if (orderId == null || !StringUtils.hasText(task.getNodeCode())) {
|
continue;
|
}
|
String userName = trimToNull(userNameMap.get(task.getAssigneeUserId()));
|
if (!StringUtils.hasText(userName)) {
|
continue;
|
}
|
grouped.computeIfAbsent(orderId, key -> new LinkedHashMap<>());
|
grouped.get(orderId).computeIfAbsent(task.getNodeCode(), key -> new LinkedHashSet<>());
|
grouped.get(orderId).get(task.getNodeCode()).add(userName);
|
}
|
Map<Long, Map<String, String>> result = new LinkedHashMap<>();
|
for (Map.Entry<Long, Map<String, LinkedHashSet<String>>> orderEntry : grouped.entrySet()) {
|
Map<String, String> nodeTextMap = new LinkedHashMap<>();
|
for (Map.Entry<String, LinkedHashSet<String>> nodeEntry : orderEntry.getValue().entrySet()) {
|
nodeTextMap.put(nodeEntry.getKey(), String.join("、", nodeEntry.getValue()));
|
}
|
result.put(orderEntry.getKey(), nodeTextMap);
|
}
|
return result;
|
}
|
|
private Map<Long, String> loadMpUserNameMap(Set<Long> userIds) {
|
if (userIds == null || userIds.isEmpty()) {
|
return Map.of();
|
}
|
List<MpUser> rows =
|
mpUserMapper.selectList(
|
new LambdaQueryWrapper<MpUser>()
|
.eq(MpUser::getDelFlag, 0)
|
.in(MpUser::getId, userIds));
|
return rows.stream()
|
.collect(
|
Collectors.toMap(
|
MpUser::getId,
|
user -> firstNonBlank(user.getNickName(), user.getMobile(), String.valueOf(user.getId())),
|
(a, b) -> a,
|
LinkedHashMap::new));
|
}
|
|
private Map<Long, Map<Long, String>> loadVisitorPlateMap(List<Long> orderIds) {
|
if (orderIds == null || orderIds.isEmpty()) {
|
return Map.of();
|
}
|
List<AppointmentVehicle> rows =
|
appointmentVehicleMapper.selectList(
|
new LambdaQueryWrapper<AppointmentVehicle>()
|
.eq(AppointmentVehicle::getDelFlag, 0)
|
.in(AppointmentVehicle::getOrderId, orderIds)
|
.orderByAsc(AppointmentVehicle::getId));
|
if (rows.isEmpty()) {
|
return Map.of();
|
}
|
Map<Long, Map<Long, List<String>>> temp = new LinkedHashMap<>();
|
for (AppointmentVehicle row : rows) {
|
if (row.getOrderId() == null || row.getDriverVisitorId() == null || !StringUtils.hasText(row.getPlateNo())) {
|
continue;
|
}
|
temp.computeIfAbsent(row.getOrderId(), key -> new LinkedHashMap<>());
|
temp.get(row.getOrderId()).computeIfAbsent(row.getDriverVisitorId(), key -> new ArrayList<>());
|
temp.get(row.getOrderId()).get(row.getDriverVisitorId()).add(row.getPlateNo().trim());
|
}
|
Map<Long, Map<Long, String>> result = new LinkedHashMap<>();
|
for (Map.Entry<Long, Map<Long, List<String>>> orderEntry : temp.entrySet()) {
|
Map<Long, String> visitorPlateTextMap = new LinkedHashMap<>();
|
for (Map.Entry<Long, List<String>> visitorEntry : orderEntry.getValue().entrySet()) {
|
String joined =
|
visitorEntry.getValue().stream()
|
.filter(StringUtils::hasText)
|
.map(String::trim)
|
.distinct()
|
.collect(Collectors.joining("、"));
|
visitorPlateTextMap.put(visitorEntry.getKey(), joined);
|
}
|
result.put(orderEntry.getKey(), visitorPlateTextMap);
|
}
|
return result;
|
}
|
|
private String resolveIdCardMasked(AppointmentVisitor row, MpVisitor visitor) {
|
String idCardPlain =
|
row != null && StringUtils.hasText(row.getIdCardCipher())
|
? SensitiveDataUtil.decryptIdCard(row.getIdCardCipher())
|
: (visitor != null && StringUtils.hasText(visitor.getIdCardCipher())
|
? SensitiveDataUtil.decryptIdCard(visitor.getIdCardCipher())
|
: null);
|
return StringUtils.hasText(idCardPlain) ? SensitiveDataUtil.maskIdCard(idCardPlain) : null;
|
}
|
|
private String formatDateTime(LocalDateTime value) {
|
return value == null ? null : value.format(DATE_TIME_FORMATTER);
|
}
|
|
private List<AppointmentAdminDetailVisitorVo> buildDetailVisitors(
|
List<AppointmentVisitor> rows, Map<Long, MpVisitor> visitorMap, Map<Long, String> plateMap) {
|
if (rows == null || rows.isEmpty()) {
|
return List.of();
|
}
|
List<AppointmentAdminDetailVisitorVo> result = new ArrayList<>();
|
for (AppointmentVisitor row : rows) {
|
MpVisitor visitor = visitorMap.get(row.getMpVisitorId());
|
AppointmentAdminDetailVisitorVo vo = new AppointmentAdminDetailVisitorVo();
|
vo.setVisitorId(row.getMpVisitorId());
|
vo.setName(firstNonBlank(row.getName(), visitor == null ? null : visitor.getVisitorName(), "-"));
|
vo.setGender(defaultText(visitor == null ? null : trimToNull(visitor.getGender())));
|
vo.setCompanyName(defaultText(visitor == null ? null : trimToNull(visitor.getVisitorCompany())));
|
vo.setMobileMasked(
|
defaultText(
|
MobileMaskUtil.mask(
|
firstNonBlank(row.getMobile(), visitor == null ? null : visitor.getVisitorMobile(), null))));
|
String idCardPlain =
|
StringUtils.hasText(row.getIdCardCipher())
|
? SensitiveDataUtil.decryptIdCard(row.getIdCardCipher())
|
: (visitor != null && StringUtils.hasText(visitor.getIdCardCipher())
|
? SensitiveDataUtil.decryptIdCard(visitor.getIdCardCipher())
|
: null);
|
vo.setIdCardMasked(
|
defaultText(
|
StringUtils.hasText(idCardPlain)
|
? SensitiveDataUtil.maskIdCard(idCardPlain)
|
: null));
|
vo.setPlateNo(defaultText(firstNonBlank(row.getPlateNo(), plateMap.get(row.getMpVisitorId()), null)));
|
vo.setDriverName(defaultText(trimToNull(row.getDriverName())));
|
result.add(vo);
|
}
|
return result;
|
}
|
|
private boolean matchesDisplayStatus(AppointmentAdminListVo row, String filter) {
|
String value = trimToNull(filter);
|
return !StringUtils.hasText(value) || Objects.equals(value, row.getDisplayStatusCode());
|
}
|
|
private boolean matchesApprovalStatus(AppointmentAdminListVo row, String filter) {
|
String value = trimToNull(filter);
|
return !StringUtils.hasText(value) || Objects.equals(value, row.getApprovalStatusCode());
|
}
|
|
private String buildVisitTypeText(AppointmentOrder order) {
|
boolean personEntry = order.getPersonEntryFlag() == null || Boolean.TRUE.equals(order.getPersonEntryFlag());
|
boolean vehicleEntry = Boolean.TRUE.equals(order.getVehicleEntryFlag());
|
if (personEntry && vehicleEntry) {
|
return "访客+车辆预约";
|
}
|
if (vehicleEntry) {
|
return "车辆预约";
|
}
|
return "访客预约";
|
}
|
|
private String buildVisitReasonText(AppointmentOrder row, Map<String, String> reasonNameMap) {
|
String code = trimToNull(row.getVisitReasonCode());
|
if (!StringUtils.hasText(code)) {
|
return null;
|
}
|
if ("OTHER".equalsIgnoreCase(code)) {
|
String other = trimToNull(row.getVisitReasonOther());
|
return StringUtils.hasText(other) ? "其他-" + other : "其他";
|
}
|
String reasonName = reasonNameMap.get(code);
|
return StringUtils.hasText(reasonName) ? reasonName : code;
|
}
|
|
private String buildVisitTimeText(AppointmentOrder row) {
|
if (row.getVisitStartDate() == null && row.getVisitEndDate() == null) {
|
return null;
|
}
|
if (Objects.equals(row.getVisitStartDate(), row.getVisitEndDate()) || row.getVisitEndDate() == null) {
|
return String.valueOf(row.getVisitStartDate());
|
}
|
return row.getVisitStartDate() + " 至 " + row.getVisitEndDate();
|
}
|
|
private String resolveVisitStartText(AppointmentOrder row) {
|
return row.getVisitStartDate() == null ? null : row.getVisitStartDate().toString();
|
}
|
|
private String resolveVisitEndText(AppointmentOrder row) {
|
LocalDate end = row.getVisitEndDate() != null ? row.getVisitEndDate() : row.getVisitStartDate();
|
return end == null ? null : end.toString();
|
}
|
|
private String resolveDisplayStatus(AppointmentOrder row) {
|
LocalDate today = LocalDate.now();
|
if (AppointmentOrderStatus.PENDING_APPROVAL.equals(row.getStatus())) {
|
return DISPLAY_STATUS_PENDING_VISIT;
|
}
|
if (AppointmentOrderStatus.APPROVED.equals(row.getStatus())) {
|
if (row.getVisitStartDate() != null && today.isBefore(row.getVisitStartDate())) {
|
return DISPLAY_STATUS_PENDING_VISIT;
|
}
|
if (row.getVisitStartDate() != null
|
&& row.getVisitEndDate() != null
|
&& (today.isEqual(row.getVisitStartDate()) || today.isAfter(row.getVisitStartDate()))
|
&& (today.isEqual(row.getVisitEndDate()) || today.isBefore(row.getVisitEndDate()))) {
|
return DISPLAY_STATUS_EFFECTIVE;
|
}
|
}
|
return DISPLAY_STATUS_INVALID;
|
}
|
|
private String resolveDisplayStatusText(String code) {
|
return switch (trimToNull(code)) {
|
case DISPLAY_STATUS_PENDING_VISIT -> "待拜访";
|
case DISPLAY_STATUS_EFFECTIVE -> "生效中";
|
default -> "已失效";
|
};
|
}
|
|
private String resolveApprovalStatusCode(AppointmentOrder order, ApprovalInstance approvalInstance) {
|
String status = trimToNull(order.getStatus());
|
if (AppointmentOrderStatus.PENDING_APPROVAL.equals(status)) {
|
String nodeCode = approvalInstance == null ? null : trimToNull(approvalInstance.getCurrentNodeCode());
|
return StringUtils.hasText(nodeCode) ? nodeCode : AppointmentOrderStatus.PENDING_APPROVAL;
|
}
|
if (AppointmentOrderStatus.APPROVED.equals(status)
|
|| AppointmentOrderStatus.VISITED.equals(status)
|
|| AppointmentOrderStatus.EXPIRED.equals(status)) {
|
return AppointmentOrderStatus.APPROVED;
|
}
|
if (AppointmentOrderStatus.REJECTED.equals(status)) {
|
return AppointmentOrderStatus.REJECTED;
|
}
|
if (AppointmentOrderStatus.CANCELLED.equals(status)) {
|
return AppointmentOrderStatus.CANCELLED;
|
}
|
return status;
|
}
|
|
private String resolveApprovalStatusText(String code) {
|
return switch (trimToNull(code)) {
|
case ApprovalNodeCode.LEVEL1_HANDLER -> "经办人";
|
case ApprovalNodeCode.LEVEL2_DEPT_LEADER -> "部门";
|
case ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER -> "属地部门";
|
case ApprovalNodeCode.LEVEL4_VISIT_DEPT -> "主管部门";
|
case ApprovalNodeCode.LEVEL5_VISIT_DEPT_LEADER -> "主管部门领导";
|
case AppointmentOrderStatus.APPROVED -> "已通过";
|
case AppointmentOrderStatus.REJECTED -> "已拒绝";
|
case AppointmentOrderStatus.CANCELLED -> "已取消";
|
case AppointmentOrderStatus.PENDING_APPROVAL -> "审批中";
|
default -> "-";
|
};
|
}
|
|
private String buildJsonNameText(String rawJson) {
|
String raw = trimToNull(rawJson);
|
if (!StringUtils.hasText(raw)) {
|
return null;
|
}
|
try {
|
List<String> rows = objectMapper.readValue(raw, STRING_LIST_TYPE);
|
return rows.stream()
|
.filter(StringUtils::hasText)
|
.map(String::trim)
|
.distinct()
|
.collect(Collectors.joining("、"));
|
} catch (Exception ignore) {
|
return raw;
|
}
|
}
|
|
private String joinUniqueTexts(List<AppointmentVisitor> rows, java.util.function.Function<AppointmentVisitor, String> getter) {
|
if (rows == null || rows.isEmpty()) {
|
return null;
|
}
|
return rows.stream()
|
.map(getter)
|
.filter(StringUtils::hasText)
|
.map(String::trim)
|
.distinct()
|
.collect(Collectors.joining("、"));
|
}
|
|
private IPage<AppointmentAdminListVo> slicePage(long page, long size, List<AppointmentAdminListVo> rowsAll) {
|
long current = page <= 0 ? 1 : page;
|
long pageSize = size <= 0 ? 10 : size;
|
int from = (int) ((current - 1) * pageSize);
|
int to = Math.min(rowsAll.size(), from + (int) pageSize);
|
List<AppointmentAdminListVo> records = from >= to ? List.of() : rowsAll.subList(from, to);
|
Page<AppointmentAdminListVo> out = new Page<>(current, pageSize);
|
out.setTotal(rowsAll.size());
|
out.setRecords(records);
|
return out;
|
}
|
|
private static String trim(String value) {
|
return value == null ? null : value.trim();
|
}
|
|
private static String trimToNull(String value) {
|
String trimmed = trim(value);
|
return StringUtils.hasText(trimmed) ? trimmed : null;
|
}
|
|
private static String defaultText(String value) {
|
return StringUtils.hasText(value) ? value : "-";
|
}
|
|
@SafeVarargs
|
private static String firstNonBlank(String... values) {
|
if (values == null) {
|
return null;
|
}
|
for (String value : values) {
|
if (StringUtils.hasText(value)) {
|
return value.trim();
|
}
|
}
|
return null;
|
}
|
}
|