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);
}
}