From 8bbe0e79325d6d5f234b71a75dbb1dd51c2760e0 Mon Sep 17 00:00:00 2001 From: shenzhidan Date: Thu, 13 Aug 2026 18:23:45 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BA=BA=E5=91=98=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E4=B8=8E=E5=AE=9E=E6=97=B6=E5=AE=9A=E4=BD=8D=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FindsStaffLocationStatusSyncService.java | 99 +++++--------- .../query/FindsStaffMappingSyncService.java | 127 +++++++++++------- .../kafka/LocationKafkaProbeListener.java | 35 ----- .../V20260811_01__personnel_location_area.sql | 40 ++++++ .../V20260812_01__expand_org_match_type.sql | 3 + 5 files changed, 160 insertions(+), 144 deletions(-) create mode 100644 web-infrastructure/src/main/resources/db/migration/mysql/V20260811_01__personnel_location_area.sql create mode 100644 web-infrastructure/src/main/resources/db/migration/mysql/V20260812_01__expand_org_match_type.sql diff --git a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/FindsStaffLocationStatusSyncService.java b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/FindsStaffLocationStatusSyncService.java index 6dd0062..2f458f4 100644 --- a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/FindsStaffLocationStatusSyncService.java +++ b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/FindsStaffLocationStatusSyncService.java @@ -11,11 +11,12 @@ import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import java.time.LocalDateTime; -import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; /** * Refreshes the persisted online/offline state for FindS personnel. @@ -33,7 +34,6 @@ public class FindsStaffLocationStatusSyncService { private static final int STAFF_COUNT_PAGE_SIZE = 1; private static final int TERMINAL_PAGE_SIZE = 2000; private static final int MAX_TERMINAL_PAGE_COUNT = 100; - private static final int BATCH_CONCURRENCY = 4; private static final int FULL_SYNC_INTERVAL_HOURS = 24; private static final List ONLINE_DEVICE_STATUSES = Arrays.asList("STATIC", "MOVING"); @@ -51,15 +51,14 @@ public class FindsStaffLocationStatusSyncService { || fullSyncDue(syncTime); int fullSyncCount = fullSync ? mappingSyncService.syncAll() : 0; - Map onlineTerminals = loadOnlineTerminals(); - int onlineMappings = updateLocationStatuses(onlineTerminals, syncTime); - int onlineStaffTerminals = countStaffTerminals(onlineTerminals); + StatusSyncCounter statusCounter = synchronizeOnlineTerminals(syncTime); + personnelMatchRepository.markStaleOffline(SOURCE_CONFIG_CODE, syncTime); LOGGER.info("FindS personnel location status synchronization completed, remoteStaffCount={}, " + "localStaffCount={}, fullSync={}, fullSyncCount={}, onlineTerminals={}, onlineMappings={}", remoteStaffCount, localStaffCount, fullSync, fullSyncCount, - onlineStaffTerminals, onlineMappings); + statusCounter.onlineStaffTerminals, statusCounter.onlineMappings); return new SyncSummary(remoteStaffCount, localStaffCount, fullSync, fullSyncCount, - onlineStaffTerminals, onlineMappings); + statusCounter.onlineStaffTerminals, statusCounter.onlineMappings); } private long queryRemoteStaffCount() { @@ -85,15 +84,17 @@ public class FindsStaffLocationStatusSyncService { || lastSyncTime.isBefore(syncTime.minusHours(FULL_SYNC_INTERVAL_HOURS)); } - private Map loadOnlineTerminals() { - Map result = new LinkedHashMap<>(); + private StatusSyncCounter synchronizeOnlineTerminals(LocalDateTime syncTime) { + StatusSyncCounter counter = new StatusSyncCounter(); for (String status : ONLINE_DEVICE_STATUSES) { - loadOnlineTerminals(status, result); + synchronizeOnlineTerminals(status, syncTime, counter); } - return result; + return counter; } - private void loadOnlineTerminals(String deviceStatus, Map result) { + private void synchronizeOnlineTerminals(String deviceStatus, + LocalDateTime syncTime, + StatusSyncCounter counter) { JsonNode firstData = successData(findsOpenApiClient.postFailFast( TERMINAL_LIST_API, terminalPageRequest(deviceStatus, 1))); JsonNode firstRows = extractRows(firstData); @@ -101,7 +102,7 @@ public class FindsStaffLocationStatusSyncService { if (total > 0 && (firstRows == null || firstRows.size() == 0)) { throw new BizException("FindS terminal status page is empty while total is " + total); } - appendOnlineTerminals(result, firstRows, deviceStatus); + synchronizeTerminalPage(firstRows, deviceStatus, syncTime, counter); int effectivePageSize = firstRows == null || firstRows.size() == 0 ? TERMINAL_PAGE_SIZE : firstRows.size(); @@ -109,20 +110,13 @@ public class FindsStaffLocationStatusSyncService { if (pageCount > MAX_TERMINAL_PAGE_COUNT) { throw new BizException("FindS online terminal pages exceed the configured safety limit: " + pageCount); } - if (pageCount <= 1) { - return; - } - - List> requests = new ArrayList<>(pageCount - 1); - for (int pageNo = 2; pageNo <= pageCount; pageNo++) { - requests.add(terminalPageRequest(deviceStatus, pageNo)); - } long receivedCount = firstRows == null ? 0 : firstRows.size(); - for (JsonNode root : findsOpenApiClient.postFailFastBatch( - TERMINAL_LIST_API, requests, BATCH_CONCURRENCY)) { - JsonNode rows = extractRows(successData(root)); + for (int pageNo = 2; pageNo <= pageCount; pageNo++) { + JsonNode data = successData(findsOpenApiClient.postFailFast( + TERMINAL_LIST_API, terminalPageRequest(deviceStatus, pageNo))); + JsonNode rows = extractRows(data); receivedCount += rows == null ? 0 : rows.size(); - appendOnlineTerminals(result, rows, deviceStatus); + synchronizeTerminalPage(rows, deviceStatus, syncTime, counter); } if (receivedCount < total) { throw new BizException("FindS online terminal result is incomplete: received=" @@ -138,9 +132,10 @@ public class FindsStaffLocationStatusSyncService { return request; } - private void appendOnlineTerminals(Map result, - JsonNode rows, - String requestedStatus) { + private void synchronizeTerminalPage(JsonNode rows, + String requestedStatus, + LocalDateTime syncTime, + StatusSyncCounter counter) { if (rows == null || !rows.isArray()) { return; } @@ -150,6 +145,9 @@ public class FindsStaffLocationStatusSyncService { if (!StringUtils.hasText(terminalNo)) { continue; } + if (!counter.seenTerminalNos.add(terminalNo)) { + continue; + } String trackType = firstText( text(firstPresent(row, "trackType")), text(firstPresent(row.path("track"), "trackType"))); @@ -159,35 +157,16 @@ public class FindsStaffLocationStatusSyncService { boolean staffTrack = "STAFF".equalsIgnoreCase(trackType) && StringUtils.hasText(trackNo); String actualStatus = firstText( text(firstPresent(row, "deviceStatus", "status", "onlineStatus")), requestedStatus); - result.put(terminalNo, new OnlineTerminal( - terminalNo, actualStatus, staffTrack ? trackNo.trim() : null)); - } - } - - private int updateLocationStatuses(Map onlineTerminals, - LocalDateTime syncTime) { - int matched = 0; - for (OnlineTerminal terminal : onlineTerminals.values()) { + String staffNo = staffTrack ? trackNo.trim() : null; personnelMatchRepository.refreshTerminalBinding( - SOURCE_CONFIG_CODE, terminal.terminalNo, terminal.staffNo, syncTime); - if (!StringUtils.hasText(terminal.staffNo)) { + SOURCE_CONFIG_CODE, terminalNo, staffNo, syncTime); + if (!StringUtils.hasText(staffNo)) { continue; } - matched += personnelMatchRepository.updateOnline( - SOURCE_CONFIG_CODE, terminal.terminalNo, terminal.deviceStatus, syncTime); + counter.onlineStaffTerminals++; + counter.onlineMappings += personnelMatchRepository.updateOnline( + SOURCE_CONFIG_CODE, terminalNo, actualStatus, syncTime); } - personnelMatchRepository.markStaleOffline(SOURCE_CONFIG_CODE, syncTime); - return matched; - } - - private int countStaffTerminals(Map onlineTerminals) { - int count = 0; - for (OnlineTerminal terminal : onlineTerminals.values()) { - if (StringUtils.hasText(terminal.staffNo)) { - count++; - } - } - return count; } private JsonNode successData(JsonNode root) { @@ -260,16 +239,10 @@ public class FindsStaffLocationStatusSyncService { } } - private static class OnlineTerminal { - private final String terminalNo; - private final String deviceStatus; - private final String staffNo; - - private OnlineTerminal(String terminalNo, String deviceStatus, String staffNo) { - this.terminalNo = terminalNo; - this.deviceStatus = deviceStatus; - this.staffNo = staffNo; - } + private static class StatusSyncCounter { + private final Set seenTerminalNos = new HashSet<>(); + private int onlineStaffTerminals; + private int onlineMappings; } public static class SyncSummary { diff --git a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/FindsStaffMappingSyncService.java b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/FindsStaffMappingSyncService.java index 47d5a04..ed0623b 100644 --- a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/FindsStaffMappingSyncService.java +++ b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/FindsStaffMappingSyncService.java @@ -34,7 +34,6 @@ public class FindsStaffMappingSyncService { private static final String SOURCE_CONFIG_CODE = "FINDS"; private static final int PAGE_SIZE = 2000; private static final int MAX_PAGE_COUNT = 50; - private static final int BATCH_CONCURRENCY = 4; private final FindsOpenApiClient findsOpenApiClient; private final PositionPersonLocalLookupService localLookupService; @@ -51,62 +50,62 @@ public class FindsStaffMappingSyncService { /** 拉取 FindS 完整组织路径并单独刷新企业映射,不执行人员映射落库。 */ public FindsCorpMappingService.PathSyncSummary syncOrganizations() { LocalDateTime syncTime = LocalDateTime.now().withNano(0); - return synchronizeOrganizations(loadAllStaff(), syncTime); + Set> orgPaths = collectOrganizationPaths(); + FindsCorpMappingService.PathSyncSummary summary = synchronizeOrganizationPaths(orgPaths, syncTime); + if (orgPaths.isEmpty()) { + forEachStaffPage(rows -> resolveOrganizations(rows)); + } + return summary; } public int syncAll() { - // The schema uses second precision; keep the same precision for the stale-row cutoff. + // 数据库时间精度为秒,保持同一精度才能准确识别本次未出现的旧数据。 LocalDateTime syncTime = LocalDateTime.now().withNano(0); - List staffRows = loadAllStaff(); - FindsCorpMappingService.PathSyncSummary orgSummary = - synchronizeOrganizations(staffRows, syncTime); + Set> orgPaths = collectOrganizationPaths(); + FindsCorpMappingService.PathSyncSummary orgSummary = synchronizeOrganizationPaths(orgPaths, syncTime); PersonIndexes localPeople = new PersonIndexes(localLookupService.findAllPeople()); - int matchedCount = 0; - for (StaffSnapshot row : staffRows) { - LocalMatch localMatch = localPeople.match(row); - upsert(row, localMatch, syncTime); - if (localMatch.person != null) { - matchedCount++; - } - } + SyncCounter counter = new SyncCounter(); + forEachStaffPage(rows -> syncStaffPage( + rows, localPeople, syncTime, orgPaths.isEmpty(), counter)); disableMissingRows(syncTime); realtimePersonRoutingCache.refreshNow(); LOGGER.info("FindS staff mapping synchronization completed, total={}, matched={}, " + "orgPaths={}, orgNodes={}, orgMatched={}, orgAmbiguous={}, orgUnmatched={}", - staffRows.size(), matchedCount, orgSummary.getDistinctPathCount(), + counter.total, counter.matched, orgSummary.getDistinctPathCount(), orgSummary.getDistinctNodeCount(), orgSummary.getMatchedCount(), orgSummary.getAmbiguousCount(), orgSummary.getUnmatchedCount()); - return staffRows.size(); + return counter.total; } - private FindsCorpMappingService.PathSyncSummary synchronizeOrganizations( - List staffRows, LocalDateTime syncTime) { + private Set> collectOrganizationPaths() { Set> orgPaths = new LinkedHashSet<>(); - for (StaffSnapshot row : staffRows) { - if (row.orgNamePath != null && !row.orgNamePath.isEmpty()) { - orgPaths.add(row.orgNamePath); + forEachStaffPage(rows -> { + for (JsonNode row : rows) { + List path = orgNamePath(row.path("orgNamePath")); + if (!path.isEmpty()) { + orgPaths.add(path); + } } - } + }); + return orgPaths; + } + + private FindsCorpMappingService.PathSyncSummary synchronizeOrganizationPaths( + Set> orgPaths, LocalDateTime syncTime) { FindsCorpMappingService.PathSyncSummary orgSummary = findsCorpMappingService.synchronizePaths(orgPaths, syncTime); if (orgSummary == null) { orgSummary = FindsCorpMappingService.PathSyncSummary.empty(); } lastOrgPathSummary = orgSummary; - if (orgPaths.isEmpty()) { - for (StaffSnapshot row : staffRows) { - findsCorpMappingService.resolve(SOURCE_CONFIG_CODE, row.findsCorpinfoId, - row.corpinfoName, row.departmentName, null); - } - } return orgSummary; } - private List loadAllStaff() { - List result = new ArrayList<>(); + private void forEachStaffPage(StaffPageConsumer consumer) { JsonNode firstData = successData( findsOpenApiClient.postFailFast(STAFF_LIST_API, pageRequest(1))); - int firstPageSize = appendRows(result, extractRows(firstData)); + JsonNode firstRows = extractRows(firstData); + int firstPageSize = rowCount(firstRows); long total = requiredTotal(firstData); if (total > 0 && firstPageSize == 0) { throw new BizException("FindS staff page is empty while total is " + total); @@ -115,23 +114,19 @@ public class FindsStaffMappingSyncService { if (pageCount > MAX_PAGE_COUNT) { throw new BizException("FindS staff pages exceed the configured safety limit: " + pageCount); } - if (pageCount <= 1) { - return result; - } - List> requests = new ArrayList<>(pageCount - 1); - for (int pageNo = 2; pageNo <= pageCount; pageNo++) { - requests.add(pageRequest(pageNo)); - } + consumer.accept(firstRows); long receivedCount = firstPageSize; - for (JsonNode root : findsOpenApiClient.postFailFastBatch( - STAFF_LIST_API, requests, BATCH_CONCURRENCY)) { - receivedCount += appendRows(result, extractRows(successData(root))); + for (int pageNo = 2; pageNo <= pageCount; pageNo++) { + JsonNode data = successData( + findsOpenApiClient.postFailFast(STAFF_LIST_API, pageRequest(pageNo))); + JsonNode rows = extractRows(data); + receivedCount += rowCount(rows); + consumer.accept(rows); } if (receivedCount < total) { throw new BizException("FindS staff result is incomplete: received=" + receivedCount + ", total=" + total); } - return result; } private Map pageRequest(int pageNo) { @@ -141,17 +136,47 @@ public class FindsStaffMappingSyncService { return request; } - private int appendRows(List result, JsonNode rows) { + private int rowCount(JsonNode rows) { + return rows != null && rows.isArray() ? rows.size() : 0; + } + + private void resolveOrganizations(JsonNode rows) { if (rows == null || !rows.isArray()) { - return 0; + return; } for (JsonNode row : rows) { StaffSnapshot snapshot = map(row); if (snapshot != null) { - result.add(snapshot); + findsCorpMappingService.resolve(SOURCE_CONFIG_CODE, snapshot.findsCorpinfoId, + snapshot.corpinfoName, snapshot.departmentName, null); + } + } + } + + private void syncStaffPage(JsonNode rows, + PersonIndexes localPeople, + LocalDateTime syncTime, + boolean resolveOrganization, + SyncCounter counter) { + if (rows == null || !rows.isArray()) { + return; + } + for (JsonNode row : rows) { + StaffSnapshot snapshot = map(row); + if (snapshot == null) { + continue; + } + if (resolveOrganization) { + findsCorpMappingService.resolve(SOURCE_CONFIG_CODE, snapshot.findsCorpinfoId, + snapshot.corpinfoName, snapshot.departmentName, null); + } + LocalMatch localMatch = localPeople.match(snapshot); + upsert(snapshot, localMatch, syncTime); + counter.total++; + if (localMatch.person != null) { + counter.matched++; } } - return rows.size(); } private StaffSnapshot map(JsonNode row) { @@ -355,6 +380,16 @@ public class FindsStaffMappingSyncService { private String terminalNo; } + @FunctionalInterface + private interface StaffPageConsumer { + void accept(JsonNode rows); + } + + private static class SyncCounter { + private int total; + private int matched; + } + private static class PersonIndexes { private final UniqueIndex idCards = new UniqueIndex(); private final UniqueIndex usernames = new UniqueIndex(); diff --git a/web-app/src/main/java/com/zcloud/personnel/positioning/integration/kafka/LocationKafkaProbeListener.java b/web-app/src/main/java/com/zcloud/personnel/positioning/integration/kafka/LocationKafkaProbeListener.java index 8e6cbfe..2e93cdf 100644 --- a/web-app/src/main/java/com/zcloud/personnel/positioning/integration/kafka/LocationKafkaProbeListener.java +++ b/web-app/src/main/java/com/zcloud/personnel/positioning/integration/kafka/LocationKafkaProbeListener.java @@ -12,11 +12,9 @@ import com.zcloud.personnel.positioning.integration.realtime.RealtimeLocationMes import com.zcloud.personnel.positioning.integration.realtime.RealtimePersonRoutingCache; import com.zcloud.personnel.positioning.integration.realtime.RealtimePersonRoutingCache.PersonRoute; import org.apache.kafka.clients.consumer.ConsumerRecord; -import org.apache.kafka.common.header.Header; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.kafka.support.Acknowledgment; @@ -27,7 +25,6 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.StringJoiner; @Component @ConditionalOnProperty( @@ -38,19 +35,16 @@ import java.util.StringJoiner; public class LocationKafkaProbeListener { private static final Logger LOGGER = LoggerFactory.getLogger(LocationKafkaProbeListener.class); - private final int maxLogBytes; private final RealtimePersonLocationMatcher locationMatcher; private final RealtimePersonRoutingCache personRoutingCache; private final ObjectMapper objectMapper; private final ObjectProvider publishers; public LocationKafkaProbeListener( - @Value("${personnel-positioning.kafka.location.max-log-bytes:16384}") int maxLogBytes, RealtimePersonLocationMatcher locationMatcher, RealtimePersonRoutingCache personRoutingCache, ObjectMapper objectMapper, ObjectProvider publishers) { - this.maxLogBytes = Math.max(1, maxLogBytes); this.locationMatcher = locationMatcher; this.personRoutingCache = personRoutingCache; this.objectMapper = objectMapper; @@ -63,21 +57,6 @@ public class LocationKafkaProbeListener { groupId = "${personnel-positioning.kafka.location.group-id:personnel-position-location-probe}" ) public void onMessage(ConsumerRecord record, Acknowledgment acknowledgment) { - LOGGER.info( - "收到Kafka定位原始消息,topic={}, partition={}, offset={}, timestamp={}, " - + "timestampType={}, keyBytes={}, valueBytes={}, key={}, headers={}, value={}", - record.topic(), - record.partition(), - record.offset(), - record.timestamp(), - record.timestampType(), - length(record.key()), - length(record.value()), - LocationKafkaPayloadPreview.render(record.key(), maxLogBytes), - renderHeaders(record), - LocationKafkaPayloadPreview.render(record.value(), maxLogBytes) - ); - try { List locations = locationMatcher.match(record.value()); int deliveries = 0; @@ -91,9 +70,6 @@ public class LocationKafkaProbeListener { } } acknowledgment.acknowledge(); - LOGGER.info("Kafka location message processed, topic={}, partition={}, offset={}, converted={}, " - + "websocketDeliveries={}", - record.topic(), record.partition(), record.offset(), locations.size(), deliveries); } catch (RealtimeLocationPayloadException e) { acknowledgment.acknowledge(); LOGGER.warn("Ignored invalid Kafka location message, topic={}, partition={}, offset={}, reason={}", @@ -140,15 +116,4 @@ public class LocationKafkaProbeListener { } } - private int length(byte[] value) { - return value == null ? 0 : value.length; - } - - private String renderHeaders(ConsumerRecord record) { - StringJoiner joiner = new StringJoiner(", ", "[", "]"); - for (Header header : record.headers()) { - joiner.add(header.key() + "=" + LocationKafkaPayloadPreview.render(header.value(), maxLogBytes)); - } - return joiner.toString(); - } } diff --git a/web-infrastructure/src/main/resources/db/migration/mysql/V20260811_01__personnel_location_area.sql b/web-infrastructure/src/main/resources/db/migration/mysql/V20260811_01__personnel_location_area.sql new file mode 100644 index 0000000..f16fc12 --- /dev/null +++ b/web-infrastructure/src/main/resources/db/migration/mysql/V20260811_01__personnel_location_area.sql @@ -0,0 +1,40 @@ +-- 保存实时定位点匹配出的当前区域;不保存实时经纬度和轨迹点。 +SET @ddl = IF( + (SELECT COUNT(1) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'personnel_match' AND COLUMN_NAME = 'location_area_id') = 0, + 'ALTER TABLE `personnel_match` ADD COLUMN `location_area_id` bigint DEFAULT NULL COMMENT ''当前定位区域id'' AFTER `location_status_time`', + 'DO 0' +); +PREPARE stmt FROM @ddl; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +SET @ddl = IF( + (SELECT COUNT(1) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'personnel_match' AND COLUMN_NAME = 'location_area_name') = 0, + 'ALTER TABLE `personnel_match` ADD COLUMN `location_area_name` varchar(255) DEFAULT NULL COMMENT ''当前定位区域名称'' AFTER `location_area_id`', + 'DO 0' +); +PREPARE stmt FROM @ddl; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +SET @ddl = IF( + (SELECT COUNT(1) FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'personnel_match' AND COLUMN_NAME = 'location_area_time') = 0, + 'ALTER TABLE `personnel_match` ADD COLUMN `location_area_time` datetime DEFAULT NULL COMMENT ''定位区域状态更新时间'' AFTER `location_area_name`', + 'DO 0' +); +PREPARE stmt FROM @ddl; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +SET @ddl = IF( + (SELECT COUNT(1) FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'personnel_match' AND INDEX_NAME = 'idx_personnel_match_location_area') = 0, + 'ALTER TABLE `personnel_match` ADD KEY `idx_personnel_match_location_area` (`location_area_id`, `location_status`)', + 'DO 0' +); +PREPARE stmt FROM @ddl; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; diff --git a/web-infrastructure/src/main/resources/db/migration/mysql/V20260812_01__expand_org_match_type.sql b/web-infrastructure/src/main/resources/db/migration/mysql/V20260812_01__expand_org_match_type.sql new file mode 100644 index 0000000..fed9712 --- /dev/null +++ b/web-infrastructure/src/main/resources/db/migration/mysql/V20260812_01__expand_org_match_type.sql @@ -0,0 +1,3 @@ +-- 组织名称标准化匹配类型超过 32 个字符,扩容以保证人员全量同步可以继续执行。 +ALTER TABLE `org_match` + MODIFY COLUMN `match_type` varchar(64) DEFAULT NULL COMMENT '组织匹配方式';