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.type.TypeReference;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.sinata.appointment.constants.AppointmentOrderStatus;
|
import com.sinata.appointment.constants.AppointmentBizConstants;
|
import com.sinata.appointment.constants.AppointmentFieldCodes;
|
import com.sinata.appointment.constants.ApprovalInstanceStatus;
|
import com.sinata.appointment.constants.ApprovalTaskStatus;
|
import com.sinata.appointment.constants.BizErrorCode;
|
import com.sinata.appointment.constants.QrcodeTicketStatus;
|
import com.sinata.appointment.constants.SystemSettingKeys;
|
import com.sinata.appointment.dto.MiniAppointmentCreateRequest;
|
import com.sinata.appointment.dto.MiniAppointmentCreateResultVo;
|
import com.sinata.appointment.dto.MiniAppointmentListVo;
|
import com.sinata.appointment.dto.MiniApprovalHistoryRow;
|
import com.sinata.appointment.dto.MiniAppointmentVehicleItemRequest;
|
import com.sinata.appointment.dto.MiniApprovalDetailVisitorVo;
|
import com.sinata.appointment.dto.MiniMyAppointmentDetailVo;
|
import com.sinata.appointment.dto.MiniMyVisitorCodeOrderVo;
|
import com.sinata.appointment.dto.MiniMyVisitorCodeTicketVo;
|
import com.sinata.appointment.dto.MiniSharedAppointmentDetailVo;
|
import com.sinata.appointment.dto.MiniVisitRecordListVo;
|
import com.sinata.appointment.dto.ApprovalFlowNodeVo;
|
import com.sinata.appointment.entity.AppointmentOrder;
|
import com.sinata.appointment.entity.ApprovalInstance;
|
import com.sinata.appointment.entity.ApprovalTask;
|
import com.sinata.appointment.entity.AppointmentVehicle;
|
import com.sinata.appointment.entity.AppointmentVisitor;
|
import com.sinata.appointment.entity.BizFieldConfig;
|
import com.sinata.appointment.entity.BizVisitReason;
|
import com.sinata.appointment.entity.MpUser;
|
import com.sinata.appointment.entity.MpVisitor;
|
import com.sinata.appointment.entity.QrcodeTicket;
|
import com.sinata.appointment.entity.SysDept;
|
import com.sinata.appointment.exception.BizException;
|
import com.sinata.appointment.mapper.AppointmentOrderMapper;
|
import com.sinata.appointment.mapper.ApprovalInstanceMapper;
|
import com.sinata.appointment.mapper.ApprovalTaskMapper;
|
import com.sinata.appointment.mapper.AppointmentVehicleMapper;
|
import com.sinata.appointment.mapper.AppointmentVisitorMapper;
|
import com.sinata.appointment.mapper.BizFieldConfigMapper;
|
import com.sinata.appointment.mapper.BizVisitReasonMapper;
|
import com.sinata.appointment.mapper.MpUserMapper;
|
import com.sinata.appointment.mapper.MpVisitorMapper;
|
import com.sinata.appointment.mapper.QrcodeTicketMapper;
|
import com.sinata.appointment.mapper.SysDeptMapper;
|
import com.sinata.appointment.util.OrderNoGenerator;
|
import com.sinata.appointment.util.MobileMaskUtil;
|
import com.sinata.appointment.util.SensitiveDataUtil;
|
import com.sinata.appointment.util.UuidAssert;
|
import java.time.LocalDate;
|
import java.time.LocalDateTime;
|
import java.time.LocalTime;
|
import java.time.temporal.ChronoUnit;
|
import java.util.ArrayList;
|
import java.util.Comparator;
|
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.function.Function;
|
import java.util.stream.Collectors;
|
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 org.springframework.context.ApplicationEventPublisher;
|
import com.sinata.appointment.service.event.AppointmentApprovedEvent;
|
import com.sinata.appointment.service.event.AppointmentApproversNotifyEvent;
|
|
@Slf4j
|
@Service
|
@RequiredArgsConstructor
|
public class MiniAppointmentApplicationService {
|
|
private static final String CANCEL_REASON_BY_SUBMITTER = "预约人取消";
|
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 final MiniAuthApplicationService miniAuthApplicationService;
|
private final MpUserMapper mpUserMapper;
|
private final MpVisitorMapper mpVisitorMapper;
|
private final BizVisitReasonMapper bizVisitReasonMapper;
|
private final AppointmentOrderMapper appointmentOrderMapper;
|
private final ApprovalTaskMapper approvalTaskMapper;
|
private final ApprovalInstanceMapper approvalInstanceMapper;
|
private final AppointmentVisitorMapper appointmentVisitorMapper;
|
private final AppointmentVehicleMapper appointmentVehicleMapper;
|
private final QrcodeTicketMapper qrcodeTicketMapper;
|
private final BizFieldConfigMapper bizFieldConfigMapper;
|
private final SysDeptMapper sysDeptMapper;
|
private final BizHolidayDateCacheService bizHolidayDateCacheService;
|
private final SysBizSettingCacheService sysBizSettingCacheService;
|
private final ApprovalEngineService approvalEngineService;
|
private final PostApprovalOrchestrationService postApprovalOrchestrationService;
|
private final ApprovalFlowViewApplicationService approvalFlowViewApplicationService;
|
private final SensitiveWordFilterService sensitiveWordFilterService;
|
private final IntegrationHikvisionService integrationHikvisionService;
|
private final ObjectMapper objectMapper;
|
private final ApplicationEventPublisher applicationEventPublisher;
|
|
@Transactional(rollbackFor = Exception.class)
|
public MiniAppointmentCreateResultVo create(MiniAppointmentCreateRequest req) {
|
MpUser current = miniAuthApplicationService.currentMpUser();
|
FieldRequiredContext required = loadFieldRequiredContext();
|
MpUser visited = loadVisitedUser(req.getVisitedUserId());
|
validateByFieldConfig(req, required);
|
validateVisitReason(req.getVisitReasonCode(), req.getVisitReasonOther());
|
|
String mode = normalizeMode(req.getVisitTimeMode());
|
validateAgainstSystemRangeMode(mode);
|
|
DateRange range = resolveDateRange(req, mode);
|
boolean holidayFlag = calcHolidayFlag(range.start(), range.end());
|
boolean personEntry = req.getPersonEntry() == null || Boolean.TRUE.equals(req.getPersonEntry());
|
boolean vehicleEntry = Boolean.TRUE.equals(req.getVehicleEntry());
|
|
Set<Long> personIds = new LinkedHashSet<>();
|
if (personEntry && req.getVisitorIds() != null) {
|
personIds.addAll(req.getVisitorIds().stream().filter(Objects::nonNull).toList());
|
}
|
|
List<MiniAppointmentVehicleItemRequest> vehicles = vehicleEntry ? normalizeVehicles(req.getVehicles()) : List.of();
|
for (MiniAppointmentVehicleItemRequest v : vehicles) {
|
personIds.add(v.getDriverVisitorId());
|
}
|
|
if (personIds.isEmpty()) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "至少选择一名进场人员");
|
}
|
|
Map<Long, MpVisitor> visitorMap = loadCurrentUserVisitors(current.getId(), personIds);
|
validateVisitorDataByFieldConfig(visitorMap.values(), required);
|
|
AppointmentOrder order = new AppointmentOrder();
|
order.setOrderNo(OrderNoGenerator.next());
|
order.setVisitType(
|
vehicleEntry
|
? AppointmentBizConstants.VISIT_TYPE_VEHICLE
|
: AppointmentBizConstants.VISIT_TYPE_VISITOR);
|
order.setVisitedUserId(visited.getId());
|
order.setVisitedUserName(StringUtils.hasText(visited.getNickName()) ? visited.getNickName() : "员工");
|
order.setVisitedDeptId(visited.getDeptId());
|
order.setVisitReasonCode(req.getVisitReasonCode().trim());
|
order.setVisitReasonOther(sensitiveWordFilterService.filter(trimToNull(req.getVisitReasonOther())));
|
order.setVisitorOrg(resolveVisitorOrg(visitorMap.values()));
|
order.setVisitTimeMode(mode);
|
order.setVisitTimeSlot(
|
AppointmentBizConstants.TIME_MODE_SINGLE.equals(mode)
|
? normalizeSlot(req.getVisitTimeSlot())
|
: null);
|
order.setVisitStartDate(range.start());
|
order.setVisitEndDate(range.end());
|
order.setPersonEntryFlag(personEntry);
|
order.setVehicleEntryFlag(vehicleEntry);
|
order.setSubmitterId(current.getId());
|
order.setSubmitterVisibleFlag(1);
|
order.setHolidayFlag(holidayFlag);
|
order.setStatus(AppointmentOrderStatus.PENDING_APPROVAL);
|
appointmentOrderMapper.insert(order);
|
|
for (Long visitorId : personIds) {
|
MpVisitor v = visitorMap.get(visitorId);
|
AppointmentVisitor av = new AppointmentVisitor();
|
av.setOrderId(order.getId());
|
av.setMpVisitorId(v.getId());
|
av.setName(v.getVisitorName());
|
av.setIdCardCipher(v.getIdCardCipher());
|
av.setMobile(v.getVisitorMobile());
|
appointmentVisitorMapper.insert(av);
|
}
|
|
for (MiniAppointmentVehicleItemRequest v : vehicles) {
|
MpVisitor driver = visitorMap.get(v.getDriverVisitorId());
|
AppointmentVehicle row = new AppointmentVehicle();
|
row.setOrderId(order.getId());
|
row.setPlateNo(v.getPlateNo().trim());
|
row.setDriverVisitorId(driver.getId());
|
row.setDriverName(driver.getVisitorName());
|
appointmentVehicleMapper.insert(row);
|
}
|
|
List<String> chain = approvalEngineService.resolveEnabledNodeChain(order);
|
if (chain.isEmpty()) {
|
applicationEventPublisher.publishEvent(new AppointmentApprovedEvent(order.getId()));
|
} else {
|
ApprovalInstance inst = approvalEngineService.startFlow(order);
|
if (inst == null) {
|
applicationEventPublisher.publishEvent(new AppointmentApprovedEvent(order.getId()));
|
} else {
|
applicationEventPublisher.publishEvent(
|
new AppointmentApproversNotifyEvent(order.getId(), inst.getId()));
|
}
|
}
|
MiniAppointmentCreateResultVo result = new MiniAppointmentCreateResultVo();
|
result.setId(order.getId());
|
result.setUuid(order.getUuid());
|
return result;
|
}
|
|
@Transactional(rollbackFor = Exception.class)
|
public void cancelBySubmitter(Long orderId, String uuid) {
|
if (orderId == null || orderId <= 0) {
|
throw new BizException(BizErrorCode.ORDER_NOT_FOUND, "预约单不存在");
|
}
|
MpUser current = miniAuthApplicationService.currentMpUser();
|
AppointmentOrder order = appointmentOrderMapper.selectById(orderId);
|
if (order == null || (order.getDelFlag() != null && order.getDelFlag() == 1)) {
|
throw new BizException(BizErrorCode.ORDER_NOT_FOUND, "预约单不存在");
|
}
|
UuidAssert.match(order, uuid);
|
if (!Objects.equals(order.getSubmitterId(), current.getId())) {
|
throw new BizException(BizErrorCode.AUTH_FORBIDDEN, "仅预约发起人可取消");
|
}
|
if (!isCancelableStatus(order.getStatus())) {
|
throw new BizException(BizErrorCode.ORDER_STATE_INVALID, "当前状态不可取消");
|
}
|
|
ApprovalInstance latestInst = loadLatestInstanceByOrderId(order.getId());
|
if (latestInst != null
|
&& ApprovalInstanceStatus.IN_PROGRESS.equals(latestInst.getStatus())) {
|
approvalEngineService.cancelInstance(latestInst, CANCEL_REASON_BY_SUBMITTER);
|
}
|
|
order.setStatus(AppointmentOrderStatus.CANCELLED);
|
order.setRejectReason(CANCEL_REASON_BY_SUBMITTER);
|
appointmentOrderMapper.updateById(order);
|
expireUnusedTicketsByOrderId(order.getId());
|
}
|
|
@Transactional(rollbackFor = Exception.class)
|
public void deleteMyOrderRecord(Long orderId, String uuid) {
|
if (orderId == null || orderId <= 0) {
|
throw new BizException(BizErrorCode.ORDER_NOT_FOUND, "预约单不存在");
|
}
|
MpUser current = miniAuthApplicationService.currentMpUser();
|
AppointmentOrder order = appointmentOrderMapper.selectById(orderId);
|
if (order == null || (order.getDelFlag() != null && order.getDelFlag() == 1)) {
|
throw new BizException(BizErrorCode.ORDER_NOT_FOUND, "预约单不存在");
|
}
|
UuidAssert.match(order, uuid);
|
if (!Objects.equals(order.getSubmitterId(), current.getId())) {
|
throw new BizException(BizErrorCode.AUTH_FORBIDDEN, "仅预约发起人可删除");
|
}
|
if (!isDeletableStatus(order.getStatus(), resolveDisplayStatus(order))) {
|
throw new BizException(BizErrorCode.ORDER_STATE_INVALID, "当前状态不可删除");
|
}
|
order.setSubmitterVisibleFlag(0);
|
appointmentOrderMapper.updateById(order);
|
}
|
|
public IPage<MiniAppointmentListVo> pageMine(
|
long page, long size, String status, String visitedUserNameKeyword, LocalDate visitDate) {
|
MpUser current = miniAuthApplicationService.currentMpUser();
|
Page<MiniApprovalHistoryRow> rowPage = new Page<>(Math.max(1L, page), Math.max(1L, size));
|
IPage<MiniApprovalHistoryRow> pagedRows =
|
approvalTaskMapper.pageMiniApprovalHistory(
|
rowPage,
|
current.getId(),
|
trimToNull(status),
|
trimToNull(visitedUserNameKeyword),
|
visitDate);
|
Page<MiniAppointmentListVo> result =
|
new Page<>(pagedRows.getCurrent(), pagedRows.getSize(), pagedRows.getTotal());
|
List<MiniApprovalHistoryRow> rows = pagedRows.getRecords();
|
if (rows == null || rows.isEmpty()) {
|
result.setRecords(List.of());
|
return result;
|
}
|
List<AppointmentOrder> orders = rows.stream().map(this::toOrderSnapshot).toList();
|
Map<Long, String> deptNameMap = loadDeptNameMap(orders);
|
Map<String, String> reasonNameMap = loadReasonNameMap(orders);
|
List<Long> orderIds = orders.stream().map(AppointmentOrder::getId).filter(Objects::nonNull).toList();
|
Map<Long, String> visitorDisplayMap = loadVisitorDisplayMapByOrderIds(orderIds);
|
Map<Long, String> plateTextMap = loadPlateTextMapByOrderIds(orderIds);
|
List<MiniAppointmentListVo> records = new ArrayList<>();
|
for (MiniApprovalHistoryRow row : rows) {
|
AppointmentOrder order = toOrderSnapshot(row);
|
MiniAppointmentListVo vo = toListVo(order);
|
vo.setStatus(row.getTaskStatus());
|
vo.setVisitedDeptName(deptNameMap.get(order.getVisitedDeptId()));
|
vo.setVisitedUserDisplay(buildVisitedUserDisplay(order.getVisitedUserName(), vo.getVisitedDeptName()));
|
vo.setVisitTimeText(buildVisitTimeText(order));
|
vo.setVisitReasonText(buildVisitReasonText(order, reasonNameMap));
|
vo.setAccessAreaText(buildAccessAreaText(order.getPassAccessAreaNames()));
|
vo.setVisitorDisplayText(visitorDisplayMap.get(order.getId()));
|
vo.setPlateNoText(plateTextMap.get(order.getId()));
|
records.add(vo);
|
}
|
result.setRecords(records);
|
return result;
|
}
|
|
public IPage<MiniVisitRecordListVo> pageVisitRecords(
|
long page, long size, boolean futureOnly, String visitorNameKeyword, LocalDate visitDate) {
|
MpUser current = miniAuthApplicationService.currentMpUser();
|
Page<AppointmentOrder> orderPage = new Page<>(Math.max(1L, page), Math.max(1L, size));
|
IPage<AppointmentOrder> pagedOrders =
|
appointmentOrderMapper.pageMiniVisitRecords(
|
orderPage, current.getId(), futureOnly, trimToNull(visitorNameKeyword), visitDate);
|
Page<MiniVisitRecordListVo> result =
|
new Page<>(pagedOrders.getCurrent(), pagedOrders.getSize(), pagedOrders.getTotal());
|
List<AppointmentOrder> orders = pagedOrders.getRecords();
|
if (orders == null || orders.isEmpty()) {
|
result.setRecords(List.of());
|
return result;
|
}
|
Map<String, String> reasonNameMap = loadReasonNameMap(orders);
|
List<Long> orderIds = orders.stream().map(AppointmentOrder::getId).filter(Objects::nonNull).toList();
|
Map<Long, List<String>> visitorNamesMap = loadVisitorNamesMap(orders.stream().map(AppointmentOrder::getId).toList());
|
Map<Long, String> visitorDisplayMap = loadVisitorDisplayMapByOrderIds(orderIds);
|
Map<Long, String> plateTextMap = loadPlateTextMapByOrderIds(orderIds);
|
List<MiniVisitRecordListVo> records = new ArrayList<>();
|
for (AppointmentOrder order : orders) {
|
MiniVisitRecordListVo vo = new MiniVisitRecordListVo();
|
vo.setOrderId(order.getId());
|
vo.setOrderNo(order.getOrderNo());
|
vo.setVisitorNames(visitorNamesMap.getOrDefault(order.getId(), List.of()));
|
vo.setVisitorDisplayText(visitorDisplayMap.get(order.getId()));
|
vo.setPlateNoText(plateTextMap.get(order.getId()));
|
vo.setVisitReasonText(buildVisitReasonText(order, reasonNameMap));
|
vo.setVisitStartDate(order.getVisitStartDate());
|
vo.setVisitEndDate(order.getVisitEndDate());
|
vo.setVisitTimeMode(order.getVisitTimeMode());
|
vo.setVisitTimeSlot(order.getVisitTimeSlot());
|
vo.setCreateTime(order.getCreateTime());
|
records.add(vo);
|
}
|
result.setRecords(records);
|
return result;
|
}
|
|
public IPage<MiniAppointmentListVo> pageMyOrders(
|
long page, long size, String status, String keyword) {
|
MpUser current = miniAuthApplicationService.currentMpUser();
|
Page<AppointmentOrder> orderPage = new Page<>(Math.max(1L, page), Math.max(1L, size));
|
IPage<AppointmentOrder> pagedOrders =
|
appointmentOrderMapper.pageMiniMyOrders(
|
orderPage, current.getId(), trimToNull(status), trimToNull(keyword));
|
Page<MiniAppointmentListVo> result =
|
new Page<>(pagedOrders.getCurrent(), pagedOrders.getSize(), pagedOrders.getTotal());
|
List<AppointmentOrder> orders = pagedOrders.getRecords();
|
if (orders == null || orders.isEmpty()) {
|
result.setRecords(List.of());
|
return result;
|
}
|
Map<Long, String> deptNameMap = loadDeptNameMap(orders);
|
Map<String, String> reasonNameMap = loadReasonNameMap(orders);
|
List<MiniAppointmentListVo> records = new ArrayList<>();
|
for (AppointmentOrder order : orders) {
|
records.add(toMyOrderListVo(order, deptNameMap, reasonNameMap));
|
}
|
result.setRecords(records);
|
return result;
|
}
|
|
public List<MiniMyVisitorCodeOrderVo> listMyVisitorCodes() {
|
MpUser current = miniAuthApplicationService.currentMpUser();
|
List<AppointmentOrder> orders =
|
appointmentOrderMapper.selectList(
|
new LambdaQueryWrapper<AppointmentOrder>()
|
.eq(AppointmentOrder::getDelFlag, 0)
|
.eq(AppointmentOrder::getSubmitterId, current.getId())
|
.eq(AppointmentOrder::getStatus, AppointmentOrderStatus.APPROVED)
|
.orderByDesc(AppointmentOrder::getVisitStartDate)
|
.orderByDesc(AppointmentOrder::getId));
|
return buildVisitorCodeOrders(orders, true);
|
}
|
|
public MiniSharedAppointmentDetailVo detailSharedOrder(String uuid) {
|
AppointmentOrder order = loadApprovedOrderByUuid(uuid);
|
String deptName = loadDeptName(order.getVisitedDeptId());
|
MiniSharedAppointmentDetailVo vo = new MiniSharedAppointmentDetailVo();
|
vo.setOrderId(order.getId());
|
vo.setOrderUuid(order.getUuid());
|
vo.setVisitedUserDisplay(buildVisitedUserDisplay(order.getVisitedUserName(), deptName));
|
vo.setAccessAreaText(buildAccessAreaText(order.getPassAccessAreaNames()));
|
vo.setVisitTimeText(buildVisitTimeText(order));
|
return vo;
|
}
|
|
public MiniMyVisitorCodeOrderVo getSharedVisitorCodes(String uuid) {
|
AppointmentOrder order = loadApprovedOrderByUuid(uuid);
|
List<MiniMyVisitorCodeOrderVo> rows = buildVisitorCodeOrders(List.of(order), false);
|
if (rows.isEmpty()) {
|
MiniMyVisitorCodeOrderVo vo = new MiniMyVisitorCodeOrderVo();
|
vo.setOrderId(order.getId());
|
vo.setOrderUuid(order.getUuid());
|
vo.setVisitedUserDisplay(buildVisitedUserDisplay(order.getVisitedUserName(), loadDeptName(order.getVisitedDeptId())));
|
vo.setAccessAreaText(buildAccessAreaText(order.getPassAccessAreaNames()));
|
vo.setVisitTimeText(buildVisitTimeText(order));
|
vo.setQrCodes(List.of());
|
return vo;
|
}
|
return rows.get(0);
|
}
|
|
public MiniMyVisitorCodeOrderVo getSharedVisitorCodeByTicketUuid(String ticketUuid) {
|
QrcodeTicket ticket = loadTicketByUuid(ticketUuid);
|
AppointmentOrder order = loadApprovedOrderById(ticket.getOrderId());
|
String deptName = loadDeptName(order.getVisitedDeptId());
|
|
MiniMyVisitorCodeOrderVo vo = new MiniMyVisitorCodeOrderVo();
|
vo.setOrderId(order.getId());
|
vo.setOrderUuid(order.getUuid());
|
vo.setVisitedUserDisplay(buildVisitedUserDisplay(order.getVisitedUserName(), deptName));
|
vo.setAccessAreaText(buildAccessAreaText(order.getPassAccessAreaNames()));
|
vo.setVisitTimeText(buildVisitTimeText(order));
|
vo.setValidStartTime(ticket.getGenerateTime());
|
vo.setValidEndTime(ticket.getExpireTime());
|
|
if (!QrcodeTicketStatus.UNUSED.equals(ticket.getTicketStatus())) {
|
vo.setQrCodes(List.of());
|
return vo;
|
}
|
|
Map<Long, MpVisitor> visitorMap =
|
loadMpVisitorMap(
|
ticket.getMpVisitorId() == null
|
? Set.of()
|
: Set.of(ticket.getMpVisitorId()));
|
List<AppointmentVisitor> orderVisitors =
|
loadOrderVisitorRows(List.of(order.getId())).getOrDefault(order.getId(), List.of());
|
vo.setQrCodes(List.of(toMyVisitorCodeTicketVo(ticket, orderVisitors, visitorMap)));
|
return vo;
|
}
|
|
private List<MiniMyVisitorCodeOrderVo> buildVisitorCodeOrders(
|
List<AppointmentOrder> orders, boolean futureOnly) {
|
if (orders.isEmpty()) {
|
return List.of();
|
}
|
|
List<AppointmentOrder> eligibleOrders = orders;
|
if (futureOnly) {
|
LocalDateTime now = LocalDateTime.now();
|
eligibleOrders =
|
orders.stream()
|
.filter(order -> resolveVisitStartAt(order).isAfter(now))
|
.toList();
|
}
|
if (eligibleOrders.isEmpty()) {
|
return List.of();
|
}
|
|
List<Long> orderIds = eligibleOrders.stream().map(AppointmentOrder::getId).toList();
|
List<QrcodeTicket> tickets =
|
qrcodeTicketMapper.selectList(
|
new LambdaQueryWrapper<QrcodeTicket>()
|
.eq(QrcodeTicket::getDelFlag, 0)
|
.in(QrcodeTicket::getOrderId, orderIds)
|
.eq(QrcodeTicket::getTicketStatus, QrcodeTicketStatus.UNUSED)
|
.orderByAsc(QrcodeTicket::getOrderId)
|
.orderByAsc(QrcodeTicket::getId));
|
if (tickets.isEmpty()) {
|
return List.of();
|
}
|
|
Map<Long, List<QrcodeTicket>> ticketMap =
|
tickets.stream().collect(Collectors.groupingBy(QrcodeTicket::getOrderId, LinkedHashMap::new, Collectors.toList()));
|
Set<Long> visitorIds =
|
tickets.stream()
|
.map(QrcodeTicket::getMpVisitorId)
|
.filter(Objects::nonNull)
|
.collect(Collectors.toCollection(LinkedHashSet::new));
|
Map<Long, MpVisitor> visitorMap = loadMpVisitorMap(visitorIds);
|
Map<Long, List<AppointmentVisitor>> orderVisitorRows = loadOrderVisitorRows(orderIds);
|
Map<Long, String> deptNameMap = loadDeptNameMap(eligibleOrders);
|
|
List<MiniMyVisitorCodeOrderVo> result = new ArrayList<>();
|
for (AppointmentOrder order : eligibleOrders) {
|
List<QrcodeTicket> orderTickets = ticketMap.get(order.getId());
|
if (orderTickets == null || orderTickets.isEmpty()) {
|
continue;
|
}
|
MiniMyVisitorCodeOrderVo vo = new MiniMyVisitorCodeOrderVo();
|
vo.setOrderId(order.getId());
|
vo.setOrderUuid(order.getUuid());
|
String deptName = deptNameMap.get(order.getVisitedDeptId());
|
vo.setVisitedUserDisplay(buildVisitedUserDisplay(order.getVisitedUserName(), deptName));
|
vo.setAccessAreaText(buildAccessAreaText(order.getPassAccessAreaNames()));
|
vo.setVisitTimeText(buildVisitTimeText(order));
|
vo.setValidStartTime(orderTickets.get(0).getGenerateTime());
|
vo.setValidEndTime(orderTickets.get(0).getExpireTime());
|
vo.setQrCodes(
|
orderTickets.stream()
|
.map(ticket -> toMyVisitorCodeTicketVo(ticket, orderVisitorRows.get(order.getId()), visitorMap))
|
.toList());
|
result.add(vo);
|
}
|
return result;
|
}
|
|
private AppointmentOrder loadApprovedOrderByUuid(String uuid) {
|
String normalizedUuid = trimToNull(uuid);
|
if (!StringUtils.hasText(normalizedUuid)) {
|
throw new BizException(BizErrorCode.ORDER_NOT_FOUND, "预约单不存在");
|
}
|
AppointmentOrder order =
|
appointmentOrderMapper.selectOne(
|
new LambdaQueryWrapper<AppointmentOrder>()
|
.eq(AppointmentOrder::getDelFlag, 0)
|
.eq(AppointmentOrder::getUuid, normalizedUuid)
|
.eq(AppointmentOrder::getStatus, AppointmentOrderStatus.APPROVED)
|
.last("limit 1"));
|
if (order == null) {
|
throw new BizException(BizErrorCode.ORDER_NOT_FOUND, "预约单不存在");
|
}
|
return order;
|
}
|
|
private AppointmentOrder loadApprovedOrderById(Long orderId) {
|
if (orderId == null || orderId <= 0) {
|
throw new BizException(BizErrorCode.ORDER_NOT_FOUND, "预约单不存在");
|
}
|
AppointmentOrder order =
|
appointmentOrderMapper.selectOne(
|
new LambdaQueryWrapper<AppointmentOrder>()
|
.eq(AppointmentOrder::getDelFlag, 0)
|
.eq(AppointmentOrder::getId, orderId)
|
.eq(AppointmentOrder::getStatus, AppointmentOrderStatus.APPROVED)
|
.last("limit 1"));
|
if (order == null) {
|
throw new BizException(BizErrorCode.ORDER_NOT_FOUND, "预约单不存在");
|
}
|
return order;
|
}
|
|
private QrcodeTicket loadTicketByUuid(String ticketUuid) {
|
String normalizedTicketUuid = trimToNull(ticketUuid);
|
if (!StringUtils.hasText(normalizedTicketUuid)) {
|
throw new BizException(BizErrorCode.ORDER_NOT_FOUND, "访客码不存在");
|
}
|
QrcodeTicket ticket =
|
qrcodeTicketMapper.selectOne(
|
new LambdaQueryWrapper<QrcodeTicket>()
|
.eq(QrcodeTicket::getDelFlag, 0)
|
.eq(QrcodeTicket::getUuid, normalizedTicketUuid)
|
.last("limit 1"));
|
if (ticket == null) {
|
throw new BizException(BizErrorCode.ORDER_NOT_FOUND, "访客码不存在");
|
}
|
return ticket;
|
}
|
|
public MiniMyAppointmentDetailVo detailMyOrder(Long orderId, String uuid) {
|
if (orderId == null || orderId <= 0) {
|
throw new BizException(BizErrorCode.ORDER_NOT_FOUND, "预约单不存在");
|
}
|
MpUser current = miniAuthApplicationService.currentMpUser();
|
AppointmentOrder order = appointmentOrderMapper.selectMiniVisibleOrderById(orderId, current.getId());
|
if (order == null) {
|
throw new BizException(BizErrorCode.ORDER_NOT_FOUND, "预约单不存在");
|
}
|
UuidAssert.match(order, uuid);
|
|
String deptName = loadDeptName(order.getVisitedDeptId());
|
String reasonName = loadReasonName(order.getVisitReasonCode());
|
String displayStatus = resolveDisplayStatus(order);
|
|
MiniMyAppointmentDetailVo vo = new MiniMyAppointmentDetailVo();
|
vo.setOrderId(order.getId());
|
vo.setOrderUuid(order.getUuid());
|
vo.setOrderNo(order.getOrderNo());
|
vo.setStatus(order.getStatus());
|
vo.setDisplayStatus(displayStatus);
|
vo.setStatusText(resolveOrderStatusText(order));
|
vo.setCancelable(isCancelableStatus(order.getStatus()));
|
vo.setDeletable(isDeletableStatus(order.getStatus(), displayStatus));
|
vo.setVisitedUserId(order.getVisitedUserId());
|
vo.setVisitedUserName(order.getVisitedUserName());
|
vo.setVisitedDeptName(deptName);
|
vo.setVisitedUserDisplay(buildVisitedUserDisplay(order.getVisitedUserName(), deptName));
|
vo.setAccessAreaText(buildAccessAreaText(order.getPassAccessAreaNames()));
|
vo.setVisitTypeText(buildVisitTypeText(order));
|
vo.setVisitReasonText(buildVisitReasonText(order, reasonName == null ? Map.of() : Map.of(trimToNull(order.getVisitReasonCode()), reasonName)));
|
vo.setVisitReasonCode(order.getVisitReasonCode());
|
vo.setVisitReasonOther(order.getVisitReasonOther());
|
vo.setVisitTimeText(buildVisitTimeText(order));
|
vo.setVisitTimeMode(order.getVisitTimeMode());
|
vo.setVisitTimeSlot(order.getVisitTimeSlot());
|
vo.setVisitStartDate(order.getVisitStartDate());
|
vo.setVisitEndDate(order.getVisitEndDate());
|
vo.setPersonEntry(order.getPersonEntryFlag() == null || Boolean.TRUE.equals(order.getPersonEntryFlag()));
|
vo.setVehicleEntry(Boolean.TRUE.equals(order.getVehicleEntryFlag()));
|
vo.setCreateTime(order.getCreateTime());
|
vo.setVisitors(loadOrderVisitors(order.getId()));
|
vo.setRebookVisitors(loadRebookVisitors(order.getId()));
|
vo.setRebookVehicles(loadRebookVehicles(order.getId()));
|
List<ApprovalFlowNodeVo> flowTree = approvalFlowViewApplicationService.treeByOrderId(order.getId());
|
vo.setFlowTree(flowTree == null ? List.of() : flowTree);
|
return vo;
|
}
|
|
private MpUser loadVisitedUser(Long visitedUserId) {
|
MpUser visited = mpUserMapper.selectById(visitedUserId);
|
if (visited == null || (visited.getDelFlag() != null && visited.getDelFlag() == 1)) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "被访人不存在");
|
}
|
if (visited.getStatus() != null && visited.getStatus() == 0) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "被访人已禁用");
|
}
|
if (visited.getDeptId() == null || visited.getDeptId() <= 0) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "被访人未绑定部门");
|
}
|
return visited;
|
}
|
|
private void validateVisitReason(String code, String other) {
|
String c = trimToNull(code);
|
if (!StringUtils.hasText(c)) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "拜访事由不能为空");
|
}
|
String txt = trimToNull(other);
|
if (!StringUtils.hasText(txt)) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "请输入事由说明");
|
}
|
if (txt.length() > 15) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "事由说明不能超过15个字");
|
}
|
if (AppointmentBizConstants.VISIT_REASON_OTHER.equalsIgnoreCase(c)) {
|
return;
|
}
|
Long cnt =
|
bizVisitReasonMapper.selectCount(
|
new LambdaQueryWrapper<BizVisitReason>()
|
.eq(BizVisitReason::getDelFlag, 0)
|
.eq(BizVisitReason::getEnabled, 1)
|
.eq(BizVisitReason::getReasonCode, c));
|
if (cnt == null || cnt <= 0) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "拜访事由无效");
|
}
|
}
|
|
private String normalizeMode(String mode) {
|
String m = trimToNull(mode);
|
if (!StringUtils.hasText(m)) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "拜访时间类型不能为空");
|
}
|
m = m.toUpperCase();
|
if (!AppointmentBizConstants.TIME_MODE_SINGLE.equals(m)
|
&& !AppointmentBizConstants.TIME_MODE_MULTI.equals(m)) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "拜访时间类型仅支持 SINGLE/MULTI");
|
}
|
return m;
|
}
|
|
private void validateAgainstSystemRangeMode(String reqMode) {
|
String cfg = trimToNull(sysBizSettingCacheService.getValue(SystemSettingKeys.BOOKING_RANGE_MODE));
|
String mode =
|
StringUtils.hasText(cfg)
|
? cfg.toUpperCase()
|
: AppointmentBizConstants.TIME_MODE_SINGLE;
|
if (!AppointmentBizConstants.TIME_MODE_SINGLE.equals(mode)
|
&& !AppointmentBizConstants.TIME_MODE_MULTI.equals(mode)) {
|
mode = AppointmentBizConstants.TIME_MODE_SINGLE;
|
}
|
if (AppointmentBizConstants.TIME_MODE_SINGLE.equals(mode)
|
&& AppointmentBizConstants.TIME_MODE_MULTI.equals(reqMode)) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "当前预约配置不支持该拜访时间类型");
|
}
|
}
|
|
private DateRange resolveDateRange(MiniAppointmentCreateRequest req, String mode) {
|
if (AppointmentBizConstants.TIME_MODE_SINGLE.equals(mode)) {
|
if (req.getVisitDate() == null) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "单日模式下拜访日期不能为空");
|
}
|
normalizeSlot(req.getVisitTimeSlot());
|
return new DateRange(req.getVisitDate(), req.getVisitDate());
|
}
|
if (req.getVisitStartDate() == null || req.getVisitEndDate() == null) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "多日模式下须填写起止日期");
|
}
|
if (req.getVisitEndDate().isBefore(req.getVisitStartDate())) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "结束日期不能早于开始日期");
|
}
|
long days = ChronoUnit.DAYS.between(req.getVisitStartDate(), req.getVisitEndDate()) + 1;
|
int maxDays = getMaxConsecutiveDays();
|
if (days > maxDays) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "多日预约不能超过最大天数: " + maxDays);
|
}
|
return new DateRange(req.getVisitStartDate(), req.getVisitEndDate());
|
}
|
|
private int getMaxConsecutiveDays() {
|
String raw = trimToNull(sysBizSettingCacheService.getValue(SystemSettingKeys.BOOKING_MAX_CONSECUTIVE_DAYS));
|
if (!StringUtils.hasText(raw)) {
|
return 7;
|
}
|
try {
|
return Math.max(1, Integer.parseInt(raw));
|
} catch (NumberFormatException ex) {
|
return 7;
|
}
|
}
|
|
private String normalizeSlot(String slot) {
|
String s = trimToNull(slot);
|
if (!StringUtils.hasText(s)) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "单日模式下须选择全天/上午/下午");
|
}
|
s = s.toUpperCase();
|
if (!AppointmentBizConstants.TIME_SLOT_ALL_DAY.equals(s)
|
&& !AppointmentBizConstants.TIME_SLOT_AM.equals(s)
|
&& !AppointmentBizConstants.TIME_SLOT_PM.equals(s)) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "单日时段仅支持 ALL_DAY/AM/PM");
|
}
|
return s;
|
}
|
|
private List<MiniAppointmentVehicleItemRequest> normalizeVehicles(List<MiniAppointmentVehicleItemRequest> raw) {
|
if (raw == null || raw.isEmpty()) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "勾选车辆进场后,进场车辆列表不能为空");
|
}
|
List<MiniAppointmentVehicleItemRequest> out = new ArrayList<>();
|
for (MiniAppointmentVehicleItemRequest v : raw) {
|
if (v == null || !StringUtils.hasText(v.getPlateNo()) || v.getDriverVisitorId() == null) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "车辆信息不完整");
|
}
|
if (v.getPlateNo().trim().length() > 32) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "车牌号长度不能超过32");
|
}
|
out.add(v);
|
}
|
return out;
|
}
|
|
private Map<Long, MpVisitor> loadCurrentUserVisitors(Long currentMpUserId, Set<Long> ids) {
|
List<MpVisitor> rows =
|
mpVisitorMapper.selectList(
|
new LambdaQueryWrapper<MpVisitor>()
|
.eq(MpVisitor::getMpUserId, currentMpUserId)
|
.eq(MpVisitor::getDelFlag, 0)
|
.in(MpVisitor::getId, ids));
|
Map<Long, MpVisitor> map = rows.stream().collect(Collectors.toMap(MpVisitor::getId, Function.identity(), (a, b) -> a));
|
for (Long id : ids) {
|
if (!map.containsKey(id)) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "来访人员不存在或不属于当前账号: " + id);
|
}
|
}
|
return map;
|
}
|
|
private void validateByFieldConfig(MiniAppointmentCreateRequest req, FieldRequiredContext required) {
|
boolean personEntry = req.getPersonEntry() == null || Boolean.TRUE.equals(req.getPersonEntry());
|
boolean vehicleEntry = Boolean.TRUE.equals(req.getVehicleEntry());
|
if (required.isRequired(AppointmentFieldCodes.RESPONDENT) && req.getVisitedUserId() == null) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "被访人不能为空");
|
}
|
if (required.isRequired(AppointmentFieldCodes.VISIT_REASON)
|
&& !StringUtils.hasText(trimToNull(req.getVisitReasonCode()))) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "拜访事由不能为空");
|
}
|
if (required.isRequired(AppointmentFieldCodes.VISIT_START_TIME)) {
|
if (AppointmentBizConstants.TIME_MODE_SINGLE.equalsIgnoreCase(trimToNull(req.getVisitTimeMode()))) {
|
if (req.getVisitDate() == null) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "拜访时间不能为空");
|
}
|
} else if (req.getVisitStartDate() == null) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "开始拜访时间不能为空");
|
}
|
}
|
if (required.isRequired(AppointmentFieldCodes.VISIT_END_TIME)
|
&& AppointmentBizConstants.TIME_MODE_MULTI.equalsIgnoreCase(trimToNull(req.getVisitTimeMode()))
|
&& req.getVisitEndDate() == null) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "结束拜访时间不能为空");
|
}
|
if (required.isRequired(AppointmentFieldCodes.VISIT_TYPE)
|
&& !personEntry
|
&& !vehicleEntry) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "来访类型不能为空");
|
}
|
if (required.isRequired(AppointmentFieldCodes.LICENSE_PLATE, AppointmentFieldCodes.DRIVER)
|
&& vehicleEntry
|
&& (req.getVehicles() == null || req.getVehicles().isEmpty())) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "车辆信息不能为空");
|
}
|
}
|
|
private void validateVisitorDataByFieldConfig(
|
java.util.Collection<MpVisitor> visitors, FieldRequiredContext required) {
|
for (MpVisitor v : visitors) {
|
if (required.isRequired(AppointmentFieldCodes.VISITOR_NAME)
|
&& !StringUtils.hasText(trimToNull(v.getVisitorName()))) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "访客姓名不能为空");
|
}
|
if (required.isRequired(AppointmentFieldCodes.ID_CARD)
|
&& !StringUtils.hasText(trimToNull(v.getIdCardCipher()))) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "身份证号码不能为空");
|
}
|
if (required.isRequired(AppointmentFieldCodes.GENDER)
|
&& !StringUtils.hasText(trimToNull(v.getGender()))) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "性别不能为空");
|
}
|
if (required.isRequired(AppointmentFieldCodes.COMPANY)
|
&& !StringUtils.hasText(trimToNull(v.getVisitorCompany()))) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "来访单位不能为空");
|
}
|
if (required.isRequired(AppointmentFieldCodes.MOBILE)
|
&& !StringUtils.hasText(trimToNull(v.getVisitorMobile()))) {
|
throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "联系电话不能为空");
|
}
|
}
|
}
|
|
private FieldRequiredContext loadFieldRequiredContext() {
|
List<BizFieldConfig> rows =
|
bizFieldConfigMapper.selectList(
|
new LambdaQueryWrapper<BizFieldConfig>()
|
.eq(BizFieldConfig::getDelFlag, 0)
|
.eq(BizFieldConfig::getVisible, 1)
|
.eq(BizFieldConfig::getRequiredFlag, 1));
|
Set<String> required =
|
rows.stream()
|
.map(BizFieldConfig::getFieldCode)
|
.filter(StringUtils::hasText)
|
.map(String::trim)
|
.collect(Collectors.toCollection(java.util.LinkedHashSet::new));
|
return new FieldRequiredContext(required);
|
}
|
|
private String resolveVisitorOrg(java.util.Collection<MpVisitor> visitors) {
|
for (MpVisitor v : visitors) {
|
String company = trimToNull(v.getVisitorCompany());
|
if (StringUtils.hasText(company)) {
|
return company;
|
}
|
}
|
return null;
|
}
|
|
private boolean calcHolidayFlag(LocalDate start, LocalDate end) {
|
Set<LocalDate> holidays = bizHolidayDateCacheService.getAllEnabledHolidayDates();
|
if (holidays.isEmpty()) {
|
return false;
|
}
|
LocalDate d = start;
|
while (!d.isAfter(end)) {
|
if (holidays.contains(d)) {
|
return true;
|
}
|
d = d.plusDays(1);
|
}
|
return false;
|
}
|
|
private static String trimToNull(String s) {
|
if (s == null) {
|
return null;
|
}
|
String t = s.trim();
|
return t.isEmpty() ? null : t;
|
}
|
|
private MiniAppointmentListVo toListVo(AppointmentOrder row) {
|
MiniAppointmentListVo vo = new MiniAppointmentListVo();
|
vo.setId(row.getId());
|
vo.setUuid(row.getUuid());
|
vo.setOrderNo(row.getOrderNo());
|
vo.setStatus(row.getStatus());
|
vo.setVisitedUserName(row.getVisitedUserName());
|
vo.setVisitStartDate(row.getVisitStartDate());
|
vo.setVisitEndDate(row.getVisitEndDate());
|
vo.setVisitReasonCode(row.getVisitReasonCode());
|
vo.setVisitReasonOther(row.getVisitReasonOther());
|
vo.setCreateTime(row.getCreateTime());
|
return vo;
|
}
|
|
private MiniAppointmentListVo toMyOrderListVo(
|
AppointmentOrder row,
|
Map<Long, String> deptNameMap,
|
Map<String, String> reasonNameMap) {
|
MiniAppointmentListVo vo = toListVo(row);
|
String deptName = deptNameMap.get(row.getVisitedDeptId());
|
vo.setVisitedDeptName(deptName);
|
vo.setVisitedUserDisplay(buildVisitedUserDisplay(row.getVisitedUserName(), deptName));
|
vo.setDisplayStatus(resolveDisplayStatus(row));
|
vo.setVisitTimeText(buildVisitTimeText(row));
|
vo.setVisitReasonText(buildVisitReasonText(row, reasonNameMap));
|
vo.setAccessAreaText(buildAccessAreaText(row.getPassAccessAreaNames()));
|
return vo;
|
}
|
|
private AppointmentOrder toOrderSnapshot(MiniApprovalHistoryRow row) {
|
AppointmentOrder order = new AppointmentOrder();
|
order.setId(row.getId());
|
order.setUuid(row.getUuid());
|
order.setOrderNo(row.getOrderNo());
|
order.setVisitedUserName(row.getVisitedUserName());
|
order.setVisitedDeptId(row.getVisitedDeptId());
|
order.setVisitStartDate(row.getVisitStartDate());
|
order.setVisitEndDate(row.getVisitEndDate());
|
order.setVisitReasonCode(row.getVisitReasonCode());
|
order.setVisitReasonOther(row.getVisitReasonOther());
|
order.setVisitTimeMode(row.getVisitTimeMode());
|
order.setVisitTimeSlot(row.getVisitTimeSlot());
|
order.setPassAccessAreaNames(row.getPassAccessAreaNames());
|
order.setStatus(row.getStatus());
|
order.setCreateTime(row.getCreateTime());
|
return order;
|
}
|
|
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)
|
.filter(code -> !AppointmentBizConstants.VISIT_REASON_OTHER.equalsIgnoreCase(code))
|
.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 Map<Long, List<String>> loadVisitorNamesMap(List<Long> orderIds) {
|
if (orderIds == null || orderIds.isEmpty()) {
|
return Map.of();
|
}
|
List<AppointmentVisitor> visitors =
|
appointmentVisitorMapper.selectList(
|
new LambdaQueryWrapper<AppointmentVisitor>()
|
.eq(AppointmentVisitor::getDelFlag, 0)
|
.in(AppointmentVisitor::getOrderId, orderIds)
|
.orderByAsc(AppointmentVisitor::getId));
|
Map<Long, List<String>> result = new LinkedHashMap<>();
|
for (AppointmentVisitor row : visitors) {
|
result.computeIfAbsent(row.getOrderId(), k -> new ArrayList<>());
|
if (StringUtils.hasText(row.getName())) {
|
result.get(row.getOrderId()).add(row.getName().trim());
|
}
|
}
|
return result;
|
}
|
|
private String buildVisitedUserDisplay(String userName, String deptName) {
|
String name = trimToNull(userName);
|
String dept = trimToNull(deptName);
|
if (StringUtils.hasText(name) && StringUtils.hasText(dept)) {
|
return name + " " + dept;
|
}
|
if (StringUtils.hasText(name)) {
|
return name;
|
}
|
return StringUtils.hasText(dept) ? dept : "-";
|
}
|
|
private Map<Long, String> loadVisitorDisplayMapByOrderIds(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));
|
Set<Long> visitorIds =
|
rows.stream()
|
.map(AppointmentVisitor::getMpVisitorId)
|
.filter(Objects::nonNull)
|
.collect(Collectors.toCollection(LinkedHashSet::new));
|
Map<Long, String> companyMap = loadMpVisitorCompanyMap(visitorIds);
|
Map<Long, List<String>> 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<Long, String> result = new LinkedHashMap<>();
|
for (Map.Entry<Long, List<String>> entry : grouped.entrySet()) {
|
String text = entry.getValue().stream().distinct().collect(Collectors.joining("、"));
|
if (StringUtils.hasText(text)) {
|
result.put(entry.getKey(), text);
|
}
|
}
|
return result;
|
}
|
|
private Map<Long, String> loadMpVisitorCompanyMap(Set<Long> visitorIds) {
|
if (visitorIds == null || visitorIds.isEmpty()) {
|
return Map.of();
|
}
|
List<MpVisitor> rows =
|
mpVisitorMapper.selectList(
|
new LambdaQueryWrapper<MpVisitor>()
|
.eq(MpVisitor::getDelFlag, 0)
|
.in(MpVisitor::getId, visitorIds));
|
Map<Long, String> result = new LinkedHashMap<>();
|
for (MpVisitor row : rows) {
|
result.put(row.getId(), trimToNull(row.getVisitorCompany()));
|
}
|
return result;
|
}
|
|
private Map<Long, String> loadPlateTextMapByOrderIds(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));
|
Map<Long, List<String>> 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<Long, String> result = new LinkedHashMap<>();
|
for (Map.Entry<Long, List<String>> entry : grouped.entrySet()) {
|
String text = entry.getValue().stream().distinct().collect(Collectors.joining("、"));
|
if (StringUtils.hasText(text)) {
|
result.put(entry.getKey(), text);
|
}
|
}
|
return result;
|
}
|
|
private String resolveOrderStatusText(AppointmentOrder row) {
|
return switch (trimToNull(row.getStatus())) {
|
case AppointmentOrderStatus.PENDING_APPROVAL -> "审批中";
|
case AppointmentOrderStatus.APPROVED,
|
AppointmentOrderStatus.VISITED,
|
AppointmentOrderStatus.EXPIRED -> "审批通过";
|
case AppointmentOrderStatus.REJECTED -> "审批驳回";
|
case AppointmentOrderStatus.CANCELLED -> "已取消";
|
default -> "已失效";
|
};
|
}
|
|
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 boolean isDeletableStatus(String status, String displayStatus) {
|
if (AppointmentOrderStatus.CANCELLED.equals(status)) {
|
return true;
|
}
|
return DISPLAY_STATUS_INVALID.equals(displayStatus);
|
}
|
|
private String buildVisitTimeText(AppointmentOrder row) {
|
if (AppointmentBizConstants.TIME_MODE_SINGLE.equals(row.getVisitTimeMode())) {
|
String slotLabel = switch (trimToNull(row.getVisitTimeSlot())) {
|
case AppointmentBizConstants.TIME_SLOT_ALL_DAY -> "00:00-23:59";
|
case AppointmentBizConstants.TIME_SLOT_AM -> "00:00-11:59";
|
case AppointmentBizConstants.TIME_SLOT_PM -> "12:00-23:59";
|
default -> "";
|
};
|
if (row.getVisitStartDate() == null) {
|
return slotLabel;
|
}
|
return StringUtils.hasText(slotLabel)
|
? row.getVisitStartDate() + " " + slotLabel
|
: row.getVisitStartDate().toString();
|
}
|
if (row.getVisitStartDate() == null && row.getVisitEndDate() == null) {
|
return "-";
|
}
|
if (Objects.equals(row.getVisitStartDate(), row.getVisitEndDate()) || row.getVisitEndDate() == null) {
|
return String.valueOf(row.getVisitStartDate());
|
}
|
return row.getVisitStartDate() + " 至 " + row.getVisitEndDate();
|
}
|
|
private String buildVisitReasonText(AppointmentOrder row, Map<String, String> reasonNameMap) {
|
String reasonName = reasonNameMap.get(trimToNull(row.getVisitReasonCode()));
|
return StringUtils.hasText(reasonName) ? reasonName : trimToNull(row.getVisitReasonCode());
|
}
|
|
private String buildAccessAreaText(String rawJson) {
|
String raw = trimToNull(rawJson);
|
if (!StringUtils.hasText(raw)) {
|
return null;
|
}
|
try {
|
List<String> rows = objectMapper.readValue(raw, STRING_LIST_TYPE);
|
String joined =
|
rows.stream()
|
.filter(StringUtils::hasText)
|
.map(String::trim)
|
.collect(Collectors.joining("、"));
|
return StringUtils.hasText(joined) ? joined : null;
|
} catch (Exception ignore) {
|
return raw;
|
}
|
}
|
|
private LocalDateTime resolveVisitStartAt(AppointmentOrder row) {
|
LocalDate date = row.getVisitEndDate() != null ? row.getVisitEndDate() : row.getVisitStartDate();
|
if (date == null) {
|
return LocalDateTime.MIN;
|
}
|
if (!AppointmentBizConstants.TIME_MODE_SINGLE.equals(row.getVisitTimeMode())) {
|
return LocalDateTime.of(date, LocalTime.MAX);
|
}
|
return switch (trimToNull(row.getVisitTimeSlot())) {
|
case AppointmentBizConstants.TIME_SLOT_AM -> LocalDateTime.of(date, LocalTime.of(12, 0));
|
default -> LocalDateTime.of(date, LocalTime.MAX);
|
};
|
}
|
|
private Map<Long, MpVisitor> loadMpVisitorMap(Set<Long> visitorIds) {
|
if (visitorIds == null || visitorIds.isEmpty()) {
|
return Map.of();
|
}
|
List<MpVisitor> rows =
|
mpVisitorMapper.selectList(
|
new LambdaQueryWrapper<MpVisitor>()
|
.eq(MpVisitor::getDelFlag, 0)
|
.in(MpVisitor::getId, visitorIds));
|
return rows.stream().collect(Collectors.toMap(MpVisitor::getId, Function.identity(), (a, b) -> a, LinkedHashMap::new));
|
}
|
|
private Map<Long, List<AppointmentVisitor>> loadOrderVisitorRows(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 MiniMyVisitorCodeTicketVo toMyVisitorCodeTicketVo(
|
QrcodeTicket ticket,
|
List<AppointmentVisitor> orderVisitors,
|
Map<Long, MpVisitor> visitorMap) {
|
MiniMyVisitorCodeTicketVo vo = new MiniMyVisitorCodeTicketVo();
|
vo.setTicketId(ticket.getId());
|
vo.setTicketUuid(ticket.getUuid());
|
vo.setMpVisitorId(ticket.getMpVisitorId());
|
vo.setPayload(integrationHikvisionService.encryptQrcodePayload(ticket.getPayload()));
|
vo.setGenerateTime(ticket.getGenerateTime());
|
vo.setExpireTime(ticket.getExpireTime());
|
vo.setVisitorName(resolveVisitorName(ticket.getMpVisitorId(), orderVisitors, visitorMap));
|
return vo;
|
}
|
|
private String resolveVisitorName(
|
Long mpVisitorId, List<AppointmentVisitor> orderVisitors, Map<Long, MpVisitor> visitorMap) {
|
if (orderVisitors != null && !orderVisitors.isEmpty()) {
|
for (AppointmentVisitor row : orderVisitors) {
|
if (Objects.equals(mpVisitorId, row.getMpVisitorId()) && StringUtils.hasText(row.getName())) {
|
return row.getName().trim();
|
}
|
}
|
}
|
MpVisitor visitor = mpVisitorId == null ? null : visitorMap.get(mpVisitorId);
|
if (visitor != null && StringUtils.hasText(visitor.getVisitorName())) {
|
return visitor.getVisitorName().trim();
|
}
|
return "访客";
|
}
|
|
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 List<MiniApprovalDetailVisitorVo> loadOrderVisitors(Long orderId) {
|
List<AppointmentVisitor> rows =
|
appointmentVisitorMapper.selectList(
|
new LambdaQueryWrapper<AppointmentVisitor>()
|
.eq(AppointmentVisitor::getDelFlag, 0)
|
.eq(AppointmentVisitor::getOrderId, orderId)
|
.orderByAsc(AppointmentVisitor::getId));
|
if (rows.isEmpty()) {
|
return List.of();
|
}
|
List<Long> visitorIds =
|
rows.stream()
|
.map(AppointmentVisitor::getMpVisitorId)
|
.filter(Objects::nonNull)
|
.toList();
|
Map<Long, MpVisitor> visitorMap = loadVisitorMap(visitorIds);
|
Map<Long, String> plateMap = loadVisitorPlateMap(orderId);
|
List<MiniApprovalDetailVisitorVo> result = new ArrayList<>();
|
for (AppointmentVisitor row : rows) {
|
MpVisitor visitor = visitorMap.get(row.getMpVisitorId());
|
MiniApprovalDetailVisitorVo vo = new MiniApprovalDetailVisitorVo();
|
vo.setVisitorId(row.getMpVisitorId());
|
vo.setName(firstNonBlank(row.getName(), visitor == null ? null : visitor.getVisitorName(), "-"));
|
vo.setGender(visitor == null ? null : visitor.getGender());
|
vo.setCompanyName(visitor == null ? null : visitor.getVisitorCompany());
|
vo.setMobileMasked(
|
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(
|
StringUtils.hasText(idCardPlain)
|
? SensitiveDataUtil.maskIdCard(idCardPlain)
|
: null);
|
vo.setPlateNo(plateMap.get(row.getMpVisitorId()));
|
result.add(vo);
|
}
|
return result;
|
}
|
|
private List<MiniMyAppointmentDetailVo.RebookVisitorItem> loadRebookVisitors(Long orderId) {
|
List<AppointmentVisitor> rows =
|
appointmentVisitorMapper.selectList(
|
new LambdaQueryWrapper<AppointmentVisitor>()
|
.eq(AppointmentVisitor::getDelFlag, 0)
|
.eq(AppointmentVisitor::getOrderId, orderId)
|
.orderByAsc(AppointmentVisitor::getId));
|
if (rows.isEmpty()) {
|
return List.of();
|
}
|
List<Long> visitorIds =
|
rows.stream()
|
.map(AppointmentVisitor::getMpVisitorId)
|
.filter(Objects::nonNull)
|
.toList();
|
Map<Long, MpVisitor> visitorMap = loadVisitorMap(visitorIds);
|
List<MiniMyAppointmentDetailVo.RebookVisitorItem> result = new ArrayList<>();
|
for (AppointmentVisitor row : rows) {
|
MpVisitor visitor = visitorMap.get(row.getMpVisitorId());
|
MiniMyAppointmentDetailVo.RebookVisitorItem item = new MiniMyAppointmentDetailVo.RebookVisitorItem();
|
item.setVisitorId(row.getMpVisitorId());
|
item.setVisitorUuid(visitor == null ? null : visitor.getUuid());
|
item.setName(firstNonBlank(row.getName(), visitor == null ? null : visitor.getVisitorName(), "-"));
|
item.setGender(visitor == null ? null : visitor.getGender());
|
item.setCompanyName(visitor == null ? null : trimToNull(visitor.getVisitorCompany()));
|
item.setMobileMasked(
|
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);
|
item.setIdCardMasked(
|
StringUtils.hasText(idCardPlain)
|
? SensitiveDataUtil.maskIdCard(idCardPlain)
|
: null);
|
result.add(item);
|
}
|
return result;
|
}
|
|
private List<MiniMyAppointmentDetailVo.RebookVehicleItem> loadRebookVehicles(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 List.of();
|
}
|
List<MiniMyAppointmentDetailVo.RebookVehicleItem> result = new ArrayList<>();
|
for (AppointmentVehicle row : rows) {
|
MiniMyAppointmentDetailVo.RebookVehicleItem item = new MiniMyAppointmentDetailVo.RebookVehicleItem();
|
item.setPlateNo(trimToNull(row.getPlateNo()));
|
item.setDriverVisitorId(row.getDriverVisitorId());
|
item.setDriverName(trimToNull(row.getDriverName()));
|
result.add(item);
|
}
|
return result;
|
}
|
|
private Map<Long, MpVisitor> loadVisitorMap(List<Long> visitorIds) {
|
if (visitorIds.isEmpty()) {
|
return Map.of();
|
}
|
List<MpVisitor> rows =
|
mpVisitorMapper.selectList(
|
new LambdaQueryWrapper<MpVisitor>()
|
.eq(MpVisitor::getDelFlag, 0)
|
.in(MpVisitor::getId, visitorIds));
|
return rows.stream().collect(Collectors.toMap(MpVisitor::getId, Function.identity(), (a, b) -> a));
|
}
|
|
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>> plateMap = new LinkedHashMap<>();
|
for (AppointmentVehicle row : rows) {
|
if (row.getDriverVisitorId() == null) {
|
continue;
|
}
|
plateMap.computeIfAbsent(row.getDriverVisitorId(), key -> new ArrayList<>());
|
if (StringUtils.hasText(row.getPlateNo())) {
|
plateMap.get(row.getDriverVisitorId()).add(row.getPlateNo().trim());
|
}
|
}
|
Map<Long, String> result = new LinkedHashMap<>();
|
for (Map.Entry<Long, List<String>> entry : plateMap.entrySet()) {
|
result.put(entry.getKey(), String.join("、", entry.getValue()));
|
}
|
return result;
|
}
|
|
private String loadDeptName(Long deptId) {
|
if (deptId == null || deptId <= 0) {
|
return null;
|
}
|
SysDept dept = sysDeptMapper.selectById(deptId);
|
return dept == null ? null : dept.getDeptName();
|
}
|
|
private String loadReasonName(String reasonCode) {
|
String code = trimToNull(reasonCode);
|
if (!StringUtils.hasText(code)
|
|| AppointmentBizConstants.VISIT_REASON_OTHER.equalsIgnoreCase(code)) {
|
return null;
|
}
|
List<BizVisitReason> rows =
|
bizVisitReasonMapper.selectList(
|
new LambdaQueryWrapper<BizVisitReason>()
|
.eq(BizVisitReason::getDelFlag, 0)
|
.eq(BizVisitReason::getReasonCode, code)
|
.last("limit 1"));
|
if (rows.isEmpty()) {
|
return null;
|
}
|
return trimToNull(rows.get(0).getReasonName());
|
}
|
|
private String firstNonBlank(String... values) {
|
if (values == null) {
|
return null;
|
}
|
for (String value : values) {
|
if (StringUtils.hasText(value)) {
|
return value.trim();
|
}
|
}
|
return null;
|
}
|
|
private boolean containsIgnoreCase(String src, String keyword) {
|
if (!StringUtils.hasText(src) || !StringUtils.hasText(keyword)) {
|
return false;
|
}
|
return src.toLowerCase().contains(keyword.toLowerCase());
|
}
|
|
private IPage<MiniAppointmentListVo> slicePage(long page, long size, List<MiniAppointmentListVo> 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<MiniAppointmentListVo> records = from >= to ? List.of() : rowsAll.subList(from, to);
|
Page<MiniAppointmentListVo> out = new Page<>(current, pageSize);
|
out.setTotal(total);
|
out.setRecords(records);
|
return out;
|
}
|
|
private IPage<MiniVisitRecordListVo> slicePageVisitRecord(
|
long page, long size, List<MiniVisitRecordListVo> 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<MiniVisitRecordListVo> records = from >= to ? List.of() : rowsAll.subList(from, to);
|
Page<MiniVisitRecordListVo> out = new Page<>(current, pageSize);
|
out.setTotal(total);
|
out.setRecords(records);
|
return out;
|
}
|
|
private int slotSortWeight(String slot) {
|
if (AppointmentBizConstants.TIME_SLOT_ALL_DAY.equals(slot)) {
|
return 3;
|
}
|
if (AppointmentBizConstants.TIME_SLOT_PM.equals(slot)) {
|
return 2;
|
}
|
if (AppointmentBizConstants.TIME_SLOT_AM.equals(slot)) {
|
return 1;
|
}
|
return 0;
|
}
|
|
private boolean isCancelableStatus(String status) {
|
if (!StringUtils.hasText(status)) {
|
return false;
|
}
|
if (AppointmentOrderStatus.REJECTED.equals(status)
|
|| AppointmentOrderStatus.APPROVED.equals(status)
|
|| AppointmentOrderStatus.VISITED.equals(status)
|
|| AppointmentOrderStatus.EXPIRED.equals(status)
|
|| AppointmentOrderStatus.CANCELLED.equals(status)) {
|
return false;
|
}
|
return true;
|
}
|
|
private ApprovalInstance loadLatestInstanceByOrderId(Long orderId) {
|
List<ApprovalInstance> rows =
|
approvalInstanceMapper.selectList(
|
new LambdaQueryWrapper<ApprovalInstance>()
|
.eq(ApprovalInstance::getDelFlag, 0)
|
.eq(ApprovalInstance::getOrderId, orderId)
|
.orderByDesc(ApprovalInstance::getId)
|
.last("limit 1"));
|
return rows.isEmpty() ? null : rows.get(0);
|
}
|
|
private void expireUnusedTicketsByOrderId(Long orderId) {
|
List<QrcodeTicket> tickets =
|
qrcodeTicketMapper.selectList(
|
new LambdaQueryWrapper<QrcodeTicket>()
|
.eq(QrcodeTicket::getDelFlag, 0)
|
.eq(QrcodeTicket::getOrderId, orderId)
|
.eq(QrcodeTicket::getTicketStatus, QrcodeTicketStatus.UNUSED));
|
for (QrcodeTicket ticket : tickets) {
|
ticket.setTicketStatus(QrcodeTicketStatus.EXPIRED);
|
qrcodeTicketMapper.updateById(ticket);
|
}
|
}
|
|
private int singleDaySlotSortWeight(MiniVisitRecordListVo row) {
|
if (!AppointmentBizConstants.TIME_MODE_SINGLE.equals(row.getVisitTimeMode())) {
|
return 0;
|
}
|
return slotSortWeight(row.getVisitTimeSlot());
|
}
|
|
private record FieldRequiredContext(Set<String> requiredCodes) {
|
private boolean isRequired(String... codes) {
|
for (String c : codes) {
|
if (requiredCodes.contains(c)) {
|
return true;
|
}
|
}
|
return false;
|
}
|
}
|
|
private record DateRange(LocalDate start, LocalDate end) {}
|
}
|