diff --git a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/AreaQueryExe.java b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/AreaQueryExe.java index acd695d..c7b9a00 100644 --- a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/AreaQueryExe.java +++ b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/AreaQueryExe.java @@ -8,26 +8,23 @@ import com.zcloud.personnel.positioning.dto.clientobject.AreaPersonStatCO; import com.zcloud.personnel.positioning.dto.clientobject.BiCompanyOnlinePersonCO; import com.zcloud.personnel.positioning.dto.clientobject.OptionCO; import com.zcloud.personnel.positioning.persistence.dataobject.AreaDO; +import com.zcloud.personnel.positioning.persistence.dataobject.ClosedAreaDO; import com.zcloud.personnel.positioning.persistence.repository.AreaRepository; import com.alibaba.cola.dto.PageResponse; import com.alibaba.cola.exception.BizException; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import com.jjb.saas.framework.auth.model.SSOUser; import com.jjb.saas.framework.auth.utils.AuthContext; import lombok.AllArgsConstructor; 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.List; import java.util.Map; /** - * web-app + * 区域查询执行器。 * * @Author fangjiakai * @Date 2026-07-07 @@ -38,7 +35,7 @@ public class AreaQueryExe { private final AreaRepository areaRepository; private final AreaCoConvertor areaCoConvertor; private final BiPersonLocationProvider biPersonLocationProvider; - private final ObjectMapper objectMapper; + private final ClosedAreaMatcher closedAreaMatcher; public PageResponse execute(AreaPageQry qry) { AreaPageQry query = qry == null ? new AreaPageQry() : qry; @@ -80,7 +77,7 @@ public class AreaQueryExe { throw new BizException("企业id不能为空"); } Long companyId = corpinfoId; - List areas = listCompanyAreas(companyId); + List areas = listCompanyClosedAreas(companyId); List personList = biPersonLocationProvider.listCompanyOnlinePersons(companyId, null, null); for (BiCompanyOnlinePersonCO person : personList) { @@ -101,16 +98,12 @@ public class AreaQueryExe { return areaCoConvertor.converDOToCO(dataObject); } - private List listCompanyAreas(Long corpinfoId) { - QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.and(wrapper -> wrapper.isNull("delete_enum").or().eq("delete_enum", "FALSE")); - queryWrapper.and(wrapper -> wrapper.isNull("status").or().eq("status", "ENABLED")); - queryWrapper.eq("corpinfo_id", corpinfoId); - queryWrapper.orderByAsc("sort_no").orderByAsc("area_name").orderByDesc("create_time"); - List rows = areaRepository.list(queryWrapper); - List result = new ArrayList<>(rows.size()); - for (AreaDO row : rows) { - result.add(new AreaStatAccumulator(row, parsePolygon(row.getPolygonJson()))); + private List listCompanyClosedAreas(Long corpinfoId) { + List rows = areaRepository.listClosedAreas(corpinfoId); + List polygons = closedAreaMatcher.prepare(rows); + List result = new ArrayList<>(polygons.size()); + for (ClosedAreaMatcher.AreaPolygon polygon : polygons) { + result.add(new AreaStatAccumulator(polygon)); } return result; } @@ -120,66 +113,13 @@ public class AreaQueryExe { return; } for (AreaStatAccumulator area : areas) { - if (pointInPolygon(person.getLon(), person.getLat(), area.points)) { + if (closedAreaMatcher.contains(person.getLon(), person.getLat(), area.polygon)) { area.add(person); return; } } } - 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 pointInPolygon(BigDecimal lon, BigDecimal lat, List points) { - if (lon == null || lat == null || 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; - } - private void fillCorpinfo(Map params) { if (params.get("eqCorpinfoId") != null || StringUtils.hasText((String) params.get("likeCorpinfoName"))) { return; @@ -202,14 +142,12 @@ public class AreaQueryExe { } private static class AreaStatAccumulator { - private final AreaDO area; - private final List points; + private final ClosedAreaMatcher.AreaPolygon polygon; private int unitCount; private int relatedCount; - private AreaStatAccumulator(AreaDO area, List points) { - this.area = area; - this.points = points; + private AreaStatAccumulator(ClosedAreaMatcher.AreaPolygon polygon) { + this.polygon = polygon; } private void add(BiCompanyOnlinePersonCO person) { @@ -221,16 +159,16 @@ public class AreaQueryExe { } private AreaPersonStatCO toCO() { + ClosedAreaDO area = polygon.getArea(); AreaPersonStatCO co = new AreaPersonStatCO(); co.setAreaId(area.getId()); - co.setAreaCode(area.getAreaCode()); - co.setAreaName(area.getAreaName()); - co.setAreaType(area.getAreaType()); - co.setCorpinfoId(area.getCorpinfoId()); - co.setCorpinfoName(area.getCorpinfoName()); - co.setCenterLon(area.getCenterLon()); - co.setCenterLat(area.getCenterLat()); - co.setPolygonJson(area.getPolygonJson()); + co.setAreaName(area.getClosedAreaName()); + co.setAreaType(area.getClosedAreaType()); + co.setCorpinfoId(area.getJurisdictionalCorpId()); + co.setCorpinfoName(area.getJurisdictionalCorpName()); + co.setCenterLon(polygon.center(0)); + co.setCenterLat(polygon.center(1)); + co.setPolygonJson(area.getLocation()); co.setUnitCount(unitCount); co.setRelatedCount(relatedCount); co.setTotalCount(unitCount + relatedCount); diff --git a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/BiPersonLocationProvider.java b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/BiPersonLocationProvider.java index bc9df51..3ae26c8 100644 --- a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/BiPersonLocationProvider.java +++ b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/BiPersonLocationProvider.java @@ -35,24 +35,37 @@ public class BiPersonLocationProvider { public List listCompanyOnlinePersons(Long corpinfoId, String corpinfoName, String corpCode) { + return listCompanyOnlinePersons(corpinfoId, corpinfoName, true); + } + + List listCompanyOnlinePersonsWithoutArea(Long corpinfoId, + String corpinfoName) { + return listCompanyOnlinePersons(corpinfoId, corpinfoName, false); + } + + private List listCompanyOnlinePersons(Long corpinfoId, + String corpinfoName, + boolean includeArea) { if (corpinfoId == null) { return Collections.emptyList(); } Map companies = new LinkedHashMap<>(); companies.put(corpinfoId, corpinfoName); - return listCompanyPersons(companies, true).getOrDefault(corpinfoId, Collections.emptyList()); + return listCompanyPersons(companies, true, includeArea) + .getOrDefault(corpinfoId, Collections.emptyList()); } Map> listCompanyPersonStatuses(Map companies) { - return listCompanyPersons(companies, false); + return listCompanyPersons(companies, false, false); } Map> listCompanyOnlinePersons(Map companies) { - return listCompanyPersons(companies, true); + return listCompanyPersons(companies, true, true); } private Map> listCompanyPersons(Map companies, - boolean includePosition) { + boolean includePosition, + boolean includeArea) { Map> result = emptyCompanyPersonMap(companies); if (result.isEmpty()) { return result; @@ -67,7 +80,7 @@ public class BiPersonLocationProvider { return result; } Map positionMap = includePosition - ? loadPositionMap(mappings) : Collections.emptyMap(); + ? loadPositionMap(mappings, includeArea) : Collections.emptyMap(); Map> personMaps = new LinkedHashMap<>(); for (Long corpinfoId : corpinfoIds) { personMaps.put(corpinfoId, new LinkedHashMap<>()); @@ -122,10 +135,14 @@ public class BiPersonLocationProvider { } private Map loadPositionMap( - List mappings) { + List mappings, + boolean includeArea) { Map result = new LinkedHashMap<>(); try { - for (PositionPersonCO row : positionPersonQueryExe.listByMappings(mappings)) { + List rows = includeArea + ? positionPersonQueryExe.listByMappings(mappings) + : positionPersonQueryExe.listByMappingsWithoutArea(mappings); + for (PositionPersonCO row : rows) { if (row.getUserId() != null) { result.putIfAbsent(row.getUserId(), row); } diff --git a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/PersonnelPositioningBiQueryExe.java b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/PersonnelPositioningBiQueryExe.java index 3593bc2..2aac54a 100644 --- a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/PersonnelPositioningBiQueryExe.java +++ b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/PersonnelPositioningBiQueryExe.java @@ -32,6 +32,7 @@ import com.zcloud.personnel.positioning.persistence.dataobject.BiAreaSnapshotDO; import com.zcloud.personnel.positioning.persistence.dataobject.BiCompanyAlarmCountDO; import com.zcloud.personnel.positioning.persistence.dataobject.BiCorpInfoDO; import com.zcloud.personnel.positioning.persistence.dataobject.BiPersonPositionUserDO; +import com.zcloud.personnel.positioning.persistence.dataobject.ClosedAreaDO; import com.zcloud.personnel.positioning.persistence.dataobject.FenceDO; import com.zcloud.personnel.positioning.persistence.repository.AreaRepository; import com.zcloud.personnel.positioning.persistence.repository.FenceRepository; @@ -57,7 +58,7 @@ import java.util.Map; import java.util.Set; /** - * BI large-screen query executor. + * 可视化大屏查询执行器。 * * @Author fangjiakai * @Date 2026-07-07 @@ -86,6 +87,7 @@ public class PersonnelPositioningBiQueryExe { private final BiPersonLocationProvider biPersonLocationProvider; private final BiOverviewCache biOverviewCache; private final PositionPersonLocalLookupService localLookupService; + private final ClosedAreaMatcher closedAreaMatcher; public BiOverviewCO queryOverview() { String cacheKey = overviewCacheKey(); @@ -301,19 +303,26 @@ public class PersonnelPositioningBiQueryExe { int pageSize = Math.max(qry.getPageSize(), 1); Long corpinfoId = qry.getCorpinfoId(); Map locationMap = loadBiPersonPositionLocationMap(corpinfoId); + List closedAreas = closedAreaMatcher.prepare( + areaRepository.listClosedAreas(qry.getCorpinfoId())); List result = new ArrayList<>(); for (BiPersonPositionUserDO user : userRepository.listBiPersonPositionUsers(corpinfoId)) { BiCompanyOnlinePersonCO location = findBiPersonPositionLocation(user, locationMap); - result.add(buildBiPersonPositionRow(user, location)); + result.add(buildBiPersonPositionRow(user, location, closedAreas)); } + result.sort((left, right) -> Integer.compare(onlineOrder(left), onlineOrder(right))); int fromIndex = Math.min((pageNo - 1) * pageSize, result.size()); int toIndex = Math.min(fromIndex + pageSize, result.size()); return PageResponse.of(result.subList(fromIndex, toIndex), result.size(), pageSize, pageNo); } + private int onlineOrder(BiPersonPositionCO person) { + return person != null && "ONLINE".equalsIgnoreCase(person.getOnlineStatus()) ? 0 : 1; + } + private Map loadBiPersonPositionLocationMap(Long corpinfoId) { List locations = - biPersonLocationProvider.listCompanyOnlinePersons(corpinfoId, null, null); + biPersonLocationProvider.listCompanyOnlinePersonsWithoutArea(corpinfoId, null); Map result = new LinkedHashMap<>(); for (BiCompanyOnlinePersonCO location : locations) { if (location.getUserId() != null) { @@ -332,7 +341,8 @@ public class PersonnelPositioningBiQueryExe { } private BiPersonPositionCO buildBiPersonPositionRow(BiPersonPositionUserDO user, - BiCompanyOnlinePersonCO location) { + BiCompanyOnlinePersonCO location, + List closedAreas) { BiPersonPositionCO co = new BiPersonPositionCO(); co.setUserId(user.getUserId()); co.setPersonName(user.getPersonName()); @@ -348,9 +358,13 @@ public class PersonnelPositioningBiQueryExe { co.setLon(location.getLon()); co.setLat(location.getLat()); co.setLastLocationTime(location.getLastLocationTime()); - // 区域已在统一人员定位查询中按实时坐标匹配,这里直接透传以避免重复计算。 - co.setAreaId(location.getLocationAreaId()); - co.setAreaName(location.getLocationAreaName()); + ClosedAreaMatcher.AreaPolygon area = closedAreaMatcher.match( + location.getLon(), location.getLat(), closedAreas); + if (area != null) { + ClosedAreaDO closedArea = area.getArea(); + co.setAreaId(closedArea.getId()); + co.setAreaName(closedArea.getClosedAreaName()); + } return co; } @@ -752,7 +766,7 @@ public class PersonnelPositioningBiQueryExe { return area; } } catch (IOException ignored) { - // Invalid port polygon data is ignored; the port remains unassigned. + // 港区多边形数据无效时忽略,人员不归属任何港区。 } } return null; diff --git a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/PositionPersonQueryExe.java b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/PositionPersonQueryExe.java index 4cd73b0..8822669 100644 --- a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/PositionPersonQueryExe.java +++ b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/PositionPersonQueryExe.java @@ -57,18 +57,24 @@ public class PositionPersonQueryExe { List listByMappings( List mappings) { - return buildRows(new PositionPersonPageQry(), true, loadPersistedMatches(mappings)); + return buildRows(new PositionPersonPageQry(), true, loadPersistedMatches(mappings), true); + } + + List listByMappingsWithoutArea( + List mappings) { + return buildRows(new PositionPersonPageQry(), true, loadPersistedMatches(mappings), false); } private List listRows(PositionPersonPageQry query) { boolean persistedMappings = localLookupService.hasPersonnelMappings(); List matched = loadMatchedPeople(query, persistedMappings); - return buildRows(query, persistedMappings, matched); + return buildRows(query, persistedMappings, matched, true); } private List buildRows(PositionPersonPageQry query, boolean persistedMappings, - List matched) { + List matched, + boolean includeArea) { Set idCardNos = new LinkedHashSet<>(); for (MatchedPerson person : matched) { if (!StringUtils.hasText(person.finds.terminalNo) @@ -124,7 +130,7 @@ public class PositionPersonQueryExe { person, terminal, currentLocation, online, locationStatus, positionSource); boolean locationQueried = StringUtils.hasText(person.finds.terminalNo) && terminalNosToLocate.contains(person.finds.terminalNo); - if (online && locationQueried) { + if (includeArea && online && locationQueried) { AreaDO area = matchLocationArea(row, areasByCorpinfoId); if (area != null) { row.setLocationAreaId(area.getId()); diff --git a/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/mapper/AreaMapper.java b/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/mapper/AreaMapper.java index 29ed566..6d97cd7 100644 --- a/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/mapper/AreaMapper.java +++ b/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/mapper/AreaMapper.java @@ -1,6 +1,7 @@ package com.zcloud.personnel.positioning.persistence.mapper; import com.zcloud.personnel.positioning.persistence.dataobject.AreaDO; +import com.zcloud.personnel.positioning.persistence.dataobject.ClosedAreaDO; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; @@ -16,4 +17,6 @@ import java.util.List; @Mapper public interface AreaMapper extends BaseMapper { List selectBiPersonPositionAreas(@Param("corpinfoId") Long corpinfoId); + + List selectClosedAreas(@Param("corpinfoId") Long corpinfoId); } diff --git a/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/repository/AreaRepository.java b/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/repository/AreaRepository.java index ce1ebcd..dc7b029 100644 --- a/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/repository/AreaRepository.java +++ b/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/repository/AreaRepository.java @@ -1,6 +1,7 @@ package com.zcloud.personnel.positioning.persistence.repository; import com.zcloud.personnel.positioning.persistence.dataobject.AreaDO; +import com.zcloud.personnel.positioning.persistence.dataobject.ClosedAreaDO; import com.alibaba.cola.dto.PageResponse; import com.jjb.saas.framework.repository.repo.BaseRepository; @@ -17,4 +18,6 @@ public interface AreaRepository extends BaseRepository { PageResponse listPage(Map params); List listBiPersonPositionAreas(Long corpinfoId); + + List listClosedAreas(Long corpinfoId); } diff --git a/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/repository/impl/AreaRepositoryImpl.java b/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/repository/impl/AreaRepositoryImpl.java index 5370021..839bd71 100644 --- a/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/repository/impl/AreaRepositoryImpl.java +++ b/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/repository/impl/AreaRepositoryImpl.java @@ -1,6 +1,7 @@ package com.zcloud.personnel.positioning.persistence.repository.impl; import com.zcloud.personnel.positioning.persistence.dataobject.AreaDO; +import com.zcloud.personnel.positioning.persistence.dataobject.ClosedAreaDO; import com.zcloud.personnel.positioning.persistence.mapper.AreaMapper; import com.zcloud.personnel.positioning.persistence.repository.AreaRepository; import com.zcloud.gbscommon.utils.PageQueryHelper; @@ -41,4 +42,9 @@ public class AreaRepositoryImpl extends BaseRepositoryImpl i public List listBiPersonPositionAreas(Long corpinfoId) { return areaMapper.selectBiPersonPositionAreas(corpinfoId); } + + @Override + public List listClosedAreas(Long corpinfoId) { + return areaMapper.selectClosedAreas(corpinfoId); + } } diff --git a/web-infrastructure/src/main/resources/mapper/AreaMapper.xml b/web-infrastructure/src/main/resources/mapper/AreaMapper.xml index 00cfa4c..39fb388 100644 --- a/web-infrastructure/src/main/resources/mapper/AreaMapper.xml +++ b/web-infrastructure/src/main/resources/mapper/AreaMapper.xml @@ -44,4 +44,22 @@ AND corpinfo_id = #{corpinfoId} ORDER BY sort_no ASC, area_name ASC, create_time DESC + +