using System.Text.RegularExpressions; namespace OASystem.Domain.Entities.Groups; /// /// 机票费用录入 /// [SugarTable("Grp_AirTicketReservations")] public class Grp_AirTicketReservations : EntityBase { /************************* 2026版数据结构 *************************/ /// /// 团组外键编号 /// [SugarColumn(IsNullable = true, ColumnDataType = "int")] public int DIId { get; set; } /// /// 记录类型:0-正常机票 1-退票记录 /// [SugarColumn(IsNullable = true, ColumnDataType = "int")] public int RecordType { get; set; } = 0; /// /// 关联的原始机票记录ID(退票记录指向原机票记录) /// [SugarColumn(IsNullable = true, ColumnDataType = "int")] public int OriginalReservationId { get; set; } /// /// 航段代码描述 /// [SugarColumn(IsNullable = true, ColumnDataType = "varchar(500)")] public string FlightsDescription { get; set; } /// /// 航班基础信息(去程、联程、返程) /// [SugarColumn(IsNullable = true, IsJson = true, ColumnDataType = "varchar(500)")] public List AirTicketBasicInfos { get; set; } = new List(); /// /// 客人名称 /// RecordType = 0 时,存储正常机票的客人名称;RecordType = 1 时,存储退票记录的客人名称(可能与原机票记录不同) /// [SugarColumn(IsNullable = true, ColumnDataType = "varchar(200)")] public string ClientName { get; set; } /// /// 客户人数 /// [SugarColumn(IsNullable = true, ColumnDataType = "int")] public int ClientNum { get; set; } /// /// 舱类型(数据类型外键) /// [SugarColumn(IsNullable = true, ColumnDataType = "int")] public int CType { get; set; } /// /// 机票全价 /// [SugarColumn(IsNullable = true, ColumnDataType = "decimal(10,2)")] public decimal Price { get; set; } /// /// 币种 /// [SugarColumn(IsNullable = true, ColumnDataType = "int")] public int Currency { get; set; } /// /// 费用信息(含退票信息) /// [SugarColumn(IsNullable = true, IsJson = true, ColumnDataType = "varchar(max)")] public List CustTicketInfos { get; set; } = new List(); /// /// 报价说明 /// [SugarColumn(IsNullable = true, ColumnDataType = "varchar(500)")] public string PriceDescription { get; set; } /************************* 2026版数据结构 *************************/ #region 旧版兼容以前的数据结构,2026版不使用 /// /// 航班号 /// [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")] public string FlightsCode { get; set; } /// /// 城市A-B /// [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")] public string FlightsCity { get; set; } /// /// 航班日期 /// [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")] public string FlightsDate { get; set; } /// /// 航班时间 /// [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")] public string FlightsTime { get; set; } /// /// 抵达时间 /// [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")] public string ArrivedTime { get; set; } /// /// 是否值机 /// 0 否 1 是 /// [SugarColumn(IsNullable = true, ColumnDataType = "int")] public int IsCheckIn { get; set; } /// /// 是否选座 /// 0 否 1 是 /// [SugarColumn(IsNullable = true, ColumnDataType = "int")] public int IsSetSeat { get; set; } /// /// 是否购买行李服务 /// 0 否 1 是 /// [SugarColumn(IsNullable = true, ColumnDataType = "int")] public int IsPackage { get; set; } /// /// 是否行李直挂 /// 0 否 1 是 /// [SugarColumn(IsNullable = true, ColumnDataType = "int")] public int IsBagHandle { get; set; } /// /// 是否火车票出票选座 /// 0 否 1 是 /// [SugarColumn(IsNullable = true, ColumnDataType = "int")] public int IsTrain { get; set; } /// /// 去程航班描述代码 /// [SugarColumn(IsNullable = true, ColumnDataType = "varchar(500)")] public string LeaveDescription { get; set; } /// /// 返程航班描述代码 /// [SugarColumn(IsNullable = true, ColumnDataType = "varchar(500)")] public string ReturnDescription { get; set; } /// /// 出票前报价 /// [SugarColumn(IsNullable = true, ColumnDataType = "decimal(10,2)")] public decimal PrePrice { get; set; } /// /// 出票前报价币种 /// [SugarColumn(IsNullable = true, ColumnDataType = "int")] public int PreCurrency { get; set; } /// /// 机票编号 /// [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")] public string TicketNumber { get; set; } /// /// 机票票号 /// [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")] public string TicketCode { get; set; } /// /// 客人类型(数据类型外键) /// [SugarColumn(IsNullable = true, ColumnDataType = "int")] public int PassengerType { get; set; } #endregion } /// /// 机票基础信息 /// public class AirTicketBasicInfo { /// /// 序号 /// public int No { get; set; } /// /// 航班号 /// public string FlightsCode { get; set; } /// /// 城市A-B /// public string FlightsCity { get; set; } /// /// 航班日期 /// public string FlightsDate { get; set; } /// /// 航班时间 /// public string FlightsTime { get; set; } /// /// 抵达时间 /// public string ArrivedTime { get; set; } /// /// 出发航站楼 /// public string DepartureTerminal { get; set; } /// /// 抵达航站楼 /// public string ArrivalTerminal { get; set; } /// /// 机型(如:359、321、32A、73H) /// public string AircraftType { get; set; } /// /// 耗时(如:11H10M、00H55M、01H35M) /// public string Duration { get; set; } } /// /// 客户机票信息 /// public class CustTicketInfo { /// /// 客户ID /// public int ClientId { get; set; } /// /// 舱类型(数据类型外键) /// public int CType { get; set; } /// /// 实际价格 /// public decimal ActualPrice { get; set; } /// /// 机票票号 /// public string TicketCode { get; set; } /// /// 机票编号 /// public string TicketNumber { get; set; } /// /// 选中服务ID集合 /// public List SelectedServiceIds { get; set; } = new List(); /// /// 附加服务 json数组 /// public List AdditionalServices { get; set; } = new List(); /// /// 机票总费用 /// 实际价格 + 附加服务费 /// public decimal TotalTicketPrice { get; set; } /// /// 是否已退票 /// public bool IsRefund { get; set; } = false; /// /// 退票记录 /// public RefundRecord RefundRecord { get; set; } = new RefundRecord(); } /// /// 退票记录 /// public class RefundRecord { /// /// 退票金额 /// public decimal RefundAmount { get; set; } /// /// 不可退税费 /// public decimal NonRefundableTax { get; set; } /// /// 退票时间 /// public string RefundTime { get; set; } /// /// 退款账户 /// Setdata Id /// public int RefundAccount { get; set; } /// /// 退票原因 /// public string RefundReason { get; set; } } /// /// 附加服务 /// public class AdditionalService { /// /// 服务类型Id(Sys_SetData) /// public int ServiceTypeId { get; set; } /// /// 金额 /// public decimal Amount { get; set; } } /// /// 航班信息 /// public class FlightInfo { /// /// 航段序号 /// public int SequenceNo { get; set; } /// /// 航班号 /// 例如:CA431 /// public string FlightNumber { get; set; } /// /// 星期 /// 例如:TU /// public string WeekDay { get; set; } /// /// 起飞日期 /// public DateTime DepartureDate { get; set; } /// /// 出发机场三字码 /// 例如:TFU /// public string DepartureAirport { get; set; } /// /// 到达机场三字码 /// 例如:FRA /// public string ArrivalAirport { get; set; } /// /// 起飞时间 /// 格式:HH:mm /// public string DepartureTime { get; set; } /// /// 到达时间 /// 格式:HH:mm /// public string ArrivalTime { get; set; } /// /// 是否跨天到达 /// public bool IsNextDayArrival { get; set; } /// /// 跨天数量 /// 例如: /// +1 表示次日到达 /// +2 表示第三天到达 /// public int NextDayCount { get; set; } /// /// 出发航站楼 /// 例如:T1 /// public string DepartureTerminal { get; set; } /// /// 到达航站楼 /// 例如:T2 /// public string ArrivalTerminal { get; set; } /// /// 机型代码 /// 例如: /// 359=A350-900 /// 321=A321 /// 32A=A320neo /// public string AircraftType { get; set; } /// /// 飞行时长 /// 例如:11H10M /// public string Duration { get; set; } /// /// 原始文本 /// public string RawText { get; set; } } /// /// 航班解析器 /// 支持格式: /// 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 /// public static class FlightParser { /// /// 月份映射表 /// private static readonly Dictionary 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 }; /// /// 解析航班信息 /// 自动推断年份 /// public static List ParseFlights(string text) { var result = new List(); if (string.IsNullOrWhiteSpace(text)) return result; // 支持: // 1.CA431 ... 2.LH098 ... // 或 // 1.CA431 ... // 2.LH098 ... var matches = Regex.Matches( text, @"\d+\..*?(?=\d+\.|$)", RegexOptions.Singleline); foreach (Match match in matches) { var flight = ParseLine(match.Value.Trim()); if (flight != null) { result.Add(flight); } } // 按序号排序 result = result .OrderBy(x => x.SequenceNo) .ToList(); // 自动修正跨年 FixYear(result); return result; } /// /// 解析单条航班 /// private static FlightInfo ParseLine(string line) { var seqMatch = Regex.Match(line, @"^(\d+)\."); if (!seqMatch.Success) return null; var flight = new FlightInfo { SequenceNo = int.Parse(seqMatch.Groups[1].Value), RawText = line }; string content = line.Substring(seqMatch.Length).Trim(); var parts = Regex.Split(content, @"\s+"); if (parts.Length < 5) return null; int idx = 0; // 航班号 flight.FlightNumber = parts[idx++]; // 日期 string dateText = parts[idx++]; flight.WeekDay = dateText[..2]; // 这里只解析月日 flight.DepartureDate = ParseMonthDay(dateText); // 航线 string route = parts[idx++]; if (route.Length >= 6) { flight.DepartureAirport = route[..3]; flight.ArrivalAirport = route.Substring(3, 3); } // 起飞时间 flight.DepartureTime = FormatTime(parts[idx++]); // 到达时间 string arrivalRaw = parts[idx++]; if (arrivalRaw.Contains('+')) { var arr = arrivalRaw.Split('+'); flight.ArrivalTime = FormatTime(arr[0]); flight.IsNextDayArrival = true; if (arr.Length > 1) { flight.NextDayCount = int.Parse(arr[1]); } } else { flight.ArrivalTime = FormatTime(arrivalRaw); } // 剩余字段 var remain = parts.Skip(idx).ToList(); ParseRemainFields(remain, flight); return flight; } ///// ///// ///// ///// ///// 例如: ///// T1 1 359 11H10M ///// 1 2 321 00H55M ///// 3 T3 359 08H55M ///// ///// /// /// 解析剩余字段 /// /// /// 例如: /// T1 1 359 11H10M /// 1 2 321 00H55M /// 3 T3 359 08H55M /// /// private static void ParseRemainFields( List remain, FlightInfo flight) { if (remain.Count < 2) return; // 最后一个字段一般是飞行时长 if (Regex.IsMatch(remain[^1], @"^\d{2}H\d{2}M$")) { flight.Duration = remain[^1]; remain.RemoveAt(remain.Count - 1); } // 倒数第二个字段一般是机型 if (remain.Count > 0 && Regex.IsMatch(remain[^1], @"^\d{2,3}[A-Z]?$")) { flight.AircraftType = remain[^1]; remain.RemoveAt(remain.Count - 1); } // 剩余字段解析为航站楼 if (remain.Count == 1) { flight.DepartureTerminal = NormalizeTerminal(remain[0]); } else if (remain.Count >= 2) { flight.DepartureTerminal = NormalizeTerminal(remain[0]); flight.ArrivalTerminal = NormalizeTerminal(remain[1]); } } /// /// 标准化航站楼 /// /// /// 原始值:1、2、 T3 /// /// /// T1、T2、T3 /// private static string NormalizeTerminal(string terminal) { if (string.IsNullOrWhiteSpace(terminal)) return null; terminal = terminal.Trim().ToUpper(); // 纯数字补T if (Regex.IsMatch(terminal, @"^\d+$")) { return $"T{terminal}"; } return terminal; } /// /// 格式化时间 /// 0135 -> 01:35 /// 905 -> 09:05 /// private static string FormatTime(string time) { if (string.IsNullOrWhiteSpace(time)) return string.Empty; time = time.Split('+')[0]; if (time.Length == 4) { return $"{time[..2]}:{time.Substring(2, 2)}"; } if (time.Length == 3) { return $"0{time[0]}:{time.Substring(1, 2)}"; } return time; } /// /// 自动修正跨年 /// private static void FixYear( List flights) { if (flights.Count <= 1) return; int currentYear = DateTime.Now.Year; DateTime previousDate = DateTime.MinValue; foreach (var flight in flights) { if (flight.DepartureDate == DateTime.MinValue) continue; var date = new DateTime( currentYear, flight.DepartureDate.Month, flight.DepartureDate.Day); // 出现日期倒退 if (previousDate != DateTime.MinValue && date < previousDate) { currentYear++; date = new DateTime( currentYear, flight.DepartureDate.Month, flight.DepartureDate.Day); } flight.DepartureDate = date; previousDate = date; } } /// /// 解析月日 /// TU27MAY -> 05-27 /// 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); } }