集成大华通行记录增量同步与定时任务
parent
2934139574
commit
d7c6c82bb8
|
|
@ -2,7 +2,6 @@ package com.zcloud.primeport;
|
|||
|
||||
import com.jjb.saas.base.starter.bootstart.JJBSpringbootApplication;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Spring Boot Starter
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
dahua:
|
||||
resource:
|
||||
sync-enabled: true
|
||||
sync-page-size: 200
|
||||
subsystem: evo-accesscontrol
|
||||
auto-match-by-name: true
|
||||
oauth:
|
||||
host: 123.183.159.35
|
||||
client-id: 123.183.159.35
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
spring:
|
||||
config:
|
||||
import:
|
||||
# - classpath:nacos.yml
|
||||
- classpath:prodnacos.yml
|
||||
- classpath:nacos.yml
|
||||
# - classpath:prodnacos.yml
|
||||
- classpath:sdk.yml
|
||||
- classpath:swagger.yml
|
||||
# - classpath:ds.yml
|
||||
|
|
|
|||
|
|
@ -85,6 +85,17 @@ message:
|
|||
closedAreaPersonReceiveAuditReject: MS000161
|
||||
# 大华平台配置
|
||||
dahua:
|
||||
access-record:
|
||||
sync-enabled: true
|
||||
sync-page-size: 1000
|
||||
sync-delay-minutes: 5
|
||||
sync-overlap-minutes: 10
|
||||
initial-lookback-minutes: 1440
|
||||
resource:
|
||||
sync-enabled: true
|
||||
sync-page-size: 200
|
||||
subsystem: evo-accesscontrol
|
||||
auto-match-by-name: true
|
||||
oauth:
|
||||
host: 123.183.159.35
|
||||
client-id: 123.183.159.35
|
||||
|
|
@ -92,4 +103,4 @@ dahua:
|
|||
username: system
|
||||
password: zcloud88888
|
||||
http: false
|
||||
port: 4443
|
||||
port: 4443
|
||||
|
|
|
|||
|
|
@ -7,9 +7,11 @@ import com.zcloud.primeport.domain.model.DaHuaCarAddCmd;
|
|||
import com.zcloud.primeport.domain.model.DaHuaCarPageCmd;
|
||||
import com.zcloud.primeport.domain.model.DaHuaPersonSyncCmd;
|
||||
import com.zcloud.primeport.domain.model.DaHuaTempVehicleSaveCmd;
|
||||
import com.zcloud.primeport.dto.DaHuaAccessRecordSyncCmd;
|
||||
import com.zcloud.primeport.dto.DaHuaCarDeleteCmd;
|
||||
import com.zcloud.primeport.dto.DaHuaPersonDeleteCmd;
|
||||
import com.zcloud.primeport.dto.DaHuaResourceSyncCmd;
|
||||
import com.zcloud.primeport.dto.clientobject.DaHuaAccessRecordSyncResultCO;
|
||||
import com.zcloud.primeport.dto.clientobject.DaHuaResourceSyncResultCO;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
|
|
@ -18,6 +20,7 @@ import org.springframework.validation.annotation.Validated;
|
|||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
import javax.validation.Valid;
|
||||
|
||||
@Api(tags = "大华平台对接")
|
||||
@RequestMapping("/${application.gateway}/dahua")
|
||||
|
|
@ -100,6 +103,16 @@ public class DaHuaController {
|
|||
return daHuaService.queryAccessRecord(cmd);
|
||||
}
|
||||
|
||||
@ApiOperation("同步大华门禁通行记录并落库")
|
||||
@PostMapping("/access/record/sync")
|
||||
public SingleResponse<DaHuaAccessRecordSyncResultCO> syncAccessRecords(
|
||||
@Valid @RequestBody DaHuaAccessRecordSyncCmd cmd) {
|
||||
if (daHuaService == null) {
|
||||
throw new RuntimeException("大华服务未配置");
|
||||
}
|
||||
return daHuaService.syncAccessRecords(cmd);
|
||||
}
|
||||
|
||||
@ApiOperation("删除人员")
|
||||
@PostMapping("/person/delete")
|
||||
public SingleResponse<Boolean> deletePerson(@Validated @RequestBody DaHuaPersonDeleteCmd cmd) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,457 @@
|
|||
package com.zcloud.primeport.service;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.zcloud.primeport.domain.gateway.DaHuaAccessRecordRepositoryGateway;
|
||||
import com.zcloud.primeport.domain.gateway.DaHuaGateway;
|
||||
import com.zcloud.primeport.domain.gateway.DaHuaResourceRepositoryGateway;
|
||||
import com.zcloud.primeport.domain.gateway.DaHuaAccessRecordSyncGateway;
|
||||
import com.zcloud.primeport.domain.model.CorpInfoSnapshotE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaAccessRecordCmd;
|
||||
import com.zcloud.primeport.domain.model.DaHuaAccessRecordE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaAccessRecordSyncResultE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaDepartmentCorpMappingE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaDeviceChannelE;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.Normalizer;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DaHuaAccessRecordSyncApplicationService implements DaHuaAccessRecordSyncGateway {
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER =
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
private static final int MAX_EXCEPTION_DETAILS = 100;
|
||||
|
||||
private final DaHuaGateway daHuaGateway;
|
||||
private final DaHuaResourceRepositoryGateway resourceRepositoryGateway;
|
||||
private final DaHuaAccessRecordRepositoryGateway accessRecordRepositoryGateway;
|
||||
|
||||
public DaHuaAccessRecordSyncResultE sync(String startSwingTime,
|
||||
String endSwingTime,
|
||||
String startCreateTime,
|
||||
String endCreateTime,
|
||||
int pageSize) {
|
||||
validateTimeRange(startSwingTime, endSwingTime, "swing time");
|
||||
validateOptionalTimeRange(startCreateTime, endCreateTime, "create time");
|
||||
if (pageSize < 1 || pageSize > 10000) {
|
||||
throw new IllegalArgumentException("pageSize must be between 1 and 10000");
|
||||
}
|
||||
|
||||
SyncIndex index = buildIndex();
|
||||
Map<String, DaHuaAccessRecordE> synchronizedRecords = new HashMap<>();
|
||||
DaHuaAccessRecordSyncResultE result = new DaHuaAccessRecordSyncResultE();
|
||||
int pageNum = 1;
|
||||
while (true) {
|
||||
List<Map<String, Object>> pageData = fetchPage(pageNum, pageSize, startSwingTime,
|
||||
endSwingTime, startCreateTime, endCreateTime);
|
||||
result.setPageCount(result.getPageCount() + 1);
|
||||
if (pageData.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
persistPage(pageData, index, synchronizedRecords, result);
|
||||
if (pageData.size() < pageSize) {
|
||||
break;
|
||||
}
|
||||
pageNum++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DaHuaAccessRecordSyncResultE syncIncremental(int pageSize,
|
||||
int delayMinutes,
|
||||
int overlapMinutes,
|
||||
int initialLookbackMinutes) {
|
||||
LocalDateTime endCreateTime = LocalDateTime.now().minusMinutes(Math.max(delayMinutes, 0));
|
||||
LocalDateTime latestCreateTime = accessRecordRepositoryGateway.findLatestDahuaCreateTime();
|
||||
LocalDateTime startCreateTime = latestCreateTime == null
|
||||
? endCreateTime.minusMinutes(Math.max(initialLookbackMinutes, 1))
|
||||
: latestCreateTime.minusMinutes(Math.max(overlapMinutes, 0));
|
||||
if (!startCreateTime.isBefore(endCreateTime)) {
|
||||
return new DaHuaAccessRecordSyncResultE();
|
||||
}
|
||||
return sync(format(startCreateTime.minusDays(2)), format(endCreateTime.plusDays(1)),
|
||||
format(startCreateTime), format(endCreateTime), pageSize);
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> fetchPage(int pageNum,
|
||||
int pageSize,
|
||||
String startSwingTime,
|
||||
String endSwingTime,
|
||||
String startCreateTime,
|
||||
String endCreateTime) {
|
||||
DaHuaAccessRecordCmd cmd = new DaHuaAccessRecordCmd();
|
||||
cmd.setPageNum(pageNum);
|
||||
cmd.setPageSize(pageSize);
|
||||
cmd.setStartSwingTime(startSwingTime);
|
||||
cmd.setEndSwingTime(endSwingTime);
|
||||
cmd.setStartCreateTime(blankToNull(startCreateTime));
|
||||
cmd.setEndCreateTime(blankToNull(endCreateTime));
|
||||
Map<String, Object> response = daHuaGateway.queryAccessRecord(cmd);
|
||||
if (response == null || !isSuccessful(response)) {
|
||||
String code = response == null ? null : stringValue(response.get("code"));
|
||||
String errMsg = response == null ? "empty response" : stringValue(response.get("errMsg"));
|
||||
throw new IllegalStateException("Dahua access record query failed: code=" + code
|
||||
+ ", errMsg=" + errMsg);
|
||||
}
|
||||
Map<String, Object> data = toMap(response.get("data"));
|
||||
return toRecordList(data.get("pageData"));
|
||||
}
|
||||
|
||||
protected void persistPage(List<Map<String, Object>> pageData,
|
||||
SyncIndex index,
|
||||
Map<String, DaHuaAccessRecordE> synchronizedRecords,
|
||||
DaHuaAccessRecordSyncResultE result) {
|
||||
Set<String> recordIds = new LinkedHashSet<>();
|
||||
for (Map<String, Object> source : pageData) {
|
||||
String recordId = blankToNull(firstString(source, "id", "recordId"));
|
||||
if (recordId != null && !synchronizedRecords.containsKey(recordId)) {
|
||||
recordIds.add(recordId);
|
||||
}
|
||||
}
|
||||
for (DaHuaAccessRecordE existing : accessRecordRepositoryGateway.listByDahuaRecordIds(recordIds)) {
|
||||
synchronizedRecords.put(existing.getDahuaRecordId(), existing);
|
||||
}
|
||||
|
||||
for (Map<String, Object> source : pageData) {
|
||||
result.setRecordTotal(result.getRecordTotal() + 1);
|
||||
try {
|
||||
String recordId = blankToNull(firstString(source, "id", "recordId"));
|
||||
if (recordId == null) {
|
||||
throw new IllegalArgumentException("record id is missing");
|
||||
}
|
||||
DaHuaAccessRecordE record = synchronizedRecords.get(recordId);
|
||||
boolean created = record == null;
|
||||
if (created) {
|
||||
record = new DaHuaAccessRecordE();
|
||||
}
|
||||
mapRecord(record, source, recordId, index);
|
||||
if (created) {
|
||||
accessRecordRepositoryGateway.add(record);
|
||||
result.setRecordCreated(result.getRecordCreated() + 1);
|
||||
} else {
|
||||
accessRecordRepositoryGateway.update(record);
|
||||
result.setRecordUpdated(result.getRecordUpdated() + 1);
|
||||
}
|
||||
synchronizedRecords.put(recordId, record);
|
||||
} catch (Exception e) {
|
||||
result.setRecordInvalid(result.getRecordInvalid() + 1);
|
||||
addDetail(result.getExceptionDetails(), "Access record sync failed: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void mapRecord(DaHuaAccessRecordE record,
|
||||
Map<String, Object> source,
|
||||
String recordId,
|
||||
SyncIndex index) {
|
||||
String channelCode = blankToNull(firstString(source, "channelCode", "acsChannelCode"));
|
||||
String departmentName = blankToNull(firstString(source, "deptName", "departmentName"));
|
||||
Long departmentId = firstLong(source, "deptId", "departmentId");
|
||||
String orgCode = blankToNull(firstString(source, "orgCode", "departmentCode", "deptCode"));
|
||||
DaHuaDeviceChannelE channel = channelCode == null ? null : index.channelsByCode.get(channelCode);
|
||||
DaHuaDepartmentCorpMappingE personMapping = resolvePersonMapping(
|
||||
index, departmentId, orgCode, departmentName);
|
||||
CorpInfoSnapshotE personCorp = personMapping == null
|
||||
? null : index.corpsById.get(personMapping.getCorpId());
|
||||
CorpInfoSnapshotE deviceCorp = channel == null
|
||||
? null : index.corpsById.get(channel.getCorpId());
|
||||
|
||||
record.setDahuaRecordId(recordId);
|
||||
record.setCorpId(null);
|
||||
record.setCorpName(null);
|
||||
record.setPortArea(null);
|
||||
record.setDahuaDepartmentId(departmentId);
|
||||
if (personMapping != null) {
|
||||
record.setCorpId(personMapping.getCorpId());
|
||||
record.setCorpName(personCorp == null ? personMapping.getCorpName() : personCorp.getCorpName());
|
||||
record.setPortArea(personCorp == null ? null : personCorp.getPortArea());
|
||||
if (record.getDahuaDepartmentId() == null) {
|
||||
record.setDahuaDepartmentId(personMapping.getDahuaDepartmentId());
|
||||
}
|
||||
}
|
||||
record.setDahuaDepartmentName(departmentName != null
|
||||
? departmentName : (personMapping == null ? null : personMapping.getDahuaDepartmentName()));
|
||||
|
||||
record.setDeviceCorpId(null);
|
||||
record.setDeviceCorpName(null);
|
||||
record.setDevicePortArea(null);
|
||||
record.setMkmjId(null);
|
||||
record.setPassageId(null);
|
||||
record.setGateId(null);
|
||||
if (channel != null) {
|
||||
record.setDeviceCorpId(channel.getCorpId());
|
||||
record.setDeviceCorpName(deviceCorp == null ? null : deviceCorp.getCorpName());
|
||||
record.setDevicePortArea(deviceCorp == null ? null : deviceCorp.getPortArea());
|
||||
record.setMkmjId(channel.getMkmjId());
|
||||
record.setPassageId(channel.getPassageId());
|
||||
record.setGateId(channel.getGateId());
|
||||
}
|
||||
|
||||
record.setChannelCode(channelCode);
|
||||
record.setChannelName(firstString(source, "channelName", "acsChannelName"));
|
||||
record.setDeviceCode(firstString(source, "deviceCode"));
|
||||
record.setDeviceName(firstString(source, "deviceName"));
|
||||
record.setPersonId(firstLong(source, "personId"));
|
||||
record.setPersonCode(firstString(source, "personCode"));
|
||||
record.setPersonName(firstString(source, "personName"));
|
||||
record.setPaperNumber(firstString(source, "paperNumber"));
|
||||
record.setCardNumber(firstString(source, "cardNumber"));
|
||||
record.setCardStatus(firstInteger(source, "cardStatus"));
|
||||
record.setCardType(firstInteger(source, "cardType"));
|
||||
record.setImageType(firstInteger(source, "imageType"));
|
||||
record.setEnterOrExit(firstInteger(source, "enterOrExit"));
|
||||
record.setOpenType(firstInteger(source, "openType"));
|
||||
record.setOpenResult(firstInteger(source, "openResult"));
|
||||
record.setRecordImageUrl(firstString(source, "recordImageUrl"));
|
||||
record.setRemark(firstString(source, "remark"));
|
||||
record.setSwingTime(firstDateTime(source, "swingTime"));
|
||||
record.setDahuaCreateTime(firstDateTime(source, "createTime"));
|
||||
record.setMaskState(firstInteger(source, "maskState"));
|
||||
record.setOverTemp(firstBoolean(source, "overTemp"));
|
||||
record.setCurrentTemperature(firstDecimal(source, "curTemp"));
|
||||
record.setSourceType("PULL");
|
||||
record.setRawData(JSONUtil.toJsonStr(source));
|
||||
record.setLastSyncTime(LocalDateTime.now());
|
||||
record.setDeleteEnum("FALSE");
|
||||
}
|
||||
|
||||
private SyncIndex buildIndex() {
|
||||
SyncIndex index = new SyncIndex();
|
||||
for (CorpInfoSnapshotE corp : nullSafe(resourceRepositoryGateway.listActiveCorps())) {
|
||||
index.corpsById.put(corp.getId(), corp);
|
||||
}
|
||||
for (DaHuaDeviceChannelE channel : nullSafe(resourceRepositoryGateway.listDeviceChannels())) {
|
||||
if (channel.getChannelCode() != null && !"TRUE".equalsIgnoreCase(channel.getDeleteEnum())) {
|
||||
index.channelsByCode.put(channel.getChannelCode(), channel);
|
||||
}
|
||||
}
|
||||
for (DaHuaDepartmentCorpMappingE mapping : nullSafe(resourceRepositoryGateway.listMappings())) {
|
||||
if (!isActive(mapping)) {
|
||||
continue;
|
||||
}
|
||||
if (mapping.getDahuaDepartmentId() != null) {
|
||||
index.mappingsByDepartmentId.put(mapping.getDahuaDepartmentId(), mapping);
|
||||
}
|
||||
String orgCode = blankToNull(mapping.getDahuaOrgCode());
|
||||
if (orgCode != null) {
|
||||
index.mappingsByOrgCode.put(orgCode, mapping);
|
||||
}
|
||||
String key = normalizeName(mapping.getDahuaDepartmentName());
|
||||
if (key != null) {
|
||||
index.mappingsByDepartmentName.computeIfAbsent(key, ignored -> new ArrayList<>()).add(mapping);
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
private DaHuaDepartmentCorpMappingE resolvePersonMapping(SyncIndex index,
|
||||
Long departmentId,
|
||||
String orgCode,
|
||||
String departmentName) {
|
||||
DaHuaDepartmentCorpMappingE mapping = departmentId == null
|
||||
? null : index.mappingsByDepartmentId.get(departmentId);
|
||||
if (mapping == null && orgCode != null) {
|
||||
mapping = index.mappingsByOrgCode.get(orgCode);
|
||||
}
|
||||
return mapping != null
|
||||
? mapping : uniqueMapping(index.mappingsByDepartmentName, departmentName);
|
||||
}
|
||||
|
||||
private boolean isSuccessful(Map<String, Object> response) {
|
||||
Object passed = response.get("passed");
|
||||
if (passed instanceof Boolean) {
|
||||
return (Boolean) passed;
|
||||
}
|
||||
Object success = response.get("success");
|
||||
return Boolean.TRUE.equals(success) || "0".equals(stringValue(response.get("code")));
|
||||
}
|
||||
|
||||
private boolean isActive(DaHuaDepartmentCorpMappingE mapping) {
|
||||
return mapping != null
|
||||
&& !"TRUE".equalsIgnoreCase(mapping.getDeleteEnum())
|
||||
&& (mapping.getBindStatus() == null || mapping.getBindStatus().intValue() == 1);
|
||||
}
|
||||
|
||||
private DaHuaDepartmentCorpMappingE uniqueMapping(
|
||||
Map<String, List<DaHuaDepartmentCorpMappingE>> mappingsByName,
|
||||
String departmentName) {
|
||||
List<DaHuaDepartmentCorpMappingE> matches = mappingsByName.get(normalizeName(departmentName));
|
||||
return matches != null && matches.size() == 1 ? matches.get(0) : null;
|
||||
}
|
||||
|
||||
private void validateOptionalTimeRange(String start, String end, String label) {
|
||||
if (isBlank(start) && isBlank(end)) {
|
||||
return;
|
||||
}
|
||||
if (isBlank(start) || isBlank(end)) {
|
||||
throw new IllegalArgumentException(label + " start and end must both be provided");
|
||||
}
|
||||
validateTimeRange(start, end, label);
|
||||
}
|
||||
|
||||
private void validateTimeRange(String start, String end, String label) {
|
||||
LocalDateTime startTime = parseRequiredDateTime(start, label + " start");
|
||||
LocalDateTime endTime = parseRequiredDateTime(end, label + " end");
|
||||
if (startTime.isAfter(endTime)) {
|
||||
throw new IllegalArgumentException(label + " start cannot be after end");
|
||||
}
|
||||
}
|
||||
|
||||
private LocalDateTime parseRequiredDateTime(String value, String field) {
|
||||
if (isBlank(value)) {
|
||||
throw new IllegalArgumentException(field + " cannot be blank");
|
||||
}
|
||||
try {
|
||||
return LocalDateTime.parse(value.trim(), DATE_TIME_FORMATTER);
|
||||
} catch (DateTimeParseException e) {
|
||||
throw new IllegalArgumentException(field + " must use yyyy-MM-dd HH:mm:ss", e);
|
||||
}
|
||||
}
|
||||
|
||||
private LocalDateTime firstDateTime(Map<String, Object> source, String... fields) {
|
||||
String value = firstString(source, fields);
|
||||
if (isBlank(value)) {
|
||||
return null;
|
||||
}
|
||||
return parseRequiredDateTime(value, fields[0]);
|
||||
}
|
||||
|
||||
private Object firstValue(Map<String, Object> source, String... fields) {
|
||||
for (String field : fields) {
|
||||
Object value = source.get(field);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String firstString(Map<String, Object> source, String... fields) {
|
||||
return stringValue(firstValue(source, fields));
|
||||
}
|
||||
|
||||
private Long firstLong(Map<String, Object> source, String... fields) {
|
||||
Object value = firstValue(source, fields);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).longValue();
|
||||
}
|
||||
return Long.valueOf(value.toString());
|
||||
}
|
||||
|
||||
private Integer firstInteger(Map<String, Object> source, String... fields) {
|
||||
Object value = firstValue(source, fields);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).intValue();
|
||||
}
|
||||
return Integer.valueOf(value.toString());
|
||||
}
|
||||
|
||||
private Boolean firstBoolean(Map<String, Object> source, String... fields) {
|
||||
Object value = firstValue(source, fields);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value instanceof Boolean) {
|
||||
return (Boolean) value;
|
||||
}
|
||||
return Boolean.valueOf(value.toString());
|
||||
}
|
||||
|
||||
private BigDecimal firstDecimal(Map<String, Object> source, String... fields) {
|
||||
Object value = firstValue(source, fields);
|
||||
return value == null ? null : new BigDecimal(value.toString());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Object> toMap(Object value) {
|
||||
if (value == null) {
|
||||
return new LinkedHashMap<>();
|
||||
}
|
||||
if (value instanceof Map) {
|
||||
return new LinkedHashMap<>((Map<String, Object>) value);
|
||||
}
|
||||
return JSONUtil.parseObj(JSONUtil.toJsonStr(value));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Map<String, Object>> toRecordList(Object value) {
|
||||
List<Map<String, Object>> result = new ArrayList<>();
|
||||
if (!(value instanceof Iterable)) {
|
||||
return result;
|
||||
}
|
||||
for (Object item : (Iterable<?>) value) {
|
||||
if (item instanceof Map) {
|
||||
result.add(new LinkedHashMap<>((Map<String, Object>) item));
|
||||
} else if (item != null) {
|
||||
result.add(JSONUtil.parseObj(JSONUtil.toJsonStr(item)));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private <T> Collection<T> nullSafe(Collection<T> values) {
|
||||
return values == null ? new ArrayList<>() : values;
|
||||
}
|
||||
|
||||
private String normalizeName(String value) {
|
||||
if (isBlank(value)) {
|
||||
return null;
|
||||
}
|
||||
return Normalizer.normalize(value, Normalizer.Form.NFKC)
|
||||
.replaceAll("\\s+", "")
|
||||
.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
private String stringValue(Object value) {
|
||||
return value == null ? null : value.toString();
|
||||
}
|
||||
|
||||
private String blankToNull(String value) {
|
||||
return isBlank(value) ? null : value.trim();
|
||||
}
|
||||
|
||||
private boolean isBlank(String value) {
|
||||
return value == null || value.trim().isEmpty();
|
||||
}
|
||||
|
||||
private String format(LocalDateTime value) {
|
||||
return value.format(DATE_TIME_FORMATTER);
|
||||
}
|
||||
|
||||
private void addDetail(List<String> details, String detail) {
|
||||
if (details.size() < MAX_EXCEPTION_DETAILS) {
|
||||
details.add(detail);
|
||||
}
|
||||
}
|
||||
|
||||
private static class SyncIndex {
|
||||
private final Map<Long, CorpInfoSnapshotE> corpsById = new HashMap<>();
|
||||
private final Map<String, DaHuaDeviceChannelE> channelsByCode = new HashMap<>();
|
||||
private final Map<Long, DaHuaDepartmentCorpMappingE> mappingsByDepartmentId = new HashMap<>();
|
||||
private final Map<String, DaHuaDepartmentCorpMappingE> mappingsByOrgCode = new HashMap<>();
|
||||
private final Map<String, List<DaHuaDepartmentCorpMappingE>> mappingsByDepartmentName =
|
||||
new HashMap<>();
|
||||
}
|
||||
}
|
||||
|
|
@ -3,9 +3,11 @@ package com.zcloud.primeport.service;
|
|||
import cn.hutool.json.JSONUtil;
|
||||
import com.zcloud.primeport.domain.gateway.DaHuaResourceGateway;
|
||||
import com.zcloud.primeport.domain.gateway.DaHuaResourceRepositoryGateway;
|
||||
import com.zcloud.primeport.domain.gateway.DaHuaResourceSyncGateway;
|
||||
import com.zcloud.primeport.domain.model.CorpInfoSnapshotE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaDepartmentCorpMappingE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaDeviceE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaDeviceChannelE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaResourcePageE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaResourceSyncResultE;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
|
@ -24,7 +26,7 @@ import java.util.Set;
|
|||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DaHuaResourceSyncApplicationService {
|
||||
public class DaHuaResourceSyncApplicationService implements DaHuaResourceSyncGateway {
|
||||
|
||||
private static final int MAX_PAGE_COUNT = 10000;
|
||||
private static final int MAX_DETAIL_COUNT = 200;
|
||||
|
|
@ -32,6 +34,7 @@ public class DaHuaResourceSyncApplicationService {
|
|||
private final DaHuaResourceGateway resourceGateway;
|
||||
private final DaHuaResourceRepositoryGateway repositoryGateway;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public DaHuaResourceSyncResultE sync(Integer pageSize, String subsystem, boolean autoMatchByName) {
|
||||
int actualPageSize = pageSize == null ? 200 : pageSize;
|
||||
|
|
@ -41,27 +44,40 @@ public class DaHuaResourceSyncApplicationService {
|
|||
|
||||
List<Map<String, Object>> departments = fetchAll(actualPageSize, subsystem, true);
|
||||
List<Map<String, Object>> devices = fetchAll(actualPageSize, subsystem, false);
|
||||
List<Map<String, Object>> channels = fetchAll(actualPageSize, subsystem, null);
|
||||
if (channels.isEmpty()) {
|
||||
channels = extractNestedChannels(devices);
|
||||
}
|
||||
|
||||
DaHuaResourceSyncResultE result = new DaHuaResourceSyncResultE();
|
||||
result.setDepartmentTotal(departments.size());
|
||||
syncDepartmentMappings(departments, autoMatchByName, result);
|
||||
result.setDeviceTotal(devices.size());
|
||||
syncDevices(devices, result);
|
||||
result.setChannelTotal(0);
|
||||
syncDeviceChannels(channels, result);
|
||||
deduplicateDetails(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> fetchAll(int pageSize, String subsystem, boolean organization) {
|
||||
private List<Map<String, Object>> fetchAll(int pageSize, String subsystem, Boolean organization) {
|
||||
List<Map<String, Object>> records = new ArrayList<>();
|
||||
long sourceRecordTotal = 0L;
|
||||
for (int pageNum = 1; pageNum <= MAX_PAGE_COUNT; pageNum++) {
|
||||
DaHuaResourcePageE page = organization
|
||||
DaHuaResourcePageE page = organization == null
|
||||
? resourceGateway.pageChannels(pageNum, pageSize, subsystem)
|
||||
: organization
|
||||
? resourceGateway.pageOrganizations(pageNum, pageSize, subsystem)
|
||||
: resourceGateway.pageDevices(pageNum, pageSize, subsystem);
|
||||
List<Map<String, Object>> pageRecords = page == null || page.getRecords() == null
|
||||
? new ArrayList<>() : page.getRecords();
|
||||
records.addAll(pageRecords);
|
||||
if (pageRecords.isEmpty() || pageRecords.size() < pageSize
|
||||
|| (page.getTotal() != null && records.size() >= page.getTotal())) {
|
||||
int sourceRecordCount = page == null || page.getSourceRecordCount() == null
|
||||
? pageRecords.size() : page.getSourceRecordCount();
|
||||
sourceRecordTotal += sourceRecordCount;
|
||||
Long remoteTotal = page == null ? null : page.getTotal();
|
||||
if (sourceRecordCount == 0 || sourceRecordCount < pageSize
|
||||
|| (remoteTotal != null && remoteTotal >= 0 && sourceRecordTotal >= remoteTotal)) {
|
||||
return records;
|
||||
}
|
||||
}
|
||||
|
|
@ -141,6 +157,7 @@ public class DaHuaResourceSyncApplicationService {
|
|||
MappingIndex mappingIndex = new MappingIndex(repositoryGateway.listMappings());
|
||||
DeviceIndex deviceIndex = new DeviceIndex(repositoryGateway.listDevices());
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
Set<String> seen = new LinkedHashSet<>();
|
||||
|
||||
for (Map<String, Object> source : devices) {
|
||||
String deviceCode = blankToNull(firstString(source, "deviceCode", "code", "resourceCode"));
|
||||
|
|
@ -148,6 +165,7 @@ public class DaHuaResourceSyncApplicationService {
|
|||
result.setDeviceInvalid(result.getDeviceInvalid() + 1);
|
||||
continue;
|
||||
}
|
||||
seen.add(deviceCode);
|
||||
|
||||
Long departmentId = firstLong(source, "departmentId", "deptId");
|
||||
String orgCode = blankToNull(firstString(source,
|
||||
|
|
@ -204,6 +222,127 @@ public class DaHuaResourceSyncApplicationService {
|
|||
addDetail(result.getUnmatchedDeviceCodes(), deviceCode);
|
||||
}
|
||||
}
|
||||
for (DaHuaDeviceE existing : deviceIndex.values()) {
|
||||
if (!seen.contains(existing.getDeviceCode()) && !"TRUE".equalsIgnoreCase(existing.getDeleteEnum())) {
|
||||
existing.setDeleteEnum("TRUE");
|
||||
existing.setLastSyncTime(now);
|
||||
repositoryGateway.updateDevice(existing);
|
||||
result.setDeviceInvalid(result.getDeviceInvalid() + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void syncDeviceChannels(List<Map<String, Object>> channels, DaHuaResourceSyncResultE result) {
|
||||
channels = expandChannelRecords(channels);
|
||||
MappingIndex mappingIndex = new MappingIndex(repositoryGateway.listMappings());
|
||||
ChannelIndex channelIndex = new ChannelIndex(repositoryGateway.listDeviceChannels());
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
Set<String> seen = new LinkedHashSet<>();
|
||||
for (Map<String, Object> source : channels) {
|
||||
try {
|
||||
Integer unitType = channelUnitType(source);
|
||||
if (unitType == null || unitType.intValue() != 7) {
|
||||
continue;
|
||||
}
|
||||
result.setChannelTotal(result.getChannelTotal() + 1);
|
||||
String channelCode = blankToNull(firstString(source, "channelCode", "code"));
|
||||
String deviceCode = blankToNull(firstString(source, "deviceCode", "deviceCode"));
|
||||
if (channelCode == null || deviceCode == null) {
|
||||
result.setChannelInvalid(result.getChannelInvalid() + 1);
|
||||
addDetail(result.getExceptionDetails(), "通道缺少channelCode或deviceCode: " + JSONUtil.toJsonStr(source));
|
||||
continue;
|
||||
}
|
||||
seen.add(channelCode);
|
||||
Long departmentId = firstLong(source, "dahuaDepartmentId", "departmentId", "deptId");
|
||||
String orgCode = blankToNull(firstString(source, "dahuaOrgCode", "orgCode", "ownerCode"));
|
||||
DaHuaDepartmentCorpMappingE mapping = departmentId == null ? null : mappingIndex.byDepartmentId.get(departmentId);
|
||||
if (mapping == null && orgCode != null) {
|
||||
mapping = mappingIndex.byOrgCode.get(orgCode);
|
||||
}
|
||||
DaHuaDeviceChannelE channel = channelIndex.byChannelCode.get(channelCode);
|
||||
boolean created = channel == null;
|
||||
if (created) {
|
||||
channel = new DaHuaDeviceChannelE();
|
||||
}
|
||||
channel.setChannelCode(channelCode);
|
||||
channel.setChannelName(firstString(source, "channelName", "name"));
|
||||
channel.setDeviceCode(deviceCode);
|
||||
channel.setCorpId(mapping == null ? null : mapping.getCorpId());
|
||||
channel.setUnitType(unitType);
|
||||
channel.setDahuaDepartmentId(departmentId != null ? departmentId : (mapping == null ? null : mapping.getDahuaDepartmentId()));
|
||||
channel.setDahuaOrgCode(orgCode);
|
||||
String orgName = firstString(source, "orgName", "departmentName", "deptName");
|
||||
channel.setDahuaOrgName(isBlank(orgName) && mapping != null ? mapping.getDahuaDepartmentName() : orgName);
|
||||
channel.setOnlineStatus(firstString(source, "onlineStatus", "isOnline", "status"));
|
||||
channel.setRawData(JSONUtil.toJsonStr(source));
|
||||
channel.setLastSyncTime(now);
|
||||
channel.setDeleteEnum("FALSE");
|
||||
if (created) {
|
||||
repositoryGateway.addDeviceChannel(channel);
|
||||
channelIndex.add(channel);
|
||||
result.setChannelCreated(result.getChannelCreated() + 1);
|
||||
} else {
|
||||
repositoryGateway.updateDeviceChannel(channel);
|
||||
result.setChannelUpdated(result.getChannelUpdated() + 1);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
result.setChannelInvalid(result.getChannelInvalid() + 1);
|
||||
addDetail(result.getExceptionDetails(), "通道同步异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
for (DaHuaDeviceChannelE existing : channelIndex.values()) {
|
||||
if (!seen.contains(existing.getChannelCode()) && !"TRUE".equalsIgnoreCase(existing.getDeleteEnum())) {
|
||||
existing.setDeleteEnum("TRUE");
|
||||
existing.setLastSyncTime(now);
|
||||
repositoryGateway.updateDeviceChannel(existing);
|
||||
result.setChannelInvalid(result.getChannelInvalid() + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> extractNestedChannels(List<Map<String, Object>> devices) {
|
||||
List<Map<String, Object>> result = new ArrayList<>();
|
||||
for (Map<String, Object> device : devices) {
|
||||
Object nested = firstValue(device, "channels", "channelList");
|
||||
if (!(nested instanceof Iterable)) {
|
||||
continue;
|
||||
}
|
||||
for (Object value : (Iterable<?>) nested) {
|
||||
if (value instanceof Map) {
|
||||
Map<String, Object> channel = new HashMap<>((Map<String, Object>) value);
|
||||
if (channel.get("deviceCode") == null) {
|
||||
channel.put("deviceCode", firstString(device, "deviceCode", "code"));
|
||||
}
|
||||
if (channel.get("orgCode") == null) {
|
||||
channel.put("orgCode", firstString(device, "orgCode", "ownerCode"));
|
||||
}
|
||||
result.add(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Map<String, Object>> expandChannelRecords(List<Map<String, Object>> records) {
|
||||
List<Map<String, Object>> result = new ArrayList<>();
|
||||
for (Map<String, Object> record : records) {
|
||||
Object nested = firstValue(record, "channels", "channelList");
|
||||
if (!(nested instanceof Iterable)) {
|
||||
result.add(record);
|
||||
continue;
|
||||
}
|
||||
for (Object value : (Iterable<?>) nested) {
|
||||
if (value instanceof Map) {
|
||||
Map<String, Object> channel = new HashMap<>((Map<String, Object>) value);
|
||||
if (channel.get("deviceCode") == null) {
|
||||
channel.put("deviceCode", firstString(record, "deviceCode", "code"));
|
||||
}
|
||||
result.add(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, List<CorpInfoSnapshotE>> groupCorpsByNormalizedName(List<CorpInfoSnapshotE> corps) {
|
||||
|
|
@ -319,6 +458,7 @@ public class DaHuaResourceSyncApplicationService {
|
|||
private void deduplicateDetails(DaHuaResourceSyncResultE result) {
|
||||
result.setUnmatchedDepartmentNames(uniqueList(result.getUnmatchedDepartmentNames()));
|
||||
result.setUnmatchedDeviceCodes(uniqueList(result.getUnmatchedDeviceCodes()));
|
||||
result.setExceptionDetails(uniqueList(result.getExceptionDetails()));
|
||||
}
|
||||
|
||||
private List<String> uniqueList(List<String> values) {
|
||||
|
|
@ -377,5 +517,51 @@ public class DaHuaResourceSyncApplicationService {
|
|||
}
|
||||
add(device);
|
||||
}
|
||||
|
||||
private List<DaHuaDeviceE> values() {
|
||||
return new ArrayList<>(byDeviceCode.values());
|
||||
}
|
||||
}
|
||||
|
||||
private Integer channelUnitType(Map<String, Object> source) {
|
||||
Integer unitType = firstInteger(source, "unitType", "unit_type");
|
||||
if (unitType != null) {
|
||||
return unitType;
|
||||
}
|
||||
String channelCode = firstString(source, "channelCode", "code");
|
||||
if (!isBlank(channelCode)) {
|
||||
String[] parts = channelCode.split("\\$");
|
||||
if (parts.length > 1) {
|
||||
try {
|
||||
return Integer.valueOf(parts[1]);
|
||||
} catch (NumberFormatException ignored) {
|
||||
// Continue as an invalid channel below.
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class ChannelIndex {
|
||||
private final Map<String, DaHuaDeviceChannelE> byChannelCode = new HashMap<>();
|
||||
|
||||
private ChannelIndex(List<DaHuaDeviceChannelE> channels) {
|
||||
if (channels == null) {
|
||||
return;
|
||||
}
|
||||
for (DaHuaDeviceChannelE channel : channels) {
|
||||
add(channel);
|
||||
}
|
||||
}
|
||||
|
||||
private void add(DaHuaDeviceChannelE channel) {
|
||||
if (channel.getChannelCode() != null) {
|
||||
byChannelCode.put(channel.getChannelCode(), channel);
|
||||
}
|
||||
}
|
||||
|
||||
private List<DaHuaDeviceChannelE> values() {
|
||||
return new ArrayList<>(byChannelCode.values());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,11 @@ import com.zcloud.gbscommon.dahua.cmd.DaHuaPersonSyncCmd;
|
|||
import com.zcloud.gbscommon.dahua.cmd.DaHuaTempVehicleSaveCmd;
|
||||
import com.zcloud.gbscommon.dahua.facade.ZcloudDaHuaFacade;
|
||||
import com.zcloud.primeport.domain.gateway.DaHuaGateway;
|
||||
import com.zcloud.primeport.domain.model.DaHuaAccessRecordSyncResultE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaResourceSyncResultE;
|
||||
import com.zcloud.primeport.dto.DaHuaAccessRecordSyncCmd;
|
||||
import com.zcloud.primeport.dto.DaHuaResourceSyncCmd;
|
||||
import com.zcloud.primeport.dto.clientobject.DaHuaAccessRecordSyncResultCO;
|
||||
import com.zcloud.primeport.dto.clientobject.DaHuaResourceSyncResultCO;
|
||||
import org.apache.dubbo.config.annotation.DubboService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
|
@ -31,6 +34,9 @@ public class DaHuaServiceImpl implements ZcloudDaHuaFacade {
|
|||
@Autowired
|
||||
private DaHuaResourceSyncApplicationService daHuaResourceSyncApplicationService;
|
||||
|
||||
@Autowired
|
||||
private DaHuaAccessRecordSyncApplicationService daHuaAccessRecordSyncApplicationService;
|
||||
|
||||
private Long resolveDahuaDeptId(Long corpId) {
|
||||
if (corpId == null) {
|
||||
return null;
|
||||
|
|
@ -236,6 +242,17 @@ public class DaHuaServiceImpl implements ZcloudDaHuaFacade {
|
|||
return resp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleResponse<DaHuaAccessRecordSyncResultCO> syncAccessRecords(DaHuaAccessRecordSyncCmd cmd) {
|
||||
int pageSize = cmd.getPageSize() == null ? 1000 : cmd.getPageSize();
|
||||
DaHuaAccessRecordSyncResultE result = daHuaAccessRecordSyncApplicationService.sync(
|
||||
cmd.getStartSwingTime(), cmd.getEndSwingTime(), cmd.getStartCreateTime(),
|
||||
cmd.getEndCreateTime(), pageSize);
|
||||
DaHuaAccessRecordSyncResultCO data = new DaHuaAccessRecordSyncResultCO();
|
||||
BeanUtils.copyProperties(result, data);
|
||||
return SingleResponse.of(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleResponse<Object> saveTempVehicle(DaHuaTempVehicleSaveCmd cmd) {
|
||||
cmd.setDepartmentId(resolveDahuaDeptId(cmd.getDepartmentId()));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,287 @@
|
|||
package com.zcloud.primeport.service;
|
||||
|
||||
import com.zcloud.primeport.domain.gateway.DaHuaAccessRecordRepositoryGateway;
|
||||
import com.zcloud.primeport.domain.gateway.DaHuaGateway;
|
||||
import com.zcloud.primeport.domain.gateway.DaHuaResourceRepositoryGateway;
|
||||
import com.zcloud.primeport.domain.model.CorpInfoSnapshotE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaAccessRecordCmd;
|
||||
import com.zcloud.primeport.domain.model.DaHuaAccessRecordE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaAccessRecordSyncResultE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaDepartmentCorpMappingE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaDeviceChannelE;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyCollection;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class DaHuaAccessRecordSyncApplicationServiceTest {
|
||||
|
||||
private static final DateTimeFormatter FORMATTER =
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@Mock
|
||||
private DaHuaGateway daHuaGateway;
|
||||
@Mock
|
||||
private DaHuaResourceRepositoryGateway resourceRepositoryGateway;
|
||||
@Mock
|
||||
private DaHuaAccessRecordRepositoryGateway accessRecordRepositoryGateway;
|
||||
|
||||
private DaHuaAccessRecordSyncApplicationService applicationService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
applicationService = new DaHuaAccessRecordSyncApplicationService(
|
||||
daHuaGateway, resourceRepositoryGateway, accessRecordRepositoryGateway);
|
||||
when(resourceRepositoryGateway.listActiveCorps()).thenReturn(Collections.emptyList());
|
||||
when(resourceRepositoryGateway.listMappings()).thenReturn(Collections.emptyList());
|
||||
when(resourceRepositoryGateway.listDeviceChannels()).thenReturn(Collections.emptyList());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFetchEveryPageAndStopOnShortPageWithoutTotals() {
|
||||
when(accessRecordRepositoryGateway.listByDahuaRecordIds(anyCollection()))
|
||||
.thenReturn(Collections.emptyList());
|
||||
when(daHuaGateway.queryAccessRecord(any(DaHuaAccessRecordCmd.class)))
|
||||
.thenAnswer(invocation -> {
|
||||
DaHuaAccessRecordCmd cmd = invocation.getArgument(0);
|
||||
if (cmd.getPageNum() == 1) {
|
||||
return response(Arrays.asList(record("R-1"), record("R-2")));
|
||||
}
|
||||
if (cmd.getPageNum() == 2) {
|
||||
return response(Collections.singletonList(record("R-3")));
|
||||
}
|
||||
return response(Collections.emptyList());
|
||||
});
|
||||
|
||||
DaHuaAccessRecordSyncResultE result = applicationService.sync(
|
||||
"2026-08-01 00:00:00", "2026-08-02 00:00:00", null, null, 2);
|
||||
|
||||
assertEquals(2, result.getPageCount());
|
||||
assertEquals(3, result.getRecordTotal());
|
||||
assertEquals(3, result.getRecordCreated());
|
||||
ArgumentCaptor<DaHuaAccessRecordCmd> commandCaptor =
|
||||
ArgumentCaptor.forClass(DaHuaAccessRecordCmd.class);
|
||||
verify(daHuaGateway, org.mockito.Mockito.times(2)).queryAccessRecord(commandCaptor.capture());
|
||||
assertEquals(Integer.valueOf(1), commandCaptor.getAllValues().get(0).getPageNum());
|
||||
assertEquals(Integer.valueOf(2), commandCaptor.getAllValues().get(1).getPageNum());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUpdateIdempotentlyAndPersistPersonDeviceAndGateDimensions() {
|
||||
CorpInfoSnapshotE personCorp = corp(100L, "人员企业", 1);
|
||||
CorpInfoSnapshotE deviceCorp = corp(200L, "设备企业", 2);
|
||||
when(resourceRepositoryGateway.listActiveCorps())
|
||||
.thenReturn(Arrays.asList(personCorp, deviceCorp));
|
||||
|
||||
DaHuaDepartmentCorpMappingE mapping = new DaHuaDepartmentCorpMappingE();
|
||||
mapping.setDahuaDepartmentId(10L);
|
||||
mapping.setDahuaDepartmentName("映射表旧部门名称");
|
||||
mapping.setCorpId(100L);
|
||||
mapping.setCorpName("人员企业");
|
||||
mapping.setBindStatus(1);
|
||||
mapping.setDeleteEnum("FALSE");
|
||||
when(resourceRepositoryGateway.listMappings()).thenReturn(Collections.singletonList(mapping));
|
||||
|
||||
DaHuaDeviceChannelE channel = new DaHuaDeviceChannelE();
|
||||
channel.setChannelCode("CH-1");
|
||||
channel.setCorpId(200L);
|
||||
channel.setMkmjId(300L);
|
||||
channel.setPassageId(301L);
|
||||
channel.setGateId(302L);
|
||||
channel.setDeleteEnum("FALSE");
|
||||
when(resourceRepositoryGateway.listDeviceChannels()).thenReturn(Collections.singletonList(channel));
|
||||
|
||||
DaHuaAccessRecordE existing = new DaHuaAccessRecordE();
|
||||
existing.setId(900L);
|
||||
existing.setDahuaRecordId("R-1");
|
||||
when(accessRecordRepositoryGateway.listByDahuaRecordIds(anyCollection()))
|
||||
.thenReturn(Collections.singletonList(existing));
|
||||
|
||||
Map<String, Object> source = record("R-1");
|
||||
source.put("deptId", 10L);
|
||||
source.put("deptName", "大华最新部门名称");
|
||||
source.put("channelCode", "CH-1");
|
||||
source.put("enterOrExit", 1);
|
||||
source.put("openResult", 1);
|
||||
when(daHuaGateway.queryAccessRecord(any(DaHuaAccessRecordCmd.class)))
|
||||
.thenReturn(response(Collections.singletonList(source)));
|
||||
|
||||
DaHuaAccessRecordSyncResultE result = applicationService.sync(
|
||||
"2026-08-01 00:00:00", "2026-08-02 00:00:00", null, null, 100);
|
||||
|
||||
assertEquals(0, result.getRecordCreated());
|
||||
assertEquals(1, result.getRecordUpdated());
|
||||
verify(accessRecordRepositoryGateway, never()).add(any(DaHuaAccessRecordE.class));
|
||||
ArgumentCaptor<DaHuaAccessRecordE> recordCaptor =
|
||||
ArgumentCaptor.forClass(DaHuaAccessRecordE.class);
|
||||
verify(accessRecordRepositoryGateway).update(recordCaptor.capture());
|
||||
DaHuaAccessRecordE saved = recordCaptor.getValue();
|
||||
assertEquals(Long.valueOf(100L), saved.getCorpId());
|
||||
assertEquals("人员企业", saved.getCorpName());
|
||||
assertEquals(Integer.valueOf(1), saved.getPortArea());
|
||||
assertEquals(Long.valueOf(10L), saved.getDahuaDepartmentId());
|
||||
assertEquals("大华最新部门名称", saved.getDahuaDepartmentName());
|
||||
assertEquals(Long.valueOf(200L), saved.getDeviceCorpId());
|
||||
assertEquals("设备企业", saved.getDeviceCorpName());
|
||||
assertEquals(Integer.valueOf(2), saved.getDevicePortArea());
|
||||
assertEquals(Long.valueOf(300L), saved.getMkmjId());
|
||||
assertEquals(Long.valueOf(301L), saved.getPassageId());
|
||||
assertEquals(Long.valueOf(302L), saved.getGateId());
|
||||
assertEquals(Integer.valueOf(1), saved.getEnterOrExit());
|
||||
assertEquals(Integer.valueOf(1), saved.getOpenResult());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMapEnterpriseByOrgCodeWhenDepartmentIdIsMissing() {
|
||||
CorpInfoSnapshotE personCorp = corp(100L, "人员企业", 1);
|
||||
when(resourceRepositoryGateway.listActiveCorps())
|
||||
.thenReturn(Collections.singletonList(personCorp));
|
||||
|
||||
DaHuaDepartmentCorpMappingE mapping = new DaHuaDepartmentCorpMappingE();
|
||||
mapping.setDahuaDepartmentId(10L);
|
||||
mapping.setDahuaOrgCode("ORG-10");
|
||||
mapping.setDahuaDepartmentName("映射表部门名称");
|
||||
mapping.setCorpId(100L);
|
||||
mapping.setCorpName("人员企业");
|
||||
mapping.setBindStatus(1);
|
||||
mapping.setDeleteEnum("FALSE");
|
||||
when(resourceRepositoryGateway.listMappings()).thenReturn(Collections.singletonList(mapping));
|
||||
when(accessRecordRepositoryGateway.listByDahuaRecordIds(anyCollection()))
|
||||
.thenReturn(Collections.emptyList());
|
||||
|
||||
Map<String, Object> source = record("R-ORG");
|
||||
source.put("orgCode", "ORG-10");
|
||||
source.put("deptName", "另一个部门名称");
|
||||
when(daHuaGateway.queryAccessRecord(any(DaHuaAccessRecordCmd.class)))
|
||||
.thenReturn(response(Collections.singletonList(source)));
|
||||
|
||||
applicationService.sync(
|
||||
"2026-08-01 00:00:00", "2026-08-02 00:00:00", null, null, 100);
|
||||
|
||||
ArgumentCaptor<DaHuaAccessRecordE> recordCaptor =
|
||||
ArgumentCaptor.forClass(DaHuaAccessRecordE.class);
|
||||
verify(accessRecordRepositoryGateway).add(recordCaptor.capture());
|
||||
assertEquals(Long.valueOf(100L), recordCaptor.getValue().getCorpId());
|
||||
assertEquals("人员企业", recordCaptor.getValue().getCorpName());
|
||||
assertEquals(Long.valueOf(10L), recordCaptor.getValue().getDahuaDepartmentId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldKeepSourceDepartmentIdWhenEnterpriseIsNotMapped() {
|
||||
when(accessRecordRepositoryGateway.listByDahuaRecordIds(anyCollection()))
|
||||
.thenReturn(Collections.emptyList());
|
||||
Map<String, Object> source = record("R-1");
|
||||
source.put("deptId", 88L);
|
||||
source.put("deptName", "未映射企业");
|
||||
when(daHuaGateway.queryAccessRecord(any(DaHuaAccessRecordCmd.class)))
|
||||
.thenReturn(response(Collections.singletonList(source)));
|
||||
|
||||
applicationService.sync(
|
||||
"2026-08-01 00:00:00", "2026-08-02 00:00:00", null, null, 100);
|
||||
|
||||
ArgumentCaptor<DaHuaAccessRecordE> recordCaptor =
|
||||
ArgumentCaptor.forClass(DaHuaAccessRecordE.class);
|
||||
verify(accessRecordRepositoryGateway).add(recordCaptor.capture());
|
||||
assertEquals(Long.valueOf(88L), recordCaptor.getValue().getDahuaDepartmentId());
|
||||
assertEquals("未映射企业", recordCaptor.getValue().getDahuaDepartmentName());
|
||||
assertEquals(null, recordCaptor.getValue().getCorpId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCountMissingRecordIdAsInvalidAndContinue() {
|
||||
Map<String, Object> invalid = record(null);
|
||||
when(daHuaGateway.queryAccessRecord(any(DaHuaAccessRecordCmd.class)))
|
||||
.thenReturn(response(Collections.singletonList(invalid)));
|
||||
when(accessRecordRepositoryGateway.listByDahuaRecordIds(anyCollection()))
|
||||
.thenReturn(Collections.emptyList());
|
||||
|
||||
DaHuaAccessRecordSyncResultE result = applicationService.sync(
|
||||
"2026-08-01 00:00:00", "2026-08-02 00:00:00", null, null, 100);
|
||||
|
||||
assertEquals(1, result.getRecordTotal());
|
||||
assertEquals(1, result.getRecordInvalid());
|
||||
assertEquals(1, result.getExceptionDetails().size());
|
||||
verify(accessRecordRepositoryGateway, never()).add(any(DaHuaAccessRecordE.class));
|
||||
verify(accessRecordRepositoryGateway, never()).update(any(DaHuaAccessRecordE.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBuildIncrementalWindowFromLatestDahuaCreateTime() {
|
||||
LocalDateTime latestCreateTime = LocalDateTime.now().minusHours(1).withNano(0);
|
||||
when(accessRecordRepositoryGateway.findLatestDahuaCreateTime()).thenReturn(latestCreateTime);
|
||||
when(daHuaGateway.queryAccessRecord(any(DaHuaAccessRecordCmd.class)))
|
||||
.thenReturn(response(Collections.emptyList()));
|
||||
LocalDateTime earliestExpectedEnd = LocalDateTime.now().minusMinutes(5).minusSeconds(2);
|
||||
|
||||
applicationService.syncIncremental(1000, 5, 10, 1440);
|
||||
|
||||
LocalDateTime latestExpectedEnd = LocalDateTime.now().minusMinutes(5).plusSeconds(2);
|
||||
ArgumentCaptor<DaHuaAccessRecordCmd> commandCaptor =
|
||||
ArgumentCaptor.forClass(DaHuaAccessRecordCmd.class);
|
||||
verify(daHuaGateway).queryAccessRecord(commandCaptor.capture());
|
||||
DaHuaAccessRecordCmd cmd = commandCaptor.getValue();
|
||||
LocalDateTime startCreateTime = LocalDateTime.parse(cmd.getStartCreateTime(), FORMATTER);
|
||||
LocalDateTime endCreateTime = LocalDateTime.parse(cmd.getEndCreateTime(), FORMATTER);
|
||||
assertEquals(latestCreateTime.minusMinutes(10), startCreateTime);
|
||||
assertFalse(endCreateTime.isBefore(earliestExpectedEnd));
|
||||
assertFalse(endCreateTime.isAfter(latestExpectedEnd));
|
||||
assertEquals(startCreateTime.minusDays(2),
|
||||
LocalDateTime.parse(cmd.getStartSwingTime(), FORMATTER));
|
||||
assertEquals(endCreateTime.plusDays(1),
|
||||
LocalDateTime.parse(cmd.getEndSwingTime(), FORMATTER));
|
||||
assertTrue(cmd.getPageSize() <= 10000);
|
||||
}
|
||||
|
||||
private CorpInfoSnapshotE corp(Long id, String name, Integer portArea) {
|
||||
CorpInfoSnapshotE corp = new CorpInfoSnapshotE();
|
||||
corp.setId(id);
|
||||
corp.setCorpName(name);
|
||||
corp.setPortArea(portArea);
|
||||
return corp;
|
||||
}
|
||||
|
||||
private Map<String, Object> record(String id) {
|
||||
Map<String, Object> value = new HashMap<>();
|
||||
if (id != null) {
|
||||
value.put("id", id);
|
||||
}
|
||||
value.put("personCode", "P-1");
|
||||
value.put("personName", "测试人员");
|
||||
value.put("swingTime", "2026-08-01 10:00:00");
|
||||
value.put("createTime", "2026-08-01 10:00:05");
|
||||
return value;
|
||||
}
|
||||
|
||||
private Map<String, Object> response(Collection<Map<String, Object>> records) {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("pageData", new ArrayList<>(records));
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("passed", true);
|
||||
response.put("success", true);
|
||||
response.put("code", "0");
|
||||
response.put("data", data);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import com.zcloud.primeport.domain.gateway.DaHuaResourceRepositoryGateway;
|
|||
import com.zcloud.primeport.domain.model.CorpInfoSnapshotE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaDepartmentCorpMappingE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaDeviceE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaDeviceChannelE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaResourcePageE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaResourceSyncResultE;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
|
@ -27,6 +28,7 @@ import static org.mockito.Mockito.doAnswer;
|
|||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.times;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class DaHuaResourceSyncApplicationServiceTest {
|
||||
|
|
@ -91,6 +93,126 @@ class DaHuaResourceSyncApplicationServiceTest {
|
|||
verify(resourceGateway, never()).pageOrganizations(any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPageChannelsFilterNonAccessChannelsAndMarkMissingAsInvalid() {
|
||||
when(resourceGateway.pageOrganizations(1, 2, "access")).thenReturn(page(Collections.emptyList(), 0L));
|
||||
when(resourceGateway.pageDevices(1, 2, "access")).thenReturn(page(Collections.emptyList(), 0L));
|
||||
when(resourceGateway.pageChannels(1, 2, "access")).thenReturn(page(
|
||||
java.util.Arrays.asList(channel("CH-1", "DEV-1", 7), channel("CH-2", "DEV-1", 8)), 2L));
|
||||
when(repositoryGateway.listActiveCorps()).thenReturn(Collections.emptyList());
|
||||
when(repositoryGateway.listMappings()).thenReturn(Collections.emptyList());
|
||||
when(repositoryGateway.listDevices()).thenReturn(Collections.emptyList());
|
||||
DaHuaDeviceChannelE old = new DaHuaDeviceChannelE();
|
||||
old.setId(9L);
|
||||
old.setChannelCode("CH-OLD");
|
||||
old.setDeleteEnum("FALSE");
|
||||
when(repositoryGateway.listDeviceChannels()).thenReturn(Collections.singletonList(old));
|
||||
|
||||
DaHuaResourceSyncResultE result = applicationService.sync(2, "access", true);
|
||||
|
||||
assertEquals(1, result.getChannelTotal());
|
||||
assertEquals(1, result.getChannelCreated());
|
||||
assertEquals(1, result.getChannelInvalid());
|
||||
verify(repositoryGateway).addDeviceChannel(any(DaHuaDeviceChannelE.class));
|
||||
verify(repositoryGateway, times(1)).updateDeviceChannel(any(DaHuaDeviceChannelE.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUpdateExistingChannelIdempotentlyAndSupportMultipleChannelsPerDevice() {
|
||||
when(resourceGateway.pageOrganizations(1, 100, "access")).thenReturn(page(Collections.emptyList(), 0L));
|
||||
when(resourceGateway.pageDevices(1, 100, "access")).thenReturn(page(Collections.emptyList(), 0L));
|
||||
when(resourceGateway.pageChannels(1, 100, "access")).thenReturn(page(
|
||||
java.util.Arrays.asList(channel("CH-1", "DEV-1", 7), channel("CH-2", "DEV-1", 7)), 2L));
|
||||
when(repositoryGateway.listActiveCorps()).thenReturn(Collections.emptyList());
|
||||
when(repositoryGateway.listMappings()).thenReturn(Collections.emptyList());
|
||||
when(repositoryGateway.listDevices()).thenReturn(Collections.emptyList());
|
||||
DaHuaDeviceChannelE existing = new DaHuaDeviceChannelE();
|
||||
existing.setId(10L);
|
||||
existing.setChannelCode("CH-1");
|
||||
existing.setDeleteEnum("TRUE");
|
||||
when(repositoryGateway.listDeviceChannels()).thenReturn(Collections.singletonList(existing));
|
||||
|
||||
DaHuaResourceSyncResultE result = applicationService.sync(100, "access", true);
|
||||
|
||||
assertEquals(2, result.getChannelTotal());
|
||||
assertEquals(1, result.getChannelCreated());
|
||||
assertEquals(1, result.getChannelUpdated());
|
||||
ArgumentCaptor<DaHuaDeviceChannelE> updated = ArgumentCaptor.forClass(DaHuaDeviceChannelE.class);
|
||||
verify(repositoryGateway).updateDeviceChannel(updated.capture());
|
||||
assertEquals("FALSE", updated.getValue().getDeleteEnum());
|
||||
assertEquals("CH-1", updated.getValue().getChannelCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFetchAllChannelPages() {
|
||||
when(resourceGateway.pageOrganizations(1, 1, "access")).thenReturn(page(Collections.emptyList(), 0L));
|
||||
when(resourceGateway.pageDevices(1, 1, "access")).thenReturn(page(Collections.emptyList(), 0L));
|
||||
when(resourceGateway.pageChannels(1, 1, "access")).thenReturn(page(
|
||||
Collections.singletonList(channel("CH-1", "DEV-1", 7)), null));
|
||||
when(resourceGateway.pageChannels(2, 1, "access")).thenReturn(page(
|
||||
Collections.singletonList(channel("CH-2", "DEV-1", 7)), null));
|
||||
when(resourceGateway.pageChannels(3, 1, "access")).thenReturn(page(Collections.emptyList(), null));
|
||||
when(repositoryGateway.listActiveCorps()).thenReturn(Collections.emptyList());
|
||||
when(repositoryGateway.listMappings()).thenReturn(Collections.emptyList());
|
||||
when(repositoryGateway.listDevices()).thenReturn(Collections.emptyList());
|
||||
when(repositoryGateway.listDeviceChannels()).thenReturn(Collections.emptyList());
|
||||
|
||||
DaHuaResourceSyncResultE result = applicationService.sync(1, "access", true);
|
||||
|
||||
assertEquals(2, result.getChannelTotal());
|
||||
verify(resourceGateway).pageChannels(2, 1, "access");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldIgnoreUnknownNegativeTotalAndContinuePaging() {
|
||||
when(resourceGateway.pageOrganizations(1, 1, "access")).thenReturn(page(
|
||||
Collections.singletonList(department(10L, "企业一", "ORG-10")), -1L));
|
||||
when(resourceGateway.pageOrganizations(2, 1, "access")).thenReturn(page(
|
||||
Collections.singletonList(department(11L, "企业二", "ORG-11")), -1L));
|
||||
when(resourceGateway.pageOrganizations(3, 1, "access"))
|
||||
.thenReturn(page(Collections.emptyList(), -1L));
|
||||
when(resourceGateway.pageDevices(1, 1, "access"))
|
||||
.thenReturn(page(Collections.emptyList(), 0L));
|
||||
when(resourceGateway.pageChannels(1, 1, "access"))
|
||||
.thenReturn(page(Collections.emptyList(), 0L));
|
||||
when(repositoryGateway.listActiveCorps()).thenReturn(Collections.emptyList());
|
||||
when(repositoryGateway.listMappings()).thenReturn(Collections.emptyList());
|
||||
when(repositoryGateway.listDevices()).thenReturn(Collections.emptyList());
|
||||
when(repositoryGateway.listDeviceChannels()).thenReturn(Collections.emptyList());
|
||||
|
||||
DaHuaResourceSyncResultE result = applicationService.sync(1, "access", false);
|
||||
|
||||
assertEquals(2, result.getDepartmentTotal());
|
||||
verify(resourceGateway).pageOrganizations(3, 1, "access");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPageChannelsByRemoteDeviceCountInsteadOfExpandedChannelCount() {
|
||||
when(resourceGateway.pageOrganizations(1, 1, "access"))
|
||||
.thenReturn(page(Collections.emptyList(), 0L));
|
||||
when(resourceGateway.pageDevices(1, 1, "access"))
|
||||
.thenReturn(page(Collections.emptyList(), 0L));
|
||||
DaHuaResourcePageE firstPage = page(Collections.emptyList(), null);
|
||||
firstPage.setSourceRecordCount(1);
|
||||
DaHuaResourcePageE secondPage = page(
|
||||
Collections.singletonList(channel("CH-2", "DEV-2", 7)), null);
|
||||
secondPage.setSourceRecordCount(1);
|
||||
DaHuaResourcePageE lastPage = page(Collections.emptyList(), null);
|
||||
lastPage.setSourceRecordCount(0);
|
||||
when(resourceGateway.pageChannels(1, 1, "access")).thenReturn(firstPage);
|
||||
when(resourceGateway.pageChannels(2, 1, "access")).thenReturn(secondPage);
|
||||
when(resourceGateway.pageChannels(3, 1, "access")).thenReturn(lastPage);
|
||||
when(repositoryGateway.listActiveCorps()).thenReturn(Collections.emptyList());
|
||||
when(repositoryGateway.listMappings()).thenReturn(Collections.emptyList());
|
||||
when(repositoryGateway.listDevices()).thenReturn(Collections.emptyList());
|
||||
when(repositoryGateway.listDeviceChannels()).thenReturn(Collections.emptyList());
|
||||
|
||||
DaHuaResourceSyncResultE result = applicationService.sync(1, "access", false);
|
||||
|
||||
assertEquals(1, result.getChannelTotal());
|
||||
verify(resourceGateway).pageChannels(3, 1, "access");
|
||||
}
|
||||
|
||||
private DaHuaResourcePageE page(List<Map<String, Object>> records, Long total) {
|
||||
DaHuaResourcePageE page = new DaHuaResourcePageE();
|
||||
page.setRecords(records);
|
||||
|
|
@ -114,4 +236,14 @@ class DaHuaResourceSyncApplicationServiceTest {
|
|||
value.put("ownerCode", orgCode);
|
||||
return value;
|
||||
}
|
||||
|
||||
private Map<String, Object> channel(String code, String deviceCode, int unitType) {
|
||||
Map<String, Object> value = new HashMap<>();
|
||||
value.put("channelCode", code);
|
||||
value.put("channelName", code);
|
||||
value.put("deviceCode", deviceCode);
|
||||
value.put("unitType", unitType);
|
||||
value.put("onlineStatus", "ON");
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,11 @@ import com.zcloud.primeport.domain.model.DaHuaCarAddCmd;
|
|||
import com.zcloud.primeport.domain.model.DaHuaCarPageCmd;
|
||||
import com.zcloud.primeport.domain.model.DaHuaPersonSyncCmd;
|
||||
import com.zcloud.primeport.domain.model.DaHuaTempVehicleSaveCmd;
|
||||
import com.zcloud.primeport.dto.DaHuaAccessRecordSyncCmd;
|
||||
import com.zcloud.primeport.dto.DaHuaCarDeleteCmd;
|
||||
import com.zcloud.primeport.dto.DaHuaPersonDeleteCmd;
|
||||
import com.zcloud.primeport.dto.DaHuaResourceSyncCmd;
|
||||
import com.zcloud.primeport.dto.clientobject.DaHuaAccessRecordSyncResultCO;
|
||||
import com.zcloud.primeport.dto.clientobject.DaHuaResourceSyncResultCO;
|
||||
|
||||
import java.util.Map;
|
||||
|
|
@ -34,6 +36,8 @@ public interface DaHuaServiceI {
|
|||
|
||||
SingleResponse<Map<String, Object>> queryAccessRecord(DaHuaAccessRecordCmd cmd);
|
||||
|
||||
SingleResponse<DaHuaAccessRecordSyncResultCO> syncAccessRecords(DaHuaAccessRecordSyncCmd cmd);
|
||||
|
||||
SingleResponse<Object> saveTempVehicle(DaHuaTempVehicleSaveCmd cmd);
|
||||
|
||||
SingleResponse<DaHuaResourceSyncResultCO> syncResources(DaHuaResourceSyncCmd cmd);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
package com.zcloud.primeport.dto;
|
||||
|
||||
import com.alibaba.cola.dto.Command;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@Data
|
||||
public class DaHuaAccessRecordSyncCmd extends Command {
|
||||
|
||||
@ApiModelProperty(value = "Dahua event start time, yyyy-MM-dd HH:mm:ss", required = true)
|
||||
@NotBlank(message = "startSwingTime cannot be blank")
|
||||
private String startSwingTime;
|
||||
|
||||
@ApiModelProperty(value = "Dahua event end time, yyyy-MM-dd HH:mm:ss", required = true)
|
||||
@NotBlank(message = "endSwingTime cannot be blank")
|
||||
private String endSwingTime;
|
||||
|
||||
@ApiModelProperty("Dahua storage start time, yyyy-MM-dd HH:mm:ss")
|
||||
private String startCreateTime;
|
||||
|
||||
@ApiModelProperty("Dahua storage end time, yyyy-MM-dd HH:mm:ss")
|
||||
private String endCreateTime;
|
||||
|
||||
@ApiModelProperty("Page size, default 1000 and maximum 10000")
|
||||
@Min(value = 1, message = "pageSize must be positive")
|
||||
@Max(value = 10000, message = "pageSize cannot exceed 10000")
|
||||
private Integer pageSize;
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.zcloud.primeport.dto.clientobject;
|
||||
|
||||
import com.alibaba.cola.dto.ClientObject;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class DaHuaAccessRecordSyncResultCO extends ClientObject {
|
||||
|
||||
private int pageCount;
|
||||
private int recordTotal;
|
||||
private int recordCreated;
|
||||
private int recordUpdated;
|
||||
private int recordInvalid;
|
||||
private List<String> exceptionDetails = new ArrayList<>();
|
||||
}
|
||||
|
|
@ -30,8 +30,18 @@ public class DaHuaResourceSyncResultCO extends ClientObject {
|
|||
private Integer deviceUnmatched = 0;
|
||||
@ApiModelProperty("无效设备数据数")
|
||||
private Integer deviceInvalid = 0;
|
||||
@ApiModelProperty("门禁通道总数")
|
||||
private Integer channelTotal = 0;
|
||||
@ApiModelProperty("新增门禁通道数")
|
||||
private Integer channelCreated = 0;
|
||||
@ApiModelProperty("更新门禁通道数")
|
||||
private Integer channelUpdated = 0;
|
||||
@ApiModelProperty("失效门禁通道数")
|
||||
private Integer channelInvalid = 0;
|
||||
@ApiModelProperty("未匹配的大华部门名称")
|
||||
private List<String> unmatchedDepartmentNames = new ArrayList<>();
|
||||
@ApiModelProperty("未匹配企业的大华设备编码")
|
||||
private List<String> unmatchedDeviceCodes = new ArrayList<>();
|
||||
@ApiModelProperty("同步异常明细")
|
||||
private List<String> exceptionDetails = new ArrayList<>();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
package com.zcloud.primeport.domain.gateway;
|
||||
|
||||
import com.zcloud.primeport.domain.model.DaHuaAccessRecordE;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public interface DaHuaAccessRecordRepositoryGateway {
|
||||
|
||||
List<DaHuaAccessRecordE> listByDahuaRecordIds(Collection<String> dahuaRecordIds);
|
||||
|
||||
LocalDateTime findLatestDahuaCreateTime();
|
||||
|
||||
void add(DaHuaAccessRecordE record);
|
||||
|
||||
void update(DaHuaAccessRecordE record);
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.zcloud.primeport.domain.gateway;
|
||||
|
||||
import com.zcloud.primeport.domain.model.DaHuaAccessRecordSyncResultE;
|
||||
|
||||
/**
|
||||
* Application port used by the scheduled access-record synchronization job.
|
||||
*/
|
||||
public interface DaHuaAccessRecordSyncGateway {
|
||||
|
||||
DaHuaAccessRecordSyncResultE syncIncremental(int pageSize, int delayMinutes,
|
||||
int overlapMinutes, int initialLookbackMinutes);
|
||||
}
|
||||
|
|
@ -7,4 +7,7 @@ public interface DaHuaResourceGateway {
|
|||
DaHuaResourcePageE pageOrganizations(Integer pageNum, Integer pageSize, String subsystem);
|
||||
|
||||
DaHuaResourcePageE pageDevices(Integer pageNum, Integer pageSize, String subsystem);
|
||||
|
||||
/** Official V5.0.18 device/subsystem/page response contains nested channels. */
|
||||
DaHuaResourcePageE pageChannels(Integer pageNum, Integer pageSize, String subsystem);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.zcloud.primeport.domain.gateway;
|
|||
import com.zcloud.primeport.domain.model.CorpInfoSnapshotE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaDepartmentCorpMappingE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaDeviceE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaDeviceChannelE;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -21,4 +22,10 @@ public interface DaHuaResourceRepositoryGateway {
|
|||
void addDevice(DaHuaDeviceE device);
|
||||
|
||||
void updateDevice(DaHuaDeviceE device);
|
||||
|
||||
List<DaHuaDeviceChannelE> listDeviceChannels();
|
||||
|
||||
void addDeviceChannel(DaHuaDeviceChannelE channel);
|
||||
|
||||
void updateDeviceChannel(DaHuaDeviceChannelE channel);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
package com.zcloud.primeport.domain.gateway;
|
||||
|
||||
import com.zcloud.primeport.domain.model.DaHuaResourceSyncResultE;
|
||||
|
||||
/**
|
||||
* Application port used by the scheduled resource synchronization job.
|
||||
*/
|
||||
public interface DaHuaResourceSyncGateway {
|
||||
|
||||
DaHuaResourceSyncResultE sync(Integer pageSize, String subsystem, boolean autoMatchByName);
|
||||
}
|
||||
|
|
@ -7,4 +7,5 @@ public class CorpInfoSnapshotE {
|
|||
|
||||
private Long id;
|
||||
private String corpName;
|
||||
private Integer portArea;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
package com.zcloud.primeport.domain.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class DaHuaAccessRecordE {
|
||||
|
||||
private Long id;
|
||||
private String dahuaRecordId;
|
||||
|
||||
private Long corpId;
|
||||
private String corpName;
|
||||
private Integer portArea;
|
||||
|
||||
private Long deviceCorpId;
|
||||
private String deviceCorpName;
|
||||
private Integer devicePortArea;
|
||||
|
||||
private Long dahuaDepartmentId;
|
||||
private String dahuaDepartmentName;
|
||||
|
||||
private Long mkmjId;
|
||||
private Long passageId;
|
||||
private Long gateId;
|
||||
|
||||
private String channelCode;
|
||||
private String channelName;
|
||||
private String deviceCode;
|
||||
private String deviceName;
|
||||
|
||||
private Long personId;
|
||||
private String personCode;
|
||||
private String personName;
|
||||
private String paperNumber;
|
||||
private String cardNumber;
|
||||
private Integer cardStatus;
|
||||
private Integer cardType;
|
||||
private Integer imageType;
|
||||
|
||||
private Integer enterOrExit;
|
||||
private Integer openType;
|
||||
private Integer openResult;
|
||||
private String recordImageUrl;
|
||||
private String remark;
|
||||
|
||||
private LocalDateTime swingTime;
|
||||
private LocalDateTime dahuaCreateTime;
|
||||
private Integer maskState;
|
||||
private Boolean overTemp;
|
||||
private BigDecimal currentTemperature;
|
||||
|
||||
private String sourceType;
|
||||
private String rawData;
|
||||
private LocalDateTime lastSyncTime;
|
||||
private String deleteEnum;
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.zcloud.primeport.domain.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class DaHuaAccessRecordSyncResultE {
|
||||
|
||||
private int pageCount;
|
||||
private int recordTotal;
|
||||
private int recordCreated;
|
||||
private int recordUpdated;
|
||||
private int recordInvalid;
|
||||
private List<String> exceptionDetails = new ArrayList<>();
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.zcloud.primeport.domain.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class DaHuaDeviceChannelE {
|
||||
|
||||
private Long id;
|
||||
private String channelCode;
|
||||
private String channelName;
|
||||
private String deviceCode;
|
||||
private Long corpId;
|
||||
private Long mkmjId;
|
||||
private Long passageId;
|
||||
private Long gateId;
|
||||
private Integer unitType;
|
||||
private Long dahuaDepartmentId;
|
||||
private String dahuaOrgCode;
|
||||
private String dahuaOrgName;
|
||||
private String onlineStatus;
|
||||
private String rawData;
|
||||
private LocalDateTime lastSyncTime;
|
||||
private String deleteEnum;
|
||||
}
|
||||
|
|
@ -11,4 +11,6 @@ public class DaHuaResourcePageE {
|
|||
|
||||
private List<Map<String, Object>> records = new ArrayList<>();
|
||||
private Long total;
|
||||
/** Number of records returned by the remote page before nested channels are expanded. */
|
||||
private Integer sourceRecordCount;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,11 @@ public class DaHuaResourceSyncResultE {
|
|||
private Integer deviceUpdated = 0;
|
||||
private Integer deviceUnmatched = 0;
|
||||
private Integer deviceInvalid = 0;
|
||||
private Integer channelTotal = 0;
|
||||
private Integer channelCreated = 0;
|
||||
private Integer channelUpdated = 0;
|
||||
private Integer channelInvalid = 0;
|
||||
private List<String> unmatchedDepartmentNames = new ArrayList<>();
|
||||
private List<String> unmatchedDeviceCodes = new ArrayList<>();
|
||||
private List<String> exceptionDetails = new ArrayList<>();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,10 @@ public class DaHuaResourceApi {
|
|||
return postPage(properties.getDevicePagePath(), body);
|
||||
}
|
||||
|
||||
public GeneralResponse pageChannels(Map<String, Object> body) throws ClientException {
|
||||
return postPage(properties.getChannelPagePath(), body);
|
||||
}
|
||||
|
||||
private GeneralResponse postPage(String path, Map<String, Object> body) throws ClientException {
|
||||
if (daHuaApiClient == null) {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
package com.zcloud.primeport.dahua.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "dahua.access-record")
|
||||
public class DaHuaAccessRecordProperties {
|
||||
|
||||
private boolean syncEnabled = true;
|
||||
private Integer syncPageSize = 1000;
|
||||
private Integer syncDelayMinutes = 5;
|
||||
private Integer syncOverlapMinutes = 10;
|
||||
private Integer initialLookbackMinutes = 1440;
|
||||
}
|
||||
|
|
@ -8,7 +8,8 @@ import org.springframework.context.annotation.Bean;
|
|||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties({DaHuaProperties.class, DaHuaResourceProperties.class})
|
||||
@EnableConfigurationProperties({DaHuaProperties.class, DaHuaResourceProperties.class,
|
||||
DaHuaAccessRecordProperties.class})
|
||||
public class DaHuaAutoConfig {
|
||||
|
||||
@Bean("dahuaApiOauthConfig")
|
||||
|
|
|
|||
|
|
@ -9,4 +9,13 @@ public class DaHuaResourceProperties {
|
|||
|
||||
private String organizationPagePath = "/evo-apigw/evo-brm/1.2.0/organization/subsystem/page";
|
||||
private String devicePagePath = "/evo-apigw/evo-brm/1.2.0/device/subsystem/page";
|
||||
/** V5.0.18 uses the same device pagination endpoint; channels are nested in pageData. */
|
||||
private String channelPagePath = "/evo-apigw/evo-brm/1.2.0/device/subsystem/page";
|
||||
private Integer channelCategory = 8;
|
||||
/** Official access-control example type; override when the platform uses another 8_x subtype. */
|
||||
private String channelType = "8_16";
|
||||
private boolean syncEnabled = true;
|
||||
private Integer syncPageSize = 200;
|
||||
private String subsystem = "evo-accesscontrol";
|
||||
private boolean autoMatchByName = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
package com.zcloud.primeport.gatewayimpl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
|
||||
import com.jjb.saas.framework.repository.basedo.BaseDO;
|
||||
import com.zcloud.primeport.domain.gateway.DaHuaAccessRecordRepositoryGateway;
|
||||
import com.zcloud.primeport.domain.model.DaHuaAccessRecordE;
|
||||
import com.zcloud.primeport.persistence.dataobject.DaHuaAccessRecordDO;
|
||||
import com.zcloud.primeport.persistence.repository.DaHuaAccessRecordRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DaHuaAccessRecordRepositoryGatewayImpl implements DaHuaAccessRecordRepositoryGateway {
|
||||
|
||||
private final DaHuaAccessRecordRepository repository;
|
||||
private final DefaultIdentifierGenerator idGenerator = new DefaultIdentifierGenerator();
|
||||
|
||||
@Override
|
||||
public List<DaHuaAccessRecordE> listByDahuaRecordIds(Collection<String> dahuaRecordIds) {
|
||||
if (dahuaRecordIds == null || dahuaRecordIds.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
LambdaQueryWrapper<DaHuaAccessRecordDO> query = new LambdaQueryWrapper<>();
|
||||
query.in(DaHuaAccessRecordDO::getDahuaRecordId, dahuaRecordIds);
|
||||
return convertList(repository.list(query));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDateTime findLatestDahuaCreateTime() {
|
||||
return repository.findLatestDahuaCreateTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(DaHuaAccessRecordE record) {
|
||||
DaHuaAccessRecordDO data = copy(record, DaHuaAccessRecordDO.class);
|
||||
initializeBase(data);
|
||||
repository.save(data);
|
||||
record.setId(data.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(DaHuaAccessRecordE record) {
|
||||
DaHuaAccessRecordDO data = copy(record, DaHuaAccessRecordDO.class);
|
||||
data.setUpdateTime(LocalDateTime.now());
|
||||
repository.updateById(data);
|
||||
}
|
||||
|
||||
private void initializeBase(BaseDO target) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
target.setId(idGenerator.nextId(target).longValue());
|
||||
target.setDeleteEnum("FALSE");
|
||||
target.setEnv("PROD");
|
||||
target.setVersion(0);
|
||||
target.setCreateTime(now);
|
||||
target.setUpdateTime(now);
|
||||
}
|
||||
|
||||
private List<DaHuaAccessRecordE> convertList(List<DaHuaAccessRecordDO> source) {
|
||||
List<DaHuaAccessRecordE> result = new ArrayList<>();
|
||||
for (DaHuaAccessRecordDO item : source) {
|
||||
result.add(copy(item, DaHuaAccessRecordE.class));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private <T> T copy(Object source, Class<T> targetType) {
|
||||
try {
|
||||
T target = targetType.newInstance();
|
||||
BeanUtils.copyProperties(source, target);
|
||||
return target;
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
throw new IllegalStateException("Failed to convert Dahua access record persistence object", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,12 +6,14 @@ import cn.hutool.json.JSONUtil;
|
|||
import com.dahuatech.icc.exception.ClientException;
|
||||
import com.dahuatech.icc.oauth.model.v202010.GeneralResponse;
|
||||
import com.zcloud.primeport.dahua.api.DaHuaResourceApi;
|
||||
import com.zcloud.primeport.dahua.config.DaHuaResourceProperties;
|
||||
import com.zcloud.primeport.domain.gateway.DaHuaResourceGateway;
|
||||
import com.zcloud.primeport.domain.model.DaHuaResourcePageE;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
|
@ -21,39 +23,90 @@ import java.util.Map;
|
|||
public class DaHuaResourceGatewayImpl implements DaHuaResourceGateway {
|
||||
|
||||
private final DaHuaResourceApi daHuaResourceApi;
|
||||
private final DaHuaResourceProperties properties;
|
||||
|
||||
@Override
|
||||
public DaHuaResourcePageE pageOrganizations(Integer pageNum, Integer pageSize, String subsystem) {
|
||||
return requestPage(pageNum, pageSize, subsystem, true);
|
||||
return requestPage(pageNum, pageSize, subsystem, true, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DaHuaResourcePageE pageDevices(Integer pageNum, Integer pageSize, String subsystem) {
|
||||
return requestPage(pageNum, pageSize, subsystem, false);
|
||||
return requestPage(pageNum, pageSize, subsystem, false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DaHuaResourcePageE pageChannels(Integer pageNum, Integer pageSize, String subsystem) {
|
||||
return requestPage(pageNum, pageSize, subsystem, false, true);
|
||||
}
|
||||
|
||||
private DaHuaResourcePageE requestPage(Integer pageNum, Integer pageSize, String subsystem,
|
||||
boolean organization) {
|
||||
boolean organization, boolean channelsOnly) {
|
||||
if (!daHuaResourceApi.isConfigured()) {
|
||||
throw new RuntimeException("大华服务未配置");
|
||||
}
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("pageNum", pageNum);
|
||||
body.put("pageSize", pageSize);
|
||||
if (channelsOnly) {
|
||||
body.put("categorys", Collections.singletonList(properties.getChannelCategory()));
|
||||
if (!isBlank(properties.getChannelType())) {
|
||||
body.put("types", Collections.singletonList(properties.getChannelType().trim()));
|
||||
}
|
||||
}
|
||||
if (!isBlank(subsystem)) {
|
||||
body.put("subsystem", subsystem.trim());
|
||||
}
|
||||
try {
|
||||
GeneralResponse response = organization
|
||||
? daHuaResourceApi.pageOrganizations(body)
|
||||
: channelsOnly ? daHuaResourceApi.pageChannels(body)
|
||||
: daHuaResourceApi.pageDevices(body);
|
||||
Object data = unwrapData(response, organization ? "部门" : "设备");
|
||||
return parsePage(data);
|
||||
Object data = unwrapData(response, organization ? "部门" : (channelsOnly ? "设备通道" : "设备"));
|
||||
return channelsOnly ? parseChannelPage(data) : parsePage(data);
|
||||
} catch (ClientException e) {
|
||||
throw new RuntimeException("调用大华" + (organization ? "部门" : "设备") + "分页接口失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private DaHuaResourcePageE parseChannelPage(Object data) {
|
||||
DaHuaResourcePageE page = new DaHuaResourcePageE();
|
||||
if (data == null) {
|
||||
return page;
|
||||
}
|
||||
JSONObject object = toJsonObject(data);
|
||||
Object value = object == null ? data : firstValue(object, "pageData", "records", "list", "dataList");
|
||||
List<Map<String, Object>> sourceRecords = toRecordList(value);
|
||||
page.setSourceRecordCount(sourceRecords.size());
|
||||
if (object != null) {
|
||||
page.setTotal(firstLong(object, "totalRows", "total", "totalCount", "recordCount"));
|
||||
}
|
||||
List<Map<String, Object>> channels = new ArrayList<>();
|
||||
for (Map<String, Object> record : sourceRecords) {
|
||||
Object nested = firstValue(record, "channels", "channelList", "children");
|
||||
List<Map<String, Object>> nestedChannels = toRecordList(nested);
|
||||
if (nestedChannels.isEmpty() && firstValue(record, "channelCode") != null) {
|
||||
channels.add(record);
|
||||
} else {
|
||||
for (Map<String, Object> channel : nestedChannels) {
|
||||
Map<String, Object> merged = new HashMap<>(channel);
|
||||
copyIfAbsent(merged, record, "deviceCode", "deviceName", "orgCode", "orgName", "departmentId");
|
||||
channels.add(merged);
|
||||
}
|
||||
}
|
||||
}
|
||||
page.setRecords(channels);
|
||||
return page;
|
||||
}
|
||||
|
||||
private void copyIfAbsent(Map<String, Object> target, Map<String, Object> source, String... fields) {
|
||||
for (String field : fields) {
|
||||
if (target.get(field) == null && source.get(field) != null) {
|
||||
target.put(field, source.get(field));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object unwrapData(GeneralResponse response, String resourceName) {
|
||||
if (response == null) {
|
||||
throw new RuntimeException("大华" + resourceName + "分页接口返回为空");
|
||||
|
|
@ -84,6 +137,7 @@ public class DaHuaResourceGatewayImpl implements DaHuaResourceGateway {
|
|||
}
|
||||
if (data instanceof JSONArray || data instanceof List) {
|
||||
page.setRecords(toRecordList(data));
|
||||
page.setSourceRecordCount(page.getRecords().size());
|
||||
return page;
|
||||
}
|
||||
JSONObject object = toJsonObject(data);
|
||||
|
|
@ -91,6 +145,7 @@ public class DaHuaResourceGatewayImpl implements DaHuaResourceGateway {
|
|||
return page;
|
||||
}
|
||||
page.setRecords(toRecordList(firstValue(object, "pageData", "records", "list", "dataList")));
|
||||
page.setSourceRecordCount(page.getRecords().size());
|
||||
page.setTotal(firstLong(object, "totalRows", "total", "totalCount", "recordCount"));
|
||||
return page;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,12 +7,15 @@ import com.zcloud.primeport.domain.gateway.DaHuaResourceRepositoryGateway;
|
|||
import com.zcloud.primeport.domain.model.CorpInfoSnapshotE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaDepartmentCorpMappingE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaDeviceE;
|
||||
import com.zcloud.primeport.domain.model.DaHuaDeviceChannelE;
|
||||
import com.zcloud.primeport.persistence.dataobject.CorpInfoSnapshotDO;
|
||||
import com.zcloud.primeport.persistence.dataobject.DaHuaDepartmentCorpMappingDO;
|
||||
import com.zcloud.primeport.persistence.dataobject.DaHuaDeviceDO;
|
||||
import com.zcloud.primeport.persistence.dataobject.DaHuaDeviceChannelDO;
|
||||
import com.zcloud.primeport.persistence.repository.CorpInfoSnapshotRepository;
|
||||
import com.zcloud.primeport.persistence.repository.DaHuaDepartmentCorpMappingRepository;
|
||||
import com.zcloud.primeport.persistence.repository.DaHuaDeviceRepository;
|
||||
import com.zcloud.primeport.persistence.repository.DaHuaDeviceChannelRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
|
@ -28,13 +31,15 @@ public class DaHuaResourceRepositoryGatewayImpl implements DaHuaResourceReposito
|
|||
private final CorpInfoSnapshotRepository corpInfoSnapshotRepository;
|
||||
private final DaHuaDepartmentCorpMappingRepository mappingRepository;
|
||||
private final DaHuaDeviceRepository deviceRepository;
|
||||
private final DaHuaDeviceChannelRepository deviceChannelRepository;
|
||||
private final DefaultIdentifierGenerator idGenerator = new DefaultIdentifierGenerator();
|
||||
|
||||
@Override
|
||||
public List<CorpInfoSnapshotE> listActiveCorps() {
|
||||
LambdaQueryWrapper<CorpInfoSnapshotDO> query = new LambdaQueryWrapper<>();
|
||||
query.eq(CorpInfoSnapshotDO::getDeleteEnum, "FALSE")
|
||||
.select(CorpInfoSnapshotDO::getId, CorpInfoSnapshotDO::getCorpName);
|
||||
.select(CorpInfoSnapshotDO::getId, CorpInfoSnapshotDO::getCorpName,
|
||||
CorpInfoSnapshotDO::getPortArea);
|
||||
return convertList(corpInfoSnapshotRepository.list(query), CorpInfoSnapshotE.class);
|
||||
}
|
||||
|
||||
|
|
@ -79,6 +84,26 @@ public class DaHuaResourceRepositoryGatewayImpl implements DaHuaResourceReposito
|
|||
deviceRepository.updateById(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DaHuaDeviceChannelE> listDeviceChannels() {
|
||||
return convertList(deviceChannelRepository.list(new LambdaQueryWrapper<>()), DaHuaDeviceChannelE.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDeviceChannel(DaHuaDeviceChannelE channel) {
|
||||
DaHuaDeviceChannelDO data = copy(channel, DaHuaDeviceChannelDO.class);
|
||||
initializeBase(data);
|
||||
deviceChannelRepository.save(data);
|
||||
channel.setId(data.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDeviceChannel(DaHuaDeviceChannelE channel) {
|
||||
DaHuaDeviceChannelDO data = copy(channel, DaHuaDeviceChannelDO.class);
|
||||
data.setUpdateTime(LocalDateTime.now());
|
||||
deviceChannelRepository.updateById(data);
|
||||
}
|
||||
|
||||
private void initializeBase(BaseDO target) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
target.setId(idGenerator.nextId(target).longValue());
|
||||
|
|
|
|||
|
|
@ -11,4 +11,5 @@ import lombok.EqualsAndHashCode;
|
|||
public class CorpInfoSnapshotDO extends BaseDO {
|
||||
|
||||
private String corpName;
|
||||
private Integer portArea;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
package com.zcloud.primeport.persistence.dataobject;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.jjb.saas.framework.repository.basedo.BaseDO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("dahua_access_record")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DaHuaAccessRecordDO extends BaseDO {
|
||||
|
||||
private String dahuaRecordId;
|
||||
|
||||
private Long corpId;
|
||||
private String corpName;
|
||||
private Integer portArea;
|
||||
|
||||
private Long deviceCorpId;
|
||||
private String deviceCorpName;
|
||||
private Integer devicePortArea;
|
||||
|
||||
private Long dahuaDepartmentId;
|
||||
private String dahuaDepartmentName;
|
||||
|
||||
private Long mkmjId;
|
||||
private Long passageId;
|
||||
private Long gateId;
|
||||
|
||||
private String channelCode;
|
||||
private String channelName;
|
||||
private String deviceCode;
|
||||
private String deviceName;
|
||||
|
||||
private Long personId;
|
||||
private String personCode;
|
||||
private String personName;
|
||||
private String paperNumber;
|
||||
private String cardNumber;
|
||||
private Integer cardStatus;
|
||||
private Integer cardType;
|
||||
private Integer imageType;
|
||||
|
||||
private Integer enterOrExit;
|
||||
private Integer openType;
|
||||
private Integer openResult;
|
||||
private String recordImageUrl;
|
||||
private String remark;
|
||||
|
||||
private LocalDateTime swingTime;
|
||||
private LocalDateTime dahuaCreateTime;
|
||||
private Integer maskState;
|
||||
private Boolean overTemp;
|
||||
private BigDecimal currentTemperature;
|
||||
|
||||
private String sourceType;
|
||||
private String rawData;
|
||||
private LocalDateTime lastSyncTime;
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.zcloud.primeport.persistence.dataobject;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.jjb.saas.framework.repository.basedo.BaseDO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("dahua_device_channel")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DaHuaDeviceChannelDO extends BaseDO {
|
||||
|
||||
private String channelCode;
|
||||
private String channelName;
|
||||
private String deviceCode;
|
||||
private Long corpId;
|
||||
private Long mkmjId;
|
||||
private Long passageId;
|
||||
private Long gateId;
|
||||
private Integer unitType;
|
||||
private Long dahuaDepartmentId;
|
||||
private String dahuaOrgCode;
|
||||
private String dahuaOrgName;
|
||||
private String onlineStatus;
|
||||
private String rawData;
|
||||
private LocalDateTime lastSyncTime;
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.zcloud.primeport.persistence.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.zcloud.primeport.persistence.dataobject.DaHuaAccessRecordDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Mapper
|
||||
public interface DaHuaAccessRecordMapper extends BaseMapper<DaHuaAccessRecordDO> {
|
||||
|
||||
LocalDateTime findLatestDahuaCreateTime();
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.zcloud.primeport.persistence.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.zcloud.primeport.persistence.dataobject.DaHuaDeviceChannelDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface DaHuaDeviceChannelMapper extends BaseMapper<DaHuaDeviceChannelDO> {
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.zcloud.primeport.persistence.repository;
|
||||
|
||||
import com.jjb.saas.framework.repository.repo.BaseRepository;
|
||||
import com.zcloud.primeport.persistence.dataobject.DaHuaAccessRecordDO;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public interface DaHuaAccessRecordRepository extends BaseRepository<DaHuaAccessRecordDO> {
|
||||
|
||||
LocalDateTime findLatestDahuaCreateTime();
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.zcloud.primeport.persistence.repository;
|
||||
|
||||
import com.jjb.saas.framework.repository.repo.BaseRepository;
|
||||
import com.zcloud.primeport.persistence.dataobject.DaHuaDeviceChannelDO;
|
||||
|
||||
public interface DaHuaDeviceChannelRepository extends BaseRepository<DaHuaDeviceChannelDO> {
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.zcloud.primeport.persistence.repository.impl;
|
||||
|
||||
import com.jjb.saas.framework.repository.repo.impl.BaseRepositoryImpl;
|
||||
import com.zcloud.primeport.persistence.dataobject.DaHuaAccessRecordDO;
|
||||
import com.zcloud.primeport.persistence.mapper.DaHuaAccessRecordMapper;
|
||||
import com.zcloud.primeport.persistence.repository.DaHuaAccessRecordRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Repository
|
||||
public class DaHuaAccessRecordRepositoryImpl
|
||||
extends BaseRepositoryImpl<DaHuaAccessRecordMapper, DaHuaAccessRecordDO>
|
||||
implements DaHuaAccessRecordRepository {
|
||||
|
||||
@Override
|
||||
public LocalDateTime findLatestDahuaCreateTime() {
|
||||
return baseMapper.findLatestDahuaCreateTime();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.zcloud.primeport.persistence.repository.impl;
|
||||
|
||||
import com.jjb.saas.framework.repository.repo.impl.BaseRepositoryImpl;
|
||||
import com.zcloud.primeport.persistence.dataobject.DaHuaDeviceChannelDO;
|
||||
import com.zcloud.primeport.persistence.mapper.DaHuaDeviceChannelMapper;
|
||||
import com.zcloud.primeport.persistence.repository.DaHuaDeviceChannelRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class DaHuaDeviceChannelRepositoryImpl
|
||||
extends BaseRepositoryImpl<DaHuaDeviceChannelMapper, DaHuaDeviceChannelDO>
|
||||
implements DaHuaDeviceChannelRepository {
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package com.zcloud.primeport.plan;
|
||||
|
||||
import com.jjb.saas.framework.job.Job;
|
||||
import com.jjb.saas.framework.job.annotation.JobRegister;
|
||||
import com.xxl.job.core.biz.model.ReturnT;
|
||||
import com.xxl.job.core.handler.annotation.XxlJob;
|
||||
import com.zcloud.primeport.dahua.config.DaHuaAccessRecordProperties;
|
||||
import com.zcloud.primeport.domain.gateway.DaHuaAccessRecordSyncGateway;
|
||||
import com.zcloud.primeport.domain.model.DaHuaAccessRecordSyncResultE;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class DaHuaAccessRecordSyncXxlJob implements Job {
|
||||
|
||||
private final DaHuaAccessRecordSyncGateway syncGateway;
|
||||
private final DaHuaAccessRecordProperties properties;
|
||||
|
||||
@Override
|
||||
@JobRegister(cron = "0 */5 * * * ?", jobDesc = "大华门禁通行记录增量同步", triggerStatus = 1)
|
||||
@XxlJob("com.zcloud.plan.DaHuaAccessRecordSyncXxlJob")
|
||||
public ReturnT<String> execute(String param) {
|
||||
if (!properties.isSyncEnabled()) {
|
||||
log.info("大华门禁通行记录增量同步已关闭");
|
||||
return ReturnT.SUCCESS;
|
||||
}
|
||||
try {
|
||||
DaHuaAccessRecordSyncResultE result = syncGateway.syncIncremental(
|
||||
properties.getSyncPageSize(), properties.getSyncDelayMinutes(),
|
||||
properties.getSyncOverlapMinutes(), properties.getInitialLookbackMinutes());
|
||||
log.info("大华门禁通行记录增量同步完成: pages={}, total={}, created={}, updated={}, invalid={}",
|
||||
result.getPageCount(), result.getRecordTotal(), result.getRecordCreated(),
|
||||
result.getRecordUpdated(), result.getRecordInvalid());
|
||||
return ReturnT.SUCCESS;
|
||||
} catch (Exception e) {
|
||||
log.error("大华门禁通行记录增量同步失败", e);
|
||||
return ReturnT.FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package com.zcloud.primeport.plan;
|
||||
|
||||
import com.jjb.saas.framework.job.Job;
|
||||
import com.jjb.saas.framework.job.annotation.JobRegister;
|
||||
import com.xxl.job.core.biz.model.ReturnT;
|
||||
import com.xxl.job.core.handler.annotation.XxlJob;
|
||||
import com.zcloud.primeport.dahua.config.DaHuaResourceProperties;
|
||||
import com.zcloud.primeport.domain.gateway.DaHuaResourceSyncGateway;
|
||||
import com.zcloud.primeport.domain.model.DaHuaResourceSyncResultE;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class DaHuaResourceSyncXxlJob implements Job {
|
||||
|
||||
private final DaHuaResourceSyncGateway syncGateway;
|
||||
private final DaHuaResourceProperties properties;
|
||||
|
||||
@Override
|
||||
@JobRegister(cron = "0 */10 * * * ?", jobDesc = "大华部门、设备和门禁通道同步", triggerStatus = 1)
|
||||
@XxlJob("com.zcloud.plan.DaHuaResourceSyncXxlJob")
|
||||
public ReturnT<String> execute(String param) {
|
||||
if (!properties.isSyncEnabled()) {
|
||||
log.info("大华资源同步已关闭");
|
||||
return ReturnT.SUCCESS;
|
||||
}
|
||||
try {
|
||||
DaHuaResourceSyncResultE result = syncGateway.sync(
|
||||
properties.getSyncPageSize(), properties.getSubsystem(),
|
||||
properties.isAutoMatchByName());
|
||||
log.info("大华资源同步完成: departments={}, devices={}, channels={}, invalidChannels={}",
|
||||
result.getDepartmentTotal(), result.getDeviceTotal(), result.getChannelTotal(),
|
||||
result.getChannelInvalid());
|
||||
return ReturnT.SUCCESS;
|
||||
} catch (Exception e) {
|
||||
log.error("大华资源同步失败", e);
|
||||
return ReturnT.FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -398,3 +398,103 @@ CREATE TABLE `dahua_device` (
|
|||
KEY `idx_dahua_device_corp` (`corp_id`),
|
||||
KEY `idx_dahua_device_match_status` (`match_status`, `delete_enum`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='大华门口设备同步表';
|
||||
|
||||
CREATE TABLE `dahua_device_channel` (
|
||||
`id` bigint NOT NULL COMMENT '主键ID',
|
||||
`channel_code` varchar(128) NOT NULL COMMENT '大华门禁通道编码',
|
||||
`channel_name` varchar(255) DEFAULT NULL COMMENT '大华门禁通道名称',
|
||||
`device_code` varchar(128) NOT NULL COMMENT '所属大华设备编码',
|
||||
`corp_id` bigint DEFAULT NULL COMMENT '平台企业ID',
|
||||
`mkmj_id` bigint DEFAULT NULL COMMENT '本平台口门ID',
|
||||
`passage_id` bigint DEFAULT NULL COMMENT '本平台通道ID',
|
||||
`gate_id` bigint DEFAULT NULL COMMENT '本平台闸机ID',
|
||||
`unit_type` int NOT NULL COMMENT '单元类型,门禁通道为7',
|
||||
`dahua_department_id` bigint DEFAULT NULL COMMENT '大华所属部门ID',
|
||||
`dahua_org_code` varchar(64) DEFAULT NULL COMMENT '大华组织编码',
|
||||
`dahua_org_name` varchar(255) DEFAULT NULL COMMENT '大华组织名称',
|
||||
`online_status` varchar(32) DEFAULT NULL COMMENT '设备在线状态',
|
||||
`raw_data` json DEFAULT NULL COMMENT '大华原始通道数据',
|
||||
`last_sync_time` datetime DEFAULT NULL COMMENT '最近同步时间',
|
||||
`delete_enum` varchar(32) NOT NULL DEFAULT 'FALSE' COMMENT '删除标识',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
`create_id` bigint DEFAULT NULL COMMENT '创建人ID',
|
||||
`update_id` bigint DEFAULT NULL COMMENT '更新人ID',
|
||||
`env` varchar(32) NOT NULL DEFAULT 'PROD' COMMENT '环境标识',
|
||||
`create_name` varchar(255) DEFAULT NULL COMMENT '创建人姓名',
|
||||
`update_name` varchar(255) DEFAULT NULL COMMENT '更新人姓名',
|
||||
`tenant_id` bigint DEFAULT NULL COMMENT '租户ID',
|
||||
`org_id` bigint DEFAULT NULL COMMENT '组织ID',
|
||||
`version` int NOT NULL DEFAULT '0' COMMENT '版本号',
|
||||
`remarks` varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_dahua_device_channel_code` (`channel_code`),
|
||||
KEY `idx_dahua_device_channel_device_code` (`device_code`),
|
||||
KEY `idx_dahua_device_channel_corp` (`corp_id`),
|
||||
KEY `idx_dahua_device_channel_mkmj` (`mkmj_id`),
|
||||
KEY `idx_dahua_device_channel_passage` (`passage_id`),
|
||||
KEY `idx_dahua_device_channel_gate` (`gate_id`),
|
||||
KEY `idx_dahua_device_channel_department` (`dahua_department_id`),
|
||||
KEY `idx_dahua_device_channel_org_code` (`dahua_org_code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='大华门禁通道同步表';
|
||||
|
||||
CREATE TABLE `dahua_access_record` (
|
||||
`id` bigint NOT NULL COMMENT '主键ID',
|
||||
`dahua_record_id` varchar(128) NOT NULL COMMENT '大华门禁通行记录ID,幂等键',
|
||||
`corp_id` bigint DEFAULT NULL COMMENT '通行人员所属本平台企业ID',
|
||||
`corp_name` varchar(255) DEFAULT NULL COMMENT '通行人员所属本平台企业名称快照',
|
||||
`port_area` int DEFAULT NULL COMMENT '通行人员所属企业港区',
|
||||
`device_corp_id` bigint DEFAULT NULL COMMENT '门禁设备所属本平台企业ID',
|
||||
`device_corp_name` varchar(255) DEFAULT NULL COMMENT '门禁设备所属本平台企业名称快照',
|
||||
`device_port_area` int DEFAULT NULL COMMENT '门禁设备所属企业港区',
|
||||
`dahua_department_id` bigint DEFAULT NULL COMMENT '大华人员部门ID',
|
||||
`dahua_department_name` varchar(255) DEFAULT NULL COMMENT '大华人员部门名称快照',
|
||||
`mkmj_id` bigint DEFAULT NULL COMMENT '本平台口门ID',
|
||||
`passage_id` bigint DEFAULT NULL COMMENT '本平台通道ID',
|
||||
`gate_id` bigint DEFAULT NULL COMMENT '本平台闸机ID',
|
||||
`channel_code` varchar(128) DEFAULT NULL COMMENT '大华门禁通道编码',
|
||||
`channel_name` varchar(255) DEFAULT NULL COMMENT '大华门禁通道名称',
|
||||
`device_code` varchar(128) DEFAULT NULL COMMENT '大华门禁设备编码',
|
||||
`device_name` varchar(255) DEFAULT NULL COMMENT '大华门禁设备名称',
|
||||
`person_id` bigint DEFAULT NULL COMMENT '大华人员ID',
|
||||
`person_code` varchar(128) DEFAULT NULL COMMENT '大华人员编号',
|
||||
`person_name` varchar(255) DEFAULT NULL COMMENT '人员名称快照',
|
||||
`paper_number` varchar(128) DEFAULT NULL COMMENT '证件号码',
|
||||
`card_number` varchar(128) DEFAULT NULL COMMENT '卡号',
|
||||
`card_status` int DEFAULT NULL COMMENT '卡状态',
|
||||
`card_type` int DEFAULT NULL COMMENT '卡类型',
|
||||
`image_type` int DEFAULT NULL COMMENT '图片类型',
|
||||
`enter_or_exit` int DEFAULT NULL COMMENT '进出方向:1-进,2-出',
|
||||
`open_type` int DEFAULT NULL COMMENT '开门类型',
|
||||
`open_result` int DEFAULT NULL COMMENT '开门结果:1-成功,0-失败',
|
||||
`record_image_url` varchar(1024) DEFAULT NULL COMMENT '通行抓拍相对地址',
|
||||
`remark` varchar(1000) DEFAULT NULL COMMENT '大华记录备注',
|
||||
`swing_time` datetime DEFAULT NULL COMMENT '通行发生时间',
|
||||
`dahua_create_time` datetime DEFAULT NULL COMMENT '记录在大华平台的入库时间',
|
||||
`mask_state` int DEFAULT NULL COMMENT '口罩状态',
|
||||
`over_temp` tinyint(1) DEFAULT NULL COMMENT '是否超温',
|
||||
`current_temperature` decimal(6,2) DEFAULT NULL COMMENT '当前体温',
|
||||
`source_type` varchar(32) NOT NULL DEFAULT 'PULL' COMMENT '数据来源',
|
||||
`raw_data` json DEFAULT NULL COMMENT '大华原始记录',
|
||||
`last_sync_time` datetime DEFAULT NULL COMMENT '最近同步时间',
|
||||
`delete_enum` varchar(32) NOT NULL DEFAULT 'FALSE' COMMENT '删除标识',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
`create_id` bigint DEFAULT NULL COMMENT '创建人ID',
|
||||
`update_id` bigint DEFAULT NULL COMMENT '更新人ID',
|
||||
`env` varchar(32) NOT NULL DEFAULT 'PROD' COMMENT '环境标识',
|
||||
`create_name` varchar(255) DEFAULT NULL COMMENT '创建人姓名',
|
||||
`update_name` varchar(255) DEFAULT NULL COMMENT '更新人姓名',
|
||||
`tenant_id` bigint DEFAULT NULL COMMENT '租户ID',
|
||||
`org_id` bigint DEFAULT NULL COMMENT '组织ID',
|
||||
`version` int NOT NULL DEFAULT '0' COMMENT '版本号',
|
||||
`remarks` varchar(500) DEFAULT NULL COMMENT '基础备注',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_dahua_access_record_record_id` (`dahua_record_id`),
|
||||
KEY `idx_dahua_access_record_corp_time` (`corp_id`, `swing_time`),
|
||||
KEY `idx_dahua_access_record_device_corp_time` (`device_corp_id`, `swing_time`),
|
||||
KEY `idx_dahua_access_record_mkmj_time` (`mkmj_id`, `swing_time`),
|
||||
KEY `idx_dahua_access_record_channel_time` (`channel_code`, `swing_time`),
|
||||
KEY `idx_dahua_access_record_person_time` (`person_code`, `swing_time`),
|
||||
KEY `idx_dahua_access_record_create_time` (`dahua_create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='大华门禁通行记录表';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
CREATE TABLE IF NOT EXISTS `dahua_device_channel` (
|
||||
`id` bigint NOT NULL,
|
||||
`channel_code` varchar(128) NOT NULL,
|
||||
`channel_name` varchar(255) DEFAULT NULL,
|
||||
`device_code` varchar(128) NOT NULL,
|
||||
`unit_type` int NOT NULL,
|
||||
`dahua_department_id` bigint DEFAULT NULL,
|
||||
`dahua_org_code` varchar(64) DEFAULT NULL,
|
||||
`dahua_org_name` varchar(255) DEFAULT NULL,
|
||||
`online_status` varchar(32) DEFAULT NULL,
|
||||
`raw_data` json DEFAULT NULL,
|
||||
`last_sync_time` datetime DEFAULT NULL,
|
||||
`delete_enum` varchar(32) NOT NULL DEFAULT 'FALSE',
|
||||
`create_time` datetime DEFAULT NULL,
|
||||
`update_time` datetime DEFAULT NULL,
|
||||
`create_id` bigint DEFAULT NULL,
|
||||
`update_id` bigint DEFAULT NULL,
|
||||
`env` varchar(32) NOT NULL DEFAULT 'PROD',
|
||||
`create_name` varchar(255) DEFAULT NULL,
|
||||
`update_name` varchar(255) DEFAULT NULL,
|
||||
`tenant_id` bigint DEFAULT NULL,
|
||||
`org_id` bigint DEFAULT NULL,
|
||||
`version` int NOT NULL DEFAULT '0',
|
||||
`remarks` varchar(500) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_dahua_device_channel_code` (`channel_code`),
|
||||
KEY `idx_dahua_device_channel_device_code` (`device_code`),
|
||||
KEY `idx_dahua_device_channel_department` (`dahua_department_id`),
|
||||
KEY `idx_dahua_device_channel_org_code` (`dahua_org_code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
ALTER TABLE `dahua_device_channel`
|
||||
ADD COLUMN `corp_id` bigint DEFAULT NULL COMMENT '平台企业ID' AFTER `device_code`,
|
||||
ADD KEY `idx_dahua_device_channel_corp` (`corp_id`);
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
ALTER TABLE `dahua_device_channel`
|
||||
ADD COLUMN `mkmj_id` bigint DEFAULT NULL COMMENT '本平台口门ID' AFTER `corp_id`,
|
||||
ADD COLUMN `passage_id` bigint DEFAULT NULL COMMENT '本平台通道ID' AFTER `mkmj_id`,
|
||||
ADD COLUMN `gate_id` bigint DEFAULT NULL COMMENT '本平台闸机ID' AFTER `passage_id`,
|
||||
ADD KEY `idx_dahua_device_channel_mkmj` (`mkmj_id`),
|
||||
ADD KEY `idx_dahua_device_channel_passage` (`passage_id`),
|
||||
ADD KEY `idx_dahua_device_channel_gate` (`gate_id`);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `dahua_access_record` (
|
||||
`id` bigint NOT NULL COMMENT '主键ID',
|
||||
`dahua_record_id` varchar(128) NOT NULL COMMENT '大华门禁通行记录ID,幂等键',
|
||||
`corp_id` bigint DEFAULT NULL COMMENT '通行人员所属本平台企业ID',
|
||||
`corp_name` varchar(255) DEFAULT NULL COMMENT '通行人员所属本平台企业名称快照',
|
||||
`port_area` int DEFAULT NULL COMMENT '通行人员所属企业港区',
|
||||
`device_corp_id` bigint DEFAULT NULL COMMENT '门禁设备所属本平台企业ID',
|
||||
`device_corp_name` varchar(255) DEFAULT NULL COMMENT '门禁设备所属本平台企业名称快照',
|
||||
`device_port_area` int DEFAULT NULL COMMENT '门禁设备所属企业港区',
|
||||
`dahua_department_id` bigint DEFAULT NULL COMMENT '大华人员部门ID',
|
||||
`dahua_department_name` varchar(255) DEFAULT NULL COMMENT '大华人员部门名称快照',
|
||||
`mkmj_id` bigint DEFAULT NULL COMMENT '本平台口门ID',
|
||||
`passage_id` bigint DEFAULT NULL COMMENT '本平台通道ID',
|
||||
`gate_id` bigint DEFAULT NULL COMMENT '本平台闸机ID',
|
||||
`channel_code` varchar(128) DEFAULT NULL COMMENT '大华门禁通道编码',
|
||||
`channel_name` varchar(255) DEFAULT NULL COMMENT '大华门禁通道名称',
|
||||
`device_code` varchar(128) DEFAULT NULL COMMENT '大华门禁设备编码',
|
||||
`device_name` varchar(255) DEFAULT NULL COMMENT '大华门禁设备名称',
|
||||
`person_id` bigint DEFAULT NULL COMMENT '大华人员ID',
|
||||
`person_code` varchar(128) DEFAULT NULL COMMENT '大华人员编号',
|
||||
`person_name` varchar(255) DEFAULT NULL COMMENT '人员名称快照',
|
||||
`paper_number` varchar(128) DEFAULT NULL COMMENT '证件号码',
|
||||
`card_number` varchar(128) DEFAULT NULL COMMENT '卡号',
|
||||
`card_status` int DEFAULT NULL COMMENT '卡状态',
|
||||
`card_type` int DEFAULT NULL COMMENT '卡类型',
|
||||
`image_type` int DEFAULT NULL COMMENT '图片类型',
|
||||
`enter_or_exit` int DEFAULT NULL COMMENT '进出方向:1-进,2-出',
|
||||
`open_type` int DEFAULT NULL COMMENT '开门类型',
|
||||
`open_result` int DEFAULT NULL COMMENT '开门结果:1-成功,0-失败',
|
||||
`record_image_url` varchar(1024) DEFAULT NULL COMMENT '通行抓拍相对地址',
|
||||
`remark` varchar(1000) DEFAULT NULL COMMENT '大华记录备注',
|
||||
`swing_time` datetime DEFAULT NULL COMMENT '通行发生时间',
|
||||
`dahua_create_time` datetime DEFAULT NULL COMMENT '记录在大华平台的入库时间',
|
||||
`mask_state` int DEFAULT NULL COMMENT '口罩状态',
|
||||
`over_temp` tinyint(1) DEFAULT NULL COMMENT '是否超温',
|
||||
`current_temperature` decimal(6,2) DEFAULT NULL COMMENT '当前体温',
|
||||
`source_type` varchar(32) NOT NULL DEFAULT 'PULL' COMMENT '数据来源',
|
||||
`raw_data` json DEFAULT NULL COMMENT '大华原始记录',
|
||||
`last_sync_time` datetime DEFAULT NULL COMMENT '最近同步时间',
|
||||
`delete_enum` varchar(32) NOT NULL DEFAULT 'FALSE' COMMENT '删除标识',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
`create_id` bigint DEFAULT NULL COMMENT '创建人ID',
|
||||
`update_id` bigint DEFAULT NULL COMMENT '更新人ID',
|
||||
`env` varchar(32) NOT NULL DEFAULT 'PROD' COMMENT '环境标识',
|
||||
`create_name` varchar(255) DEFAULT NULL COMMENT '创建人姓名',
|
||||
`update_name` varchar(255) DEFAULT NULL COMMENT '更新人姓名',
|
||||
`tenant_id` bigint DEFAULT NULL COMMENT '租户ID',
|
||||
`org_id` bigint DEFAULT NULL COMMENT '组织ID',
|
||||
`version` int NOT NULL DEFAULT '0' COMMENT '版本号',
|
||||
`remarks` varchar(500) DEFAULT NULL COMMENT '基础备注',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_dahua_access_record_record_id` (`dahua_record_id`),
|
||||
KEY `idx_dahua_access_record_corp_time` (`corp_id`, `swing_time`),
|
||||
KEY `idx_dahua_access_record_device_corp_time` (`device_corp_id`, `swing_time`),
|
||||
KEY `idx_dahua_access_record_mkmj_time` (`mkmj_id`, `swing_time`),
|
||||
KEY `idx_dahua_access_record_channel_time` (`channel_code`, `swing_time`),
|
||||
KEY `idx_dahua_access_record_person_time` (`person_code`, `swing_time`),
|
||||
KEY `idx_dahua_access_record_create_time` (`dahua_create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='大华门禁通行记录表';
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="com.zcloud.primeport.persistence.mapper.DaHuaAccessRecordMapper">
|
||||
|
||||
<select id="findLatestDahuaCreateTime" resultType="java.time.LocalDateTime">
|
||||
SELECT dahua_create_time
|
||||
FROM dahua_access_record
|
||||
WHERE dahua_create_time IS NOT NULL
|
||||
ORDER BY dahua_create_time DESC
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
Loading…
Reference in New Issue