| | |
| | | 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 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 |
| | |
| | | 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; |
| | | private final ApplicationEventPublisher applicationEventPublisher; |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long create(MiniAppointmentCreateRequest req) { |
| | | public MiniAppointmentCreateResultVo create(MiniAppointmentCreateRequest req) { |
| | | MpUser current = miniAuthApplicationService.currentMpUser(); |
| | | FieldRequiredContext required = loadFieldRequiredContext(); |
| | | MpUser visited = loadVisitedUser(req.getVisitedUserId()); |
| | |
| | | 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); |
| | | } |
| | | applicationEventPublisher.publishEvent( |
| | | new AppointmentApproversNotifyEvent(order.getId(), inst.getId())); |
| | | } |
| | | } |
| | | return order.getId(); |
| | | MiniAppointmentCreateResultVo result = new MiniAppointmentCreateResultVo(); |
| | | result.setId(order.getId()); |
| | | result.setUuid(order.getUuid()); |
| | | return result; |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | |
| | | 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); |
| | |
| | | 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); |
| | |
| | | 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()); |
| | |
| | | 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( |
| | |
| | | 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, "预约单不存在"); |
| | |
| | | if (!StringUtils.hasText(txt)) { |
| | | throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "选择其他时须填写自定义内容"); |
| | | } |
| | | if (txt.length() > 255) { |
| | | throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "自定义内容不能超过255字"); |
| | | if (txt.length() > 15) { |
| | | throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "自定义内容不能超过15字"); |
| | | } |
| | | return; |
| | | } |
| | |
| | | vo.setVisitStartDate(row.getVisitStartDate()); |
| | | vo.setVisitEndDate(row.getVisitEndDate()); |
| | | vo.setVisitReasonCode(row.getVisitReasonCode()); |
| | | vo.setVisitReasonOther(row.getVisitReasonOther()); |
| | | vo.setCreateTime(row.getCreateTime()); |
| | | return vo; |
| | | } |
| | |
| | | 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 -> "审批中"; |
| | |
| | | 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 -> "全天"; |
| | | case AppointmentBizConstants.TIME_SLOT_AM -> "上午"; |
| | | case AppointmentBizConstants.TIME_SLOT_PM -> "下午"; |
| | | 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) { |
| | |
| | | private String buildVisitReasonText(AppointmentOrder row, Map<String, String> reasonNameMap) { |
| | | if (AppointmentBizConstants.VISIT_REASON_OTHER.equalsIgnoreCase(trimToNull(row.getVisitReasonCode()))) { |
| | | String other = trimToNull(row.getVisitReasonOther()); |
| | | return StringUtils.hasText(other) ? "其他-" + other : "其他"; |
| | | return StringUtils.hasText(other) ? "其他:" + other : "其他"; |
| | | } |
| | | String reasonName = reasonNameMap.get(trimToNull(row.getVisitReasonCode())); |
| | | return StringUtils.hasText(reasonName) ? reasonName : trimToNull(row.getVisitReasonCode()); |