| | |
| | | import com.sinata.appointment.constants.IntegrationConstant; |
| | | import com.sinata.appointment.constants.QrcodeTicketStatus; |
| | | import com.sinata.appointment.constants.SystemSettingKeys; |
| | | import com.sinata.appointment.config.HikvisionQrcodeProperties; |
| | | import com.sinata.appointment.entity.ApprovalInstance; |
| | | import com.sinata.appointment.entity.ApprovalTask; |
| | | import com.sinata.appointment.entity.AppointmentOrder; |
| | | import com.sinata.appointment.entity.AppointmentVisitor; |
| | | import com.sinata.appointment.entity.AppointmentVehicle; |
| | | 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.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 java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.UUID; |
| | | import java.util.stream.Stream; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | |
| | | private final QrcodeTicketMapper qrcodeTicketMapper; |
| | | private final AppointmentOrderMapper appointmentOrderMapper; |
| | | private final ApprovalInstanceMapper approvalInstanceMapper; |
| | | private final ApprovalTaskMapper approvalTaskMapper; |
| | | private final AppointmentVisitorMapper appointmentVisitorMapper; |
| | | private final AppointmentVehicleMapper appointmentVehicleMapper; |
| | | private final MpVisitorMapper mpVisitorMapper; |
| | | private final MpUserMapper mpUserMapper; |
| | | private final BizVisitReasonMapper bizVisitReasonMapper; |
| | | private final SysBizSettingCacheService sysBizSettingCacheService; |
| | | private final HikvisionQrcodeProperties hikvisionQrcodeProperties; |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void onOrderFullyApproved(AppointmentOrder order) { |
| | |
| | | .eq(AppointmentVisitor::getDelFlag, 0) |
| | | .eq(AppointmentVisitor::getOrderId, order.getId()) |
| | | .orderByAsc(AppointmentVisitor::getId)); |
| | | Map<Long, String> driverPlateMap = loadDriverPlateMap(order.getId()); |
| | | Map<Long, MpVisitor> visitorProfileMap = loadVisitorProfileMap(visitors); |
| | | Map<Long, QrcodeTicket> existingTicketMap = loadActiveUnusedTicketMap(order.getId(), visitors); |
| | | int validMinutes = getMaxConsecutiveDays() * 24 * 60; |
| | | int validMinutes = resolveValidMinutes(); |
| | | LocalDateTime generateTime = resolveGenerateTime(order); |
| | | LocalDateTime expireTime = generateTime.plusMinutes(validMinutes); |
| | | String visitPurpose = buildVisitPurpose(order); |
| | |
| | | continue; |
| | | } |
| | | MpVisitor visitorProfile = visitorProfileMap.get(visitor.getMpVisitorId()); |
| | | visitor.setPlateNo(driverPlateMap.get(visitor.getMpVisitorId())); |
| | | String ticketUuid = UUID.randomUUID().toString().replace("-", ""); |
| | | String visitorIdCardPlain = resolveVisitorIdCardPlain(visitor, visitorProfile); |
| | | String payload = |
| | |
| | | existingTicketMap.put(visitor.getMpVisitorId(), ticket); |
| | | createdCount++; |
| | | log.info( |
| | | "{} raw-payload orderId={} mpVisitorId={} payload={}", |
| | | "{} raw-payload orderId={} mpVisitorId={} ticketUuid={} payloadLength={}", |
| | | IntegrationConstant.HIKVISION_QRCODE_EVENT, |
| | | order.getId(), |
| | | visitor.getMpVisitorId(), |
| | | payload); |
| | | ticketUuid, |
| | | payload == null ? 0 : payload.length()); |
| | | } |
| | | |
| | | if (visitors.isEmpty()) { |
| | |
| | | |
| | | try { |
| | | integrationWechatService.notifyApprovalFinalResult(order, true); |
| | | integrationWechatService.notifyApprovalNoticeUsers( |
| | | order, true, loadNoticeUsers(latestInstance.getId()), visitors.size()); |
| | | } catch (Exception e) { |
| | | log.warn("{} orderId={} failed: {}", IntegrationConstant.WECHAT_NOTIFY_EVENT, order.getId(), e.getMessage()); |
| | | } |
| | |
| | | appointmentOrderMapper.updateById(order); |
| | | try { |
| | | integrationWechatService.notifyApprovalFinalResult(order, false); |
| | | ApprovalInstance latestInstance = loadLatestInstance(order.getId()); |
| | | if (latestInstance != null) { |
| | | List<AppointmentVisitor> visitors = |
| | | appointmentVisitorMapper.selectList( |
| | | new LambdaQueryWrapper<AppointmentVisitor>() |
| | | .eq(AppointmentVisitor::getDelFlag, 0) |
| | | .eq(AppointmentVisitor::getOrderId, order.getId())); |
| | | integrationWechatService.notifyApprovalNoticeUsers( |
| | | order, false, loadNoticeUsers(latestInstance.getId()), visitors.size()); |
| | | } |
| | | } catch (Exception e) { |
| | | log.warn( |
| | | "{} orderId={} failed: {}", |
| | |
| | | order.getId(), |
| | | e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | private ApprovalInstance loadLatestInstance(Long orderId) { |
| | | if (orderId == null) { |
| | | return null; |
| | | } |
| | | return approvalInstanceMapper.selectOne( |
| | | new LambdaQueryWrapper<ApprovalInstance>() |
| | | .eq(ApprovalInstance::getDelFlag, 0) |
| | | .eq(ApprovalInstance::getOrderId, orderId) |
| | | .orderByDesc(ApprovalInstance::getId) |
| | | .last("limit 1")); |
| | | } |
| | | |
| | | private List<MpUser> loadNoticeUsers(Long instanceId) { |
| | | if (instanceId == null) { |
| | | return List.of(); |
| | | } |
| | | List<ApprovalTask> tasks = |
| | | approvalTaskMapper.selectList( |
| | | new LambdaQueryWrapper<ApprovalTask>() |
| | | .eq(ApprovalTask::getDelFlag, 0) |
| | | .eq(ApprovalTask::getInstanceId, instanceId) |
| | | .isNotNull(ApprovalTask::getNoticeIds) |
| | | .ne(ApprovalTask::getNoticeIds, "") |
| | | .orderByAsc(ApprovalTask::getId)); |
| | | if (tasks.isEmpty()) { |
| | | return List.of(); |
| | | } |
| | | LinkedHashSet<Long> noticeUserIds = new LinkedHashSet<>(); |
| | | for (ApprovalTask task : tasks) { |
| | | noticeUserIds.addAll(parseCommaSeparatedIds(task.getNoticeIds())); |
| | | } |
| | | if (noticeUserIds.isEmpty()) { |
| | | return List.of(); |
| | | } |
| | | List<MpUser> users = |
| | | mpUserMapper.selectList( |
| | | new LambdaQueryWrapper<MpUser>() |
| | | .eq(MpUser::getDelFlag, 0) |
| | | .in(MpUser::getId, noticeUserIds)); |
| | | Map<Long, MpUser> userMap = new LinkedHashMap<>(); |
| | | for (MpUser user : users) { |
| | | userMap.putIfAbsent(user.getId(), user); |
| | | } |
| | | return noticeUserIds.stream().map(userMap::get).filter(Objects::nonNull).toList(); |
| | | } |
| | | |
| | | private List<Long> parseCommaSeparatedIds(String raw) { |
| | | if (!StringUtils.hasText(raw)) { |
| | | return List.of(); |
| | | } |
| | | return Stream.of(raw.split(",")) |
| | | .filter(StringUtils::hasText) |
| | | .map(String::trim) |
| | | .map(value -> { |
| | | try { |
| | | return Long.parseLong(value); |
| | | } catch (NumberFormatException ex) { |
| | | return null; |
| | | } |
| | | }) |
| | | .filter(Objects::nonNull) |
| | | .filter(id -> id > 0) |
| | | .distinct() |
| | | .toList(); |
| | | } |
| | | |
| | | private Map<Long, MpVisitor> loadVisitorProfileMap(List<AppointmentVisitor> visitors) { |
| | |
| | | Map<Long, MpVisitor> result = new LinkedHashMap<>(); |
| | | for (MpVisitor row : rows) { |
| | | result.putIfAbsent(row.getId(), row); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private Map<Long, String> loadDriverPlateMap(Long orderId) { |
| | | if (orderId == null) { |
| | | return Map.of(); |
| | | } |
| | | 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>> grouped = new LinkedHashMap<>(); |
| | | for (AppointmentVehicle row : rows) { |
| | | if (row.getDriverVisitorId() == null || !StringUtils.hasText(row.getPlateNo())) { |
| | | continue; |
| | | } |
| | | grouped.computeIfAbsent(row.getDriverVisitorId(), key -> new java.util.ArrayList<>()).add(row.getPlateNo().trim()); |
| | | } |
| | | Map<Long, String> result = new LinkedHashMap<>(); |
| | | for (Map.Entry<Long, List<String>> entry : grouped.entrySet()) { |
| | | result.put(entry.getKey(), String.join(",", entry.getValue())); |
| | | } |
| | | return result; |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | private int resolveValidMinutes() { |
| | | Integer configured = hikvisionQrcodeProperties.getValidMinutes(); |
| | | if (configured != null && configured > 0) { |
| | | return configured; |
| | | } |
| | | return getMaxConsecutiveDays() * 24 * 60; |
| | | } |
| | | |
| | | private String buildVisitPurpose(AppointmentOrder order) { |
| | | String code = trimToNull(order.getVisitReasonCode()); |
| | | if (!StringUtils.hasText(code)) { |
| | | return ""; |
| | | } |
| | | if (AppointmentBizConstants.VISIT_REASON_OTHER.equalsIgnoreCase(code)) { |
| | | String other = trimToNull(order.getVisitReasonOther()); |
| | | return StringUtils.hasText(other) ? "其他-" + other : "其他"; |
| | | return "其他"; |
| | | } |
| | | BizVisitReason reason = |
| | | bizVisitReasonMapper.selectOne( |