|
|
@@ -13,7 +13,7 @@ export function parseFlightCode(code) {
|
|
|
let codeStr = code.trim()
|
|
|
|
|
|
// 去除开头的序号(如 "1."、"2."、"3."、"4." 等)
|
|
|
- codeStr = codeStr.replace(/^\d+\./, '')
|
|
|
+ codeStr = codeStr.replace(/^\d+\.\s*/, '')
|
|
|
|
|
|
if (codeStr.length === 0) {
|
|
|
return { error: '航班代码不能为空' }
|
|
|
@@ -22,6 +22,12 @@ export function parseFlightCode(code) {
|
|
|
// 按空格分割
|
|
|
const parts = codeStr.trim().split(/\s+/)
|
|
|
|
|
|
+ // 黑屏格式:航班号 舱位 星期日期 航线 状态 起飞 到达 [机型/经停/餐食/电子客票/航站楼等]
|
|
|
+ // 仅在原有字段数量无法正确解析,或末尾包含黑屏航站楼标识时启用,避免影响原有识别逻辑
|
|
|
+ if (shouldUseBlackScreenParser(parts)) {
|
|
|
+ return parseBlackScreenFormat(parts)
|
|
|
+ }
|
|
|
+
|
|
|
// 根据字段数量判断格式
|
|
|
if (parts.length === 9) {
|
|
|
// 格式: 航班号 操作码+日期 航线 起飞时间 到达时间 预订码 航站楼 机型 时长
|
|
|
@@ -34,21 +40,156 @@ export function parseFlightCode(code) {
|
|
|
// 格式: 航班号 操作码+日期 航线 起飞时间 到达时间 航站楼 预订码 机型 时长
|
|
|
// 例如: CA431 TU27MAY TFUFRA 0135 0645 T1 1 359 11H10M
|
|
|
return parseFormat11(parts)
|
|
|
+ } else if (parts.length === 12) {
|
|
|
+ // 格式: 航班号 舱位 操作码+日期 航线 状态码 起飞时间 到达时间 机型 经停 服务等级 航站楼 序号
|
|
|
+ // 例如: LH172 U WE10JUN FRABER DK1 0645 0755 321 0 E 1 1
|
|
|
+ return parseFormat12(parts)
|
|
|
} else {
|
|
|
return {
|
|
|
error: '航班代码格式错误,字段数量不正确',
|
|
|
- expected: '9、10或11个字段',
|
|
|
+ expected: '9、10、11、12个字段或标准黑屏格式',
|
|
|
received: parts.length + '个字段',
|
|
|
parts: parts
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * 判断是否为标准黑屏格式:
|
|
|
+ * 航班号 舱位 星期日期 航线 状态码 起飞时间 到达时间 ...
|
|
|
+ */
|
|
|
+function isBlackScreenFormat(parts) {
|
|
|
+ return parts.length >= 7 &&
|
|
|
+ /^[A-Z0-9]{2,8}$/.test(parts[0]) &&
|
|
|
+ /^[A-Z][A-Z0-9]?$/.test(parts[1]) &&
|
|
|
+ /^[A-Z]{2}\d{2}[A-Z]{3}$/.test(parts[2]) &&
|
|
|
+ /^[A-Z]{6}$/.test(parts[3]) &&
|
|
|
+ /^[A-Z]{2}\d+$/.test(parts[4]) &&
|
|
|
+ /^\d{4}$/.test(parts[5]) &&
|
|
|
+ /^\d{4}(\+\d+)?$/.test(parts[6])
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 是否使用新增黑屏解析器。
|
|
|
+ * - 7/13/14等原逻辑不支持的黑屏字段数直接启用
|
|
|
+ * - 10/11字段若第3列才是日期,原解析会错位,启用
|
|
|
+ * - 12字段保留原 parseFormat12;仅当扩展字段中出现 T2--、--T2、T1 等航站楼标识时启用
|
|
|
+ */
|
|
|
+function shouldUseBlackScreenParser(parts) {
|
|
|
+ if (!isBlackScreenFormat(parts)) return false
|
|
|
+
|
|
|
+ if (parts.length === 12) {
|
|
|
+ return parts.slice(7).some(isTerminalToken)
|
|
|
+ }
|
|
|
+
|
|
|
+ return parts.length !== 9
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 解析黑屏航班格式
|
|
|
+ * 例:
|
|
|
+ * MU5191 Y SA09AUG TFUJHG DK1 0925 1105 32L 0 R E T2--
|
|
|
+ * CA4418 Y WE13AUG KMGCTU DK1 1750 1925 32N 0 R E --T2
|
|
|
+ * 3U8673 B SU28SEP CTUKMG HK1 1350 1540
|
|
|
+ */
|
|
|
+function parseBlackScreenFormat(parts) {
|
|
|
+ const [
|
|
|
+ flightNumber,
|
|
|
+ cabin,
|
|
|
+ actionDate,
|
|
|
+ route,
|
|
|
+ statusCode,
|
|
|
+ departureTime,
|
|
|
+ arrivalTimeRaw,
|
|
|
+ ...extras
|
|
|
+ ] = parts
|
|
|
+
|
|
|
+ const actionCode = actionDate.slice(0, 2)
|
|
|
+ const dateStr = actionDate.slice(2)
|
|
|
+ const origin = route.slice(0, 3)
|
|
|
+ const destination = route.slice(3)
|
|
|
+
|
|
|
+ let arrivalTime = arrivalTimeRaw
|
|
|
+ let plusDays = 0
|
|
|
+ if (arrivalTimeRaw && arrivalTimeRaw.includes('+')) {
|
|
|
+ const [time, days] = arrivalTimeRaw.split('+')
|
|
|
+ arrivalTime = time
|
|
|
+ plusDays = parseInt(days)
|
|
|
+ }
|
|
|
+
|
|
|
+ const terminalInfo = parseBlackScreenTerminals(extras)
|
|
|
+ const nonTerminalExtras = extras.filter(item => !isTerminalToken(item))
|
|
|
+ const aircraft = nonTerminalExtras[0] || ''
|
|
|
+ const stopCount = nonTerminalExtras.find(item => /^\d+$/.test(item)) || ''
|
|
|
+ const remainingExtras = nonTerminalExtras.filter((item, index) => index !== 0 && item !== stopCount)
|
|
|
+ const serviceClass = remainingExtras.length > 0 ? remainingExtras[remainingExtras.length - 1] : ''
|
|
|
+ const sequence = remainingExtras.length > 1 ? remainingExtras.slice(0, -1).join(' ') : ''
|
|
|
+
|
|
|
+ const duration = calculateDuration(departureTime, arrivalTime, plusDays)
|
|
|
+
|
|
|
+ return buildFlightInfo({
|
|
|
+ flightNumber,
|
|
|
+ actionCode,
|
|
|
+ dateStr,
|
|
|
+ origin,
|
|
|
+ destination,
|
|
|
+ departureTime,
|
|
|
+ arrivalTime,
|
|
|
+ terminal: terminalInfo.arrivalTerminal,
|
|
|
+ bookingCode: terminalInfo.departureTerminal,
|
|
|
+ aircraft,
|
|
|
+ duration,
|
|
|
+ plusDays,
|
|
|
+ cabin,
|
|
|
+ statusCode,
|
|
|
+ stopCount,
|
|
|
+ serviceClass,
|
|
|
+ sequence,
|
|
|
+ blackScreenExtras: extras
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 黑屏航站楼标识:
|
|
|
+ * T2-- 表示出发航站楼 T2;--T2 表示到达航站楼 T2;T1 作为单独末尾字段时按到达航站楼处理
|
|
|
+ */
|
|
|
+function isTerminalToken(value) {
|
|
|
+ return /^T\d+--$/.test(value) ||
|
|
|
+ /^--T\d+$/.test(value) ||
|
|
|
+ /^T\d+$/.test(value) ||
|
|
|
+ /^T\d+T\d+$/.test(value)
|
|
|
+}
|
|
|
+
|
|
|
+function parseBlackScreenTerminals(extras) {
|
|
|
+ let departureTerminal = ''
|
|
|
+ let arrivalTerminal = ''
|
|
|
+
|
|
|
+ extras.forEach(item => {
|
|
|
+ if (/^T\d+--$/.test(item)) {
|
|
|
+ departureTerminal = item.replace('--', '')
|
|
|
+ } else if (/^--T\d+$/.test(item)) {
|
|
|
+ arrivalTerminal = item.replace('--', '')
|
|
|
+ } else if (/^T\d+$/.test(item)) {
|
|
|
+ arrivalTerminal = item
|
|
|
+ } else if (/^T\d+T\d+$/.test(item)) {
|
|
|
+ const terminals = item.match(/T\d+/g) || []
|
|
|
+ departureTerminal = terminals[0] || departureTerminal
|
|
|
+ arrivalTerminal = terminals[1] || arrivalTerminal
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ return {
|
|
|
+ departureTerminal,
|
|
|
+ arrivalTerminal
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* 解析9字段格式(最常用)
|
|
|
* CA878 SA31MAY CPHPEK 1905 1000+1 3 T3 359 08H55M
|
|
|
*/
|
|
|
function parseFormat9(parts) {
|
|
|
+ console.log(parts) // 调试用,确认文件被正确加载
|
|
|
const [flightNumber, actionDate, route, departureTime, arrivalTimeRaw, bookingCode, terminal, aircraft, duration] = parts
|
|
|
|
|
|
// 提取操作码和日期
|
|
|
@@ -137,6 +278,77 @@ function parseFormat11(parts) {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * 解析12字段格式
|
|
|
+ * LH172 U WE10JUN FRABER DK1 0645 0755 321 0 E 1 1
|
|
|
+ */
|
|
|
+function parseFormat12(parts) {
|
|
|
+ const [
|
|
|
+ flightNumber,
|
|
|
+ cabin,
|
|
|
+ actionDate,
|
|
|
+ route,
|
|
|
+ statusCode,
|
|
|
+ departureTime,
|
|
|
+ arrivalTimeRaw,
|
|
|
+ aircraft,
|
|
|
+ stopCount,
|
|
|
+ serviceClass,
|
|
|
+ terminal,
|
|
|
+ sequence
|
|
|
+ ] = parts
|
|
|
+
|
|
|
+ const actionCode = actionDate.slice(0, 2)
|
|
|
+ const dateStr = actionDate.slice(2)
|
|
|
+ const origin = route.slice(0, 3)
|
|
|
+ const destination = route.slice(3)
|
|
|
+
|
|
|
+ let arrivalTime = arrivalTimeRaw
|
|
|
+ let plusDays = 0
|
|
|
+ if (arrivalTimeRaw && arrivalTimeRaw.includes('+')) {
|
|
|
+ const [time, days] = arrivalTimeRaw.split('+')
|
|
|
+ arrivalTime = time
|
|
|
+ plusDays = parseInt(days)
|
|
|
+ }
|
|
|
+
|
|
|
+ const duration = calculateDuration(departureTime, arrivalTime, plusDays)
|
|
|
+
|
|
|
+ return buildFlightInfo({
|
|
|
+ flightNumber, actionCode, dateStr, origin, destination,
|
|
|
+ departureTime, arrivalTime, terminal, bookingCode: statusCode, aircraft, duration,
|
|
|
+ plusDays, cabin, statusCode, stopCount, serviceClass, sequence
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 根据起飞和到达时间计算航班时长
|
|
|
+ */
|
|
|
+function calculateDuration(departureTime, arrivalTime, plusDays = 0) {
|
|
|
+ const depHour = parseInt(departureTime.slice(0, 2))
|
|
|
+ const depMinute = parseInt(departureTime.slice(2))
|
|
|
+ const arrHour = parseInt(arrivalTime.slice(0, 2))
|
|
|
+ const arrMinute = parseInt(arrivalTime.slice(2))
|
|
|
+
|
|
|
+ if ([depHour, depMinute, arrHour, arrMinute].some(value => Number.isNaN(value))) {
|
|
|
+ return '00H00M'
|
|
|
+ }
|
|
|
+
|
|
|
+ const depTotalMinutes = depHour * 60 + depMinute
|
|
|
+ const arrTotalMinutes = arrHour * 60 + arrMinute
|
|
|
+ let totalMinutes = arrTotalMinutes - depTotalMinutes
|
|
|
+
|
|
|
+ if (plusDays > 0) {
|
|
|
+ totalMinutes += plusDays * 24 * 60
|
|
|
+ } else if (totalMinutes < 0) {
|
|
|
+ totalMinutes += 24 * 60
|
|
|
+ }
|
|
|
+
|
|
|
+ const hours = Math.floor(totalMinutes / 60)
|
|
|
+ const minutes = totalMinutes % 60
|
|
|
+
|
|
|
+ return hours.toString().padStart(2, '0') + 'H' + minutes.toString().padStart(2, '0') + 'M'
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* 构建航班信息对象
|
|
|
*/
|
|
|
@@ -144,7 +356,7 @@ function buildFlightInfo(params) {
|
|
|
const {
|
|
|
flightNumber, actionCode, dateStr, origin, destination,
|
|
|
departureTime, arrivalTime, terminal, bookingCode, aircraft, duration,
|
|
|
- plusDays = 0
|
|
|
+ plusDays = 0, cabin, statusCode, stopCount, serviceClass, sequence
|
|
|
} = params
|
|
|
|
|
|
// 解析日期
|
|
|
@@ -220,6 +432,11 @@ function buildFlightInfo(params) {
|
|
|
formatted: formatTime(arrHour, arrMinute)
|
|
|
},
|
|
|
terminal: terminal,
|
|
|
+ cabin: cabin,
|
|
|
+ statusCode: statusCode,
|
|
|
+ stopCount: stopCount,
|
|
|
+ serviceClass: serviceClass,
|
|
|
+ sequence: sequence,
|
|
|
// bookingCode: parseInt(bookingCode),
|
|
|
bookingCode: bookingCode,
|
|
|
aircraft: aircraft,
|
|
|
@@ -382,7 +599,11 @@ function getAirportName(code) {
|
|
|
*/
|
|
|
export function validateFlightCode(code) {
|
|
|
if (!code || typeof code !== 'string') return false
|
|
|
- var cleanCode = code.trim().replace(/^\d+\./, '')
|
|
|
+ var cleanCode = code.trim().replace(/^\d+\.\s*/, '')
|
|
|
var parts = cleanCode.split(/\s+/)
|
|
|
- return parts.length === 9 || parts.length === 10 || parts.length === 11
|
|
|
-}
|
|
|
+ return parts.length === 9 ||
|
|
|
+ parts.length === 10 ||
|
|
|
+ parts.length === 11 ||
|
|
|
+ parts.length === 12 ||
|
|
|
+ isBlackScreenFormat(parts)
|
|
|
+}
|