完善定位卡位置与告警类型查询
parent
03fa6f56c7
commit
de78e0c26a
|
|
@ -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<String> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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")));
|
||||
|
|
|
|||
|
|
@ -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<OptionCO> 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<AlarmTypeStatDO> 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) {
|
||||
|
|
|
|||
|
|
@ -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<FencePolygon> prepare(List<FenceDO> fences) {
|
||||
if (fences == null || fences.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<FencePolygon> result = new ArrayList<>(fences.size());
|
||||
for (FenceDO fence : fences) {
|
||||
List<BigDecimal[]> 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<FencePolygon> 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<BigDecimal[]> parsePolygon(String polygonJson) {
|
||||
if (!StringUtils.hasText(polygonJson)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
List<BigDecimal[]> points = new ArrayList<>();
|
||||
collectPoints(objectMapper.readTree(polygonJson), points);
|
||||
return points;
|
||||
} catch (IOException ignored) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
private void collectPoints(JsonNode node, List<BigDecimal[]> 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<BigDecimal[]> 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<BigDecimal[]> 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<BigDecimal[]> points;
|
||||
private final BigDecimal area;
|
||||
|
||||
private FencePolygon(FenceDO fence, List<BigDecimal[]> points, BigDecimal area) {
|
||||
this.fence = fence;
|
||||
this.points = points;
|
||||
this.area = area;
|
||||
}
|
||||
|
||||
private BigDecimal getArea() {
|
||||
return area;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<TerminalCardCO> cards) {
|
||||
Set<String> terminalNos = terminalNos(cards);
|
||||
if (terminalNos.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
final Map<String, JsonNode> locations;
|
||||
try {
|
||||
locations = locate(terminalNos);
|
||||
} catch (RuntimeException e) {
|
||||
LOGGER.warn("从FindS补全定位卡围栏位置失败:{}", e.getMessage());
|
||||
return;
|
||||
}
|
||||
List<FenceLocationMatcher.FencePolygon> 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<FenceDO> loadActiveFences() {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
QueryWrapper<FenceDO> 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<String> terminalNos(List<TerminalCardCO> cards) {
|
||||
if (cards == null || cards.isEmpty()) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
Set<String> result = new LinkedHashSet<>();
|
||||
for (TerminalCardCO card : cards) {
|
||||
if (card != null && StringUtils.hasText(card.getTerminalNo())) {
|
||||
result.add(card.getTerminalNo().trim());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, JsonNode> locate(Set<String> terminalNos) {
|
||||
Map<String, JsonNode> result = new LinkedHashMap<>();
|
||||
List<String> 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<String> terminalNos, Map<String, JsonNode> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<TerminalCardCompanyCO> 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<TerminalCardDetailDO> page = terminalRepository.listCards(params);
|
||||
return PageResponse.of(terminalCardCoConvertor.convertCards(page.getData()),
|
||||
List<TerminalCardCO> 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<String, Object> 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<TerminalCardPersonOptionCO> listPersonOptions(TerminalCardPersonOptionQry qry) {
|
||||
|
|
|
|||
|
|
@ -85,6 +85,8 @@
|
|||
<include refid="mappedCompanyJoin"/>
|
||||
WHERE (ar.delete_enum IS NULL OR ar.delete_enum = 'FALSE')
|
||||
AND <include refid="mappedCompanyId"/> IS NOT NULL
|
||||
AND ar.alarm_code IS NOT NULL
|
||||
AND TRIM(ar.alarm_code) != ''
|
||||
<if test="corpinfoId != null and corpinfoId != ''">
|
||||
AND <include refid="mappedCompanyId"/> = CAST(#{corpinfoId} AS CHAR)
|
||||
</if>
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
<!-- FindS终端的trackNo对应personnel_match.finds_staff_no,优先展示已匹配的本平台人员姓名。 -->
|
||||
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')
|
||||
<include refid="mappedCompanyJoins"/>
|
||||
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}, '%')
|
||||
</if>
|
||||
<if test="params.staffName != null and params.staffName != ''">
|
||||
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}, '%'))
|
||||
</if>
|
||||
<if test="params.staffNo != null and params.staffNo != ''">
|
||||
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})
|
||||
</if>
|
||||
<if test="params.idCardNo != null and params.idCardNo != ''">
|
||||
AND COALESCE(tb.id_card_no, t.bind_id_card_no) = #{params.idCardNo}
|
||||
|
|
|
|||
Loading…
Reference in New Issue