diff --git a/web-adapter/src/main/java/com/zcloud/personnel/positioning/websocket/PersonLocationWebSocketHandler.java b/web-adapter/src/main/java/com/zcloud/personnel/positioning/websocket/PersonLocationWebSocketHandler.java index 9936cec..c0b9991 100644 --- a/web-adapter/src/main/java/com/zcloud/personnel/positioning/websocket/PersonLocationWebSocketHandler.java +++ b/web-adapter/src/main/java/com/zcloud/personnel/positioning/websocket/PersonLocationWebSocketHandler.java @@ -5,6 +5,7 @@ import com.zcloud.personnel.positioning.integration.realtime.RealtimeLocationMes import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.TextMessage; @@ -87,6 +88,15 @@ public class PersonLocationWebSocketHandler extends TextWebSocketHandler impleme return sessions.size(); } + @Scheduled(fixedDelayString = "${personnel-positioning.websocket.location.session-cleanup-ms:30000}") + public void cleanupClosedSessions() { + for (Map.Entry entry : sessions.entrySet()) { + if (!entry.getValue().session.isOpen()) { + sessions.remove(entry.getKey(), entry.getValue()); + } + } + } + private void removeAndClose(String sessionId) { SubscriptionSession subscriptionSession = sessions.remove(sessionId); WebSocketSession session = subscriptionSession == null ? null : subscriptionSession.session; diff --git a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/BiOverviewCache.java b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/BiOverviewCache.java index c419839..a70abf0 100644 --- a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/BiOverviewCache.java +++ b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/BiOverviewCache.java @@ -35,6 +35,9 @@ public class BiOverviewCache { if (local != null && local.expireAt > now) { return local.value; } + if (local != null) { + localCache.remove(key, local); + } if (stringRedisTemplate == null) { return null; } 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 2aac54a..9b0e624 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 @@ -229,23 +229,27 @@ public class PersonnelPositioningBiQueryExe { public PageResponse listCompanyFence(BiCompanyFenceQry qry) { BiCompanyFenceQry query = qry == null ? new BiCompanyFenceQry() : qry; - List matched = new ArrayList<>(); - for (BiAreaFenceCO fence : listAllAreaFence()) { - if (matchesCompanyFence(fence, query)) { - matched.add(fence); - } - } int pageNo = Math.max(query.getPageIndex(), 1); int pageSize = Math.max(query.getPageSize(), 1); - int fromIndex = Math.min((pageNo - 1) * pageSize, matched.size()); - int toIndex = Math.min(fromIndex + pageSize, matched.size()); - return PageResponse.of(matched.subList(fromIndex, toIndex), matched.size(), pageSize, pageNo); + QueryWrapper wrapper = companyFenceQuery(query); + long total = fenceRepository.count(wrapper); + wrapper.orderByDesc("update_time", "id"); + wrapper.last("LIMIT " + pageSize + " OFFSET " + ((pageNo - 1) * pageSize)); + return PageResponse.of(mapLocalFences(fenceRepository.list(wrapper)), + safeLongToInt(total), pageSize, pageNo); } public PageResponse listPersonLocation(BiPersonLocationQry qry) { BiPersonLocationQry query = qry == null ? new BiPersonLocationQry() : qry; int pageNo = Math.max(query.getPageIndex(), 1); int pageSize = Math.max(query.getPageSize(), 1); + if (query.getCorpinfoId() != null && localLookupService.hasPersonnelMappings()) { + PageResponse mappingPage = + localLookupService.findMatchedPersonnelPage(query); + List pageRows = loadPersistedPersonLocations( + query, mappingPage.getData()); + return PageResponse.of(pageRows, mappingPage.getTotalCount(), pageSize, pageNo); + } List rows = loadMatchedPersonLocations(query); int fromIndex = Math.min((pageNo - 1) * pageSize, rows.size()); int toIndex = Math.min(fromIndex + pageSize, rows.size()); @@ -526,11 +530,19 @@ public class PersonnelPositioningBiQueryExe { localQuery.setCorpinfoName(query.getCompanyName()); localQuery.setStaffName(query.getStaffName()); + return loadPersistedPersonLocations(query, localLookupService.findMatchedPersonnel(localQuery)); + } + + private List loadPersistedPersonLocations( + BiPersonLocationQry query, + List mappings) { + if (mappings == null || mappings.isEmpty()) { + return Collections.emptyList(); + } List matchedStaff = new ArrayList<>(); Set matchedUserIds = new HashSet<>(); Set terminalNos = new LinkedHashSet<>(); - for (PositionPersonLocalLookupService.PersonMapping mapping - : localLookupService.findMatchedPersonnel(localQuery)) { + for (PositionPersonLocalLookupService.PersonMapping mapping : mappings) { PositionPersonLocalLookupService.LocalPerson local = mapping.getLocal(); if (local == null || (local.getUserId() != null && !matchedUserIds.add(local.getUserId()))) { continue; @@ -654,6 +666,29 @@ public class PersonnelPositioningBiQueryExe { return wrapper; } + private QueryWrapper companyFenceQuery(BiCompanyFenceQry query) { + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.and(value -> value.isNull("delete_enum").or().eq("delete_enum", "FALSE")); + if (query.getId() != null) { + wrapper.and(value -> value.eq("id", query.getId()).or().eq("finds_fence_id", query.getId())); + } + if (StringUtils.hasText(query.getNameLike())) { + wrapper.like("fence_name", query.getNameLike()); + } + if (query.getCorpinfoId() != null) { + wrapper.eq("corpinfo_id", query.getCorpinfoId()); + } + if (StringUtils.hasText(query.getCompanyName())) { + String companyName = query.getCompanyName(); + wrapper.and(value -> value.eq("corpinfo_name", companyName).or().eq("org_name", companyName)); + } + if (StringUtils.hasText(query.getOrgName())) { + String orgName = query.getOrgName(); + wrapper.and(value -> value.eq("corpinfo_name", orgName).or().eq("org_name", orgName)); + } + return wrapper; + } + private List mapLocalFences(List fences) { List result = new ArrayList<>(); if (fences == null || fences.isEmpty()) { diff --git a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/PositionPersonLocalLookupService.java b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/PositionPersonLocalLookupService.java index a344075..e8fb6d0 100644 --- a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/PositionPersonLocalLookupService.java +++ b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/PositionPersonLocalLookupService.java @@ -1,6 +1,8 @@ package com.zcloud.personnel.positioning.command.query; +import com.alibaba.cola.dto.PageResponse; import com.zcloud.personnel.positioning.dto.PositionPersonPageQry; +import com.zcloud.personnel.positioning.dto.BiPersonLocationQry; import com.zcloud.personnel.positioning.persistence.dataobject.PersonnelLocalPersonDO; import com.zcloud.personnel.positioning.persistence.dataobject.PersonnelLocationAreaUpdateDO; import com.zcloud.personnel.positioning.persistence.dataobject.PersonnelMappingSnapshotDO; @@ -42,6 +44,36 @@ class PositionPersonLocalLookupService { return findMatchedPersonnel(query, null); } + PageResponse findMatchedPersonnelPage(PositionPersonPageQry query) { + Map params = matchedPersonnelParams(query, null); + return findMatchedPersonnelPage(params, query.getPageIndex(), query.getPageSize()); + } + + PageResponse findMatchedPersonnelPage(BiPersonLocationQry query) { + Map params = new HashMap<>(); + params.put("staffName", normalize(query.getStaffName())); + params.put("corpinfoId", query.getCorpinfoId()); + params.put("biCompanyName", normalize(query.getCompanyName())); + params.put("staffNo", normalize(query.getStaffNo())); + params.put("idCardNo", normalize(query.getIdCardNo())); + params.put("mobileNo", normalize(query.getMobileNo())); + params.put("terminalNo", normalize(query.getTerminalNo())); + params.put("sourceOrgName", normalize(query.getOrgName())); + params.put("orgCode", normalize(query.getOrgCode())); + params.put("creditCode", normalize(query.getCreditCode())); + return findMatchedPersonnelPage(params, query.getPageIndex(), query.getPageSize()); + } + + private PageResponse findMatchedPersonnelPage(Map params, + int pageIndex, + int pageSize) { + params.put("pageIndex", Math.max(pageIndex, 1)); + params.put("pageSize", Math.max(pageSize, 1)); + PageResponse page = personnelMatchRepository.listMatchedPage(params); + return PageResponse.of(toPersonMappings(page.getData()), page.getTotalCount(), + page.getPageSize(), page.getPageIndex()); + } + List findMatchedPersonnelByCorpinfoIds(Set corpinfoIds) { List result = new ArrayList<>(); for (Set batch : idBatches(corpinfoIds)) { @@ -52,6 +84,12 @@ class PositionPersonLocalLookupService { private List findMatchedPersonnel(PositionPersonPageQry query, Set corpinfoIds) { + return toPersonMappings(personnelMatchRepository.listMatched( + matchedPersonnelParams(query, corpinfoIds))); + } + + private Map matchedPersonnelParams(PositionPersonPageQry query, + Set corpinfoIds) { Map params = new HashMap<>(); if (query != null) { params.put("userId", query.getUserId()); @@ -60,12 +98,18 @@ class PositionPersonLocalLookupService { params.put("corpinfoName", normalize(query.getCorpinfoName())); params.put("departmentId", query.getDepartmentId()); params.put("departmentName", normalize(query.getDepartmentName())); + params.put("locationStatus", upper(query.getLocationStatus())); + params.put("positionSource", upper(query.getPositionSource())); } if (corpinfoIds != null && !corpinfoIds.isEmpty()) { params.put("corpinfoIds", corpinfoIds); } + return params; + } + + private List toPersonMappings(List rows) { List result = new ArrayList<>(); - for (PersonnelMappingSnapshotDO row : personnelMatchRepository.listMatched(params)) { + for (PersonnelMappingSnapshotDO row : rows) { PersonMapping mapping = new PersonMapping(); mapping.setFindsStaffNo(normalize(row.getFindsStaffNo())); mapping.setFindsStaffNoType(normalize(row.getFindsStaffNoType())); @@ -86,6 +130,11 @@ class PositionPersonLocalLookupService { return result; } + private String upper(String value) { + String normalized = normalize(value); + return normalized == null ? null : normalized.toUpperCase(java.util.Locale.ROOT); + } + /** * 批量保存实时定位点匹配出的当前区域。 */ 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 8822669..16e0c92 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 @@ -47,6 +47,14 @@ public class PositionPersonQueryExe { public PageResponse list(PositionPersonPageQry qry) { PositionPersonPageQry query = qry == null ? new PositionPersonPageQry() : qry; + if (localLookupService.hasPersonnelMappings()) { + PageResponse mappingPage = + localLookupService.findMatchedPersonnelPage(query); + List pageRows = buildRows( + query, true, loadPersistedMatches(mappingPage.getData()), true); + return PageResponse.of(pageRows, mappingPage.getTotalCount(), + mappingPage.getPageSize(), mappingPage.getPageIndex()); + } List rows = listRows(query); int pageIndex = Math.max(query.getPageIndex(), 1); int pageSize = Math.max(query.getPageSize(), 1); @@ -260,12 +268,11 @@ public class PositionPersonQueryExe { for (int pageNo = 2; pageNo <= pageCount; pageNo++) { requests.add(staffPageRequest(pageNo)); } - List roots = findsOpenApiClient.postFailFastBatch( - STAFF_LIST_API, requests, FINDS_BATCH_CONCURRENCY); - for (JsonNode root : roots) { - JsonNode data = assertSuccess(root, STAFF_LIST_API).path("data"); - appendFindsStaff(result, extractRows(data)); - } + findsOpenApiClient.consumeFailFastBatch( + STAFF_LIST_API, requests, FINDS_BATCH_CONCURRENCY, (index, root) -> { + JsonNode data = assertSuccess(root, STAFF_LIST_API).path("data"); + appendFindsStaff(result, extractRows(data)); + }); return result; } @@ -330,12 +337,10 @@ public class PositionPersonQueryExe { for (List terminalBatch : batches) { requests.add(Collections.singletonMap("terminalNoList", terminalBatch)); } - List roots = findsOpenApiClient.postFailFastBatch( - POINT_LOCATE_API, requests, FINDS_BATCH_CONCURRENCY); Map result = new LinkedHashMap<>(); - for (int index = 0; index < roots.size(); index++) { - appendLocatedTerminals(batches.get(index), roots.get(index), result); - } + findsOpenApiClient.consumeFailFastBatch( + POINT_LOCATE_API, requests, FINDS_BATCH_CONCURRENCY, + (index, root) -> appendLocatedTerminals(batches.get(index), root, result)); return result; } diff --git a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/RealtimePersonLocationMatcher.java b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/RealtimePersonLocationMatcher.java index c97c69d..a1d68b4 100644 --- a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/RealtimePersonLocationMatcher.java +++ b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/RealtimePersonLocationMatcher.java @@ -195,8 +195,6 @@ public class RealtimePersonLocationMatcher { co.setOnline(online == null ? Boolean.FALSE : online); co.setAlarmStatus(booleanValue(firstValue(location, row, "alarmStatus", "isAlarm"))); co.setAlarmCount(integerValue(firstValue(location, row, "alarmCount", "unclosedAlarmCount"))); - co.setStaffRawJson(row.toString()); - co.setLocationRawJson(location.toString()); return co; } diff --git a/web-app/src/main/java/com/zcloud/personnel/positioning/integration/finds/FindsOpenApiClient.java b/web-app/src/main/java/com/zcloud/personnel/positioning/integration/finds/FindsOpenApiClient.java index 78914b0..cde87a4 100644 --- a/web-app/src/main/java/com/zcloud/personnel/positioning/integration/finds/FindsOpenApiClient.java +++ b/web-app/src/main/java/com/zcloud/personnel/positioning/integration/finds/FindsOpenApiClient.java @@ -27,10 +27,12 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.function.BiConsumer; /** * FindS OpenAPI signed client. @@ -43,6 +45,7 @@ import java.util.concurrent.Future; public class FindsOpenApiClient { private static final Logger LOGGER = LoggerFactory.getLogger(FindsOpenApiClient.class); private static final String SIK_PATH_SEGMENT = "sik"; + private static final int MAX_RESPONSE_BYTES = 16 * 1024 * 1024; @Value("${finds.openapi.connect-timeout-ms:10000}") private int connectTimeoutMs = 10000; @@ -73,34 +76,38 @@ public class FindsOpenApiClient { failFastConnectTimeoutMs, failFastReadTimeoutMs, 1); } - public List postFailFastBatch(String apiName, List> requests, - int maxConcurrency) { + public void consumeFailFastBatch(String apiName, List> requests, + int maxConcurrency, + BiConsumer responseConsumer) { if (requests == null || requests.isEmpty()) { - return Collections.emptyList(); + return; } + Objects.requireNonNull(responseConsumer, "responseConsumer"); OpenapiConfigDO config = loadEnabledConfig(); int concurrency = Math.max(1, Math.min(Math.min(maxConcurrency, 8), requests.size())); if (concurrency == 1) { - List result = new ArrayList<>(requests.size()); - for (Map request : requests) { - result.add(post(config, apiName, request, + for (int index = 0; index < requests.size(); index++) { + responseConsumer.accept(index, post(config, apiName, requests.get(index), failFastConnectTimeoutMs, failFastReadTimeoutMs, 1)); } - return result; + return; } ExecutorService executor = Executors.newFixedThreadPool(concurrency); try { - List> futures = new ArrayList<>(requests.size()); - for (Map request : requests) { - futures.add(executor.submit(() -> post(config, apiName, request, - failFastConnectTimeoutMs, failFastReadTimeoutMs, 1))); + // 每个窗口完成后立即交给调用方处理,避免所有分页响应同时滞留在 Future 中。 + for (int from = 0; from < requests.size(); from += concurrency) { + int to = Math.min(from + concurrency, requests.size()); + List> futures = new ArrayList<>(to - from); + for (int index = from; index < to; index++) { + Map request = requests.get(index); + futures.add(executor.submit(() -> post(config, apiName, request, + failFastConnectTimeoutMs, failFastReadTimeoutMs, 1))); + } + for (int offset = 0; offset < futures.size(); offset++) { + responseConsumer.accept(from + offset, futures.get(offset).get()); + } } - List result = new ArrayList<>(requests.size()); - for (Future future : futures) { - result.add(future.get()); - } - return result; } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new BizException("FindS OpenAPI批量请求被中断"); @@ -247,13 +254,18 @@ public class FindsOpenApiClient { if (inputStream == null) { return ""; } - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(8192); byte[] buffer = new byte[4096]; + int total = 0; int len; while ((len = inputStream.read(buffer)) != -1) { + if (len > MAX_RESPONSE_BYTES - total) { + throw new IOException("FindS OpenAPI response exceeds 16 MiB safety limit"); + } outputStream.write(buffer, 0, len); + total += len; } - return new String(outputStream.toByteArray(), StandardCharsets.UTF_8); + return outputStream.toString(StandardCharsets.UTF_8.name()); } private String writeJson(Object value) { diff --git a/web-app/src/main/java/com/zcloud/personnel/positioning/integration/finds/OpenapiConfigCache.java b/web-app/src/main/java/com/zcloud/personnel/positioning/integration/finds/OpenapiConfigCache.java index 216a8ed..2c8d613 100644 --- a/web-app/src/main/java/com/zcloud/personnel/positioning/integration/finds/OpenapiConfigCache.java +++ b/web-app/src/main/java/com/zcloud/personnel/positioning/integration/finds/OpenapiConfigCache.java @@ -96,6 +96,9 @@ public class OpenapiConfigCache { if (local != null && local.expireAt > now) { return local.config; } + if (local != null) { + localCache.remove(key, local); + } OpenapiConfigDO redisConfig = readRedis(key); if (redisConfig != null) { putLocal(key, redisConfig); diff --git a/web-app/src/main/java/com/zcloud/personnel/positioning/integration/realtime/RealtimePersonRoutingCache.java b/web-app/src/main/java/com/zcloud/personnel/positioning/integration/realtime/RealtimePersonRoutingCache.java index 54f68e6..d3d8d9f 100644 --- a/web-app/src/main/java/com/zcloud/personnel/positioning/integration/realtime/RealtimePersonRoutingCache.java +++ b/web-app/src/main/java/com/zcloud/personnel/positioning/integration/realtime/RealtimePersonRoutingCache.java @@ -12,9 +12,8 @@ import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; +import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -83,8 +82,7 @@ public class RealtimePersonRoutingCache { return current; } try { - Map routes = buildRoutes( - personnelMatchRepository.listRealtimePersonRoutes(SOURCE_CONFIG_CODE)); + Map routes = buildRoutes(); snapshot = new CacheSnapshot(Collections.unmodifiableMap(routes), now + ttlMillis); LOGGER.info("Realtime WebSocket person-routing cache refreshed, size={}", routes.size()); } catch (RuntimeException e) { @@ -97,49 +95,10 @@ public class RealtimePersonRoutingCache { } } - private Map buildRoutes(List rows) { - Map persistedRoutes = new LinkedHashMap<>(); - UniqueRouteIndex usernames = new UniqueRouteIndex(); - UniqueRouteIndex phones = new UniqueRouteIndex(); - if (rows != null) { - for (RealtimePersonRoutingDO row : rows) { - if (row == null) { - continue; - } - PersonRoute route = toRoute(row); - if (row.isPersistedMatch()) { - String staffNo = normalize(row.getFindsStaffNo()); - if (staffNo != null) { - persistedRoutes.put(staffNo, route); - } - continue; - } - usernames.add(row.getLocalStaffNo(), route); - phones.add(row.getMobileNo(), route); - } - } - - Map routes = new LinkedHashMap<>(persistedRoutes); - Set fallbackKeys = new LinkedHashSet<>(); - fallbackKeys.addAll(usernames.keys()); - fallbackKeys.addAll(phones.keys()); - for (String key : fallbackKeys) { - if (routes.containsKey(key)) { - continue; - } - IndexResult username = usernames.resolve(key); - if (username.present) { - if (!username.ambiguous) { - routes.put(key, username.route); - } - continue; - } - IndexResult phone = phones.resolve(key); - if (phone.present && !phone.ambiguous) { - routes.put(key, phone.route); - } - } - return routes; + private Map buildRoutes() { + RouteAccumulator accumulator = new RouteAccumulator(); + personnelMatchRepository.scanRealtimePersonRoutes(SOURCE_CONFIG_CODE, accumulator::add); + return accumulator.toRoutes(); } private PersonRoute toRoute(RealtimePersonRoutingDO row) { @@ -179,8 +138,8 @@ public class RealtimePersonRoutingCache { } private static final class UniqueRouteIndex { - private final Map unique = new LinkedHashMap<>(); - private final Set ambiguous = new LinkedHashSet<>(); + private final Map unique = new HashMap<>(); + private final Set ambiguous = new HashSet<>(); private void add(String rawKey, PersonRoute route) { String key = normalize(rawKey); @@ -194,18 +153,18 @@ public class RealtimePersonRoutingCache { } } - private Set keys() { - Set keys = new LinkedHashSet<>(unique.keySet()); - keys.addAll(ambiguous); - return keys; + private boolean contains(String key) { + return unique.containsKey(key) || ambiguous.contains(key); } - private IndexResult resolve(String key) { - if (ambiguous.contains(key)) { - return IndexResult.ambiguous(); + private void appendUniqueRoutes(Map routes, + UniqueRouteIndex preferredIndex) { + for (Map.Entry entry : unique.entrySet()) { + if (preferredIndex != null && preferredIndex.contains(entry.getKey())) { + continue; + } + routes.putIfAbsent(entry.getKey(), entry.getValue()); } - PersonRoute route = unique.get(key); - return route == null ? IndexResult.absent() : IndexResult.unique(route); } private boolean sameUser(PersonRoute first, PersonRoute second) { @@ -213,20 +172,33 @@ public class RealtimePersonRoutingCache { } } - private static final class IndexResult { - private final boolean present; - private final boolean ambiguous; - private final PersonRoute route; + private final class RouteAccumulator { + private final Map routes = new HashMap<>(); + private final UniqueRouteIndex usernames = new UniqueRouteIndex(); + private final UniqueRouteIndex phones = new UniqueRouteIndex(); - private IndexResult(boolean present, boolean ambiguous, PersonRoute route) { - this.present = present; - this.ambiguous = ambiguous; - this.route = route; + private void add(RealtimePersonRoutingDO row) { + if (row == null) { + return; + } + PersonRoute route = toRoute(row); + if (row.isPersistedMatch()) { + String staffNo = normalize(row.getFindsStaffNo()); + if (staffNo != null) { + routes.put(staffNo, route); + } + return; + } + usernames.add(row.getLocalStaffNo(), route); + phones.add(row.getMobileNo(), route); } - private static IndexResult absent() { return new IndexResult(false, false, null); } - private static IndexResult ambiguous() { return new IndexResult(true, true, null); } - private static IndexResult unique(PersonRoute route) { return new IndexResult(true, false, route); } + private Map toRoutes() { + usernames.appendUniqueRoutes(routes, null); + // 用户名存在但有歧义时也不能回退到同值手机号,保持原有匹配优先级。 + phones.appendUniqueRoutes(routes, usernames); + return routes; + } } private static final class CacheSnapshot { diff --git a/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/mapper/PersonnelMatchMapper.java b/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/mapper/PersonnelMatchMapper.java index cd7c1dc..45cd396 100644 --- a/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/mapper/PersonnelMatchMapper.java +++ b/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/mapper/PersonnelMatchMapper.java @@ -7,8 +7,10 @@ import com.zcloud.personnel.positioning.persistence.dataobject.RealtimePersonRou import com.zcloud.personnel.positioning.persistence.dataobject.TerminalBindingSnapshotDO; import com.zcloud.personnel.positioning.persistence.dataobject.TerminalLocationSnapshotDO; import com.zcloud.personnel.positioning.persistence.dataobject.CorpInfoSnapshotDO; +import com.baomidou.mybatisplus.core.metadata.IPage; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.session.ResultHandler; import java.time.LocalDateTime; import java.util.List; @@ -51,11 +53,16 @@ public interface PersonnelMatchMapper { @Param("updates") List updates, @Param("syncTime") LocalDateTime syncTime); - List listRealtimePersonRoutes( - @Param("sourceConfigCode") String sourceConfigCode); + void scanRealtimePersonRoutes( + @Param("sourceConfigCode") String sourceConfigCode, + ResultHandler resultHandler); List listMatched(@Param("params") Map params); + IPage listMatchedPage( + IPage page, + @Param("params") Map params); + List listPeopleByPhones(@Param("values") List values); List listAllPeople(); diff --git a/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/repository/PersonnelMatchRepository.java b/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/repository/PersonnelMatchRepository.java index 4229649..ab500d0 100644 --- a/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/repository/PersonnelMatchRepository.java +++ b/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/repository/PersonnelMatchRepository.java @@ -1,5 +1,6 @@ package com.zcloud.personnel.positioning.persistence.repository; +import com.alibaba.cola.dto.PageResponse; import com.zcloud.personnel.positioning.persistence.dataobject.PersonnelLocalPersonDO; import com.zcloud.personnel.positioning.persistence.dataobject.PersonnelLocationAreaUpdateDO; import com.zcloud.personnel.positioning.persistence.dataobject.PersonnelMappingSnapshotDO; @@ -11,6 +12,7 @@ import com.zcloud.personnel.positioning.persistence.dataobject.CorpInfoSnapshotD import java.time.LocalDateTime; import java.util.List; import java.util.Map; +import java.util.function.Consumer; public interface PersonnelMatchRepository { long countActive(String sourceConfigCode); @@ -36,10 +38,12 @@ public interface PersonnelMatchRepository { List updates, LocalDateTime syncTime); - List listRealtimePersonRoutes(String sourceConfigCode); + void scanRealtimePersonRoutes(String sourceConfigCode, Consumer consumer); List listMatched(Map params); + PageResponse listMatchedPage(Map params); + List listPeopleByPhones(List values); List listAllPeople(); diff --git a/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/repository/impl/PersonnelMatchRepositoryImpl.java b/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/repository/impl/PersonnelMatchRepositoryImpl.java index 96f027f..698be83 100644 --- a/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/repository/impl/PersonnelMatchRepositoryImpl.java +++ b/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/repository/impl/PersonnelMatchRepositoryImpl.java @@ -1,5 +1,9 @@ package com.zcloud.personnel.positioning.persistence.repository.impl; +import com.alibaba.cola.dto.PageResponse; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.jjb.saas.framework.repository.common.PageHelper; +import com.zcloud.gbscommon.utils.Query; import com.zcloud.personnel.positioning.persistence.dataobject.PersonnelLocalPersonDO; import com.zcloud.personnel.positioning.persistence.dataobject.PersonnelLocationAreaUpdateDO; import com.zcloud.personnel.positioning.persistence.dataobject.PersonnelMappingSnapshotDO; @@ -18,6 +22,8 @@ import java.time.LocalDateTime; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.function.Consumer; @Repository @RequiredArgsConstructor @@ -63,12 +69,21 @@ public class PersonnelMatchRepositoryImpl implements PersonnelMatchRepository { } return affected; } - @Override public List listRealtimePersonRoutes(String code) { - return personnelMatchMapper.listRealtimePersonRoutes(code); + @Override + public void scanRealtimePersonRoutes(String code, Consumer consumer) { + Objects.requireNonNull(consumer, "consumer"); + // ResultHandler 在 Mapper 调用期间逐行消费,避免先将全量人员结果装入 List。 + personnelMatchMapper.scanRealtimePersonRoutes( + code, context -> consumer.accept(context.getResultObject())); } @Override public List listMatched(Map params) { return personnelMatchMapper.listMatched(params); } + @Override public PageResponse listMatchedPage(Map params) { + IPage page = new Query().getPage(params); + IPage result = personnelMatchMapper.listMatchedPage(page, params); + return PageHelper.pageToResponse(result, result.getRecords()); + } @Override public List listPeopleByPhones(List values) { return personnelMatchMapper.listPeopleByPhones(values); } diff --git a/web-infrastructure/src/main/resources/mapper/PersonnelMatchMapper.xml b/web-infrastructure/src/main/resources/mapper/PersonnelMatchMapper.xml index 51a4d44..c649117 100644 --- a/web-infrastructure/src/main/resources/mapper/PersonnelMatchMapper.xml +++ b/web-infrastructure/src/main/resources/mapper/PersonnelMatchMapper.xml @@ -113,19 +113,18 @@ - @@ -255,6 +251,102 @@ ORDER BY u.id DESC, pm.finds_staff_no + +