package com.sinata.appointment.service; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.sinata.appointment.constants.AppointmentBizConstants; import com.sinata.appointment.entity.AppointmentOrder; import com.sinata.appointment.entity.AppointmentVisitor; import com.sinata.appointment.entity.MpUser; import com.sinata.appointment.entity.MpVisitor; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.LinkedHashMap; import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; /** * 海康出码适配:当前按 PDF 示例组装二维码 payload 字符串。 */ @Slf4j @Service @RequiredArgsConstructor public class IntegrationHikvisionService { private static final String VISITOR_ID_CARD_TYPE = "111"; private static final String VALID_TIME_PREFIX = "m"; private static final String EMPTY = ""; private static final DateTimeFormatter QR_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); private static final DateTimeFormatter QR_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyMMddHHmmss.SSS"); private final ObjectMapper objectMapper; public String requestQrcodePayload( AppointmentOrder order, AppointmentVisitor visitor, MpVisitor visitorProfile, MpUser visitedUser, String visitPurpose, String areas, LocalDateTime qrGenerateTime, LocalDateTime qrExpireTime, int validMinutes, String codeId, String visitorIdCardPlain) { LocalDateTime startTime = resolveVisitStartTime(order); LocalDateTime endTime = resolveVisitEndTime(order); Map payload = new LinkedHashMap<>(); payload.put("visitorName", firstNonBlank(visitor.getName(), valueOf(visitorProfile, MpVisitor::getVisitorName))); payload.put("visitorIdCard", trimToEmpty(visitorIdCardPlain)); payload.put("visitorIdCardType", VISITOR_ID_CARD_TYPE); payload.put("visitorPhone", firstNonBlank(visitor.getMobile(), valueOf(visitorProfile, MpVisitor::getVisitorMobile))); payload.put("workUnit", firstNonBlank(valueOf(visitorProfile, MpVisitor::getVisitorCompany), order.getVisitorOrg())); payload.put("carNumber", trimToEmpty(visitor.getPlateNo())); payload.put("visitPurpose", trimToEmpty(visitPurpose)); payload.put("jobNo", valueOf(visitedUser, MpUser::getStaffNo)); payload.put("beVisitorPhone", valueOf(visitedUser, MpUser::getMobile)); payload.put("startTime", formatDateTime(startTime, QR_TIME_FORMATTER)); payload.put("endTime", formatDateTime(endTime, QR_TIME_FORMATTER)); payload.put("areas", trimToEmpty(areas)); payload.put("beVisitorName", firstNonBlank(order.getVisitedUserName(), valueOf(visitedUser, MpUser::getNickName))); payload.put("date", formatDateTime(qrGenerateTime, QR_DATE_FORMATTER)); payload.put("validTime", VALID_TIME_PREFIX + Math.max(validMinutes, 1)); payload.put("codeId", trimToEmpty(codeId)); payload.put("customa", EMPTY); payload.put("customb", EMPTY); payload.put("customc", EMPTY); try { String json = objectMapper.writeValueAsString(payload); log.info( "Hikvision payload prepared orderId={} mpVisitorId={} codeId={} generateTime={} expireTime={} payload={}", order.getId(), visitor.getMpVisitorId(), codeId, qrGenerateTime, qrExpireTime, json); return json; } catch (JsonProcessingException ex) { throw new IllegalStateException("二维码内容生成失败", ex); } } private LocalDateTime resolveVisitStartTime(AppointmentOrder order) { LocalDate visitDate = firstNonNull(order.getVisitStartDate(), order.getVisitEndDate(), LocalDate.now()); if (!AppointmentBizConstants.TIME_MODE_MULTI.equalsIgnoreCase(trimToEmpty(order.getVisitTimeMode()))) { String slot = trimToEmpty(order.getVisitTimeSlot()).toUpperCase(); if (AppointmentBizConstants.TIME_SLOT_PM.equals(slot)) { return LocalDateTime.of(visitDate, LocalTime.of(12, 0)); } } return LocalDateTime.of(visitDate, LocalTime.MIN); } private LocalDateTime resolveVisitEndTime(AppointmentOrder order) { LocalDate visitDate = firstNonNull(order.getVisitEndDate(), order.getVisitStartDate(), LocalDate.now()); if (!AppointmentBizConstants.TIME_MODE_MULTI.equalsIgnoreCase(trimToEmpty(order.getVisitTimeMode()))) { String slot = trimToEmpty(order.getVisitTimeSlot()).toUpperCase(); if (AppointmentBizConstants.TIME_SLOT_AM.equals(slot)) { return LocalDateTime.of(visitDate, LocalTime.of(11, 59, 59)); } } return LocalDateTime.of(visitDate, LocalTime.of(23, 59, 59)); } private String formatDateTime(LocalDateTime value, DateTimeFormatter formatter) { return value == null ? EMPTY : value.format(formatter); } private String trimToEmpty(String value) { return value == null ? EMPTY : value.trim(); } private String firstNonBlank(String... values) { if (values == null) { return EMPTY; } for (String value : values) { if (StringUtils.hasText(value)) { return value.trim(); } } return EMPTY; } @SafeVarargs private final T firstNonNull(T... values) { if (values == null) { return null; } for (T value : values) { if (value != null) { return value; } } return null; } private String valueOf(T source, StringGetter getter) { if (source == null) { return EMPTY; } String value = getter.get(source); return StringUtils.hasText(value) ? value.trim() : EMPTY; } @FunctionalInterface private interface StringGetter { String get(T source); } }