未测试:添加航司二三字码映射功能,支持航班号转换
This commit is contained in:
parent
9e36afb133
commit
88f4c0be8b
@ -18,12 +18,19 @@ import org.springframework.web.socket.client.WebSocketClient;
|
|||||||
import org.springframework.lang.NonNull;
|
import org.springframework.lang.NonNull;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.format.DateTimeParseException;
|
import java.time.format.DateTimeParseException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
@ -41,6 +48,7 @@ public class AdxpFlightServiceWebSocketClient implements WebSocketHandler {
|
|||||||
private static final DateTimeFormatter BIZ_KEY_DATE_8 = DateTimeFormatter.ofPattern("yyyyMMdd");
|
private static final DateTimeFormatter BIZ_KEY_DATE_8 = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||||
private static final long DEFAULT_DFIE_TTL_SECONDS = TimeUnit.DAYS.toSeconds(2);
|
private static final long DEFAULT_DFIE_TTL_SECONDS = TimeUnit.DAYS.toSeconds(2);
|
||||||
private static final long MIN_DFIE_TTL_SECONDS = 60L;
|
private static final long MIN_DFIE_TTL_SECONDS = 60L;
|
||||||
|
private static final Map<String, String> ICAO_TO_IATA_PREFIX = loadIcaoToIataPrefixMap();
|
||||||
|
|
||||||
private final WebSocketClient webSocketClient;
|
private final WebSocketClient webSocketClient;
|
||||||
private final FlightSdkProperties properties;
|
private final FlightSdkProperties properties;
|
||||||
@ -474,6 +482,62 @@ public class AdxpFlightServiceWebSocketClient implements WebSocketHandler {
|
|||||||
return s.toUpperCase();
|
return s.toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TISFLIGHT 的 FlNo/FlightId 可能使用三字航司码(ICAO),
|
||||||
|
* 这里按内置对照表转换为二字码(IATA)后再参与 Redis key 拼装。
|
||||||
|
*/
|
||||||
|
private static String normalizeTisFlightNo(String raw) {
|
||||||
|
String normalized = normalizeFlightNo(raw);
|
||||||
|
if (normalized == null || normalized.length() < 4) {
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
String prefix = normalized.substring(0, 3);
|
||||||
|
String mappedPrefix = ICAO_TO_IATA_PREFIX.get(prefix);
|
||||||
|
if (mappedPrefix == null || mappedPrefix.isBlank()) {
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
return mappedPrefix + normalized.substring(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, String> loadIcaoToIataPrefixMap() {
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
try (InputStream in = AdxpFlightServiceWebSocketClient.class.getResourceAsStream("/airline-code-map.csv")) {
|
||||||
|
if (in == null) {
|
||||||
|
log.warn("未找到航司二三字码映射资源: /airline-code-map.csv");
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
|
||||||
|
String line;
|
||||||
|
boolean firstLine = true;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
String row = line.trim();
|
||||||
|
if (row.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (firstLine) {
|
||||||
|
firstLine = false;
|
||||||
|
if (row.toUpperCase().startsWith("IATA,ICAO")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String[] parts = row.split(",", 2);
|
||||||
|
if (parts.length != 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String iata = parts[0].trim().toUpperCase();
|
||||||
|
String icao = parts[1].trim().toUpperCase();
|
||||||
|
if (iata.matches("^[A-Z0-9]{2}$") && icao.matches("^[A-Z0-9]{3}$")) {
|
||||||
|
map.put(icao, iata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("加载航司二三字码映射失败,将跳过三字码转二字码: {}", e.getMessage());
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableMap(map);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将 BizKey 按最多 3 段安全切分,避免空值导致 NPE。
|
* 将 BizKey 按最多 3 段安全切分,避免空值导致 NPE。
|
||||||
*/
|
*/
|
||||||
@ -1009,8 +1073,8 @@ public class AdxpFlightServiceWebSocketClient implements WebSocketHandler {
|
|||||||
String fuId = getTextContentAny(root, "FuId", "FUID", "FuID");
|
String fuId = getTextContentAny(root, "FuId", "FUID", "FuID");
|
||||||
String flightIdRaw = getTextContentAny(root, "FlightId", "FlightID", "FlId", "FLID");
|
String flightIdRaw = getTextContentAny(root, "FlightId", "FlightID", "FlId", "FLID");
|
||||||
String flightNumberRaw = getTextContent(root, "FlNo");
|
String flightNumberRaw = getTextContent(root, "FlNo");
|
||||||
String flightNumber = normalizeFlightNo(flightNumberRaw);
|
String flightNumber = normalizeTisFlightNo(flightNumberRaw);
|
||||||
String flightId = normalizeFlightNo(flightIdRaw);
|
String flightId = normalizeTisFlightNo(flightIdRaw);
|
||||||
String contactCross = getTextContent(root, "ContactCross");
|
String contactCross = getTextContent(root, "ContactCross");
|
||||||
String type = getTextContent(root, "Type");
|
String type = getTextContent(root, "Type");
|
||||||
long nowMs = System.currentTimeMillis();
|
long nowMs = System.currentTimeMillis();
|
||||||
@ -1036,11 +1100,13 @@ public class AdxpFlightServiceWebSocketClient implements WebSocketHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String fuIdFlightNo = firstSegmentFromBizLike(fuId);
|
String fuIdFlightNo = normalizeTisFlightNo(firstSegmentFromBizLike(fuId));
|
||||||
String bizFlightNo = firstSegmentFromBizLike(bizKey);
|
String bizFlightNo = normalizeTisFlightNo(firstSegmentFromBizLike(bizKey));
|
||||||
String resolvedFlightNo = flightNumber != null ? flightNumber
|
String resolvedFlightNo = flightNumber != null ? flightNumber
|
||||||
: (flightId != null ? flightId
|
: (flightId != null ? flightId
|
||||||
: (fuIdFlightNo != null ? fuIdFlightNo : bizFlightNo));
|
: (fuIdFlightNo != null ? fuIdFlightNo : bizFlightNo));
|
||||||
|
log.info("TISFLIGHT航班号转换: flNoRaw={}, flNoNorm={}, flightIdRaw={}, flightIdNorm={}, resolvedFlightNo={}",
|
||||||
|
flightNumberRaw, flightNumber, flightIdRaw, flightId, resolvedFlightNo);
|
||||||
|
|
||||||
FlightNotificationDTO dto = new FlightNotificationDTO();
|
FlightNotificationDTO dto = new FlightNotificationDTO();
|
||||||
dto.setFlightNo(resolvedFlightNo != null ? resolvedFlightNo : flightNumberRaw);
|
dto.setFlightNo(resolvedFlightNo != null ? resolvedFlightNo : flightNumberRaw);
|
||||||
@ -1049,32 +1115,19 @@ public class AdxpFlightServiceWebSocketClient implements WebSocketHandler {
|
|||||||
dto.setContactCross(contactCross);
|
dto.setContactCross(contactCross);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TISFLIGHT 特殊:可能无 BizKey。无 BizKey 时先 FuId 关联,再按航班号回退。
|
|
||||||
String normalizedBizKey = resolveTisFlightBizKey(bizKey, fuId, flightId, resolvedFlightNo);
|
|
||||||
String flightKey = buildFlightRedisKey(resolvedFlightNo);
|
String flightKey = buildFlightRedisKey(resolvedFlightNo);
|
||||||
String bizRedisKey = buildBizRedisKey(normalizedBizKey);
|
|
||||||
boolean canUpdateRouteParams = normalizedBizKey != null;
|
|
||||||
|
|
||||||
if (flightKey != null || bizRedisKey != null) {
|
if (flightKey != null) {
|
||||||
try {
|
try {
|
||||||
setValueOnFlightAndBizKeys(flightKey, bizRedisKey, "flightNumber", resolvedFlightNo);
|
redisCache.setCacheMapValue(flightKey, "flightNumber", resolvedFlightNo);
|
||||||
if (normalizedBizKey != null) {
|
|
||||||
setValueOnFlightAndBizKeys(flightKey, bizRedisKey, "bizKey", normalizedBizKey);
|
|
||||||
}
|
|
||||||
updateActiveBizKey(flightKey, normalizedBizKey, nowMs);
|
|
||||||
// contactCross 仅对进港有效,避免进出港参数串用
|
// contactCross 仅对进港有效,避免进出港参数串用
|
||||||
if ("IN".equalsIgnoreCase(routeType)
|
if ("IN".equalsIgnoreCase(routeType)
|
||||||
&& contactCross != null && !contactCross.isBlank()) {
|
&& contactCross != null && !contactCross.isBlank()) {
|
||||||
setValueOnFlightAndBizKeys(flightKey, bizRedisKey, "contactCross", contactCross);
|
redisCache.setCacheMapValue(flightKey, "contactCross", contactCross);
|
||||||
setValueOnFlightAndBizKeys(flightKey, bizRedisKey, "contactCrossTs", String.valueOf(nowMs));
|
redisCache.setCacheMapValue(flightKey, "contactCrossTs", String.valueOf(nowMs));
|
||||||
}
|
}
|
||||||
// TISFLIGHT 也可提供 seat,作为 CRAFTSEAT 的补充来源
|
log.info("TISFLIGHT航班号解析(已应用三字转二字): flNoRaw={}, flightIdRaw={}, fuId={}, bizKey={}, resolvedFlightNo={}",
|
||||||
if (!canUpdateRouteParams && contactCross != null) {
|
flightNumberRaw, flightIdRaw, fuId, bizKey, resolvedFlightNo);
|
||||||
log.warn("TISFLIGHT未建立BizKey关联,跳过contactCross/seat覆盖: flNo={}, flightId={}, fuId={}, bizKey={}, resolvedFlightNo={}",
|
|
||||||
flightNumber, flightId, fuId, bizKey, resolvedFlightNo);
|
|
||||||
}
|
|
||||||
log.info("TISFLIGHT航班号解析: flNo={}, flightId={}, fuId={}, bizKey={}, selectedBizKey={}, resolvedFlightNo={}, hasBizKey={}",
|
|
||||||
flightNumber, flightId, fuId, bizKey, normalizedBizKey, resolvedFlightNo, normalizedBizKey != null);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("存储航班TISFLIGHT数据到Redis失败: {}", e.getMessage());
|
log.error("存储航班TISFLIGHT数据到Redis失败: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1149,11 +1149,6 @@ public class DataProcessingService {
|
|||||||
normalizedBizKey = normalizeRedisString(redisCache.getCacheMapValue(flightKey, "activeBizKey"));
|
normalizedBizKey = normalizeRedisString(redisCache.getCacheMapValue(flightKey, "activeBizKey"));
|
||||||
normalizedBizKey = normalizeBizKey(normalizedBizKey);
|
normalizedBizKey = normalizeBizKey(normalizedBizKey);
|
||||||
}
|
}
|
||||||
if (normalizedBizKey == null) {
|
|
||||||
log.info("路由Redis参数未齐全(缺少bizKey),等待补齐后重试: flightNo={}, routeType={}",
|
|
||||||
normalizedFlightNo, normalizedRouteType);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
String bizRedisKey = buildBizRedisKey(normalizedBizKey);
|
String bizRedisKey = buildBizRedisKey(normalizedBizKey);
|
||||||
|
|
||||||
// 从 Redis 获取参数快照
|
// 从 Redis 获取参数快照
|
||||||
|
|||||||
198
qaup-collision/src/main/resources/airline-code-map.csv
Normal file
198
qaup-collision/src/main/resources/airline-code-map.csv
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
IATA,ICAO
|
||||||
|
PO,CYW
|
||||||
|
PR,PAL
|
||||||
|
PY,PYN
|
||||||
|
RB,RBW
|
||||||
|
C3,TDR
|
||||||
|
SC,CDG
|
||||||
|
TG,THA
|
||||||
|
TH,TSE
|
||||||
|
TI,TIS
|
||||||
|
TW,TWB
|
||||||
|
UI,UVS
|
||||||
|
US,USA
|
||||||
|
VP,CDF
|
||||||
|
WU,CWU
|
||||||
|
8L,LKE
|
||||||
|
YD,YDH
|
||||||
|
YZ,YZZ
|
||||||
|
ZG,ZGG
|
||||||
|
ZH,CSZ
|
||||||
|
ZJ,CJG
|
||||||
|
ZZ,ZZZ
|
||||||
|
WC,WCC
|
||||||
|
HO,DKH
|
||||||
|
SZ,SZZ
|
||||||
|
CJ,CJJ
|
||||||
|
XO,XOO
|
||||||
|
3Q,3QQ
|
||||||
|
WD,DSR
|
||||||
|
EY,ETD
|
||||||
|
9C,CQH
|
||||||
|
BK,OKA
|
||||||
|
MK,MKA
|
||||||
|
SN,BEL
|
||||||
|
GJ,CDC
|
||||||
|
OQ,CQN
|
||||||
|
O3,CSS
|
||||||
|
Z2,EZD
|
||||||
|
VD,VDD
|
||||||
|
GE,TNA
|
||||||
|
XF,VLK
|
||||||
|
QF,QFA
|
||||||
|
QW,QDA
|
||||||
|
QG,CTV
|
||||||
|
SQ,SIA
|
||||||
|
GX,CBG
|
||||||
|
XW,NCT
|
||||||
|
KJ,AIH
|
||||||
|
D7,XAX
|
||||||
|
AZ,AZA
|
||||||
|
TR,TGW
|
||||||
|
5K,HFY
|
||||||
|
DB,MLT
|
||||||
|
6W,FBS
|
||||||
|
8M,MMA
|
||||||
|
FP,PVV
|
||||||
|
NZ,ANZ
|
||||||
|
V8,VAS
|
||||||
|
BO,KLJ
|
||||||
|
RS,ASV
|
||||||
|
B3,BLV
|
||||||
|
5B,BSX
|
||||||
|
A6,OTC
|
||||||
|
GI,LHA
|
||||||
|
GT,CGH
|
||||||
|
HT,CTJ
|
||||||
|
LT,SNG
|
||||||
|
VN,HVN
|
||||||
|
ZC,RSN
|
||||||
|
ZA,SWM
|
||||||
|
RY,CJX
|
||||||
|
JR,JOY
|
||||||
|
AQ,JYH
|
||||||
|
9H,CGN
|
||||||
|
E3,VGO
|
||||||
|
MZ,MZT
|
||||||
|
GA,GIA
|
||||||
|
DR,RLH
|
||||||
|
UQ,CUH
|
||||||
|
GD,GSC
|
||||||
|
CN,GDC
|
||||||
|
FU,FZA
|
||||||
|
TV,TBA
|
||||||
|
DL,DAL
|
||||||
|
UO,HKE
|
||||||
|
DZ,EPA
|
||||||
|
1M,AEB
|
||||||
|
BJ,BJN
|
||||||
|
KN,CUA
|
||||||
|
FE,FEA
|
||||||
|
KA,HDA
|
||||||
|
CG,HZX
|
||||||
|
JF,JAA
|
||||||
|
KL,KLM
|
||||||
|
K9,TSP
|
||||||
|
B7,UIA
|
||||||
|
VI,VDA
|
||||||
|
ZF,ZFH
|
||||||
|
B8,B80
|
||||||
|
NS,HBH
|
||||||
|
CI,CAL
|
||||||
|
7C,JJA
|
||||||
|
TZ,SCO
|
||||||
|
LH,DLH
|
||||||
|
FA,FAV
|
||||||
|
G5,HXA
|
||||||
|
RA,RAA
|
||||||
|
8C,DXH
|
||||||
|
JD,CBJ
|
||||||
|
EJ,EJM
|
||||||
|
YV,YVA
|
||||||
|
CQ,MAJ
|
||||||
|
ES,ESL
|
||||||
|
UN,UNN
|
||||||
|
5X,UPS
|
||||||
|
RU,RUS
|
||||||
|
CK,CKK
|
||||||
|
GS,GCR
|
||||||
|
BH,BHE
|
||||||
|
UA,UUA
|
||||||
|
HX,CRK
|
||||||
|
VJ,VJC
|
||||||
|
J5,JJ5
|
||||||
|
KY,KNA
|
||||||
|
AE,MDA
|
||||||
|
LJ,JNA
|
||||||
|
CF,CYZ
|
||||||
|
BX,ABL
|
||||||
|
EF,DDD
|
||||||
|
Y8,YZR
|
||||||
|
PN,CHB
|
||||||
|
BG,ADB
|
||||||
|
YL,YAL
|
||||||
|
NW,VIA
|
||||||
|
IJ,GWL
|
||||||
|
UW,UTP
|
||||||
|
JH,JHH
|
||||||
|
JS,KOR
|
||||||
|
5Y,GTI
|
||||||
|
FR,FRT
|
||||||
|
3U,CSC
|
||||||
|
AB,ABD
|
||||||
|
CA,CCA
|
||||||
|
KK,CFZ
|
||||||
|
CX,CPA
|
||||||
|
CZ,CSN
|
||||||
|
DE,DER
|
||||||
|
EU,UEA
|
||||||
|
FD,CFA
|
||||||
|
FM,CSH
|
||||||
|
FX,FDX
|
||||||
|
GP,CTH
|
||||||
|
GY,CGZ
|
||||||
|
HA,HAH
|
||||||
|
HC,CHC
|
||||||
|
HK,EAS
|
||||||
|
HR,CU2
|
||||||
|
HU,CHH
|
||||||
|
JL,JAL
|
||||||
|
KE,KAL
|
||||||
|
LX,LXH
|
||||||
|
MF,CXA
|
||||||
|
MH,MAS
|
||||||
|
MI,SLK
|
||||||
|
MU,CES
|
||||||
|
N2,MGG
|
||||||
|
NH,ANA
|
||||||
|
NX,AMU
|
||||||
|
OM,OMA
|
||||||
|
OZ,AAR
|
||||||
|
K4,CKS
|
||||||
|
8Y,AAV
|
||||||
|
BR,EVA
|
||||||
|
3V,TAY
|
||||||
|
YG,HYT
|
||||||
|
JB,JBA
|
||||||
|
T8,TXC
|
||||||
|
I9,HLF
|
||||||
|
AY,FIN
|
||||||
|
N8,NCR
|
||||||
|
8S,GTR
|
||||||
|
JG,JDL
|
||||||
|
VZ,TVJ
|
||||||
|
DV,VSV
|
||||||
|
FV,SDM
|
||||||
|
IA,IAW
|
||||||
|
RD,RDA
|
||||||
|
SE,URO
|
||||||
|
TE,IGA
|
||||||
|
H9,HIM
|
||||||
|
SL,TLM
|
||||||
|
C6,MFX
|
||||||
|
4B,TUP
|
||||||
|
U6,SVR
|
||||||
|
AF,AFR
|
||||||
|
MR,861
|
||||||
|
IO,IAE
|
||||||
|
RF,EOK
|
||||||
|
Loading…
Reference in New Issue
Block a user