Explorar el Código

航班解析增强,新增IsNew字段并调整新数据逻辑

增强FlightParser对多格式航班文本的解析能力,优化航站楼、机型、时长等字段提取。Grp_AirTicketReservations实体新增IsNew字段,相关业务逻辑统一以IsNew==1判断新数据,替换原有CreateTime判断。同步调整控制器和仓储中的新数据处理逻辑,提升兼容性和健壮性。
Lyyyi hace 10 horas
padre
commit
ce82936eff

+ 1 - 1
OASystem/OASystem.Api/Controllers/GroupsController.cs

@@ -11626,7 +11626,7 @@ FROM
                                 }
 
                                 // 2026 版 费用名称 处理(新版机票费用格式、费用创建时间))
-                                if (jpRes.AirTicketBasicInfos.Any() && jpRes.CreateTime >= GlobalConfig.AirTicketIntegrationDateTime)
+                                if (jpRes.IsNew == 1)
                                 {
                                     var flights = FlightParser.ParseFlights(jpRes.FlightsDescription);
                                     var flightNumbers = flights.Select(x => x.FlightNumber).ToList();

+ 1 - 1
OASystem/OASystem.Api/Controllers/StatisticsController.cs

@@ -868,7 +868,7 @@ ORDER BY
                     var currFlight = flights.Where(it => it.Id == item.AirId).FirstOrDefault();
                     if (currFlight != null)
                     {
-                        if (currFlight.AirTicketBasicInfos.Any() && currFlight.CreateTime >= GlobalConfig.AirTicketIntegrationDateTime)
+                        if (currFlight.IsNew == 1)
                         {
                             item.FlightsCode = string.Join("、", currFlight.AirTicketBasicInfos.Select(x => x.FlightsCode).ToList());
                             item.FlightsCity = string.Join("/", currFlight.AirTicketBasicInfos.Select(x => x.FlightsCity).ToList());

+ 158 - 146
OASystem/OASystem.Domain/Entities/Groups/Grp_AirTicketReservations.cs

@@ -16,6 +16,13 @@ public class Grp_AirTicketReservations : EntityBase
     [SugarColumn(IsNullable = true, ColumnDataType = "int")]
     public int DIId { get; set; }
 
+
+    /// <summary>
+    /// 数据标识:0-旧数据 1-新数据
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+    public int IsNew { get; set; }
+
     /// <summary>
     /// 记录类型:0-正常机票 1-退票记录
     /// </summary>
@@ -452,18 +459,10 @@ public class FlightInfo
 }
 
 /// <summary>
-/// 航班解析器
-/// 支持格式:
-/// 1.CA431 TU27MAY TFUFRA 0135 0645 T1 1 359 11H10M
-/// 2.LH098 TU28MAY FRAMUC 0915 1010 1 2 321 00H55M
-/// 3.LH2440 TH29MAY MUCCPH 1025 1200 2 2 32A 01H35M
-/// 4.CA878 SA31MAY CPHPEK 1905 1000+1 3 T3 359 08H55M
+/// 航班解析器 - 支持多种格式
 /// </summary>
 public static class FlightParser
 {
-    /// <summary>
-    /// 月份映射表
-    /// </summary>
     private static readonly Dictionary<string, int> MonthMap = new()
     {
         ["JAN"] = 1,
@@ -480,10 +479,6 @@ public static class FlightParser
         ["DEC"] = 12
     };
 
-    /// <summary>
-    /// 解析航班信息
-    /// 自动推断年份
-    /// </summary>
     public static List<FlightInfo> ParseFlights(string text)
     {
         var result = new List<FlightInfo>();
@@ -491,11 +486,7 @@ public static class FlightParser
         if (string.IsNullOrWhiteSpace(text))
             return result;
 
-        // 支持:
-        // 1.CA431 ... 2.LH098 ...
-        // 或
-        // 1.CA431 ...
-        // 2.LH098 ...
+        // 匹配所有以数字开头的行
         var matches = Regex.Matches(
             text,
             @"\d+\..*?(?=\d+\.|$)",
@@ -504,31 +495,21 @@ public static class FlightParser
         foreach (Match match in matches)
         {
             var flight = ParseLine(match.Value.Trim());
-
             if (flight != null)
             {
                 result.Add(flight);
             }
         }
 
-        // 按序号排序
-        result = result
-            .OrderBy(x => x.SequenceNo)
-            .ToList();
-
-        // 自动修正跨年
+        result = result.OrderBy(x => x.SequenceNo).ToList();
         FixYear(result);
 
         return result;
     }
 
-    /// <summary>
-    /// 解析单条航班
-    /// </summary>
     private static FlightInfo ParseLine(string line)
     {
         var seqMatch = Regex.Match(line, @"^(\d+)\.");
-
         if (!seqMatch.Success)
             return null;
 
@@ -539,136 +520,179 @@ public static class FlightParser
         };
 
         string content = line.Substring(seqMatch.Length).Trim();
+        var parts = Regex.Split(content, @"\s+")
+            .Where(p => !string.IsNullOrWhiteSpace(p))
+            .ToList();
 
-        var parts = Regex.Split(content, @"\s+");
-
-        if (parts.Length < 5)
+        if (parts.Count < 5)
             return null;
 
         int idx = 0;
 
-        // 航班号
+        // 1. 航班号
         flight.FlightNumber = parts[idx++];
 
-        // 日期
+        // 2. 跳过可选的舱位代码(单个大写字母)
+        if (idx < parts.Count && Regex.IsMatch(parts[idx], @"^[A-Z]$"))
+        {
+            idx++;
+        }
+
+        // 3. 解析日期(格式:TU23JUN 或 TU27MAY)
+        if (idx >= parts.Count)
+            return null;
+
         string dateText = parts[idx++];
 
-        flight.WeekDay = dateText[..2];
+        // 提取星期和日期
+        var dateMatch = Regex.Match(dateText, @"^([A-Z]{2})(\d{2})([A-Z]{3})$");
+        if (dateMatch.Success)
+        {
+            flight.WeekDay = dateMatch.Groups[1].Value;
+            flight.DepartureDate = ParseDate(
+                int.Parse(dateMatch.Groups[2].Value),
+                dateMatch.Groups[3].Value
+            );
+        }
+        else
+        {
+            // 兼容旧格式:TU27MAY 直接解析
+            flight.WeekDay = dateText.Length >= 2 ? dateText[..2] : "";
+            flight.DepartureDate = ParseMonthDay(dateText);
+        }
 
-        // 这里只解析月日
-        flight.DepartureDate = ParseMonthDay(dateText);
+        // 4. 航线(6位:出发+到达)
+        if (idx >= parts.Count)
+            return null;
 
-        // 航线
         string route = parts[idx++];
-
         if (route.Length >= 6)
         {
             flight.DepartureAirport = route[..3];
             flight.ArrivalAirport = route.Substring(3, 3);
         }
 
-        // 起飞时间
+        // 5. 跳过预订状态码(如 HK4, HK, KL 等)
+        if (idx < parts.Count && Regex.IsMatch(parts[idx], @"^[A-Z]{2}\d*$"))
+        {
+            idx++;
+        }
+
+        // 6. 起飞时间
+        if (idx >= parts.Count)
+            return null;
         flight.DepartureTime = FormatTime(parts[idx++]);
 
-        // 到达时间
+        // 7. 到达时间
+        if (idx >= parts.Count)
+            return null;
+
         string arrivalRaw = parts[idx++];
+        ParseArrivalTime(arrivalRaw, flight);
 
+        // 8. 跳过可选的舱位代码(如 E)
+        if (idx < parts.Count && Regex.IsMatch(parts[idx], @"^[A-Z]$"))
+        {
+            idx++;
+        }
+
+        // 9. 解析剩余字段(航站楼、机型、飞行时长)
+        var remain = parts.Skip(idx).ToList();
+        ParseRemainFields(remain, flight);
+
+        return flight;
+    }
+
+    /// <summary>
+    /// 解析到达时间(支持 +1 格式)
+    /// </summary>
+    private static void ParseArrivalTime(string arrivalRaw, FlightInfo flight)
+    {
         if (arrivalRaw.Contains('+'))
         {
             var arr = arrivalRaw.Split('+');
-
             flight.ArrivalTime = FormatTime(arr[0]);
-
             flight.IsNextDayArrival = true;
-
-            if (arr.Length > 1)
+            if (arr.Length > 1 && int.TryParse(arr[1], out int days))
             {
-                flight.NextDayCount = int.Parse(arr[1]);
+                flight.NextDayCount = days;
             }
         }
         else
         {
             flight.ArrivalTime = FormatTime(arrivalRaw);
         }
-
-        // 剩余字段
-        var remain = parts.Skip(idx).ToList();
-
-        ParseRemainFields(remain, flight);
-
-        return flight;
     }
 
-    ///// <summary>
-    ///// 
-    ///// </summary>
-    ///// <param name="remain">
-    ///// 例如:
-    ///// T1 1 359 11H10M
-    ///// 1 2 321 00H55M
-    ///// 3 T3 359 08H55M
-    ///// </param>
-    ///// 
-
     /// <summary>
-    /// 解析剩余字段
+    /// 解析剩余字段(航站楼、机型、飞行时长)
     /// </summary>
-    /// <param name="remain">
-    /// 例如:
-    /// T1 1 359 11H10M
-    /// 1 2 321 00H55M
-    /// 3 T3 359 08H55M
-    /// </param>
-    /// <param name="flight"></param>
-    private static void ParseRemainFields(
-        List<string> remain,
-        FlightInfo flight)
+    private static void ParseRemainFields(List<string> remain, FlightInfo flight)
     {
-        if (remain.Count < 2)
+        if (remain.Count == 0)
             return;
 
-        // 最后一个字段一般是飞行时长
-        if (Regex.IsMatch(remain[^1], @"^\d{2}H\d{2}M$"))
-        {
-            flight.Duration = remain[^1];
+        // 从后往前解析
+        int idx = remain.Count - 1;
 
-            remain.RemoveAt(remain.Count - 1);
+        // 1. 最后一个字段:飞行时长(格式:11H10M 或 00H55M)
+        if (idx >= 0 && Regex.IsMatch(remain[idx], @"^\d{2}H\d{2}M$"))
+        {
+            flight.Duration = remain[idx];
+            idx--;
         }
 
-        // 倒数第二个字段一般是机型
-        if (remain.Count > 0 &&
-            Regex.IsMatch(remain[^1], @"^\d{2,3}[A-Z]?$"))
+        // 2. 倒数第二个字段:机型(格式:359, 321, 32A, 737 等)
+        if (idx >= 0 && Regex.IsMatch(remain[idx], @"^[A-Z]?\d{2,3}[A-Z]?$"))
         {
-            flight.AircraftType = remain[^1];
-
-            remain.RemoveAt(remain.Count - 1);
+            flight.AircraftType = remain[idx];
+            idx--;
         }
 
-        // 剩余字段解析为航站楼
-        if (remain.Count == 1)
+        // 3. 剩余字段解析航站楼
+        // 情况1:一个航站楼(出发和到达相同)
+        if (idx == 0)
         {
-            flight.DepartureTerminal =
-                NormalizeTerminal(remain[0]);
+            flight.DepartureTerminal = NormalizeTerminal(remain[0]);
+            flight.ArrivalTerminal = flight.DepartureTerminal;
         }
-        else if (remain.Count >= 2)
+        // 情况2:两个航站楼(出发和到达不同)
+        else if (idx >= 1)
         {
-            flight.DepartureTerminal =
-                NormalizeTerminal(remain[0]);
+            // 检查是否是两个航站楼
+            string terminal1 = remain[0];
+            string terminal2 = remain[1];
 
-            flight.ArrivalTerminal =
-                NormalizeTerminal(remain[1]);
+            // 如果两个都是航站楼格式(T1, T2, 1, 2, T3等)
+            if (IsTerminalFormat(terminal1) && IsTerminalFormat(terminal2))
+            {
+                flight.DepartureTerminal = NormalizeTerminal(terminal1);
+                flight.ArrivalTerminal = NormalizeTerminal(terminal2);
+            }
+            // 如果第一个是航站楼,第二个不是,则只解析第一个
+            else if (IsTerminalFormat(terminal1))
+            {
+                flight.DepartureTerminal = NormalizeTerminal(terminal1);
+            }
         }
     }
 
+    /// <summary>
+    /// 判断是否为航站楼格式
+    /// </summary>
+    private static bool IsTerminalFormat(string value)
+    {
+        if (string.IsNullOrWhiteSpace(value))
+            return false;
+
+        value = value.Trim().ToUpper();
+        // 匹配:T1, T2, T3, 1, 2, 3 等
+        return Regex.IsMatch(value, @"^T?\d+$");
+    }
+
     /// <summary>
     /// 标准化航站楼
     /// </summary>
-    /// <param name="terminal">
-    /// 原始值:1、2、 T3
-    /// </param>
-    /// <returns>
-    /// T1、T2、T3
-    /// </returns>
     private static string NormalizeTerminal(string terminal)
     {
         if (string.IsNullOrWhiteSpace(terminal))
@@ -676,7 +700,7 @@ public static class FlightParser
 
         terminal = terminal.Trim().ToUpper();
 
-        // 纯数字补T
+        // 纯数字补 T
         if (Regex.IsMatch(terminal, @"^\d+$"))
         {
             return $"T{terminal}";
@@ -687,8 +711,6 @@ public static class FlightParser
 
     /// <summary>
     /// 格式化时间
-    /// 0135 -> 01:35
-    /// 905 -> 09:05
     /// </summary>
     private static string FormatTime(string time)
     {
@@ -710,17 +732,44 @@ public static class FlightParser
         return time;
     }
 
+    /// <summary>
+    /// 解析日期
+    /// </summary>
+    private static DateTime ParseDate(int day, string month)
+    {
+        if (!MonthMap.TryGetValue(month.ToUpper(), out int monthNum))
+            return DateTime.MinValue;
+
+        return new DateTime(DateTime.Now.Year, monthNum, day);
+    }
+
+    /// <summary>
+    /// 解析月日(兼容旧格式)
+    /// </summary>
+    private static DateTime ParseMonthDay(string value)
+    {
+        value = Regex.Replace(value, @"^[A-Z]{2}", "");
+
+        var match = Regex.Match(value, @"(\d{2})([A-Z]{3})");
+        if (!match.Success)
+            return DateTime.MinValue;
+
+        int day = int.Parse(match.Groups[1].Value);
+        if (!MonthMap.TryGetValue(match.Groups[2].Value, out int month))
+            return DateTime.MinValue;
+
+        return new DateTime(DateTime.Now.Year, month, day);
+    }
+
     /// <summary>
     /// 自动修正跨年
     /// </summary>
-    private static void FixYear(
-        List<FlightInfo> flights)
+    private static void FixYear(List<FlightInfo> flights)
     {
         if (flights.Count <= 1)
             return;
 
         int currentYear = DateTime.Now.Year;
-
         DateTime previousDate = DateTime.MinValue;
 
         foreach (var flight in flights)
@@ -733,9 +782,7 @@ public static class FlightParser
                 flight.DepartureDate.Month,
                 flight.DepartureDate.Day);
 
-            // 出现日期倒退
-            if (previousDate != DateTime.MinValue &&
-                date < previousDate)
+            if (previousDate != DateTime.MinValue && date < previousDate)
             {
                 currentYear++;
                 date = new DateTime(
@@ -745,42 +792,7 @@ public static class FlightParser
             }
 
             flight.DepartureDate = date;
-
             previousDate = date;
         }
     }
-
-    /// <summary>
-    /// 解析月日
-    /// TU27MAY -> 05-27
-    /// </summary>
-    private static DateTime ParseMonthDay(string value)
-    {
-        value = Regex.Replace(
-            value,
-            @"^[A-Z]{2}",
-            "");
-
-        var match = Regex.Match(
-            value,
-            @"(\d{2})([A-Z]{3})");
-
-        if (!match.Success)
-            return DateTime.MinValue;
-
-        int day = int.Parse(match.Groups[1].Value);
-
-        if (!MonthMap.TryGetValue(
-                match.Groups[2].Value,
-                out int month))
-        {
-            return DateTime.MinValue;
-        }
-
-        // 先使用当前年份
-        return new DateTime(
-            DateTime.Now.Year,
-            month,
-            day);
-    }
 }

+ 2 - 2
OASystem/OASystem.Infrastructure/Repositories/Groups/AirTicketResRepository.cs

@@ -2619,7 +2619,7 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
                 item.ClientName = string.Join("、", clientNames);
 
                 // 新数据单独处理
-                if (item.CreateTime < GlobalConfig.AirTicketIntegrationDateTime) continue;
+                if (item.IsNew == 0) continue;
 
                 // 航段文本深度解析
                 var flightInfoList = FlightParser.ParseFlights(item.FlightsDescription);
@@ -2739,7 +2739,7 @@ Order By a.CreateTime desc", dto.DiId);
             item.ClientName = string.Join("、", clientNames);
 
             // 新数据单独处理
-            if (item.CreateTime < GlobalConfig.AirTicketIntegrationDateTime) continue;
+            if (item.IsNew == 0) continue;
 
             // 航段文本深度解析
             var flightInfoList = FlightParser.ParseFlights(item.FlightsDescription);