From 25df88d0ab9d7444ed1d162b9efa1259ac2f6f66 Mon Sep 17 00:00:00 2001
From: longz <longzhaoz@qq.com>
Date: 星期一, 27 四月 2026 10:13:53 +0800
Subject: [PATCH] 小程序对接finish
---
backend/src/main/java/com/sinata/appointment/service/MiniAppointmentApplicationService.java | 205 ++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 187 insertions(+), 18 deletions(-)
diff --git a/backend/src/main/java/com/sinata/appointment/service/MiniAppointmentApplicationService.java b/backend/src/main/java/com/sinata/appointment/service/MiniAppointmentApplicationService.java
index 344c06b..1436d30 100644
--- a/backend/src/main/java/com/sinata/appointment/service/MiniAppointmentApplicationService.java
+++ b/backend/src/main/java/com/sinata/appointment/service/MiniAppointmentApplicationService.java
@@ -21,6 +21,7 @@
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;
@@ -64,6 +65,7 @@
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;
@@ -71,6 +73,7 @@
import org.springframework.context.ApplicationEventPublisher;
import com.sinata.appointment.service.event.AppointmentApprovedEvent;
+@Slf4j
@Service
@RequiredArgsConstructor
public class MiniAppointmentApplicationService {
@@ -96,6 +99,7 @@
private final BizHolidayDateCacheService bizHolidayDateCacheService;
private final SysBizSettingCacheService sysBizSettingCacheService;
private final ApprovalEngineService approvalEngineService;
+ private final IntegrationWechatService integrationWechatService;
private final PostApprovalOrchestrationService postApprovalOrchestrationService;
private final ApprovalFlowViewApplicationService approvalFlowViewApplicationService;
private final ObjectMapper objectMapper;
@@ -156,6 +160,7 @@
order.setPersonEntryFlag(personEntry);
order.setVehicleEntryFlag(vehicleEntry);
order.setSubmitterId(current.getId());
+ order.setSubmitterVisibleFlag(1);
order.setHolidayFlag(holidayFlag);
order.setStatus(AppointmentOrderStatus.PENDING_APPROVAL);
appointmentOrderMapper.insert(order);
@@ -188,6 +193,12 @@
ApprovalInstance inst = approvalEngineService.startFlow(order);
if (inst == null) {
applicationEventPublisher.publishEvent(new AppointmentApprovedEvent(order.getId()));
+ } else {
+ try {
+ integrationWechatService.notifyCurrentApprovers(order, inst.getId());
+ } catch (Exception ex) {
+ log.warn("notify current approvers failed, orderId={}", order.getId(), ex);
+ }
}
}
return order.getId();
@@ -214,13 +225,34 @@
ApprovalInstance latestInst = loadLatestInstanceByOrderId(order.getId());
if (latestInst != null
&& ApprovalInstanceStatus.IN_PROGRESS.equals(latestInst.getStatus())) {
- approvalEngineService.rejectInstance(latestInst, CANCEL_REASON_BY_SUBMITTER);
+ 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(
@@ -261,12 +293,12 @@
}
public IPage<MiniVisitRecordListVo> pageVisitRecords(
- long page, long size, String visitorNameKeyword, LocalDate visitDate) {
+ 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(), trimToNull(visitorNameKeyword), visitDate);
+ orderPage, current.getId(), futureOnly, trimToNull(visitorNameKeyword), visitDate);
Page<MiniVisitRecordListVo> result =
new Page<>(pagedOrders.getCurrent(), pagedOrders.getSize(), pagedOrders.getTotal());
List<AppointmentOrder> orders = pagedOrders.getRecords();
@@ -326,15 +358,51 @@
.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);
+ }
+
+ private List<MiniMyVisitorCodeOrderVo> buildVisitorCodeOrders(
+ List<AppointmentOrder> orders, boolean futureOnly) {
if (orders.isEmpty()) {
return List.of();
}
- LocalDateTime now = LocalDateTime.now();
- List<AppointmentOrder> eligibleOrders =
- orders.stream()
- .filter(order -> resolveVisitStartAt(order).isAfter(now))
- .toList();
+ 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();
}
@@ -387,31 +455,48 @@
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;
+ }
+
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.selectById(orderId);
- if (order == null || (order.getDelFlag() != null && order.getDelFlag() == 1)) {
+ AppointmentOrder order = appointmentOrderMapper.selectMiniVisibleOrderById(orderId, current.getId());
+ if (order == null) {
throw new BizException(BizErrorCode.ORDER_NOT_FOUND, "预约单不存在");
}
UuidAssert.match(order, uuid);
- if (!Objects.equals(order.getSubmitterId(), current.getId())) {
- throw new BizException(BizErrorCode.AUTH_FORBIDDEN, "无权查看该预约详情");
- }
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(resolveDisplayStatus(order));
+ 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);
@@ -419,9 +504,19 @@
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;
@@ -842,6 +937,13 @@
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())) {
@@ -894,16 +996,16 @@
}
private LocalDateTime resolveVisitStartAt(AppointmentOrder row) {
- LocalDate date = row.getVisitStartDate() != null ? row.getVisitStartDate() : row.getVisitEndDate();
+ 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.MIN);
+ return LocalDateTime.of(date, LocalTime.MAX);
}
return switch (trimToNull(row.getVisitTimeSlot())) {
- case AppointmentBizConstants.TIME_SLOT_PM -> LocalDateTime.of(date, LocalTime.of(12, 0));
- default -> LocalDateTime.of(date, LocalTime.MIN);
+ case AppointmentBizConstants.TIME_SLOT_AM -> LocalDateTime.of(date, LocalTime.of(12, 0));
+ default -> LocalDateTime.of(date, LocalTime.MAX);
};
}
@@ -1023,6 +1125,73 @@
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();
--
Gitblit v1.7.1