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 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; } // 序号
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; } // 原始文本
}
///
/// 航班解析器
///
public static class FlightParser
{
#region 常量定义
///
/// 月份映射
///
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 }
};
///
/// 星期映射
///
private static readonly Dictionary WeekDayMap = new()
{
{ "MO", "周一" }, { "TU", "周二" }, { "WE", "周三" }, { "TH", "周四" },
{ "FR", "周五" }, { "SA", "周六" }, { "SU", "周日" }
};
#endregion
#region 主解析方法
///
/// 解析航班文本(自动识别格式)
///
public static List ParseFlights(string rawText, int year = 2024)
{
if (string.IsNullOrWhiteSpace(rawText))
return new List();
var lines = rawText.Trim().Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
var result = new List();
foreach (var line in lines)
{
var flight = ParseLine(line, year);
if (flight != null)
{
result.Add(flight);
}
}
return result.OrderBy(x => x.SequenceNo).ToList();
}
///
/// 解析单行文本(自动识别格式)
///
private static FlightInfo ParseLine(string line, int year)
{
line = line.Trim();
if (string.IsNullOrEmpty(line))
return null;
// 格式1:HU7086 C MO18MAY TFUHAK HK1 1605 1820 E T2T2
if (line.Contains("HK1") || line.Contains("KK1") || line.Contains("TK1"))
{
return ParseFormat1(line, year);
}
// 格式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);
}
return null;
}
#endregion
#region 格式1解析
///
/// 解析格式1
///
private static FlightInfo ParseFormat1(string line, int year)
{
// 提取序号
var seqMatch = Regex.Match(line, @"^(\d+)\.\s*");
if (!seqMatch.Success) return null;
var sequenceNo = int.Parse(seqMatch.Groups[1].Value);
var content = line.Substring(seqMatch.Length).Trim();
// 正则匹配
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);
if (!match.Success) return null;
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;
return new FlightInfo
{
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解析(修复版)
///
/// 解析格式2
/// 示例:1.CA431 TU27MAY TFUFRA 0135 0645 T1 1 359 11H10M
///
private static FlightInfo ParseFormat2(string line, int year)
{
// 1. 提取序号(支持 "1." 和 "1. ")
var seqMatch = Regex.Match(line, @"^(\d+)\.\s*");
if (!seqMatch.Success) return null;
var sequenceNo = int.Parse(seqMatch.Groups[1].Value);
var content = line.Substring(seqMatch.Length).Trim();
// 2. 分割空格
var parts = content.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length < 8) return null;
try
{
var flight = new FlightInfo
{
SequenceNo = sequenceNo,
RawText = line
};
int idx = 0;
// 航班号
flight.FlightNumber = parts[idx++];
// 日期(如 TU27MAY)
var dateRaw = parts[idx++];
if (dateRaw.Length >= 7)
{
flight.WeekDay = dateRaw.Substring(0, 2);
flight.DepartureDateRaw = dateRaw;
}
// 机场(如 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++];
}
// 起飞时间
flight.DepartureTimeRaw = parts[idx++];
flight.DepartureTime = FormatTime(flight.DepartureTimeRaw);
// 到达时间(可能带 +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);
// 航站楼(可选,以T开头)
if (idx < parts.Length && parts[idx].StartsWith("T"))
{
flight.Terminal = parts[idx++];
}
// 序号(可选)
if (idx < parts.Length && int.TryParse(parts[idx], out _))
{
// 这个序号可能是经停次数或行程序号
idx++;
}
// 机型(可选,数字或数字+字母)
if (idx < parts.Length && Regex.IsMatch(parts[idx], @"^\d{2,3}[A-Z]?$"))
{
flight.AircraftType = parts[idx++];
}
// 飞行时长(可选,包含H)
if (idx < parts.Length && parts[idx].Contains('H'))
{
flight.Duration = parts[idx++];
}
// 解析日期
flight.DepartureDate = ParseDate(dateRaw, year);
return flight;
}
catch
{
return null;
}
}
#endregion
#region 辅助方法
///
/// 格式化时间(1905 -> 19:05)
///
private static string FormatTime(string time)
{
if (string.IsNullOrWhiteSpace(time))
return string.Empty;
// 移除 +1 等后缀
var cleanTime = time.Split('+')[0];
// 4位数字
if (cleanTime.Length == 4 && int.TryParse(cleanTime, out _))
{
return $"{cleanTime.Substring(0, 2)}:{cleanTime.Substring(2, 2)}";
}
// 3位数字(905 -> 09:05)
if (cleanTime.Length == 3 && int.TryParse(cleanTime, out _))
{
return $"0{cleanTime.Substring(0, 1)}:{cleanTime.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;
}
///
/// 解析日期(27MAY -> 2024-05-27)
///
private static DateTime ParseDate(string dateStr, int year)
{
if (string.IsNullOrWhiteSpace(dateStr))
return DateTime.MinValue;
// 移除星期前缀
var cleanDate = Regex.Replace(dateStr, @"^[A-Z]{2}", "");
var match = Regex.Match(cleanDate, @"(\d{2})([A-Z]{3})");
if (match.Success)
{
var day = int.Parse(match.Groups[1].Value);
var monthStr = match.Groups[2].Value;
if (MonthMap.TryGetValue(monthStr.ToUpper(), out var month))
{
try
{
return new DateTime(year, month, day);
}
catch
{
return DateTime.MinValue;
}
}
}
return DateTime.MinValue;
}
///
/// 获取完整起飞时间(含时分)
///
public static DateTime GetFullDepartureTime(FlightInfo flight, int year)
{
if (flight.DepartureDate == DateTime.MinValue || string.IsNullOrEmpty(flight.DepartureTimeRaw))
return DateTime.MinValue;
var time = ParseTimeToTimeSpan(flight.DepartureTimeRaw);
return flight.DepartureDate.Add(time);
}
///
/// 获取完整到达时间(含时分和跨天)
///
public static DateTime GetFullArrivalTime(FlightInfo flight, int year)
{
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);
}
return arrivalDate;
}
///
/// 解析时间字符串为 TimeSpan
///
private static TimeSpan ParseTimeToTimeSpan(string time)
{
var cleanTime = time.Split('+')[0];
if (cleanTime.Length == 4)
{
return new TimeSpan(
int.Parse(cleanTime.Substring(0, 2)),
int.Parse(cleanTime.Substring(2, 2)),
0);
}
if (cleanTime.Length == 3)
{
return new TimeSpan(
int.Parse(cleanTime.Substring(0, 1)),
int.Parse(cleanTime.Substring(1, 2)),
0);
}
return TimeSpan.Zero;
}
///
/// 获取星期中文名称
///
public static string GetWeekDayName(string weekDay)
{
return WeekDayMap.TryGetValue(weekDay.ToUpper(), out var name) ? name : weekDay;
}
#endregion
}