未测试:添加航司二三字码映射功能,支持航班号转换

This commit is contained in:
sladro 2026-02-27 11:15:25 +08:00
parent 9e36afb133
commit 88f4c0be8b
4 changed files with 275 additions and 28 deletions

View File

@ -18,12 +18,19 @@ import org.springframework.web.socket.client.WebSocketClient;
import org.springframework.lang.NonNull;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Collections;
import java.util.HashMap;
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.TimeUnit;
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 long DEFAULT_DFIE_TTL_SECONDS = TimeUnit.DAYS.toSeconds(2);
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 FlightSdkProperties properties;
@ -474,6 +482,62 @@ public class AdxpFlightServiceWebSocketClient implements WebSocketHandler {
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
*/
@ -1009,8 +1073,8 @@ public class AdxpFlightServiceWebSocketClient implements WebSocketHandler {
String fuId = getTextContentAny(root, "FuId", "FUID", "FuID");
String flightIdRaw = getTextContentAny(root, "FlightId", "FlightID", "FlId", "FLID");
String flightNumberRaw = getTextContent(root, "FlNo");
String flightNumber = normalizeFlightNo(flightNumberRaw);
String flightId = normalizeFlightNo(flightIdRaw);
String flightNumber = normalizeTisFlightNo(flightNumberRaw);
String flightId = normalizeTisFlightNo(flightIdRaw);
String contactCross = getTextContent(root, "ContactCross");
String type = getTextContent(root, "Type");
long nowMs = System.currentTimeMillis();
@ -1036,11 +1100,13 @@ public class AdxpFlightServiceWebSocketClient implements WebSocketHandler {
}
}
String fuIdFlightNo = firstSegmentFromBizLike(fuId);
String bizFlightNo = firstSegmentFromBizLike(bizKey);
String fuIdFlightNo = normalizeTisFlightNo(firstSegmentFromBizLike(fuId));
String bizFlightNo = normalizeTisFlightNo(firstSegmentFromBizLike(bizKey));
String resolvedFlightNo = flightNumber != null ? flightNumber
: (flightId != null ? flightId
: (fuIdFlightNo != null ? fuIdFlightNo : bizFlightNo));
log.info("TISFLIGHT航班号转换: flNoRaw={}, flNoNorm={}, flightIdRaw={}, flightIdNorm={}, resolvedFlightNo={}",
flightNumberRaw, flightNumber, flightIdRaw, flightId, resolvedFlightNo);
FlightNotificationDTO dto = new FlightNotificationDTO();
dto.setFlightNo(resolvedFlightNo != null ? resolvedFlightNo : flightNumberRaw);
@ -1049,32 +1115,19 @@ public class AdxpFlightServiceWebSocketClient implements WebSocketHandler {
dto.setContactCross(contactCross);
}
// TISFLIGHT 特殊可能无 BizKey BizKey 时先 FuId 关联再按航班号回退
String normalizedBizKey = resolveTisFlightBizKey(bizKey, fuId, flightId, resolvedFlightNo);
String flightKey = buildFlightRedisKey(resolvedFlightNo);
String bizRedisKey = buildBizRedisKey(normalizedBizKey);
boolean canUpdateRouteParams = normalizedBizKey != null;
if (flightKey != null || bizRedisKey != null) {
if (flightKey != null) {
try {
setValueOnFlightAndBizKeys(flightKey, bizRedisKey, "flightNumber", resolvedFlightNo);
if (normalizedBizKey != null) {
setValueOnFlightAndBizKeys(flightKey, bizRedisKey, "bizKey", normalizedBizKey);
}
updateActiveBizKey(flightKey, normalizedBizKey, nowMs);
redisCache.setCacheMapValue(flightKey, "flightNumber", resolvedFlightNo);
// contactCross 仅对进港有效避免进出港参数串用
if ("IN".equalsIgnoreCase(routeType)
&& contactCross != null && !contactCross.isBlank()) {
setValueOnFlightAndBizKeys(flightKey, bizRedisKey, "contactCross", contactCross);
setValueOnFlightAndBizKeys(flightKey, bizRedisKey, "contactCrossTs", String.valueOf(nowMs));
redisCache.setCacheMapValue(flightKey, "contactCross", contactCross);
redisCache.setCacheMapValue(flightKey, "contactCrossTs", String.valueOf(nowMs));
}
// TISFLIGHT 也可提供 seat作为 CRAFTSEAT 的补充来源
if (!canUpdateRouteParams && contactCross != null) {
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);
log.info("TISFLIGHT航班号解析(已应用三字转二字): flNoRaw={}, flightIdRaw={}, fuId={}, bizKey={}, resolvedFlightNo={}",
flightNumberRaw, flightIdRaw, fuId, bizKey, resolvedFlightNo);
} catch (Exception e) {
log.error("存储航班TISFLIGHT数据到Redis失败: {}", e.getMessage());
}

View File

@ -1149,11 +1149,6 @@ public class DataProcessingService {
normalizedBizKey = normalizeRedisString(redisCache.getCacheMapValue(flightKey, "activeBizKey"));
normalizedBizKey = normalizeBizKey(normalizedBizKey);
}
if (normalizedBizKey == null) {
log.info("路由Redis参数未齐全(缺少bizKey),等待补齐后重试: flightNo={}, routeType={}",
normalizedFlightNo, normalizedRouteType);
return false;
}
String bizRedisKey = buildBizRedisKey(normalizedBizKey);
// Redis 获取参数快照

View 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
1 IATA ICAO
2 PO CYW
3 PR PAL
4 PY PYN
5 RB RBW
6 C3 TDR
7 SC CDG
8 TG THA
9 TH TSE
10 TI TIS
11 TW TWB
12 UI UVS
13 US USA
14 VP CDF
15 WU CWU
16 8L LKE
17 YD YDH
18 YZ YZZ
19 ZG ZGG
20 ZH CSZ
21 ZJ CJG
22 ZZ ZZZ
23 WC WCC
24 HO DKH
25 SZ SZZ
26 CJ CJJ
27 XO XOO
28 3Q 3QQ
29 WD DSR
30 EY ETD
31 9C CQH
32 BK OKA
33 MK MKA
34 SN BEL
35 GJ CDC
36 OQ CQN
37 O3 CSS
38 Z2 EZD
39 VD VDD
40 GE TNA
41 XF VLK
42 QF QFA
43 QW QDA
44 QG CTV
45 SQ SIA
46 GX CBG
47 XW NCT
48 KJ AIH
49 D7 XAX
50 AZ AZA
51 TR TGW
52 5K HFY
53 DB MLT
54 6W FBS
55 8M MMA
56 FP PVV
57 NZ ANZ
58 V8 VAS
59 BO KLJ
60 RS ASV
61 B3 BLV
62 5B BSX
63 A6 OTC
64 GI LHA
65 GT CGH
66 HT CTJ
67 LT SNG
68 VN HVN
69 ZC RSN
70 ZA SWM
71 RY CJX
72 JR JOY
73 AQ JYH
74 9H CGN
75 E3 VGO
76 MZ MZT
77 GA GIA
78 DR RLH
79 UQ CUH
80 GD GSC
81 CN GDC
82 FU FZA
83 TV TBA
84 DL DAL
85 UO HKE
86 DZ EPA
87 1M AEB
88 BJ BJN
89 KN CUA
90 FE FEA
91 KA HDA
92 CG HZX
93 JF JAA
94 KL KLM
95 K9 TSP
96 B7 UIA
97 VI VDA
98 ZF ZFH
99 B8 B80
100 NS HBH
101 CI CAL
102 7C JJA
103 TZ SCO
104 LH DLH
105 FA FAV
106 G5 HXA
107 RA RAA
108 8C DXH
109 JD CBJ
110 EJ EJM
111 YV YVA
112 CQ MAJ
113 ES ESL
114 UN UNN
115 5X UPS
116 RU RUS
117 CK CKK
118 GS GCR
119 BH BHE
120 UA UUA
121 HX CRK
122 VJ VJC
123 J5 JJ5
124 KY KNA
125 AE MDA
126 LJ JNA
127 CF CYZ
128 BX ABL
129 EF DDD
130 Y8 YZR
131 PN CHB
132 BG ADB
133 YL YAL
134 NW VIA
135 IJ GWL
136 UW UTP
137 JH JHH
138 JS KOR
139 5Y GTI
140 FR FRT
141 3U CSC
142 AB ABD
143 CA CCA
144 KK CFZ
145 CX CPA
146 CZ CSN
147 DE DER
148 EU UEA
149 FD CFA
150 FM CSH
151 FX FDX
152 GP CTH
153 GY CGZ
154 HA HAH
155 HC CHC
156 HK EAS
157 HR CU2
158 HU CHH
159 JL JAL
160 KE KAL
161 LX LXH
162 MF CXA
163 MH MAS
164 MI SLK
165 MU CES
166 N2 MGG
167 NH ANA
168 NX AMU
169 OM OMA
170 OZ AAR
171 K4 CKS
172 8Y AAV
173 BR EVA
174 3V TAY
175 YG HYT
176 JB JBA
177 T8 TXC
178 I9 HLF
179 AY FIN
180 N8 NCR
181 8S GTR
182 JG JDL
183 VZ TVJ
184 DV VSV
185 FV SDM
186 IA IAW
187 RD RDA
188 SE URO
189 TE IGA
190 H9 HIM
191 SL TLM
192 C6 MFX
193 4B TUP
194 U6 SVR
195 AF AFR
196 MR 861
197 IO IAE
198 RF EOK

View File

@ -1,5 +1,6 @@
### 命令
```
#编译
mvn -pl qaup-admin -am clean package -DskipTests
```