未测试:添加航司二三字码映射功能,支持航班号转换
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 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());
|
||||
}
|
||||
|
||||
@ -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 获取参数快照
|
||||
|
||||
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