longz
2026-04-21 c4db0a4526f61758272c92396aa788d84f098102
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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<String, Object> 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> T firstNonNull(T... values) {
        if (values == null) {
            return null;
        }
        for (T value : values) {
            if (value != null) {
                return value;
            }
        }
        return null;
    }
 
    private <T> String valueOf(T source, StringGetter<T> getter) {
        if (source == null) {
            return EMPTY;
        }
        String value = getter.get(source);
        return StringUtils.hasText(value) ? value.trim() : EMPTY;
    }
 
    @FunctionalInterface
    private interface StringGetter<T> {
        String get(T source);
    }
}