Lyyyi 2 dni temu
rodzic
commit
193a691ba3

+ 49 - 1
OASystem/OASystem.Api/Controllers/BusinessController.cs

@@ -1,6 +1,7 @@
 using OASystem.API.OAMethodLib;
 using OASystem.API.OAMethodLib.ALiYun;
 using OASystem.API.OAMethodLib.ExcelOutput;
+using OASystem.Domain.AesEncryption;
 using OASystem.Domain.Dtos.Business;
 using OASystem.Domain.Dtos.CRM;
 using OASystem.Domain.Dtos.FileDto;
@@ -170,7 +171,6 @@ namespace OASystem.API.Controllers
             return Ok(JsonView(_view));
         }
 
-
         /// <summary>
         /// 查询团组简略详情列表
         /// Page 根据Ctable And User 返回可操作的团
@@ -186,6 +186,54 @@ namespace OASystem.API.Controllers
 
         #endregion
 
+        #region 接团客户名单 2026版
+
+        /// <summary>
+        /// 接团客户名单 2026版
+        /// 根据团组Id查询客户信息
+        /// </summary>
+        /// <param name="groupId"></param>
+        /// <returns></returns>
+        [HttpGet("/api/group/client/list/{groupId}")]
+        [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
+        public async Task<IActionResult> GetTourClientListByDiId(int groupId)
+        {
+            if (groupId < 1) return Ok(JsonView(false, "请传入有效的团组Id"));
+
+            var result = await _busRep._sqlSugar.Queryable<Grp_TourClientList>()
+                .LeftJoin<Crm_DeleClient>((x, y) => x.ClientId == y.Id)
+                .LeftJoin<Crm_CustomerCert>((x, y, z) => y.Id == z.DcId && z.PassportType == 773)
+                .Where((x, y, z) => x.DiId == groupId && x.IsDel == 0 && y.IsDel == 0)
+                .Select((x, y, z) => new
+                {
+                    TourClientId = x.Id,
+                    CrmId = y.Id,
+                    y.FirstName,
+                    y.LastName,
+                    y.Pinyin,
+                    y.BirthDay,
+                    z.CertNo,
+                    y.Sex,
+                    x.ShippingSpaceTypeId
+                }).ToListAsync();
+
+
+            var view = result.Select(x =>
+            {
+                string firstName = AesEncryptionHelper.Decrypt(x.FirstName);
+                string lastName = AesEncryptionHelper.Decrypt(x.LastName);
+                string pinyin = AesEncryptionHelper.Decrypt(x.Pinyin);
+
+                return new { x.TourClientId, x.CrmId, firstName , lastName, pinyin,x.CertNo, x.BirthDay, x.Sex, x.ShippingSpaceTypeId };
+            }).ToList();
+
+            if(result.Any()) return Ok(JsonView(true, "查询成功", view));
+
+            return Ok(JsonView(false, "暂无数据"));
+        }
+
+        #endregion
+
         #region 币种 List
         /// <summary>
         /// 币种 List

Plik diff jest za duży
+ 1001 - 248
OASystem/OASystem.Api/Controllers/GroupsController.cs


+ 6 - 4
OASystem/OASystem.Api/Controllers/PersonnelModuleController.cs

@@ -2211,11 +2211,13 @@ namespace OASystem.API.Controllers
             //企业微信消息通知
             if (res.Code == StatusCodes.Status200OK && dto.Id < 1)
             {
-                var signProperty = res.Data?.GetType().GetProperty("sign");
-                int sign = signProperty != null && signProperty.GetValue(res.Data, null) is int value ? value : 0;
+                _ = Task.Run(async () => {
+                    var signProperty = res.Data?.GetType().GetProperty("sign");
+                    int sign = signProperty != null && signProperty.GetValue(res.Data, null) is int value ? value : 0;
 
-                //物资领用通知
-                await AppNoticeLibrary.SendUserMsg_CompanyShare_ToHR(sign, dto.GroupId, dto.CurrUserId);
+                    //物资领用通知
+                    await AppNoticeLibrary.SendUserMsg_CompanyShare_ToHR(sign, dto.GroupId, dto.CurrUserId);
+                });
             }
 
             return Ok(res);

+ 16 - 0
OASystem/OASystem.Api/Controllers/StatisticsController.cs

@@ -857,9 +857,25 @@ ORDER BY
                 //    GWCAveragePrice = (GWCCNYTotalPrice / GWCPeopleNum).ConvertToDecimal1();
                 //}
 
+                // 原始数据
+                var flights = await _sqlSugar.Queryable<Grp_AirTicketReservations>().Where(it => it.DIId == _dto.DiId && it.IsDel == 0).ToListAsync();
+
                 int Index = 0;
                 foreach (var item in groupAirFeeViews)
                 {
+                    // 2026版处理 航班信息
+                    var currFlight = flights.Where(it => it.Id == item.AirId).FirstOrDefault();
+                    if (currFlight != null)
+                    {
+                        if (currFlight.CreateTime >= Convert.ToDateTime("2026-01-01 00:00:00"))
+                        {
+                            item.FlightsCode = string.Join("、", currFlight.AirTicketBasicInfos.Select(x => x.FlightsCode).ToList());
+                            item.FlightsCity = string.Join("/", currFlight.AirTicketBasicInfos.Select(x => x.FlightsCity).ToList());
+                            item.FlightsDate = currFlight.AirTicketBasicInfos.FirstOrDefault()?.FlightsDate ?? "";
+                            item.FlightsTime = currFlight.AirTicketBasicInfos.FirstOrDefault()?.FlightsTime ?? "";
+                        }
+                    }
+
                     if (item.AirId > 2924)
                     {
                         string itemClientName = "";

+ 12 - 0
OASystem/OASystem.Domain/Common/GlobalConfig.cs

@@ -0,0 +1,12 @@
+namespace OASystem.Domain.Common;
+
+/// <summary>
+/// 全局配置类
+/// </summary>
+public static class GlobalConfig
+{
+    /// <summary>
+    /// 机票老、新数据整合时间点
+    /// </summary>
+    public static readonly DateTime AirTicketIntegrationDateTime = new DateTime(2026, 6, 3, 0, 0, 0);
+}

+ 13 - 0
OASystem/OASystem.Domain/Dtos/Groups/AirTicketResDto.cs

@@ -346,6 +346,10 @@ public class CountryDataTime
     }
 }
 
+public class BlackCodeDto
+{
+    public string Codes { get; set; }
+}
 
 public class AirTicketFeeListDto : AirTicketResDto
 { }
@@ -380,3 +384,12 @@ public class CancelClientRefundDto : AirTicketCostTripUserDelDto
 { 
     
 }
+
+
+public class AirTicketCostDeriveDto:DtoBase
+{
+    public int DiId { get; set; } = 0;
+public int UserId { get; set; } = 0;
+public int IsPaySign { get; set; } = -1;
+
+}

+ 334 - 292
OASystem/OASystem.Domain/Entities/Groups/Grp_AirTicketReservations.cs

@@ -225,6 +225,26 @@ public class AirTicketBasicInfo
     /// </summary>
     public string ArrivedTime { get; set; }
 
+    /// <summary>
+    /// 出发航站楼
+    /// </summary>
+    public string DepartureTerminal { get; set; }
+
+    /// <summary>
+    /// 抵达航站楼
+    /// </summary>
+    public string ArrivalTerminal { get; set; }
+
+    /// <summary>
+    /// 机型(如:359、321、32A、73H)
+    /// </summary>
+    public string AircraftType { get; set; }
+
+    /// <summary>
+    /// 耗时(如:11H10M、00H55M、01H35M)
+    /// </summary>
+    public string Duration { get; set; }
+
 }
 
 /// <summary>
@@ -339,406 +359,428 @@ public class AdditionalService
 /// </summary>
 public class FlightInfo
 {
-    public int SequenceNo { get; set; }           // 序号
-    public string FlightNumber { get; set; }      // 航班号
-    public string CabinClass { get; set; }        // 舱位等级
-    public string WeekDay { get; set; }           // 星期(MO,TU,WE,TH,FR,SA,SU)
-    public string DepartureDateRaw { get; set; }  // 原始日期字符串
-    public DateTime DepartureDate { get; set; }   // 起飞日期
-    public string DepartureTimeRaw { get; set; }  // 原始起飞时间
-    public string DepartureTime { get; set; }     // 格式化起飞时间
-    public string ArrivalTimeRaw { get; set; }    // 原始到达时间
-    public string ArrivalTime { get; set; }       // 格式化到达时间
-    public string DepartureAirport { get; set; }  // 起飞机场
-    public string ArrivalAirport { get; set; }    // 到达机场
-    public bool IsNextDayArrival { get; set; }    // 是否次日到达
-    public int NextDayCount { get; set; }         // 跨天天数(+1表示第二天)
-    public string Terminal { get; set; }          // 航站楼
-    public string AircraftType { get; set; }      // 机型
-    public string Duration { get; set; }          // 飞行时长
-    public string Status { get; set; }            // 状态(HK1,KK1,TK1等)
-    public string RawText { get; set; }           // 原始文本
+    /// <summary>
+    /// 航段序号
+    /// </summary>
+    public int SequenceNo { get; set; }
+
+    /// <summary>
+    /// 航班号
+    /// 例如:CA431
+    /// </summary>
+    public string FlightNumber { get; set; }
+
+    /// <summary>
+    /// 星期
+    /// 例如:TU
+    /// </summary>
+    public string WeekDay { get; set; }
+
+    /// <summary>
+    /// 起飞日期
+    /// </summary>
+    public DateTime DepartureDate { get; set; }
+
+    /// <summary>
+    /// 出发机场三字码
+    /// 例如:TFU
+    /// </summary>
+    public string DepartureAirport { get; set; }
+
+    /// <summary>
+    /// 到达机场三字码
+    /// 例如:FRA
+    /// </summary>
+    public string ArrivalAirport { get; set; }
+
+    /// <summary>
+    /// 起飞时间
+    /// 格式:HH:mm
+    /// </summary>
+    public string DepartureTime { get; set; }
+
+    /// <summary>
+    /// 到达时间
+    /// 格式:HH:mm
+    /// </summary>
+    public string ArrivalTime { get; set; }
+
+    /// <summary>
+    /// 是否跨天到达
+    /// </summary>
+    public bool IsNextDayArrival { get; set; }
+
+    /// <summary>
+    /// 跨天数量
+    /// 例如:
+    /// +1 表示次日到达
+    /// +2 表示第三天到达
+    /// </summary>
+    public int NextDayCount { get; set; }
+
+    /// <summary>
+    /// 出发航站楼
+    /// 例如:T1
+    /// </summary>
+    public string DepartureTerminal { get; set; }
+
+    /// <summary>
+    /// 到达航站楼
+    /// 例如:T2
+    /// </summary>
+    public string ArrivalTerminal { get; set; }
+
+    /// <summary>
+    /// 机型代码
+    /// 例如:
+    /// 359=A350-900
+    /// 321=A321
+    /// 32A=A320neo
+    /// </summary>
+    public string AircraftType { get; set; }
+
+    /// <summary>
+    /// 飞行时长
+    /// 例如:11H10M
+    /// </summary>
+    public string Duration { get; set; }
+
+    /// <summary>
+    /// 原始文本
+    /// </summary>
+    public string RawText { get; set; }
 }
 
 /// <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
 {
-    #region 常量定义
-
     /// <summary>
-    /// 月份映射
+    /// 月份映射表
     /// </summary>
     private static readonly Dictionary<string, int> MonthMap = new()
     {
-        { "JAN", 1 }, { "FEB", 2 }, { "MAR", 3 }, { "APR", 4 },
-        { "MAY", 5 }, { "JUN", 6 }, { "JUL", 7 }, { "AUG", 8 },
-        { "SEP", 9 }, { "OCT", 10 }, { "NOV", 11 }, { "DEC", 12 }
+        ["JAN"] = 1,
+        ["FEB"] = 2,
+        ["MAR"] = 3,
+        ["APR"] = 4,
+        ["MAY"] = 5,
+        ["JUN"] = 6,
+        ["JUL"] = 7,
+        ["AUG"] = 8,
+        ["SEP"] = 9,
+        ["OCT"] = 10,
+        ["NOV"] = 11,
+        ["DEC"] = 12
     };
 
     /// <summary>
-    /// 星期映射
+    /// 解析航班信息
+    /// 自动推断年份
     /// </summary>
-    private static readonly Dictionary<string, string> WeekDayMap = new()
+    public static List<FlightInfo> ParseFlights(string text)
     {
-        { "MO", "周一" }, { "TU", "周二" }, { "WE", "周三" }, { "TH", "周四" },
-        { "FR", "周五" }, { "SA", "周六" }, { "SU", "周日" }
-    };
+        var result = new List<FlightInfo>();
 
-    #endregion
+        if (string.IsNullOrWhiteSpace(text))
+            return result;
 
-    #region 主解析方法
+        // 支持:
+        // 1.CA431 ... 2.LH098 ...
+        // 或
+        // 1.CA431 ...
+        // 2.LH098 ...
+        var matches = Regex.Matches(
+            text,
+            @"\d+\..*?(?=\d+\.|$)",
+            RegexOptions.Singleline);
 
-    /// <summary>
-    /// 解析航班文本(自动识别格式)
-    /// </summary>
-    public static List<FlightInfo> ParseFlights(string rawText, int year = 2024)
-    {
-        if (string.IsNullOrWhiteSpace(rawText))
-            return new List<FlightInfo>();
-
-        var lines = rawText.Trim().Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
-        var result = new List<FlightInfo>();
-
-        foreach (var line in lines)
+        foreach (Match match in matches)
         {
-            var flight = ParseLine(line, year);
+            var flight = ParseLine(match.Value.Trim());
+
             if (flight != null)
             {
                 result.Add(flight);
             }
         }
 
-        return 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, int year)
+    private static FlightInfo ParseLine(string line)
     {
-        line = line.Trim();
-        if (string.IsNullOrEmpty(line))
+        var seqMatch = Regex.Match(line, @"^(\d+)\.");
+
+        if (!seqMatch.Success)
             return null;
 
-        // 格式1:HU7086 C   MO18MAY  TFUHAK HK1   1605 1820          E T2T2
-        if (line.Contains("HK1") || line.Contains("KK1") || line.Contains("TK1"))
+        var flight = new FlightInfo
         {
-            return ParseFormat1(line, year);
-        }
+            SequenceNo = int.Parse(seqMatch.Groups[1].Value),
+            RawText = line
+        };
 
-        // 格式2:1.CA431 TU27MAY TFUFRA 0135 0645 T1 1 359 11H10M
-        // 格式2变体:2. LH098 TU28MAY FRAMUC 0915 1010 1 2 321 00H55M
-        if (Regex.IsMatch(line, @"^\d+\.[A-Z0-9]+\s+[A-Z]{2}\d{2}[A-Z]{3}"))
-        {
-            return ParseFormat2(line, year);
-        }
+        string content = line.Substring(seqMatch.Length).Trim();
 
-        return null;
-    }
+        var parts = Regex.Split(content, @"\s+");
 
-    #endregion
+        if (parts.Length < 5)
+            return null;
 
-    #region 格式1解析
+        int idx = 0;
 
-    /// <summary>
-    /// 解析格式1
-    /// </summary>
-    private static FlightInfo ParseFormat1(string line, int year)
-    {
-        // 提取序号
-        var seqMatch = Regex.Match(line, @"^(\d+)\.\s*");
-        if (!seqMatch.Success) return null;
+        // 航班号
+        flight.FlightNumber = parts[idx++];
 
-        var sequenceNo = int.Parse(seqMatch.Groups[1].Value);
-        var content = line.Substring(seqMatch.Length).Trim();
+        // 日期
+        string dateText = parts[idx++];
 
-        // 正则匹配
-        var pattern = @"^([A-Z0-9]+)\s+([A-Z])\s+([A-Z]{2})(\d{2}[A-Z]{3})\s+([A-Z]{3})([A-Z]{3})\s+([A-Z]{2,3}\d?)\s+(\d{3,4})\s+(\d{3,4})(?:\+(\d+))?\s+([A-Z])\s+([A-Z0-9]+)";
-        var match = Regex.Match(content, pattern);
+        flight.WeekDay = dateText[..2];
 
-        if (!match.Success) return null;
+        // 这里只解析月日
+        flight.DepartureDate = ParseMonthDay(dateText);
 
-        var weekDay = match.Groups[3].Value;
-        var dateRaw = match.Groups[4].Value;
-        var departureAirport = match.Groups[5].Value;
-        var arrivalAirport = match.Groups[6].Value;
-        var departureTime = match.Groups[8].Value;
-        var arrivalTime = match.Groups[9].Value;
-        var isNextDay = match.Groups[10].Success && match.Groups[10].Value == "1";
-        var terminalFlag = match.Groups[12].Value;
+        // 航线
+        string route = parts[idx++];
 
-        return new FlightInfo
+        if (route.Length >= 6)
         {
-            SequenceNo = sequenceNo,
-            FlightNumber = match.Groups[1].Value,
-            CabinClass = match.Groups[2].Value,
-            WeekDay = weekDay,
-            DepartureDateRaw = $"{weekDay}{dateRaw}",
-            DepartureAirport = departureAirport,
-            ArrivalAirport = arrivalAirport,
-            DepartureTimeRaw = departureTime,
-            ArrivalTimeRaw = arrivalTime,
-            IsNextDayArrival = isNextDay,
-            DepartureTime = FormatTime(departureTime),
-            ArrivalTime = FormatTime(arrivalTime),
-            DepartureDate = ParseDate(dateRaw, year),
-            Status = match.Groups[7].Value,
-            Terminal = terminalFlag,
-            RawText = line
-        };
-    }
-
-    #endregion
-
-    #region 格式2解析(修复版)
-
-    /// <summary>
-    /// 解析格式2
-    /// 示例:1.CA431 TU27MAY TFUFRA 0135 0645 T1 1 359 11H10M
-    /// </summary>
-    private static FlightInfo ParseFormat2(string line, int year)
-    {
-        // 1. 提取序号(支持 "1." 和 "1. ")
-        var seqMatch = Regex.Match(line, @"^(\d+)\.\s*");
-        if (!seqMatch.Success) return null;
+            flight.DepartureAirport = route[..3];
+            flight.ArrivalAirport = route.Substring(3, 3);
+        }
 
-        var sequenceNo = int.Parse(seqMatch.Groups[1].Value);
-        var content = line.Substring(seqMatch.Length).Trim();
+        // 起飞时间
+        flight.DepartureTime = FormatTime(parts[idx++]);
 
-        // 2. 分割空格
-        var parts = content.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
-        if (parts.Length < 8) return null;
+        // 到达时间
+        string arrivalRaw = parts[idx++];
 
-        try
+        if (arrivalRaw.Contains('+'))
         {
-            var flight = new FlightInfo
-            {
-                SequenceNo = sequenceNo,
-                RawText = line
-            };
+            var arr = arrivalRaw.Split('+');
 
-            int idx = 0;
+            flight.ArrivalTime = FormatTime(arr[0]);
 
-            // 航班号
-            flight.FlightNumber = parts[idx++];
+            flight.IsNextDayArrival = true;
 
-            // 日期(如 TU27MAY)
-            var dateRaw = parts[idx++];
-            if (dateRaw.Length >= 7)
+            if (arr.Length > 1)
             {
-                flight.WeekDay = dateRaw.Substring(0, 2);
-                flight.DepartureDateRaw = dateRaw;
+                flight.NextDayCount = int.Parse(arr[1]);
             }
+        }
+        else
+        {
+            flight.ArrivalTime = FormatTime(arrivalRaw);
+        }
 
-            // 机场(如 TFUFRA 或分开的格式)
-            var airportStr = parts[idx++];
-            if (airportStr.Length >= 6)
-            {
-                // 连写格式:TFUFRA -> TFU + FRA
-                flight.DepartureAirport = airportStr.Substring(0, 3);
-                flight.ArrivalAirport = airportStr.Substring(3, 3);
-            }
-            else
-            {
-                // 分开格式
-                flight.DepartureAirport = airportStr;
-                flight.ArrivalAirport = parts[idx++];
-            }
+        // 剩余字段
+        var remain = parts.Skip(idx).ToList();
 
-            // 起飞时间
-            flight.DepartureTimeRaw = parts[idx++];
-            flight.DepartureTime = FormatTime(flight.DepartureTimeRaw);
+        ParseRemainFields(remain, flight);
 
-            // 到达时间(可能带 +1)
-            var arrivalRaw = parts[idx++];
-            if (arrivalRaw.Contains('+'))
-            {
-                var arrParts = arrivalRaw.Split('+');
-                flight.ArrivalTimeRaw = arrParts[0];
-                flight.IsNextDayArrival = true;
-                if (arrParts.Length > 1 && int.TryParse(arrParts[1], out var days))
-                {
-                    flight.NextDayCount = days;
-                }
-            }
-            else
-            {
-                flight.ArrivalTimeRaw = arrivalRaw;
-            }
-            flight.ArrivalTime = FormatTime(flight.ArrivalTimeRaw);
+        return flight;
+    }
 
-            // 航站楼(可选,以T开头)
-            if (idx < parts.Length && parts[idx].StartsWith("T"))
-            {
-                flight.Terminal = parts[idx++];
-            }
+    ///// <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)
+    {
+        if (remain.Count < 2)
+            return;
 
-            // 序号(可选)
-            if (idx < parts.Length && int.TryParse(parts[idx], out _))
-            {
-                // 这个序号可能是经停次数或行程序号
-                idx++;
-            }
+        // 最后一个字段一般是飞行时长
+        if (Regex.IsMatch(remain[^1], @"^\d{2}H\d{2}M$"))
+        {
+            flight.Duration = remain[^1];
 
-            // 机型(可选,数字或数字+字母)
-            if (idx < parts.Length && Regex.IsMatch(parts[idx], @"^\d{2,3}[A-Z]?$"))
-            {
-                flight.AircraftType = parts[idx++];
-            }
+            remain.RemoveAt(remain.Count - 1);
+        }
 
-            // 飞行时长(可选,包含H)
-            if (idx < parts.Length && parts[idx].Contains('H'))
-            {
-                flight.Duration = parts[idx++];
-            }
+        // 倒数第二个字段一般是机型
+        if (remain.Count > 0 &&
+            Regex.IsMatch(remain[^1], @"^\d{2,3}[A-Z]?$"))
+        {
+            flight.AircraftType = remain[^1];
 
-            // 解析日期
-            flight.DepartureDate = ParseDate(dateRaw, year);
+            remain.RemoveAt(remain.Count - 1);
+        }
 
-            return flight;
+        // 剩余字段解析为航站楼
+        if (remain.Count == 1)
+        {
+            flight.DepartureTerminal =
+                NormalizeTerminal(remain[0]);
         }
-        catch
+        else if (remain.Count >= 2)
         {
-            return null;
+            flight.DepartureTerminal =
+                NormalizeTerminal(remain[0]);
+
+            flight.ArrivalTerminal =
+                NormalizeTerminal(remain[1]);
         }
     }
 
-    #endregion
+    /// <summary>
+    /// 标准化航站楼
+    /// </summary>
+    /// <param name="terminal">
+    /// 原始值:1、2、 T3
+    /// </param>
+    /// <returns>
+    /// T1、T2、T3
+    /// </returns>
+    private static string NormalizeTerminal(string terminal)
+    {
+        if (string.IsNullOrWhiteSpace(terminal))
+            return null;
+
+        terminal = terminal.Trim().ToUpper();
 
-    #region 辅助方法
+        // 纯数字补T
+        if (Regex.IsMatch(terminal, @"^\d+$"))
+        {
+            return $"T{terminal}";
+        }
+
+        return terminal;
+    }
 
     /// <summary>
-    /// 格式化时间(1905 -> 19:05)
+    /// 格式化时间
+    /// 0135 -> 01:35
+    /// 905 -> 09:05
     /// </summary>
     private static string FormatTime(string time)
     {
         if (string.IsNullOrWhiteSpace(time))
             return string.Empty;
 
-        // 移除 +1 等后缀
-        var cleanTime = time.Split('+')[0];
+        time = time.Split('+')[0];
 
-        // 4位数字
-        if (cleanTime.Length == 4 && int.TryParse(cleanTime, out _))
+        if (time.Length == 4)
         {
-            return $"{cleanTime.Substring(0, 2)}:{cleanTime.Substring(2, 2)}";
+            return $"{time[..2]}:{time.Substring(2, 2)}";
         }
 
-        // 3位数字(905 -> 09:05)
-        if (cleanTime.Length == 3 && int.TryParse(cleanTime, out _))
+        if (time.Length == 3)
         {
-            return $"0{cleanTime.Substring(0, 1)}:{cleanTime.Substring(1, 2)}";
+            return $"0{time[0]}:{time.Substring(1, 2)}";
         }
 
-        // 已经是 HH:mm 格式
-        if (Regex.IsMatch(cleanTime, @"^\d{1,2}:\d{2}$"))
-        {
-            var parts = cleanTime.Split(':');
-            return $"{parts[0].PadLeft(2, '0')}:{parts[1]}";
-        }
-
-        return cleanTime;
+        return time;
     }
 
     /// <summary>
-    /// 解析日期(27MAY -> 2024-05-27)
+    /// 自动修正跨年
     /// </summary>
-    private static DateTime ParseDate(string dateStr, int year)
+    private static void FixYear(
+        List<FlightInfo> flights)
     {
-        if (string.IsNullOrWhiteSpace(dateStr))
-            return DateTime.MinValue;
+        if (flights.Count <= 1)
+            return;
 
-        // 移除星期前缀
-        var cleanDate = Regex.Replace(dateStr, @"^[A-Z]{2}", "");
+        int currentYear = DateTime.Now.Year;
 
-        var match = Regex.Match(cleanDate, @"(\d{2})([A-Z]{3})");
-        if (match.Success)
+        DateTime previousDate = DateTime.MinValue;
+
+        foreach (var flight in flights)
         {
-            var day = int.Parse(match.Groups[1].Value);
-            var monthStr = match.Groups[2].Value;
+            if (flight.DepartureDate == DateTime.MinValue)
+                continue;
+
+            var date = new DateTime(
+                currentYear,
+                flight.DepartureDate.Month,
+                flight.DepartureDate.Day);
 
-            if (MonthMap.TryGetValue(monthStr.ToUpper(), out var month))
+            // 出现日期倒退
+            if (previousDate != DateTime.MinValue &&
+                date < previousDate)
             {
-                try
-                {
-                    return new DateTime(year, month, day);
-                }
-                catch
-                {
-                    return DateTime.MinValue;
-                }
+                currentYear++;
+                date = new DateTime(
+                    currentYear,
+                    flight.DepartureDate.Month,
+                    flight.DepartureDate.Day);
             }
-        }
-
-        return DateTime.MinValue;
-    }
 
-    /// <summary>
-    /// 获取完整起飞时间(含时分)
-    /// </summary>
-    public static DateTime GetFullDepartureTime(FlightInfo flight, int year)
-    {
-        if (flight.DepartureDate == DateTime.MinValue || string.IsNullOrEmpty(flight.DepartureTimeRaw))
-            return DateTime.MinValue;
+            flight.DepartureDate = date;
 
-        var time = ParseTimeToTimeSpan(flight.DepartureTimeRaw);
-        return flight.DepartureDate.Add(time);
+            previousDate = date;
+        }
     }
 
     /// <summary>
-    /// 获取完整到达时间(含时分和跨天)
+    /// 解析月日
+    /// TU27MAY -> 05-27
     /// </summary>
-    public static DateTime GetFullArrivalTime(FlightInfo flight, int year)
+    private static DateTime ParseMonthDay(string value)
     {
-        if (flight.DepartureDate == DateTime.MinValue || string.IsNullOrEmpty(flight.ArrivalTimeRaw))
-            return DateTime.MinValue;
-
-        var time = ParseTimeToTimeSpan(flight.ArrivalTimeRaw);
-        var arrivalDate = flight.DepartureDate.Add(time);
-
-        if (flight.IsNextDayArrival)
-        {
-            arrivalDate = arrivalDate.AddDays(flight.NextDayCount > 0 ? flight.NextDayCount : 1);
-        }
+        value = Regex.Replace(
+            value,
+            @"^[A-Z]{2}",
+            "");
 
-        return arrivalDate;
-    }
+        var match = Regex.Match(
+            value,
+            @"(\d{2})([A-Z]{3})");
 
-    /// <summary>
-    /// 解析时间字符串为 TimeSpan
-    /// </summary>
-    private static TimeSpan ParseTimeToTimeSpan(string time)
-    {
-        var cleanTime = time.Split('+')[0];
+        if (!match.Success)
+            return DateTime.MinValue;
 
-        if (cleanTime.Length == 4)
-        {
-            return new TimeSpan(
-                int.Parse(cleanTime.Substring(0, 2)),
-                int.Parse(cleanTime.Substring(2, 2)),
-                0);
-        }
+        int day = int.Parse(match.Groups[1].Value);
 
-        if (cleanTime.Length == 3)
+        if (!MonthMap.TryGetValue(
+                match.Groups[2].Value,
+                out int month))
         {
-            return new TimeSpan(
-                int.Parse(cleanTime.Substring(0, 1)),
-                int.Parse(cleanTime.Substring(1, 2)),
-                0);
+            return DateTime.MinValue;
         }
 
-        return TimeSpan.Zero;
-    }
-
-    /// <summary>
-    /// 获取星期中文名称
-    /// </summary>
-    public static string GetWeekDayName(string weekDay)
-    {
-        return WeekDayMap.TryGetValue(weekDay.ToUpper(), out var name) ? name : weekDay;
+        // 先使用当前年份
+        return new DateTime(
+            DateTime.Now.Year,
+            month,
+            day);
     }
-
-    #endregion
 }

+ 1 - 0
OASystem/OASystem.Domain/OASystem.Domain.csproj

@@ -32,6 +32,7 @@
   </ItemGroup>
 
   <ItemGroup>
+    <Folder Include="Common\Constants\" />
     <Folder Include="ViewModels\VisaFormDetails\" />
   </ItemGroup>
 

+ 32 - 1
OASystem/OASystem.Domain/ViewModels/Groups/AirTicketReservationsView.cs

@@ -51,6 +51,37 @@ public class AirGroupCostParameterView
     public int GWCRS { get; set; }
 }
 
+public class DeriveInfo : Grp_AirTicketReservations
+{
+    public string CTypeName { get; set; }
+
+    /// <summary>
+    /// 支付方式
+    /// </summary>
+    public string PayType { get; set; }
+    /// <summary>
+    /// 费用表示
+    /// </summary>
+    public string OrbitalPrivateTransfer { get; set; }
+    /// <summary>
+    /// 卡号
+    /// </summary>
+    public string BankNo { get; set; }
+    /// <summary>
+    /// 卡类型
+    /// </summary>
+    public string BankType { get; set; }
+    /// <summary>
+    /// 收款方
+    /// </summary>
+    public string Payee { get; set; }
+    /// <summary>
+    /// 币种
+    /// </summary>
+    public string CurrencyStr { get; set; }
+}
+
+
 public class AirTicketReservationsPayView
 {
     /// <summary>
@@ -411,7 +442,7 @@ public class AirTicketFeeInfo
     /// <summary>
     /// 支付方式
     /// </summary>
-    public int PayDId { get; set; } = 0;
+    public int PayDId { get; set; } 
     /// <summary>
     /// 消费方式
     /// </summary>

Plik diff jest za duży
+ 764 - 800
OASystem/OASystem.Infrastructure/Repositories/Groups/AirTicketResRepository.cs