diff --git a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/PositionAreaMatcher.java b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/PositionAreaMatcher.java new file mode 100644 index 0000000..f023b34 --- /dev/null +++ b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/PositionAreaMatcher.java @@ -0,0 +1,122 @@ +package com.zcloud.personnel.positioning.command.query; + +import com.alibaba.cola.exception.BizException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.zcloud.personnel.positioning.persistence.dataobject.AreaDO; +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.List; + +/** + * 使用实时定位坐标匹配本平台区域。 + */ +@Component +@RequiredArgsConstructor +class PositionAreaMatcher { + private final ObjectMapper objectMapper; + + /** + * 预解析区域多边形,避免同一次人员查询中为每个人重复解析 JSON。 + */ + List prepare(List areas) { + if (areas == null || areas.isEmpty()) { + return Collections.emptyList(); + } + List result = new ArrayList<>(areas.size()); + for (AreaDO area : areas) { + result.add(new AreaPolygon(area, parsePolygon(area.getPolygonJson()))); + } + return result; + } + + /** + * 使用实时经纬度匹配区域;区域重叠时按区域查询顺序返回第一个命中项。 + */ + AreaDO match(BigDecimal lon, BigDecimal lat, List areas) { + if (lon == null || lat == null || areas == null) { + return null; + } + for (AreaPolygon area : areas) { + if (isPointInPolygon(lon, lat, area.points)) { + return area.area; + } + } + return null; + } + + /** + * 将区域多边形的多层 JSON 结构展开为经纬度点集合,兼容 FindS 的嵌套坐标格式。 + */ + private List parsePolygon(String polygonJson) { + List points = new ArrayList<>(); + if (!StringUtils.hasText(polygonJson)) { + return points; + } + try { + collectPoints(objectMapper.readTree(polygonJson), points); + } catch (IOException e) { + throw new BizException("区域边界坐标JSON格式错误"); + } + return points; + } + + private void collectPoints(JsonNode node, List points) { + if (node == null || node.isNull() || node.isMissingNode()) { + return; + } + if (node.isObject()) { + node.elements().forEachRemaining(child -> collectPoints(child, points)); + return; + } + if (!node.isArray()) { + return; + } + if (node.size() >= 2 && node.get(0).isNumber() && node.get(1).isNumber()) { + points.add(new BigDecimal[]{node.get(0).decimalValue(), node.get(1).decimalValue()}); + return; + } + for (JsonNode child : node) { + collectPoints(child, points); + } + } + + /** + * 使用射线法判断实时定位点是否位于区域多边形内。 + */ + private boolean isPointInPolygon(BigDecimal lon, BigDecimal lat, List points) { + if (points == null || points.size() < 3) { + return false; + } + 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 ((yi > y) != (yj > y) + && x < (xj - xi) * (y - yi) / (yj - yi) + xi) { + inside = !inside; + } + } + return inside; + } + + static class AreaPolygon { + private final AreaDO area; + private final List points; + + private AreaPolygon(AreaDO area, List points) { + this.area = area; + this.points = points; + } + } +} diff --git a/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/dataobject/PersonnelLocationAreaUpdateDO.java b/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/dataobject/PersonnelLocationAreaUpdateDO.java new file mode 100644 index 0000000..dc542c2 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/dataobject/PersonnelLocationAreaUpdateDO.java @@ -0,0 +1,15 @@ +package com.zcloud.personnel.positioning.persistence.dataobject; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * 人员当前定位区域批量更新参数。 + */ +@Data +@AllArgsConstructor +public class PersonnelLocationAreaUpdateDO { + private String findsStaffNo; + private Long locationAreaId; + private String locationAreaName; +}