From de78e0c26af1255db0e2130d16d4adab916d81ee Mon Sep 17 00:00:00 2001 From: shenzhidan Date: Thu, 13 Aug 2026 18:17:12 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=AE=9A=E4=BD=8D=E5=8D=A1?= =?UTF-8?q?=E4=BD=8D=E7=BD=AE=E4=B8=8E=E5=91=8A=E8=AD=A6=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plan/FindsTerminalCardSyncJob.java | 38 ++++ .../command/TerminalCardSyncExe.java | 7 +- .../command/query/AlarmRecordQueryExe.java | 83 ++++---- .../command/query/FenceLocationMatcher.java | 133 ++++++++++++ .../TerminalCardFenceLocationEnricher.java | 191 ++++++++++++++++++ .../command/query/TerminalCardQueryExe.java | 9 +- .../resources/mapper/AlarmRecordMapper.xml | 2 + .../main/resources/mapper/TerminalMapper.xml | 24 ++- 8 files changed, 435 insertions(+), 52 deletions(-) create mode 100644 web-adapter/src/main/java/com/zcloud/personnel/positioning/plan/FindsTerminalCardSyncJob.java create mode 100644 web-app/src/main/java/com/zcloud/personnel/positioning/command/query/FenceLocationMatcher.java create mode 100644 web-app/src/main/java/com/zcloud/personnel/positioning/command/query/TerminalCardFenceLocationEnricher.java diff --git a/web-adapter/src/main/java/com/zcloud/personnel/positioning/plan/FindsTerminalCardSyncJob.java b/web-adapter/src/main/java/com/zcloud/personnel/positioning/plan/FindsTerminalCardSyncJob.java new file mode 100644 index 0000000..bf66c35 --- /dev/null +++ b/web-adapter/src/main/java/com/zcloud/personnel/positioning/plan/FindsTerminalCardSyncJob.java @@ -0,0 +1,38 @@ +package com.zcloud.personnel.positioning.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.context.XxlJobHelper; +import com.xxl.job.core.handler.annotation.XxlJob; +import com.zcloud.personnel.positioning.command.TerminalCardSyncExe; +import com.zcloud.personnel.positioning.dto.clientobject.TerminalCardSyncCO; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +/** 每五分钟同步一次 FindS 定位卡及其当前绑定人员快照。 */ +@Component +@RequiredArgsConstructor +public class FindsTerminalCardSyncJob implements Job { + private final TerminalCardSyncExe terminalCardSyncExe; + + @Override + @JobRegister(cron = "0 */5 * * * ?", jobDesc = "FindS定位卡快照同步", + author = "system", triggerStatus = 1) + @XxlJob("com.zcloud.personnel.positioning.plan.FindsTerminalCardSyncJob") + public ReturnT execute(String param) { + try { + TerminalCardSyncCO result = terminalCardSyncExe.syncCards(null); + String message = "FindS定位卡同步完成,拉取=" + + result.getPulledCount() + ",新增=" + result.getInsertedCount() + + ",更新=" + result.getUpdatedCount() + ",跳过=" + result.getSkippedCount() + + ",绑定=" + result.getBindingCount(); + XxlJobHelper.log(message); + return new ReturnT<>(ReturnT.SUCCESS_CODE, message); + } catch (Exception e) { + String message = "FindS定位卡同步失败:" + e.getMessage(); + XxlJobHelper.log(message); + return new ReturnT<>(ReturnT.FAIL_CODE, message); + } + } +} diff --git a/web-app/src/main/java/com/zcloud/personnel/positioning/command/TerminalCardSyncExe.java b/web-app/src/main/java/com/zcloud/personnel/positioning/command/TerminalCardSyncExe.java index 951cad9..a70b57d 100644 --- a/web-app/src/main/java/com/zcloud/personnel/positioning/command/TerminalCardSyncExe.java +++ b/web-app/src/main/java/com/zcloud/personnel/positioning/command/TerminalCardSyncExe.java @@ -397,13 +397,14 @@ public class TerminalCardSyncExe { return null; } PersonSnapshot person = new PersonSnapshot(); - person.findsTrackId = longValue(firstPresentAny(child(node, "id"), child(node, "trackId"), + // FindS终端列表的trackNo是人员工号,后续用于关联personnel_match.finds_staff_no。 + person.findsTrackId = longValue(firstPresentAny(child(node, "trackId"), child(node, "id"), child(node, "staffId"), child(node, "findsTrackId"))); person.idCardNo = textValue(firstPresentAny(child(node, "idCardNo"), child(node, "identityNo"), child(node, "certificateNo"), child(node, "cardNo"))); - person.staffNo = textValue(firstPresentAny(child(node, "staffNo"), child(node, "code"), + person.staffNo = textValue(firstPresentAny(child(node, "trackNo"), child(node, "staffNo"), child(node, "code"), child(node, "employeeNo"), child(node, "workNo"))); - person.staffName = textValue(firstPresentAny(child(node, "staffName"), child(node, "name"), + person.staffName = textValue(firstPresentAny(child(node, "trackName"), child(node, "staffName"), child(node, "name"), child(node, "realName"), child(node, "userName"))); person.mobileNo = textValue(firstPresentAny(child(node, "mobileNo"), child(node, "phone"), child(node, "phoneNo"))); diff --git a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/AlarmRecordQueryExe.java b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/AlarmRecordQueryExe.java index 471dd01..b6f0081 100644 --- a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/AlarmRecordQueryExe.java +++ b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/AlarmRecordQueryExe.java @@ -26,7 +26,6 @@ import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; @@ -129,20 +128,19 @@ public class AlarmRecordQueryExe { } public List listAlarmTypeOptions() { - return Arrays.asList( - new OptionCO("禁入围栏告警", "CROSS_IN"), - new OptionCO("离开围栏告警", "CROSS_OUT"), - new OptionCO("超时停留告警", "RETENTION"), - new OptionCO("脱岗告警", "LACKED"), - new OptionCO("停留告警", "STAY"), - new OptionCO("聚集告警", "CLUSTER"), - new OptionCO("超员告警", "CROWDED"), - new OptionCO("静止告警", "STATIC"), - new OptionCO("超速告警", "OVER_SPEED"), - new OptionCO("碰撞告警", "CRASH"), - new OptionCO("低电量告警", "LOW_BATTERY"), - new OptionCO("SOS告警", "SOS") - ); + SSOUser user = AuthContext.getCurrentUser(); + Long corpinfoId = user == null ? null : user.getCompanyId(); + // 只返回当前企业告警记录中实际出现过的编码,名称使用FindS确定的编码映射。 + List rows = alarmRecordRepository.listBiAlarmType(corpinfoId); + if (rows == null || rows.isEmpty()) { + return Collections.emptyList(); + } + return rows.stream() + .filter(Objects::nonNull) + .filter(row -> StringUtils.hasText(row.getAlarmCode())) + .map(row -> new OptionCO(alarmNameOf(row.getAlarmCode()), + row.getAlarmCode())) + .collect(Collectors.toList()); } private OptionCO mapCompanyOption(CompanyOptionDO dataObject) { @@ -234,40 +232,41 @@ public class AlarmRecordQueryExe { return null; } String code = value.trim(); - if ("进入围栏告警".equals(code) || "禁入围栏告警".equals(code)) { + if ("闯入报警".equals(code) || "进入围栏告警".equals(code) || "禁入围栏告警".equals(code)) { return "CROSS_IN"; } - if ("离开围栏".equals(code) || "离开围栏告警".equals(code) || "区域越界告警".equals(code)) { + if ("离开报警".equals(code) || "离开围栏".equals(code) + || "离开围栏告警".equals(code) || "区域越界告警".equals(code)) { return "CROSS_OUT"; } - if ("超时停留告警".equals(code)) { + if ("滞留报警".equals(code) || "超时停留告警".equals(code)) { return "RETENTION"; } - if ("脱岗告警".equals(code)) { + if ("缺员报警".equals(code) || "脱岗告警".equals(code)) { return "LACKED"; } - if ("停留告警".equals(code)) { + if ("停留报警".equals(code) || "停留告警".equals(code)) { return "STAY"; } - if ("聚集告警".equals(code)) { + if ("聚集报警".equals(code) || "聚集告警".equals(code)) { return "CLUSTER"; } - if ("超员告警".equals(code)) { + if ("超员报警".equals(code) || "超员告警".equals(code)) { return "CROWDED"; } - if ("静止告警".equals(code)) { + if ("静止报警".equals(code) || "静止告警".equals(code)) { return "STATIC"; } - if ("超速告警".equals(code)) { + if ("超速报警".equals(code) || "超速告警".equals(code)) { return "OVER_SPEED"; } - if ("碰撞告警".equals(code)) { + if ("碰撞报警".equals(code) || "碰撞告警".equals(code)) { return "CRASH"; } - if ("低电量告警".equals(code)) { + if ("低电量报警".equals(code) || "低电量告警".equals(code)) { return "LOW_BATTERY"; } - if ("SOS告警".equalsIgnoreCase(code)) { + if ("SOS报警".equalsIgnoreCase(code) || "SOS告警".equalsIgnoreCase(code)) { return "SOS"; } return code; @@ -275,50 +274,46 @@ public class AlarmRecordQueryExe { private String alarmNameOf(String alarmCode) { if ("CROSS_IN".equals(alarmCode)) { - return "禁入围栏告警"; + return "闯入报警"; } if ("CROSS_OUT".equals(alarmCode)) { - return "离开围栏告警"; + return "离开报警"; } if ("RETENTION".equals(alarmCode)) { - return "超时停留告警"; + return "滞留报警"; } if ("LACKED".equals(alarmCode)) { - return "脱岗告警"; + return "缺员报警"; } if ("STAY".equals(alarmCode)) { - return "停留告警"; + return "停留报警"; } if ("CLUSTER".equals(alarmCode)) { - return "聚集告警"; + return "聚集报警"; } if ("CROWDED".equals(alarmCode)) { - return "超员告警"; + return "超员报警"; } if ("STATIC".equals(alarmCode)) { - return "静止告警"; + return "静止报警"; } if ("OVER_SPEED".equals(alarmCode)) { - return "超速告警"; + return "超速报警"; } if ("CRASH".equals(alarmCode)) { - return "碰撞告警"; + return "碰撞报警"; } if ("LOW_BATTERY".equals(alarmCode)) { - return "低电量告警"; + return "低电量报警"; } if ("SOS".equals(alarmCode)) { - return "SOS告警"; + return "SOS报警"; } return alarmCode; } private String alarmTypeNameOf(String alarmCode, String alarmName) { - String name = defaultIfBlank(alarmName, alarmNameOf(alarmCode)); - if (name != null && name.contains("-")) { - return name.substring(name.lastIndexOf('-') + 1); - } - return name; + return alarmNameOf(alarmCode); } private String defaultIfBlank(String value, String defaultValue) { diff --git a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/FenceLocationMatcher.java b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/FenceLocationMatcher.java new file mode 100644 index 0000000..6dc6da1 --- /dev/null +++ b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/FenceLocationMatcher.java @@ -0,0 +1,133 @@ +package com.zcloud.personnel.positioning.command.query; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.zcloud.personnel.positioning.persistence.dataobject.FenceDO; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** 根据终端坐标匹配本地电子围栏,不依赖 area 表的企业关联数据。 */ +@Component +@RequiredArgsConstructor +class FenceLocationMatcher { + private final ObjectMapper objectMapper; + + List prepare(List fences) { + if (fences == null || fences.isEmpty()) { + return Collections.emptyList(); + } + List result = new ArrayList<>(fences.size()); + for (FenceDO fence : fences) { + List points = parsePolygon(fence.getPolygonJson()); + if (points.size() >= 3) { + result.add(new FencePolygon(fence, points, polygonArea(points))); + } + } + // 重叠时优先显示范围更精确的小围栏,避免依赖数据库返回顺序。 + result.sort(Comparator.comparing(FencePolygon::getArea)); + return result; + } + + FenceDO match(BigDecimal lon, BigDecimal lat, List fences) { + if (lon == null || lat == null || fences == null) { + return null; + } + for (FencePolygon fence : fences) { + if (contains(lon, lat, fence.points)) { + return fence.fence; + } + } + return null; + } + + String displayName(FenceDO fence) { + return fence == null ? null : fence.getFenceName(); + } + + private List parsePolygon(String polygonJson) { + if (!StringUtils.hasText(polygonJson)) { + return Collections.emptyList(); + } + try { + List points = new ArrayList<>(); + collectPoints(objectMapper.readTree(polygonJson), points); + return points; + } catch (IOException ignored) { + return Collections.emptyList(); + } + } + + private void collectPoints(JsonNode node, List points) { + if (node == null || node.isNull() || node.isMissingNode()) { + return; + } + if (node.isArray() && node.size() >= 2 && node.get(0).isNumber() && node.get(1).isNumber()) { + points.add(new BigDecimal[]{node.get(0).decimalValue(), node.get(1).decimalValue()}); + return; + } + if (node.isContainerNode()) { + node.elements().forEachRemaining(child -> collectPoints(child, points)); + } + } + + private BigDecimal polygonArea(List points) { + double area = 0D; + for (int i = 0, j = points.size() - 1; i < points.size(); j = i++) { + area += points.get(j)[0].doubleValue() * points.get(i)[1].doubleValue() + - points.get(i)[0].doubleValue() * points.get(j)[1].doubleValue(); + } + return BigDecimal.valueOf(Math.abs(area / 2D)); + } + + private boolean contains(BigDecimal lon, BigDecimal lat, List points) { + boolean inside = false; + double x = lon.doubleValue(); + double y = lat.doubleValue(); + for (int i = 0, j = points.size() - 1; i < points.size(); j = i++) { + double xi = points.get(i)[0].doubleValue(); + double yi = points.get(i)[1].doubleValue(); + double xj = points.get(j)[0].doubleValue(); + double yj = points.get(j)[1].doubleValue(); + if (onSegment(x, y, xi, yi, xj, yj)) { + return true; + } + if ((yi > y) != (yj > y) && x < (xj - xi) * (y - yi) / (yj - yi) + xi) { + inside = !inside; + } + } + return inside; + } + + private boolean onSegment(double x, double y, double x1, double y1, double x2, double y2) { + double cross = (x - x1) * (y2 - y1) - (y - y1) * (x2 - x1); + if (Math.abs(cross) > 1e-10) { + return false; + } + return x >= Math.min(x1, x2) && x <= Math.max(x1, x2) + && y >= Math.min(y1, y2) && y <= Math.max(y1, y2); + } + + static class FencePolygon { + private final FenceDO fence; + private final List points; + private final BigDecimal area; + + private FencePolygon(FenceDO fence, List points, BigDecimal area) { + this.fence = fence; + this.points = points; + this.area = area; + } + + private BigDecimal getArea() { + return area; + } + } +} diff --git a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/TerminalCardFenceLocationEnricher.java b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/TerminalCardFenceLocationEnricher.java new file mode 100644 index 0000000..0fa547e --- /dev/null +++ b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/TerminalCardFenceLocationEnricher.java @@ -0,0 +1,191 @@ +package com.zcloud.personnel.positioning.command.query; + +import com.alibaba.cola.exception.BizException; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.fasterxml.jackson.databind.JsonNode; +import com.zcloud.personnel.positioning.dto.clientobject.TerminalCardCO; +import com.zcloud.personnel.positioning.integration.finds.FindsOpenApiClient; +import com.zcloud.personnel.positioning.persistence.dataobject.FenceDO; +import com.zcloud.personnel.positioning.persistence.repository.FenceRepository; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import java.math.BigDecimal; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** 使用 FindS 最新坐标补全定位卡本次查询结果中的定位时间和围栏名称。 */ +@Component +@RequiredArgsConstructor +class TerminalCardFenceLocationEnricher { + private static final Logger LOGGER = LoggerFactory.getLogger(TerminalCardFenceLocationEnricher.class); + private static final String POINT_LOCATE_API = "finds.point.locate"; + private static final int BATCH_SIZE = 100; + + private final FindsOpenApiClient findsOpenApiClient; + private final FenceRepository fenceRepository; + private final FenceLocationMatcher fenceLocationMatcher; + + void enrich(List cards) { + Set terminalNos = terminalNos(cards); + if (terminalNos.isEmpty()) { + return; + } + final Map locations; + try { + locations = locate(terminalNos); + } catch (RuntimeException e) { + LOGGER.warn("从FindS补全定位卡围栏位置失败:{}", e.getMessage()); + return; + } + List fences = fenceLocationMatcher.prepare(loadActiveFences()); + for (TerminalCardCO card : cards) { + if (card == null || !StringUtils.hasText(card.getTerminalNo())) { + continue; + } + JsonNode location = locations.get(card.getTerminalNo().trim()); + LocalDateTime time = locationTime(location); + if (time != null) { + card.setLastLocationTime(time); + } + BigDecimal lon = decimal(first(location, "lon", "lng", "longitude")); + BigDecimal lat = decimal(first(location, "lat", "latitude")); + if (lon == null || lat == null) { + continue; + } + FenceDO fence = fenceLocationMatcher.match(lon, lat, fences); + card.setLastLocationName(fenceLocationMatcher.displayName(fence)); + } + } + + private List loadActiveFences() { + LocalDateTime now = LocalDateTime.now(); + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.and(value -> value.isNull("delete_enum").or().eq("delete_enum", "FALSE")); + wrapper.eq("status", "ENABLED"); + wrapper.in("fence_scope", "ELECTRONIC", "WORK_AREA"); + wrapper.and(value -> value.isNull("effect_start").or().le("effect_start", now)); + wrapper.and(value -> value.isNull("effect_end").or().ge("effect_end", now)); + wrapper.isNotNull("polygon_json"); + return fenceRepository.list(wrapper); + } + + private Set terminalNos(List cards) { + if (cards == null || cards.isEmpty()) { + return Collections.emptySet(); + } + Set result = new LinkedHashSet<>(); + for (TerminalCardCO card : cards) { + if (card != null && StringUtils.hasText(card.getTerminalNo())) { + result.add(card.getTerminalNo().trim()); + } + } + return result; + } + + private Map locate(Set terminalNos) { + Map result = new LinkedHashMap<>(); + List batch = new ArrayList<>(BATCH_SIZE); + for (String terminalNo : terminalNos) { + batch.add(terminalNo); + if (batch.size() == BATCH_SIZE) { + locateBatch(batch, result); + batch.clear(); + } + } + if (!batch.isEmpty()) { + locateBatch(batch, result); + } + return result; + } + + private void locateBatch(List terminalNos, Map result) { + JsonNode root = findsOpenApiClient.postFailFast(POINT_LOCATE_API, + Collections.singletonMap("terminalNoList", new ArrayList<>(terminalNos))); + int code = root == null ? -1 : root.path("code").asInt(-1); + if (code != 0 && code != 200) { + throw new BizException("FindS定位查询失败,响应码=" + code); + } + JsonNode rows = rows(root.path("data")); + if (rows == null) { + return; + } + for (JsonNode row : rows) { + String terminalNo = text(first(row, "terminalNo")); + if (!StringUtils.hasText(terminalNo) && terminalNos.size() == 1) { + terminalNo = terminalNos.get(0); + } + if (StringUtils.hasText(terminalNo)) { + result.put(terminalNo.trim(), row); + } + } + } + + private JsonNode rows(JsonNode data) { + if (data == null || data.isNull() || data.isMissingNode()) { + return null; + } + if (data.isArray()) { + return data; + } + for (String field : new String[]{"data", "rows", "list", "records"}) { + JsonNode value = data.get(field); + if (value != null && value.isArray()) { + return value; + } + } + return null; + } + + private JsonNode first(JsonNode node, String... fields) { + if (node == null || node.isNull() || node.isMissingNode()) { + return null; + } + for (String field : fields) { + JsonNode value = node.get(field); + if (value != null && !value.isNull() && !value.isMissingNode()) { + return value; + } + } + return null; + } + + private String text(JsonNode node) { + return node == null || !node.isValueNode() ? null : node.asText(); + } + + private BigDecimal decimal(JsonNode node) { + try { + return node == null || !node.isValueNode() ? null : new BigDecimal(node.asText()); + } catch (NumberFormatException ignored) { + return null; + } + } + + private LocalDateTime locationTime(JsonNode location) { + JsonNode node = first(location, "gt", "time", "locateTime", "locationTime", "timestamp"); + if (node == null || !node.isValueNode()) { + return null; + } + try { + long epoch = Long.parseLong(node.asText()); + if (epoch < 100000000000L) { + epoch *= 1000L; + } + return LocalDateTime.ofInstant(Instant.ofEpochMilli(epoch), ZoneId.systemDefault()); + } catch (NumberFormatException ignored) { + return null; + } + } +} diff --git a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/TerminalCardQueryExe.java b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/TerminalCardQueryExe.java index 50a72c9..e71d123 100644 --- a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/TerminalCardQueryExe.java +++ b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/TerminalCardQueryExe.java @@ -31,6 +31,7 @@ import java.util.Map; public class TerminalCardQueryExe { private final TerminalRepository terminalRepository; private final TerminalCardCoConvertor terminalCardCoConvertor; + private final TerminalCardFenceLocationEnricher fenceLocationEnricher; public PageResponse listCompanyPage(TerminalCardCompanyPageQry qry) { TerminalCardCompanyPageQry query = qry == null ? new TerminalCardCompanyPageQry() : qry; @@ -54,7 +55,9 @@ public class TerminalCardQueryExe { "eqCorpinfoName", "likeCorpinfoName", "orgCode", "orgName", "deviceStatus"); applyCurrentCompany(params, "eqCorpinfoId", "eqCorpinfoName", "likeCorpinfoName"); PageResponse page = terminalRepository.listCards(params); - return PageResponse.of(terminalCardCoConvertor.convertCards(page.getData()), + List rows = terminalCardCoConvertor.convertCards(page.getData()); + fenceLocationEnricher.enrich(rows); + return PageResponse.of(rows, page.getTotalCount(), page.getPageSize(), page.getPageIndex()); } @@ -65,7 +68,9 @@ public class TerminalCardQueryExe { Map params = new HashMap<>(); params.put("id", id); TerminalCardDetailDO dataObject = terminalRepository.getCardDetail(params); - return terminalCardCoConvertor.convertCard(dataObject); + TerminalCardCO card = terminalCardCoConvertor.convertCard(dataObject); + fenceLocationEnricher.enrich(card == null ? null : java.util.Collections.singletonList(card)); + return card; } public PageResponse listPersonOptions(TerminalCardPersonOptionQry qry) { diff --git a/web-infrastructure/src/main/resources/mapper/AlarmRecordMapper.xml b/web-infrastructure/src/main/resources/mapper/AlarmRecordMapper.xml index f416129..3973ebe 100644 --- a/web-infrastructure/src/main/resources/mapper/AlarmRecordMapper.xml +++ b/web-infrastructure/src/main/resources/mapper/AlarmRecordMapper.xml @@ -85,6 +85,8 @@ WHERE (ar.delete_enum IS NULL OR ar.delete_enum = 'FALSE') AND IS NOT NULL + AND ar.alarm_code IS NOT NULL + AND TRIM(ar.alarm_code) != '' AND = CAST(#{corpinfoId} AS CHAR) diff --git a/web-infrastructure/src/main/resources/mapper/TerminalMapper.xml b/web-infrastructure/src/main/resources/mapper/TerminalMapper.xml index acd36c0..be62ffd 100644 --- a/web-infrastructure/src/main/resources/mapper/TerminalMapper.xml +++ b/web-infrastructure/src/main/resources/mapper/TerminalMapper.xml @@ -61,7 +61,7 @@ t.org_code, t.org_name, t.enable, t.device_status, COALESCE(tb.id_card_no, t.bind_id_card_no) AS active_id_card_no, COALESCE(tb.staff_no_snapshot, u.username) AS staff_no, - COALESCE(u.name, tb.staff_name_snapshot) AS staff_name, + COALESCE(mu.name, u.name, tb.staff_name_snapshot) AS staff_name, u.phone AS staff_phone, tb.bind_status, t.finds_terminal_id, t.last_location_time, t.last_location_name, t.last_sync_time, t.sync_status, t.remarks @@ -81,6 +81,21 @@ LEFT JOIN user_scope_v u ON u.user_id_card = COALESCE(tb.id_card_no, t.bind_id_card_no) AND (u.delete_enum IS NULL OR u.delete_enum = 'FALSE') + + LEFT JOIN personnel_match pm ON pm.id = ( + SELECT pm2.id + FROM personnel_match pm2 + WHERE pm2.source_config_code = 'FINDS' + AND pm2.finds_staff_no = tb.staff_no_snapshot + AND pm2.match_status = 'MATCHED' + AND pm2.local_user_id IS NOT NULL + AND (pm2.delete_enum IS NULL OR pm2.delete_enum = 'FALSE') + ORDER BY pm2.last_sync_time DESC, pm2.id DESC + LIMIT 1 + ) + LEFT JOIN user_scope_v mu + ON mu.id = pm.local_user_id + AND (mu.delete_enum IS NULL OR mu.delete_enum = 'FALSE') WHERE (t.delete_enum IS NULL OR t.delete_enum = 'FALSE') AND t.device_type = 'CARD' @@ -92,11 +107,14 @@ AND t.terminal_no LIKE CONCAT('%', #{params.terminalNo}, '%') - AND (u.name LIKE CONCAT('%', #{params.staffName}, '%') + AND (mu.name LIKE CONCAT('%', #{params.staffName}, '%') + OR u.name LIKE CONCAT('%', #{params.staffName}, '%') OR tb.staff_name_snapshot LIKE CONCAT('%', #{params.staffName}, '%')) - AND (u.username = #{params.staffNo} OR tb.staff_no_snapshot = #{params.staffNo}) + AND (mu.username = #{params.staffNo} + OR u.username = #{params.staffNo} + OR tb.staff_no_snapshot = #{params.staffNo}) AND COALESCE(tb.id_card_no, t.bind_id_card_no) = #{params.idCardNo}