Sfoglia il codice sorgente

团组机票费用2026版:接口与数据结构重构

重构并扩展团组机票费用(2026版)相关接口、DTO、实体、视图模型及仓储方法,支持机票费用的增删查改、智能搜索、审核概览、推送通知等全流程。优化预付款/尾款逻辑,统一返回结构,兼容旧版数据,提升数据处理与业务扩展能力。
Lyyyi 2 settimane fa
parent
commit
ab683567d1
25 ha cambiato i file con 3451 aggiunte e 953 eliminazioni
  1. 0 2
      OASystem/OASystem.Api/Controllers/BusinessController.cs
  2. 367 61
      OASystem/OASystem.Api/Controllers/GroupsController.cs
  3. 1 1
      OASystem/OASystem.Api/Controllers/ResourceController.cs
  4. 180 20
      OASystem/OASystem.Api/Controllers/SearchController.cs
  5. 2 4
      OASystem/OASystem.Api/OAMethodLib/GeneralMethod.cs
  6. 3 5
      OASystem/OASystem.Api/OAMethodLib/KiMiApi/KiMiApi.cs
  7. 4 5
      OASystem/OASystem.Api/OAMethodLib/QiYeWeChatAPI/AppNotice/Library.cs
  8. 0 1
      OASystem/OASystem.Domain/AesEncryption/EncryptionProcessor.cs
  9. 11 0
      OASystem/OASystem.Domain/AutoMappers/_baseMappingProfile.cs
  10. 47 54
      OASystem/OASystem.Domain/Common/DepartmentCode.cs
  11. 330 329
      OASystem/OASystem.Domain/Dtos/Groups/AirTicketResDto.cs
  12. 13 0
      OASystem/OASystem.Domain/Dtos/Groups/GrpCreditCardPaymentDto.cs
  13. 1 7
      OASystem/OASystem.Domain/Dtos/KiMi/KiMiSetting.cs
  14. 698 147
      OASystem/OASystem.Domain/Entities/Groups/Grp_AirTicketReservations.cs
  15. 1 0
      OASystem/OASystem.Domain/Entities/Groups/Grp_CreditCardPayment.cs
  16. 9 3
      OASystem/OASystem.Domain/Entities/Groups/Grp_DecreasePayments.cs
  17. 586 248
      OASystem/OASystem.Domain/ViewModels/Groups/AirTicketReservationsView.cs
  18. 22 29
      OASystem/OASystem.Domain/ViewModels/Groups/DelegationInfoView.cs
  19. 54 0
      OASystem/OASystem.Domain/ViewModels/Groups/Grp_CreditCardPaymentView.cs
  20. 110 0
      OASystem/OASystem.Domain/ViewModels/JsonView.cs
  21. 1 1
      OASystem/OASystem.Infrastructure/Repositories/BaseRepository.cs
  22. 933 3
      OASystem/OASystem.Infrastructure/Repositories/Groups/AirTicketResRepository.cs
  23. 32 11
      OASystem/OASystem.Infrastructure/Repositories/Groups/DecreasePaymentsRepository.cs
  24. 34 0
      OASystem/OASystem.Infrastructure/Repositories/Groups/DelegationInfoRepository.cs
  25. 12 22
      OASystem/OASystem.Infrastructure/Repositories/Groups/TeamRateRepository.cs

+ 0 - 2
OASystem/OASystem.Api/Controllers/BusinessController.cs

@@ -493,8 +493,6 @@ Where c.ConfListId = {0}", ConfId);
 
         #endregion
 
-
-
         #region 文件删除
         /// <summary>
         ///  文件操作 

File diff suppressed because it is too large
+ 367 - 61
OASystem/OASystem.Api/Controllers/GroupsController.cs


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

@@ -4442,7 +4442,7 @@ Inner Join Sys_Department as d With(Nolock) On u.DepId=d.Id Where m.Id={0} ", _m
             try
             {
                 // 初始化检查 (Progress: 5%)
-                var invAiInfo = await _sqlSugar.Queryable<Res_InvitationAI>().Where(x => x.Id == dto.Id).FirstAsync();
+                var invAiInfo = await _sqlSugar.Queryable<Res_InvitationAI_NoGroup>().Where(x => x.Id == dto.Id).FirstAsync();
                 if (invAiInfo?.EntryInfo == null)
                 {
                     await HttpContext.SendSseStepAsync(-1, "请先设置关键字信息!");

+ 180 - 20
OASystem/OASystem.Api/Controllers/SearchController.cs

@@ -16,6 +16,7 @@ using OASystem.Domain.ViewModels.Search;
 using OASystem.Infrastructure.Repositories.CRM;
 using OASystem.Infrastructure.Repositories.Groups;
 using OASystem.Infrastructure.Repositories.System;
+using System.Diagnostics;
 using static iTextSharp.text.pdf.AcroFields;
 
 namespace OASystem.API.Controllers
@@ -32,6 +33,7 @@ namespace OASystem.API.Controllers
         private readonly DynamicSearchService<Grp_DelegationInfo> _groupSearchService;
         private readonly DynamicSearchService<NewClientDataView> _clientSearchService;
         private readonly DynamicSearchService<InvitationAIInvNameView> _invAISearchService;
+        private readonly DynamicSearchService<AirInfo> _airSearchService;
         private readonly NewClientDataRepository _clientDataRepository;
 
         public SearchController(
@@ -40,14 +42,16 @@ namespace OASystem.API.Controllers
             DynamicSearchService<Grp_DelegationInfo> groupSearchService,
             DynamicSearchService<NewClientDataView> clientSearchService,
             DynamicSearchService<InvitationAIInvNameView> invAISearchService,
+            DynamicSearchService<AirInfo> airSearchService,
             NewClientDataRepository clientDataRepository
            )
         {
             _sqlSugar = sqlSugar;
             _groupRep = groupRep;
+            _clientDataRepository = clientDataRepository;
             _groupSearchService = groupSearchService;
             _clientSearchService = clientSearchService;
-            _clientDataRepository = clientDataRepository;
+            _airSearchService = airSearchService;
             _invAISearchService = invAISearchService;
         }
 
@@ -170,7 +174,6 @@ namespace OASystem.API.Controllers
                     x.Location = AesEncryptionHelper.Decrypt(x.Location);
                 });
 
-
                 var searchRequest = new DynamicSearchRequest
                 {
                     Keyword = keyword,
@@ -476,6 +479,124 @@ namespace OASystem.API.Controllers
             }
         }
 
+        #region 团组费用-机票费用
+
+        /// <summary>
+        /// 团组机票费用基本信息
+        /// </summary>
+        /// <param name="groupIds"></param>
+        /// <returns></returns>
+        private async Task<List<AirInfo>> GetAirInfos(List<int> groupIds)
+        {
+            // 1. 查询
+            var groupInfos = await _sqlSugar.Queryable<Grp_DelegationInfo>()
+                .Where(x => x.IsDel == 0 && groupIds.Contains(x.Id))
+                .Select(x => new { x.Id, x.TeamName })
+                .ToListAsync();
+
+            var airInfos = await _sqlSugar.Queryable<Grp_AirTicketReservations>()
+                .Where(x => x.IsDel == 0 && groupIds.Contains(x.DIId) && !string.IsNullOrEmpty(x.TicketCode))
+                .Select(x => new { x.DIId, x.TicketCode, x.ClientName })
+                .ToListAsync();
+
+            // 2. 解析客户名称ID
+            var clientIds = new HashSet<int>();
+
+            foreach (var item in airInfos)
+            {
+                if (string.IsNullOrWhiteSpace(item.ClientName))
+                    continue;
+
+                var ids = item.ClientName.Split(',')
+                               .Select(s => s.Trim())
+                               .Where(s => int.TryParse(s, out _))
+                               .Select(int.Parse);
+
+                foreach (var id in ids)
+                {
+                    clientIds.Add(id);
+                }
+            }
+
+            // 3. 批量查询客户名称
+            var clientNames = new Dictionary<int, string>();
+
+            if (clientIds.Any())
+            {
+                var nameInfos = await _sqlSugar.Queryable<Crm_DeleClient>()
+                    .Where(x => x.IsDel == 0 && clientIds.Contains(x.Id) &&
+                                !string.IsNullOrEmpty(x.FirstName) && !string.IsNullOrEmpty(x.LastName))
+                    .Select(x => new { x.Id, x.FirstName, x.LastName })
+                    .ToListAsync();
+
+                clientNames = nameInfos.ToDictionary(
+                    x => x.Id,
+                    x => AesEncryptionHelper.Decrypt(x.LastName) + AesEncryptionHelper.Decrypt(x.FirstName)
+                );
+            }
+
+            // 4. 构建 ClientName 到客户名称列表的映射(预解析)
+            var clientNameCache = new Dictionary<string, List<string>>();
+
+            foreach (var item in airInfos)
+            {
+                if (string.IsNullOrWhiteSpace(item.ClientName))
+                    continue;
+
+                if (!clientNameCache.ContainsKey(item.ClientName))
+                {
+                    var ids = item.ClientName.Split(',')
+                                   .Select(s => s.Trim())
+                                   .Where(s => int.TryParse(s, out _))
+                                   .Select(int.Parse)
+                                   .ToList();
+
+                    var names = ids.SelectMany(id =>
+                        clientNames.ContainsKey(id)
+                            ? new[] { clientNames[id] }
+                            : Array.Empty<string>()
+                    ).ToList();
+
+                    clientNameCache[item.ClientName] = names;
+                }
+            }
+
+            // 5. 构建分组数据
+            var airLookup = airInfos
+                .GroupBy(x => x.DIId)
+                .ToDictionary(g => g.Key, g => g.Select(x => x.TicketCode).Distinct().ToList());
+
+            var clientLookup = airInfos
+                .Where(x => !string.IsNullOrEmpty(x.ClientName) && clientNameCache.ContainsKey(x.ClientName))
+                .GroupBy(x => x.DIId)
+                .ToDictionary(g => g.Key, g => g.SelectMany(x => clientNameCache[x.ClientName]).Distinct().ToList());
+
+            // 6. 组装结果
+            var result = groupInfos.Select(group => new AirInfo
+            {
+                Id = group.Id,
+                TeamName = group.TeamName,
+                TicketCodes = airLookup.GetValueOrDefault(group.Id, new List<string>()),
+                Clients = clientLookup.GetValueOrDefault(group.Id, new List<string>())
+            }).ToList();
+
+            return result;
+        }
+
+        public class AirInfo
+        {
+            public int Id { get; set; }
+            public string TeamName { get; set; }
+
+            public string TicketCodesLabel => string.Join("、", TicketCodes);
+            public string ClientsLabel => string.Join("、", Clients);
+
+            public List<string> TicketCodes { get; set; }
+            public List<string> Clients { get; set; }
+        }
+
+        #endregion
+
         /// <summary>
         /// 团组各项费用录入 关键字输入提示(智能版)
         /// 76	酒店预订
@@ -498,6 +619,7 @@ namespace OASystem.API.Controllers
         [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
         public async Task<IActionResult> GroupFeeKeywordSearch(int userId, int feeType, string keyword)
         {
+            var stopwatch = Stopwatch.StartNew();
             try
             {
                 #region 参数验证
@@ -514,26 +636,17 @@ namespace OASystem.API.Controllers
                     .Where(x => x.IsDel == 0 && x.Id == userId)
                     .AnyAsync();
 
-                if (!userExists)
-                {
-                    return Ok(JsonView(false, "用户不存在或已被删除"));
-                }
+                if (!userExists) return Ok(JsonView(false, "用户不存在或已被删除"));
 
                 // 验证费用类型是否有效
                 var feeTypeExists = await _sqlSugar.Queryable<Sys_SetData>()
                     .Where(x => x.IsDel == 0 && x.STid == 16 && x.Id == feeType)
                     .AnyAsync();
 
-                if (!feeTypeExists)
-                {
-                    return Ok(JsonView(false, "无效的费用类型"));
-                }
+                if (!feeTypeExists) return Ok(JsonView(false, "无效的费用类型"));
 
                 // 验证关键字
-                if (string.IsNullOrWhiteSpace(keyword))
-                {
-                    return Ok(JsonView(false, "请输入搜索关键字"));
-                }
+                if (string.IsNullOrWhiteSpace(keyword)) return Ok(JsonView(false, "请输入搜索关键字"));
 
                 #endregion
 
@@ -546,13 +659,60 @@ namespace OASystem.API.Controllers
                     .Distinct()
                     .ToListAsync();
 
-                if (!authorizedGroupIds.Any())
-                {
-                    return Ok(JsonView(true, "暂无数据", new List<object>(), 0));
-                }
+                if (!authorizedGroupIds.Any()) return Ok(JsonView(true, "暂无数据", new List<object>(), 0));
 
                 #endregion
 
+                // 类型等于 85 时单独处理
+                if (feeType == 85)
+                {
+                    var airInfos = await GetAirInfos(authorizedGroupIds);
+                    if (airInfos == null) return Ok(JsonView(false, "获取机票费用信息失败"));
+
+                    var airSearchReq = new DynamicSearchRequest()
+                    {
+                        Keyword = keyword.Trim(),
+                        RequireAllSingleChars = true,
+                        PageIndex = 1,
+                        PageSize = 20, // 限制返回数量,提高性能
+                        FieldWeights = new Dictionary<string, int>
+                            {
+                                { "TeamName", 10 },
+                                { "TicketCodesLabel", 10 },
+                                { "ClientsLabel", 10 }
+                            }
+                    };
+
+                    var airResult = _airSearchService.SearchDataSource(airSearchReq, airInfos);
+
+                    if (!airResult.Success)
+                    {
+                        return Ok(JsonView(false, airResult.Message ?? "搜索失败"));
+                    }
+
+                    if (airResult.Items == null || !airResult.Items.Any())
+                    {
+                        return Ok(JsonView(true, "未找到匹配的团组", new List<object>(), 0));
+                    }
+
+                    var airRespData = airResult.Items
+                        .Where(item => item.Data != null)
+                        .Select(item => new
+                        {
+                            item.Data.Id,
+                            item.Data.TeamName,
+                            item.Data.TicketCodes,
+                            item.Data.Clients,
+                        })
+                        .Where(x => !string.IsNullOrWhiteSpace(x.TeamName)) // 过滤空名称
+                        .Distinct() // 去重
+                        .ToList();
+
+                    stopwatch.Stop();
+                    return Ok(JsonView(true, $"搜索成功,耗时:{stopwatch.ElapsedMilliseconds}ms", airRespData, airRespData.Count));
+
+                }
+
                 #region 构建搜索请求
 
                 var searchRequest = new DynamicSearchRequest
@@ -619,8 +779,8 @@ namespace OASystem.API.Controllers
                     .ToList();
 
                 #endregion
-
-                return Ok(JsonView(true, "搜索成功", responseData, responseData.Count));
+                stopwatch.Stop();
+                return Ok(JsonView(true, $"搜索成功,耗时:{stopwatch.ElapsedMilliseconds} ms", responseData, responseData.Count));
             }
             catch (Exception ex)
             {

+ 2 - 4
OASystem/OASystem.Api/OAMethodLib/GeneralMethod.cs

@@ -2253,9 +2253,7 @@ AI 必须按顺序执行:
         /// <returns></returns>
         public static async Task<TeamRateModelGeneralView> PostGroupTeamRateByDiIdAndCTableId(int portType, int diId, int cTable)
         {
-            TeamRateModelGeneralView _view = new TeamRateModelGeneralView();
-            _view = await _teamRateRep.PostGroupTeamRateByDiIdAndCTableId(portType, diId, cTable);
-            return _view;
+            return await _teamRateRep.PostGroupTeamRateByDiIdAndCTableId(portType, diId, cTable);
         }
 
         /// <summary>
@@ -2269,6 +2267,7 @@ AI 必须按顺序执行:
             List<TeamRateDescAddCurrencyIdView> _view = new List<TeamRateDescAddCurrencyIdView>();
 
             List<SetDataInfoView> currencyDatas = new List<SetDataInfoView>();
+
             #region 获取所有币种
 
             string sql = string.Format(@"select * from Sys_SetData where STid = {0} and isdel = 0", 66);
@@ -2288,7 +2287,6 @@ AI 必须按顺序执行:
 
             #endregion
 
-
             #region 拆分remark里的汇率
 
             if (string.IsNullOrEmpty(rateRemark))

+ 3 - 5
OASystem/OASystem.Api/OAMethodLib/KiMiApi/KiMiApi.cs

@@ -15,7 +15,7 @@ namespace OASystem.API.OAMethodLib.KiMiApi
         public KiMiApiClient()
         {
             _httpClient = new HttpClient();
-
+            _httpClient.Timeout = TimeSpan.FromMinutes(30); 
             _kimiSetting = AutofacIocManager.Instance.GetService<KiMiSetting>();
 
             // 设置公共请求头
@@ -67,7 +67,6 @@ namespace OASystem.API.OAMethodLib.KiMiApi
 
             message = fileContentText;
 
-
             return message;
         }
 
@@ -100,7 +99,6 @@ namespace OASystem.API.OAMethodLib.KiMiApi
             return JsonConvert.DeserializeObject<KiMiRoot>(respStr);
         }
 
-
         private async Task<string> SeedAsync(List<SeedMessages> messages)
         {
             var completionRequest = new
@@ -112,7 +110,8 @@ namespace OASystem.API.OAMethodLib.KiMiApi
                     x.Content
                 }),
                 temperature = 1,
-                max_tokens = 4096
+                max_tokens = 4096,
+                // thinking = new { type = "disabled" }  // 禁用 thinking 模式
             };
 
             var completionJson = JsonConvert.SerializeObject(completionRequest);
@@ -191,5 +190,4 @@ namespace OASystem.API.OAMethodLib.KiMiApi
             };
         }
     }
-
 }

+ 4 - 5
OASystem/OASystem.Api/OAMethodLib/QiYeWeChatAPI/AppNotice/Library.cs

@@ -129,15 +129,15 @@ namespace OASystem.API.OAMethodLib.QiYeWeChatAPI.AppNotice
         /// <summary>
         /// 向指定群聊发送- 费用申请 -通知
         /// </summary>
-        /// <param name="Grp_CreditCardPaymentId"></param>
+        /// <param name="creditCardId"></param>
         /// <param name="sign"></param>
         /// <param name="qwEnum"></param>
         /// <returns></returns>
-        public static async Task<bool> SendChatMsg_GroupStatus_ApplyFee(int Grp_CreditCardPaymentId, int sign, QiyeWeChatEnum qwEnum)
+        public static async Task<bool> SendChatMsg_GroupStatus_ApplyFee(int creditCardId, int sign, QiyeWeChatEnum qwEnum)
         {
             string chatId = qwEnum.GetEnumDescription();
 
-            Grp_CreditCardPayment ccp = _grpDeleRep.Query<Grp_CreditCardPayment>(s => s.Id == Grp_CreditCardPaymentId).First();
+            Grp_CreditCardPayment ccp = _grpDeleRep.Query<Grp_CreditCardPayment>(s => s.Id == creditCardId).First();
             Grp_DelegationInfo group = _grpDeleRep.Query<Grp_DelegationInfo>(s => s.Id == ccp.DIId).First();
 
             Sys_SetData payMoneyCurrencySetData = _grpDeleRep.Query<Sys_SetData>(s => s.Id == ccp.PaymentCurrency).First();
@@ -159,7 +159,7 @@ namespace OASystem.API.OAMethodLib.QiYeWeChatAPI.AppNotice
                 info.TitleStr = "[更新]一项费用待审核";
             }
 
-            //CTable
+            // CTable
             #region CTable
 
             if (ccp.CTable == 76)//76,酒店预订
@@ -250,7 +250,6 @@ namespace OASystem.API.OAMethodLib.QiYeWeChatAPI.AppNotice
             }
             #endregion
 
-
             //发送信息
             ResponseBase result = await _qiYeWeChatApiService.GroupStatus_SendChatMsg_Markdown(chatId, MarkdownLib.GroupStatus_ApplyFee(info));
             if (result.errcode != 0)

+ 0 - 1
OASystem/OASystem.Domain/AesEncryption/EncryptionProcessor.cs

@@ -8,7 +8,6 @@ namespace OASystem.Domain.AesEncryption
     /// </summary>
     public static class EncryptionProcessor
     {
-
         /// <summary>
         /// aes 加密
         /// </summary>

+ 11 - 0
OASystem/OASystem.Domain/AutoMappers/_baseMappingProfile.cs

@@ -107,6 +107,17 @@ namespace OASystem.Domain.AutoMappers
             CreateMap<CardPayment, Grp_CreditCardPayment>();
             CreateMap<Grp_GroupCostParameter, AirGroupCostParameterView>();
             CreateMap<Edit_GrpCreditCardPaymentDto, Grp_CreditCardPayment>();
+
+            #region 2026版
+            CreateMap<AirTicketFeeInfo, Grp_AirTicketReservations>();
+            CreateMap<AirTicketFeeInfo, Grp_CreditCardPayment>();
+            CreateMap<AirTicketFeeInfo, AirTicketFeeOpInfo>();
+            CreateMap<AirTicketFeeOpInfo, Grp_AirTicketReservations>();
+            CreateMap<AirTicketFeeOpInfo, Grp_CreditCardPayment>();
+            CreateMap<GroupAirTicketList, GroupAirTicketInfo>();
+
+            #endregion
+
             #endregion
 
             #region 团组增减款项

+ 47 - 54
OASystem/OASystem.Domain/Common/DepartmentCode.cs

@@ -1,57 +1,50 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+namespace OASystem.Domain.Common;
 
-namespace OASystem.Domain.Common
+public class DepartmentCode
 {
-    public class DepartmentCode
-    {
-        /// <summary>
-        /// 总经办
-        /// </summary>
-        public static int GMO = 1;
-
-        /// <summary>
-        /// 信息部
-        /// </summary>
-        public static int INFO = 2;
-
-        /// <summary>
-        /// 财务部
-        /// </summary>
-        public static int FIN = 3;
-
-        /// <summary>
-        /// 人事部
-        /// </summary>
-        public static int HR = 4;
-
-        /// <summary>
-        /// 策划部
-        /// </summary>
-        public static int PLAN = 5;
-
-        /// <summary>
-        /// 市场部
-        /// </summary>
-        public static int MARKET = 6;
-
-        /// <summary>
-        /// 国交部
-        /// </summary>
-        public static int IC = 7;
-
-        /// <summary>
-        /// 项目部
-        /// </summary>
-        public static int PROJECT = 8;
-
-        /// <summary>
-        /// OA管理部
-        /// </summary>
-        public static int OA = 9;
-
-    }
+    /// <summary>
+    /// 总经办
+    /// </summary>
+    public static int GMO = 1;
+
+    /// <summary>
+    /// 信息部
+    /// </summary>
+    public static int INFO = 2;
+
+    /// <summary>
+    /// 财务部
+    /// </summary>
+    public static int FIN = 3;
+
+    /// <summary>
+    /// 人事部
+    /// </summary>
+    public static int HR = 4;
+
+    /// <summary>
+    /// 策划部
+    /// </summary>
+    public static int PLAN = 5;
+
+    /// <summary>
+    /// 市场部
+    /// </summary>
+    public static int MARKET = 6;
+
+    /// <summary>
+    /// 国交部
+    /// </summary>
+    public static int IC = 7;
+
+    /// <summary>
+    /// 项目部
+    /// </summary>
+    public static int PROJECT = 8;
+
+    /// <summary>
+    /// OA管理部
+    /// </summary>
+    public static int OA = 9;
+
 }

+ 330 - 329
OASystem/OASystem.Domain/Dtos/Groups/AirTicketResDto.cs

@@ -1,353 +1,354 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using OASystem.Domain.ViewModels.Groups;
 
-namespace OASystem.Domain.Dtos.Groups
+namespace OASystem.Domain.Dtos.Groups;
+
+/// <summary>
+/// 根据登录用户查询
+/// </summary>
+public class AirTicketResDto : DtoBase
+{
+    public int DiId { get; set; } = 0;
+    public int UserId { get; set; } = 0;
+    public int IsPaySign { get; set; } = -1;
+
+}
+public class ItineraryAirTicketResDto
+{
+    public int DiId { get; set; } = 0;
+    public int UserId { get; set; } = 0;
+    public string Language { get; set; }
+}
+/// <summary>
+/// 根据团组Id及
+/// </summary>
+public class AirTicketResDeriveDto
+{
+
+    public int CreateUserId { get; set; } = 0;
+}
+public class AirTicketResByIdDto
+{
+    public int Id { get; set; }
+}
+
+public class QueryClientInfoByDIIDDto
+{
+    public int DIID { get; set; }
+}
+
+public class AirTicketResOpDto
 {
     /// <summary>
-    /// 根据登录用户查询
+    /// 操作状态
+    /// 1 添加 
+    /// 2 修改 
     /// </summary>
-    public class AirTicketResDto : DtoBase
-    {
-        public int DiId { get; set; } = 0;
-        public int UserId { get; set; } = 0;
-        public int IsPaySign { get; set; } = -1;
+    public int Status { get; set; }
+    public AirTicketResOp AirTicketResOpData { get; set; }
+    public CardPayment CardPaymentOpData { get; set; }
 
-    }
-    public class ItineraryAirTicketResDto
-    {
-        public int DiId { get; set; } = 0;
-        public int UserId { get; set; } = 0;
-        public string Language { get; set; }
-    }
+}
+/// <summary>
+/// 机票费用录入表参数
+/// </summary>
+public class AirTicketResOp
+{
     /// <summary>
-    /// 根据团组Id及
+    /// 编号
     /// </summary>
-    public class AirTicketResDeriveDto
-    {
+    public int Id { get; set; }
 
-        public int CreateUserId { get; set; } = 0;
-    }
-    public class AirTicketResByIdDto
-    {
-        public int Id { get; set; }
-    }
+    public int DiId { get; set; }
+    /// <summary>
+    /// 舱类型
+    /// </summary>
+    public int CType { get; set; }
+    /// <summary>
+    /// 出票前报价
+    /// </summary>
+    public decimal PrePrice { get; set; }
 
-    public class QueryClientInfoByDIIDDto
-    {
-        public int DIID { get; set; }
-    }
+    /// <summary>
+    /// 出票前报价币种
+    /// </summary>
+    public int PreCurrency { get; set; }
+    /// <summary>
+    /// 机票全价
+    /// </summary>
+    public decimal Price { get; set; }
+    /// <summary>
+    /// 币种
+    /// </summary>
+    public int Currency { get; set; }
+    /// <summary>
+    /// 客户人数
+    /// </summary>
+    public int ClientNum { get; set; }
+    /// <summary>
+    /// 客人名称
+    /// </summary>
+    public List<int> ClientName { get; set; }
+    /// <summary>
+    /// 是否值机
+    /// </summary>
+    public int IsCheckIn { get; set; }
+    /// <summary>
+    /// 是否选座
+    /// </summary>
+    public int IsSetSeat { get; set; }
 
-    public class AirTicketResOpDto
-    {
-        /// <summary>
-        /// 操作状态
-        /// 1 添加 
-        /// 2 修改 
-        /// </summary>
-        public int Status { get; set; }
-        public AirTicketResOp AirTicketResOpData { get; set; }
-        public CardPayment CardPaymentOpData { get; set; }
+    /// <summary>
+    /// 是否购买行李服务
+    /// </summary>
+    public int IsPackage { get; set; }
+    /// <summary>
+    /// 是否行李直挂
+    /// </summary>
+    public int IsBagHandle { get; set; }
+    /// <summary>
+    /// 是否火车票出票选座
+    /// </summary>
+    public int IsTrain { get; set; }
+    /// <summary>
+    /// 航班日期
+    /// </summary>
+    public string FlightsDate { get; set; }
 
-    }
     /// <summary>
-    /// 机票费用录入表参数
+    /// 航班时间
     /// </summary>
-    public class AirTicketResOp
-    {
-        /// <summary>
-        /// 编号
-        /// </summary>
-        public int Id { get; set; }
+    public string FlightsTime { get; set; }
+    /// <summary>
+    /// 航班号
+    /// </summary>
+    public string FlightsCode { get; set; }
+    /// <summary>
+    /// 城市A-B
+    /// </summary>
+    public string FlightsCity { get; set; }
+    /// /// <summary>
+    /// 内陆段航班描述
+    /// </summary>
+    public string FlightsDescription { get; set; }
 
-        public int DiId { get; set; }
-        /// <summary>
-        /// 舱类型
-        /// </summary>
-        public int CType { get; set; }
-        /// <summary>
-        /// 出票前报价
-        /// </summary>
-        public decimal PrePrice { get; set; }
+    /// <summary>
+    /// 报价说明
+    /// </summary>、
+    public string PriceDescription { get; set; }
+    /// <summary>
+    /// 机票编号
+    /// </summary>
+    public string TicketNumber { get; set; }
+    /// <summary>
+    /// 机票票号
+    /// </summary>
+    public string TicketCode { get; set; }
+    /// <summary>
+    /// 创建者Id
+    /// </summary>
+    public int CreateUserId { get; set; }
+    /// <summary>
+    /// 备注
+    /// </summary>
+    public string Remark { get; set; }
 
-        /// <summary>
-        /// 出票前报价币种
-        /// </summary>
-        public int PreCurrency { get; set; }
-        /// <summary>
-        /// 机票全价
-        /// </summary>
-        public decimal Price { get; set; }
-        /// <summary>
-        /// 币种
-        /// </summary>
-        public int Currency { get; set; }
-        /// <summary>
-        /// 客户人数
-        /// </summary>
-        public int ClientNum { get; set; }
-        /// <summary>
-        /// 客人名称
-        /// </summary>
-        public List<int> ClientName { get; set; }
-        /// <summary>
-        /// 是否值机
-        /// </summary>
-        public int IsCheckIn { get; set; }
-        /// <summary>
-        /// 是否选座
-        /// </summary>
-        public int IsSetSeat { get; set; }
+}
+/// <summary>
+/// C表参数
+/// </summary>
+public class CardPayment
+{
+    /// <summary>
+    /// 编号
+    /// </summary>
+    public int Id { get; set; }
+    /// <summary>
+    /// 支付方式
+    /// </summary>
+    public int PayDId { get; set; }
+    /// <summary>
+    /// 消费方式
+    /// </summary>
+    public string ConsumptionPatterns { get; set; }
+    /// <summary>
+    /// 消费日期
+    /// </summary>
+    public string ConsumptionDate { get; set; }
+    /// <summary>
+    /// 卡类型
+    /// </summary>
+    public int CTDId { get; set; }
+    /// <summary>
+    /// 银行卡号
+    /// </summary>
+    public string BankNo { get; set; }
+    /// <summary>
+    /// 持卡人姓名
+    /// </summary>
+    public string CardholderName { get; set; }
+    /// <summary>
+    /// 付款金额
+    /// </summary>
+    public decimal PayMoney { get; set; }
+    /// <summary>
+    /// 付款币种 数据类型Id
+    /// </summary>
+    public int PaymentCurrency { get; set; }
+    ///// <summary>
+    ///// 当天汇率 计算
+    ///// </summary>
+    //public string DayRate { get; set; }
+    /// <summary>
+    /// 公司银行卡号
+    /// </summary>
+    public string CompanyBankNo { get; set; }
+    /// <summary>
+    /// 对方开户行
+    /// </summary>
+    public string OtherBankName { get; set; }
+    /// <summary>
+    /// 对方银行账号
+    /// </summary>
+    public string OtherSideNo { get; set; }
+    /// <summary>
+    /// 对方姓名
+    /// </summary>
+    public string OtherSideName { get; set; }
+    /// <summary>
+    /// 财务操作人 用户Id
+    /// </summary>
+    public int MFOperator { get; set; }
+    /// <summary>
+    /// 财务操作时间
+    /// </summary>
+    public string MFOperatorDate { get; set; }
+    ///// <summary>
+    ///// 部门经理是否审核 0否1是
+    ///// </summary>
+    //public int IsAuditDM { get; set; }
+    ///// <summary>
+    ///// 部门经理审核人 用户Id
+    ///// </summary>
+    //public int AuditDMOperate { get; set; }
+    ///// <summary>
+    ///// 部门经理审核时间
+    ///// </summary>
+    //public DateTime AuditDMDate { get; set; }
+    ///// <summary>
+    ///// 财务部是否审核  0否1是
+    ///// </summary>
+    //public int IsAuditMF { get; set; }
+    ///// <summary>
+    ///// 财务部审核人
+    ///// </summary>
+    //public int AuditMFOperate { get; set; }
+    ///// <summary>
+    ///// 财务部审核时间
+    ///// </summary>
+    //public DateTime AuditMFDate { get; set; }
+    ///// <summary>
+    ///// 总经理是否审核  0否1是
+    ///// </summary>
+    //public int IsAuditGM { get; set; }
+    ///// <summary>
+    ///// 总经理审核人
+    ///// </summary>
+    //public int AuditGMOperate { get; set; }
+    ///// <summary>
+    ///// 总经理审核时间
+    ///// </summary>
+    //public DateTime AuditGMDate { get; set; }
+    ///// <summary> 
+    ///// 是否付款  0否1是 判断进行
+    ///// </summary> 
+    //public int IsPay { get; set; }
+    /// <summary>
+    /// 团组外键编号
+    /// </summary>
+    public int DIId { get; set; }
+    ///// <summary>
+    ///// 指向表外键编号 机票费用录入表Id
+    ///// </summary>
+    //public int CId { get; set; }
+    ///// <summary>
+    ///// 指向标识 设置数据外键编号 固定机票 85
+    ///// </summary>
+    //public int CTable { get; set; }
+    ///// <summary>
+    ///// 此次付款百分比 固定1
+    ///// </summary>
+    //public decimal PayPercentage { get; set; }
+    /// <summary>
+    /// 此次付款金额
+    /// </summary>
+    public decimal PayThenMoney { get; set; }
+    ///// <summary>
+    ///// 上次付款百分比 查询并计算最近一次
+    ///// </summary>
+    //public decimal PayPercentageOld { get; set; }
+    ///// <summary>
+    ///// 上次付款金额 查询上一次
+    ///// </summary>
+    //public decimal PayThenMoneyOld { get; set; }
+    ///// <summary>
+    ///// 上次付款时间 查询上一次
+    ///// </summary>
+    //public DateTime UpdateDate { get; set; }
+    /// <summary>
+    /// 收款方
+    /// </summary>
+    public string Payee { get; set; }
+    ///// <summary>
+    ///// 人民币费用 换算
+    ///// </summary>
+    //[SugarColumn(IsNullable = true, ColumnDataType = "decimal(10, 2)")]
+    //public decimal RMBPrice { get; set; }
+    /// <summary>
+    /// 费用标识
+    /// </summary>
+    public int OrbitalPrivateTransfer { get; set; }
+    ///// <summary>
+    ///// 超出预算比例 换算
+    ///// </summary>
+    //[SugarColumn(IsNullable = true, ColumnDataType = "decimal(10, 2)")]
+    //public decimal ExceedBudget { get; set; }
+    /// <summary>
+    /// 创建者Id
+    /// </summary>
+    public int CreateUserId { get; set; }
+    /// <summary>
+    /// 备注
+    /// </summary>
+    public string Remark { get; set; }
 
-        /// <summary>
-        /// 是否购买行李服务
-        /// </summary>
-        public int IsPackage { get; set; }
-        /// <summary>
-        /// 是否行李直挂
-        /// </summary>
-        public int IsBagHandle { get; set; }
-        /// <summary>
-        /// 是否火车票出票选座
-        /// </summary>
-        public int IsTrain { get; set; }
-        /// <summary>
-        /// 航班日期
-        /// </summary>
-        public string FlightsDate { get; set; }
+}
 
-        /// <summary>
-        /// 航班时间
-        /// </summary>
-        public string FlightsTime { get; set; }
-        /// <summary>
-        /// 航班号
-        /// </summary>
-        public string FlightsCode { get; set; }
-        /// <summary>
-        /// 城市A-B
-        /// </summary>
-        public string FlightsCity { get; set; }
-        /// /// <summary>
-        /// 内陆段航班描述
-        /// </summary>
-        public string FlightsDescription { get; set; }
+/// <summary>
+/// 国家出入时间
+/// </summary>
+public class CountryDataTime
+{
+    // 定义属性
+    public string Code { get; set; } = string.Empty;
+    public DateTime StartTime { get; set; } = DateTime.MinValue;
+    public DateTime EndTime { get; set; } = DateTime.MinValue;
+    public string Country { get; set; } = string.Empty;
 
-        /// <summary>
-        /// 报价说明
-        /// </summary>、
-        public string PriceDescription { get; set; }
-        /// <summary>
-        /// 机票编号
-        /// </summary>
-        public string TicketNumber { get; set; }
-        /// <summary>
-        /// 机票票号
-        /// </summary>
-        public string TicketCode { get; set; }
-        /// <summary>
-        /// 创建者Id
-        /// </summary>
-        public int CreateUserId { get; set; }
-        /// <summary>
-        /// 备注
-        /// </summary>
-        public string Remark { get; set; }
+    // 构造函数(可选)
+    public CountryDataTime() { }
 
-    }
-    /// <summary>
-    /// C表参数
-    /// </summary>
-    public class CardPayment
+    public CountryDataTime(string code, DateTime startTime, DateTime endTime, string country)
     {
-        /// <summary>
-        /// 编号
-        /// </summary>
-        public int Id { get; set; }
-        /// <summary>
-        /// 支付方式
-        /// </summary>
-        public int PayDId { get; set; }
-        /// <summary>
-        /// 消费方式
-        /// </summary>
-        public string ConsumptionPatterns { get; set; }
-        /// <summary>
-        /// 消费日期
-        /// </summary>
-        public string ConsumptionDate { get; set; }
-        /// <summary>
-        /// 卡类型
-        /// </summary>
-        public int CTDId { get; set; }
-        /// <summary>
-        /// 银行卡号
-        /// </summary>
-        public string BankNo { get; set; }
-        /// <summary>
-        /// 持卡人姓名
-        /// </summary>
-        public string CardholderName { get; set; }
-        /// <summary>
-        /// 付款金额
-        /// </summary>
-        public decimal PayMoney { get; set; }
-        /// <summary>
-        /// 付款币种 数据类型Id
-        /// </summary>
-        public int PaymentCurrency { get; set; }
-        ///// <summary>
-        ///// 当天汇率 计算
-        ///// </summary>
-        //public string DayRate { get; set; }
-        /// <summary>
-        /// 公司银行卡号
-        /// </summary>
-        public string CompanyBankNo { get; set; }
-        /// <summary>
-        /// 对方开户行
-        /// </summary>
-        public string OtherBankName { get; set; }
-        /// <summary>
-        /// 对方银行账号
-        /// </summary>
-        public string OtherSideNo { get; set; }
-        /// <summary>
-        /// 对方姓名
-        /// </summary>
-        public string OtherSideName { get; set; }
-        /// <summary>
-        /// 财务操作人 用户Id
-        /// </summary>
-        public int MFOperator { get; set; }
-        /// <summary>
-        /// 财务操作时间
-        /// </summary>
-        public string MFOperatorDate { get; set; }
-        ///// <summary>
-        ///// 部门经理是否审核 0否1是
-        ///// </summary>
-        //public int IsAuditDM { get; set; }
-        ///// <summary>
-        ///// 部门经理审核人 用户Id
-        ///// </summary>
-        //public int AuditDMOperate { get; set; }
-        ///// <summary>
-        ///// 部门经理审核时间
-        ///// </summary>
-        //public DateTime AuditDMDate { get; set; }
-        ///// <summary>
-        ///// 财务部是否审核  0否1是
-        ///// </summary>
-        //public int IsAuditMF { get; set; }
-        ///// <summary>
-        ///// 财务部审核人
-        ///// </summary>
-        //public int AuditMFOperate { get; set; }
-        ///// <summary>
-        ///// 财务部审核时间
-        ///// </summary>
-        //public DateTime AuditMFDate { get; set; }
-        ///// <summary>
-        ///// 总经理是否审核  0否1是
-        ///// </summary>
-        //public int IsAuditGM { get; set; }
-        ///// <summary>
-        ///// 总经理审核人
-        ///// </summary>
-        //public int AuditGMOperate { get; set; }
-        ///// <summary>
-        ///// 总经理审核时间
-        ///// </summary>
-        //public DateTime AuditGMDate { get; set; }
-        ///// <summary> 
-        ///// 是否付款  0否1是 判断进行
-        ///// </summary> 
-        //public int IsPay { get; set; }
-        /// <summary>
-        /// 团组外键编号
-        /// </summary>
-        public int DIId { get; set; }
-        ///// <summary>
-        ///// 指向表外键编号 机票费用录入表Id
-        ///// </summary>
-        //public int CId { get; set; }
-        ///// <summary>
-        ///// 指向标识 设置数据外键编号 固定机票 85
-        ///// </summary>
-        //public int CTable { get; set; }
-        ///// <summary>
-        ///// 此次付款百分比 固定1
-        ///// </summary>
-        //public decimal PayPercentage { get; set; }
-        /// <summary>
-        /// 此次付款金额
-        /// </summary>
-        public decimal PayThenMoney { get; set; }
-        ///// <summary>
-        ///// 上次付款百分比 查询并计算最近一次
-        ///// </summary>
-        //public decimal PayPercentageOld { get; set; }
-        ///// <summary>
-        ///// 上次付款金额 查询上一次
-        ///// </summary>
-        //public decimal PayThenMoneyOld { get; set; }
-        ///// <summary>
-        ///// 上次付款时间 查询上一次
-        ///// </summary>
-        //public DateTime UpdateDate { get; set; }
-        /// <summary>
-        /// 收款方
-        /// </summary>
-        public string Payee { get; set; }
-        ///// <summary>
-        ///// 人民币费用 换算
-        ///// </summary>
-        //[SugarColumn(IsNullable = true, ColumnDataType = "decimal(10, 2)")]
-        //public decimal RMBPrice { get; set; }
-        /// <summary>
-        /// 费用标识
-        /// </summary>
-        public int OrbitalPrivateTransfer { get; set; }
-        ///// <summary>
-        ///// 超出预算比例 换算
-        ///// </summary>
-        //[SugarColumn(IsNullable = true, ColumnDataType = "decimal(10, 2)")]
-        //public decimal ExceedBudget { get; set; }
-        /// <summary>
-        /// 创建者Id
-        /// </summary>
-        public int CreateUserId { get; set; }
-        /// <summary>
-        /// 备注
-        /// </summary>
-        public string Remark { get; set; }
-
+        this.Code = code;
+        StartTime = startTime;
+        EndTime = endTime;
+        Country = country;
     }
+}
 
-    /// <summary>
-    /// 国家出入时间
-    /// </summary>
-    public class CountryDataTime
-    {
-        // 定义属性
-        public string Code { get; set; } = string.Empty;
-        public DateTime StartTime { get; set; } = DateTime.MinValue;
-        public DateTime EndTime { get; set; } = DateTime.MinValue;
-        public string Country { get; set; } = string.Empty;
-
-        // 构造函数(可选)
-        public CountryDataTime() { }
 
-        public CountryDataTime(string code, DateTime startTime, DateTime endTime, string country)
-        {
-            this.Code = code;
-            StartTime = startTime;
-            EndTime = endTime;
-            Country = country;
-        }
-    }
+public class AirTicketFeeListDto : AirTicketResDto
+{ }
 
-}
+public class AirTicketFeeSaveDto : AirTicketFeeInfoView
+{ }

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

@@ -41,6 +41,19 @@
         public int AuditStatus { get; set; }
     }
 
+    public class ExpenseAuditOverviewDto : UserPageFuncDtoBase
+    {
+        /// <summary>
+        /// 团组Id
+        /// </summary>
+        public int DiId { get; set; }
+
+        /// <summary>
+        /// 审核状态 0/1/2,未审核/审核通过/审核不通过 ,-1:所有
+        /// </summary>
+        public int AuditStatus { get; set; }
+    }
+
     public class Edit_GrpCreditCardPaymentDto : UserPageFuncDtoBase
     {
         public string CreditIdStr { get; set; }

+ 1 - 7
OASystem/OASystem.Domain/Dtos/KiMi/KiMiSetting.cs

@@ -1,10 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace OASystem.Domain.Dtos.KiMi
+namespace OASystem.Domain.Dtos.KiMi
 {
     public class KiMiSetting
     {

+ 698 - 147
OASystem/OASystem.Domain/Entities/Groups/Grp_AirTicketReservations.cs

@@ -1,156 +1,707 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Text.RegularExpressions;
 
-namespace OASystem.Domain.Entities.Groups
+namespace OASystem.Domain.Entities.Groups;
+
+/// <summary>
+/// 机票费用录入
+/// </summary>
+[SugarTable("Grp_AirTicketReservations")]
+public class Grp_AirTicketReservations : EntityBase
 {
+    /************************* 2026版数据结构 *************************/
+
+    /// <summary>
+    /// 团组外键编号
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+    public int DIId { get; set; }
+
+    /// <summary>
+    /// 记录类型:0-正常机票 1-退票记录
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+    public int RecordType { get; set; } = 0;
+
+    /// <summary>
+    /// 关联的原始机票记录ID(退票记录指向原机票记录)
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+    public int OriginalReservationId { get; set; }
+
+    /// <summary>
+    /// 航段描述
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "varchar(500)")]
+    public string FlightsDescription { get; set; }
+
+    /// <summary>
+    /// 航班基础信息(去程、联程、返程)
+    /// </summary>
+    [SugarColumn(IsNullable = true, IsJson = true, ColumnDataType = "varchar(300)")]
+    public List<AirTicketBasicInfo> AirTicketBasicInfos { get; set; } = new List<AirTicketBasicInfo>();
+
+    /// <summary>
+    /// 客人名称
+    /// RecordType = 0 时,存储正常机票的客人名称;RecordType = 1 时,存储退票记录的客人名称(可能与原机票记录不同)
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "varchar(200)")]
+    public string ClientName { get; set; }
+
+    /// <summary>
+    /// 客户人数
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+    public int ClientNum { get; set; }
+
+    /// <summary>
+    /// 舱类型(数据类型外键)
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+    public int CType { get; set; }
+
+    /// <summary>
+    /// 机票全价
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "decimal(10,2)")]
+    public decimal Price { get; set; }
+
+    /// <summary>
+    /// 币种
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+    public int Currency { get; set; }
+
+    /// <summary>
+    /// 费用信息(含退票信息)
+    /// </summary>
+    [SugarColumn(IsNullable = true, IsJson = true, ColumnDataType = "varchar(max)")]
+    public List<CustTicketInfo> CustTicketInfos { get; set; } = new List<CustTicketInfo>();
+
+    /// <summary>
+    /// 报价说明
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "varchar(500)")]
+    public string PriceDescription { get; set; }
+
+    /************************* 2026版数据结构 *************************/
+
+    #region 旧版兼容以前的数据结构,2026版不使用
+
+    /// <summary>
+    /// 航班号
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")]
+    public string FlightsCode { get; set; }
+
+    /// <summary>
+    /// 城市A-B
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")]
+    public string FlightsCity { get; set; }
+
+    /// <summary>
+    /// 航班日期
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
+    public string FlightsDate { get; set; }
+
+    /// <summary>
+    /// 航班时间
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
+    public string FlightsTime { get; set; }
+
+    /// <summary>
+    /// 抵达时间
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
+    public string ArrivedTime { get; set; }
+
+    /// <summary>
+    /// 是否值机
+    /// 0 否 1 是
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+    public int IsCheckIn { get; set; }
+    /// <summary>
+    /// 是否选座
+    /// 0 否 1 是
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+    public int IsSetSeat { get; set; }
+
+    /// <summary>
+    /// 是否购买行李服务
+    /// 0 否 1 是
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+    public int IsPackage { get; set; }
+    /// <summary>
+    /// 是否行李直挂
+    /// 0 否 1 是
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+    public int IsBagHandle { get; set; }
+    /// <summary>
+    /// 是否火车票出票选座
+    /// 0 否 1 是
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+    public int IsTrain { get; set; }
+
+    /// <summary>
+    /// 去程航班描述代码
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "varchar(500)")]
+    public string LeaveDescription { get; set; }
+    /// <summary>
+    /// 返程航班描述代码
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "varchar(500)")]
+    public string ReturnDescription { get; set; }
+    /// <summary>
+    /// 出票前报价
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "decimal(10,2)")]
+    public decimal PrePrice { get; set; }
+    /// <summary>
+    /// 出票前报价币种
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+    public int PreCurrency { get; set; }
+   
+    /// <summary>
+    /// 机票编号
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")]
+    public string TicketNumber { get; set; }
+    /// <summary>
+    /// 机票票号
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")]
+    public string TicketCode { get; set; }
+
+    /// <summary>
+    /// 客人类型(数据类型外键)
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+    public int PassengerType { get; set; }
+    #endregion
+
+}
+
+/// <summary>
+/// 机票基础信息
+/// </summary>
+public class AirTicketBasicInfo
+{
+    /// <summary>
+    /// 序号
+    /// </summary>
+    public int No { get; set; }
+
+    /// <summary>
+    /// 航班号
+    /// </summary>
+    public string FlightsCode { get; set; }
+
+    /// <summary>
+    /// 城市A-B
+    /// </summary>
+    public string FlightsCity { get; set; }
+
+    /// <summary>
+    /// 航班日期
+    /// </summary>
+    public string FlightsDate { get; set; }
+
+    /// <summary>
+    /// 航班时间
+    /// </summary>
+    public string FlightsTime { get; set; }
+
+    /// <summary>
+    /// 抵达时间
+    /// </summary>
+    public string ArrivedTime { get; set; }
+
+}
+
+/// <summary>
+/// 客户机票信息
+/// </summary>
+public class CustTicketInfo
+{
+    /// <summary>
+    /// 客户ID
+    /// </summary>
+    public int ClientId { get; set; }
+
+    /// <summary>
+    /// 舱类型(数据类型外键)
+    /// </summary>
+    public int CType { get; set; }
+
+    /// <summary>
+    /// 实际价格
+    /// </summary>
+    public decimal ActualPrice { get; set; }
+
+    /// <summary>
+    /// 机票票号
+    /// </summary>
+    public string TicketCode { get; set; }
+
+    /// <summary>
+    /// 机票编号
+    /// </summary>
+    public string TicketNumber { get; set; }
+
+    /// <summary>
+    /// 选中服务ID集合
+    /// </summary>
+    public List<int> SelectedServiceIds { get; set; } = new List<int>();
+
+    /// <summary>
+    /// 附加服务 json数组
+    /// </summary>
+    public List<AdditionalService> AdditionalServices { get; set; } = new List<AdditionalService>();
+
+    /// <summary>
+    /// 机票总费用
+    /// 实际价格 + 附加服务费
+    /// </summary>
+    public decimal TotalTicketPrice { get; set; }
+
+    /// <summary>
+    /// 是否已退票
+    /// </summary>
+    public bool IsRefund { get; set; } = false;
+
+    /// <summary>
+    /// 退票记录
+    /// </summary>
+    public RefundRecord RefundRecord { get; set; } = new RefundRecord();
+
+}
+
+/// <summary>
+/// 退票记录
+/// </summary>
+public class RefundRecord
+{
+    /// <summary>
+    /// 退票金额
+    /// </summary>
+    public decimal RefundAmount { get; set; }
+
+    /// <summary>
+    /// 不可退税费
+    /// </summary>
+    public decimal NonRefundableTax { get; set; }
+
+    /// <summary>
+    /// 退票时间
+    /// </summary>
+    public string RefundTime { get; set; }
+
+    /// <summary>
+    /// 退款账户 
+    /// Setdata Id
+    /// </summary>
+    public int RefundAccount { get; set; }
 
     /// <summary>
-    /// 机票费用录入
+    /// 退票原因
     /// </summary>
-    [SugarTable("Grp_AirTicketReservations")]
-    public class Grp_AirTicketReservations : EntityBase
+    public string RefundReason { get; set; }
+}
+
+/// <summary>
+/// 附加服务
+/// </summary>
+public class AdditionalService
+{
+    /// <summary>
+    /// 服务类型Id(Sys_SetData)
+    /// </summary>
+    public int ServiceTypeId { get; set; }
+
+    /// <summary>
+    /// 金额
+    /// </summary>
+    public decimal Amount { get; set; }
+
+}
+
+
+
+/// <summary>
+/// 航班信息实体(通用)
+/// </summary>
+public class FlightInfo
+{
+    /// <summary>
+    /// 序号
+    /// </summary>
+    public int SequenceNo { get; set; }
+
+    /// <summary>
+    /// 航班号
+    /// </summary>
+    public string FlightNumber { get; set; }
+
+    /// <summary>
+    /// 舱位等级/舱位代码
+    /// </summary>
+    public string CabinClass { get; set; }
+
+    /// <summary>
+    /// 星期
+    /// </summary>
+    public string WeekDay { get; set; }
+
+    /// <summary>
+    /// 出发日期(原始格式)
+    /// </summary>
+    public string DepartureDateRaw { get; set; }
+
+    /// <summary>
+    /// 出发日期(标准格式 yyyy-MM-dd)
+    /// </summary>
+    public string DepartureDate { get; set; }
+
+    /// <summary>
+    /// 出发机场代码
+    /// </summary>
+    public string DepartureAirport { get; set; }
+
+    /// <summary>
+    /// 到达机场代码
+    /// </summary>
+    public string ArrivalAirport { get; set; }
+
+    /// <summary>
+    /// 出发时间(原始格式)
+    /// </summary>
+    public string DepartureTimeRaw { get; set; }
+
+    /// <summary>
+    /// 出发时间(格式化 HH:mm)
+    /// </summary>
+    public string DepartureTime { get; set; }
+
+    /// <summary>
+    /// 到达时间(原始格式)
+    /// </summary>
+    public string ArrivalTimeRaw { get; set; }
+
+    /// <summary>
+    /// 到达时间(格式化 HH:mm)
+    /// </summary>
+    public string ArrivalTime { get; set; }
+
+    /// <summary>
+    /// 是否次日到达
+    /// </summary>
+    public bool IsNextDayArrival { get; set; }
+
+    /// <summary>
+    /// 航站楼信息
+    /// </summary>
+    public string Terminal { get; set; }
+
+    /// <summary>
+    /// 机型
+    /// </summary>
+    public string AircraftType { get; set; }
+
+    /// <summary>
+    /// 飞行时长
+    /// </summary>
+    public string Duration { get; set; }
+
+    /// <summary>
+    /// 状态(HK/KK等)
+    /// </summary>
+    public string Status { get; set; }
+
+    /// <summary>
+    /// 原始文本
+    /// </summary>
+    public string RawText { get; set; }
+
+    /// <summary>
+    /// 显示信息
+    /// </summary>
+    public string DisplayInfo => $"{SequenceNo}.{FlightNumber} {DepartureAirport}→{ArrivalAirport} {DepartureTime}→{ArrivalTime}{(IsNextDayArrival ? "+1" : "")}";
+}
+
+
+/// <summary>
+/// 航班解析器
+/// </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 }
+    };
+
+    /// <summary>
+    /// 星期映射
+    /// </summary>
+    private static readonly Dictionary<string, string> WeekDayMap = new()
     {
-        /// <summary>
-        /// 团组外键编号
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "int")]
-        public int DIId { get; set; }
-        /// <summary>
-        /// 航班号
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")]
-        public string FlightsCode { get; set; }
-
-        /// <summary>
-        /// 航班日期
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
-        public string FlightsDate { get; set; }
-
-        /// <summary>
-        /// 航班时间
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
-        public string FlightsTime { get; set; }
-        /// <summary>
-        /// 抵达时间
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
-        public string ArrivedTime { get; set; }
-        /// <summary>
-        /// 是否值机
-        /// 0 否 1 是
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "int")]
-        public int IsCheckIn { get; set; }
-        /// <summary>
-        /// 是否选座
-        /// 0 否 1 是
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "int")]
-        public int IsSetSeat { get; set; }
-
-        /// <summary>
-        /// 是否购买行李服务
-        /// 0 否 1 是
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "int")]
-        public int IsPackage { get; set; }
-        /// <summary>
-        /// 是否行李直挂
-        /// 0 否 1 是
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "int")]
-        public int IsBagHandle { get; set; }
-        /// <summary>
-        /// 是否火车票出票选座
-        /// 0 否 1 是
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "int")]
-        public int IsTrain { get; set; }
-        /// <summary>
-        /// 城市A-B
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")]
-        public string FlightsCity { get; set; }
-        /// <summary>
-        /// 去程航班描述代码
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(500)")]
-        public string LeaveDescription { get; set; }
-        /// /// <summary>
-        /// 内陆段航班描述
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(500)")]
-        public string FlightsDescription { get; set; }
-        /// <summary>
-        /// 返程航班描述代码
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(500)")]
-        public string ReturnDescription { get; set; }
-        /// <summary>
-        /// 客户人数
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "int")]
-        public int ClientNum { get; set; }
-        /// <summary>
-        /// 客人名称
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(125)")]
-        public string ClientName { get; set; }
-        /// <summary>
-        /// 出票前报价
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(10,2)")]
-        public decimal PrePrice { get; set; }
-        /// <summary>
-        /// 出票前报价币种
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "int")]
-        public int PreCurrency { get; set; }
-        /// <summary>
-        /// 机票全价
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(10,2)")]
-        public decimal Price { get; set; }
-        /// <summary>
-        /// 币种
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "int")]
-        public int Currency { get; set; }
-
-        /// <summary>
-        /// 机票编号
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")]
-        public string TicketNumber { get; set; }
-        /// <summary>
-        /// 机票票号
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")]
-        public string TicketCode { get; set; }
-
-        /// <summary>
-        /// 报价说明
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(1000)")]
-        public string PriceDescription { get; set; }
-
-        /// <summary>
-        /// 舱类型(数据类型外键)
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "int")]
-        public int CType { get; set; }
-
-        /// <summary>
-        /// 客人类型(数据类型外键)
-        /// </summary>
-        [SugarColumn(IsNullable = true, ColumnDataType = "int")]
-        public int PassengerType { get; set; }
+        { "MO", "周一" }, { "TU", "周二" }, { "WE", "周三" }, { "TH", "周四" },
+        { "FR", "周五" }, { "SA", "周六" }, { "SU", "周日" }
+    };
+
+    #endregion
 
+    #region 主解析方法
 
+    /// <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)
+        {
+            var flight = ParseLine(line, year);
+            if (flight != null)
+            {
+                result.Add(flight);
+            }
+        }
+
+        return result.OrderBy(x => x.SequenceNo).ToList();
     }
-}
+
+    /// <summary>
+    /// 解析单行文本(自动识别格式)
+    /// </summary>
+    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
+        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解析(HU7086 C   MO18MAY  TFUHAK HK1   1605 1820          E T2T2)
+
+    /// <summary>
+    /// 解析格式1
+    /// 示例:1.  HU7086 C   MO18MAY  TFUHAK HK1   1605 1820          E T2T2
+    /// </summary>
+    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;      // MO
+        var dateRaw = match.Groups[4].Value;      // 18MAY
+        var departureAirport = match.Groups[5].Value; // TFU
+        var arrivalAirport = match.Groups[6].Value;   // HAK
+        var status = match.Groups[7].Value;       // HK1
+        var departureTime = match.Groups[8].Value; // 1605
+        var arrivalTime = match.Groups[9].Value;   // 1820
+        var isNextDay = match.Groups[10].Success && match.Groups[10].Value == "1";
+        var terminalFlag = match.Groups[12].Value; // T2T2
+
+        var flight = 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,
+            Status = status,
+            Terminal = terminalFlag,
+            RawText = line
+        };
+
+        // 格式化时间
+        flight.DepartureTime = FormatTime(departureTime);
+        flight.ArrivalTime = FormatTime(arrivalTime);
+
+        // 解析日期
+        flight.DepartureDate = ParseDate(dateRaw, year);
+
+        return flight;
+    }
+
+    #endregion
+
+    #region 格式2解析(1.CA431 TU27MAY TFUFRA 0135 0645 T1 1 359 11H10M)
+
+    /// <summary>
+    /// 解析格式2
+    /// 示例:1.CA431 TU27MAY TFUFRA 0135 0645 T1 1 359 11H10M
+    /// </summary>
+    private static FlightInfo ParseFormat2(string line, int year)
+    {
+        // 提取序号和航班号
+        var pattern = @"^(\d+)\.([A-Z0-9]+)\s+([A-Z]{2})(\d{2}[A-Z]{3})\s+([A-Z]{3})([A-Z]{3})\s+(\d{3,4})\s+(\d{3,4})(?:\+(\d+))?\s+([A-Z0-9]+)\s+(\d+)\s+([A-Z0-9]+)\s+([\dH]+)";
+        var match = Regex.Match(line.Trim(), pattern);
+
+        if (!match.Success) return null;
+
+        var weekDay = match.Groups[3].Value;      // TU
+        var dateRaw = match.Groups[4].Value;      // 27MAY
+        var departureAirport = match.Groups[5].Value; // TFU
+        var arrivalAirport = match.Groups[6].Value;   // FRA
+        var departureTime = match.Groups[7].Value;    // 0135
+        var arrivalTime = match.Groups[8].Value;      // 0645
+        var isNextDay = match.Groups[9].Success && match.Groups[9].Value == "1";
+        var terminal = match.Groups[10].Value;    // T1
+        var stopCount = match.Groups[11].Value;   // 1
+        var aircraftType = match.Groups[12].Value; // 359
+        var duration = match.Groups[13].Value;     // 11H10M
+
+        var flight = new FlightInfo
+        {
+            SequenceNo = int.Parse(match.Groups[1].Value),
+            FlightNumber = match.Groups[2].Value,
+            WeekDay = weekDay,
+            DepartureDateRaw = $"{weekDay}{dateRaw}",
+            DepartureAirport = departureAirport,
+            ArrivalAirport = arrivalAirport,
+            DepartureTimeRaw = departureTime,
+            ArrivalTimeRaw = arrivalTime,
+            IsNextDayArrival = isNextDay,
+            Terminal = terminal,
+            AircraftType = aircraftType,
+            Duration = duration,
+            RawText = line
+        };
+
+        // 格式化时间
+        flight.DepartureTime = FormatTime(departureTime);
+        flight.ArrivalTime = FormatTime(arrivalTime);
+
+        // 解析日期
+        flight.DepartureDate = ParseDate(dateRaw, year);
+
+        return flight;
+    }
+
+    #endregion
+
+    #region 辅助方法
+
+    /// <summary>
+    /// 格式化时间(1905 -> 19:05)
+    /// </summary>
+    private static string FormatTime(string time)
+    {
+        if (string.IsNullOrWhiteSpace(time))
+            return string.Empty;
+
+        // 4位数字
+        if (time.Length == 4 && int.TryParse(time, out _))
+        {
+            return $"{time.Substring(0, 2)}:{time.Substring(2, 2)}";
+        }
+
+        // 3位数字(905 -> 09:05)
+        if (time.Length == 3 && int.TryParse(time, out _))
+        {
+            return $"0{time.Substring(0, 1)}:{time.Substring(1, 2)}";
+        }
+
+        // 已经是 HH:mm 格式
+        if (Regex.IsMatch(time, @"^\d{1,2}:\d{2}$"))
+        {
+            var parts = time.Split(':');
+            return $"{parts[0].PadLeft(2, '0')}:{parts[1]}";
+        }
+
+        return time;
+    }
+
+    /// <summary>
+    /// 解析日期(27MAY -> 2024-05-27)
+    /// </summary>
+    private static string ParseDate(string dateStr, int year)
+    {
+        if (string.IsNullOrWhiteSpace(dateStr))
+            return string.Empty;
+
+        var match = Regex.Match(dateStr, @"(\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
+                {
+                    var date = new DateTime(year, month, day);
+                    return date.ToString("yyyy-MM-dd");
+                }
+                catch
+                {
+                    return dateStr;
+                }
+            }
+        }
+
+        return dateStr;
+    }
+
+    /// <summary>
+    /// 获取星期中文名称
+    /// </summary>
+    public static string GetWeekDayName(string weekDay)
+    {
+        return WeekDayMap.TryGetValue(weekDay.ToUpper(), out var name) ? name : weekDay;
+    }
+
+    #endregion
+}

+ 1 - 0
OASystem/OASystem.Domain/Entities/Groups/Grp_CreditCardPayment.cs

@@ -157,6 +157,7 @@ namespace OASystem.Domain.Entities.Groups
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
         public string AuditGMDate { get; set; }
+
         /// <summary> 
         /// 是否付款  0 否 1 是
         /// </summary> 

+ 9 - 3
OASystem/OASystem.Domain/Entities/Groups/Grp_DecreasePayments.cs

@@ -1,8 +1,7 @@
 namespace OASystem.Domain.Entities.Groups;
 
 /// <summary>
-/// 团组增减款项表
-/// --其他款项
+/// 团组增减款项表 --> 其他款项
 /// </summary>
 [SugarTable("Grp_DecreasePayments")]
 public class Grp_DecreasePayments : EntityBase
@@ -13,6 +12,13 @@ public class Grp_DecreasePayments : EntityBase
     [SugarColumn(IsNullable = true, ColumnDataType = "int")]
     public int DiId { get; set; }
 
+    /// <summary>
+    /// 主费用编号
+    /// 用于处理一笔费用包含多条款项的情况,第一条预付款 ID 为 1,尾款 主费用编号 为 1,第二笔费用的预付款 ID 为 2,尾款主费用编号为 2,以此类推
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+    public int ParentId { get; set; }
+
     /// <summary>
     /// 供应商地区
     /// 1 国内(默认) 2 国外
@@ -75,7 +81,7 @@ public class Grp_DecreasePayments : EntityBase
     /// <summary>
     /// 付款百分比
     /// </summary>
-    [SugarColumn(IsNullable = true, ColumnDataType = "decimal(5,2)")]
+    [SugarColumn(IsNullable = true, ColumnDataType = "decimal(10,4)")]
     public decimal PaymentPercent { get; set; }
 
     /// <summary>

+ 586 - 248
OASystem/OASystem.Domain/ViewModels/Groups/AirTicketReservationsView.cs

@@ -1,253 +1,591 @@
 using OASystem.Domain.Entities.Groups;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
-namespace OASystem.Domain.ViewModels.Groups
+namespace OASystem.Domain.ViewModels.Groups;
+
+public class AirTicketReservationsView : Grp_AirTicketReservations
 {
-    public class AirTicketReservationsView:Grp_AirTicketReservations
-    {
-        /// <summary>
-        /// 总经理是否审核
-        /// </summary>
-        public int IsAuditGM { get; set; }
-        /// <summary>
-        /// 舱类型
-        /// </summary>
-        public string CTypeName { get; set; }
-        public string PreCurrencyStr { get; set; }
-        public string CurrencyStr { get; set; }
-
-        public string FlightDescription { get; set; }
-        public string ClientNameStr { get; set; }
-        public int IsPay { get; set; }
-    }
-
-    public class AirGroupCostParameterView
-    {
-        /// <summary>
-        /// 经济舱成本
-        /// </summary>
-        public decimal JJCCB { get; set; }
-        /// <summary>
-        /// 经济舱系数
-        /// </summary>
-        public decimal JJCXS { get; set; }
-        /// <summary>
-        /// 经济舱人数
-        /// </summary>
-        public int JJCRS { get; set; }
-        /// <summary>
-        /// 公务舱成本
-        /// </summary>
-        public decimal GWCCB { get; set; }
-        /// <summary>
-        /// 公务舱系数
-        /// </summary>
-        public decimal GWCXS { get; set; }
-        /// <summary>
-        /// 公务舱人数
-        /// </summary>
-        public int GWCRS { get; set; }
-    }
-
-    public class AirTicketReservationsPayView
-    {
-        /// <summary>
-        /// 团组号
-        /// </summary>
-        public string TourCode { get; set; }
-        /// <summary>
-        /// 团组名称
-        /// </summary>
-        public string TeamName { get; set; }
-        /// <summary>
-        /// 城市A-B
-        /// </summary>
-        public string FlightsCity { get; set; }
-        /// <summary>
-        /// 航班说明
-        /// </summary>
-        public string FlightsDescription { get; set; }
-        /// <summary>
-        /// 航班号
-        /// </summary>
-        public string FlightsCode { get; set; }
-        /// <summary>
-        /// 舱位
-        /// </summary>
-        public string CTypeName { get; set; }
-        /// <summary>
-        /// 客人姓名
-        /// </summary>
-        public string ClientName { get; set; }
-        /// <summary>
-        /// 舱位人数
-        /// </summary>
-        public int ClientNum { get; set; }
-        /// <summary>
-        /// 单价
-        /// </summary>
-        public decimal PrePrice { get; set; }
-        /// <summary>
-        /// 总价
-        /// </summary>
-        public decimal Price { 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 DateTime CreateTime { get; set; }
-        /// <summary>
-        /// 收款方
-        /// </summary>
-        public string Payee { get; set; }
-        /// <summary>
-        /// 费用说明
-        /// </summary>
-        public string PriceDescription { get; set; }
-        /// <summary>
-        /// 币种
-        /// </summary>
-        public string CurrencyStr { get; set; }
-    }
-
-    /// <summary>
-    /// 导出行程单数据
-    /// </summary>
-    public class Itinerary
-    {
-        /// <summary>
-        /// 航空公司记录编码
-        /// </summary>
-        public string AirlineRecordCode { get; set; } = "--";
-        /// <summary>
-        /// 订座记录编码
-        /// </summary>
-        public string ReservationRecordCode{ get; set; } = "--";
-        /// <summary>
-        /// 旅客姓名
-        /// </summary>
-        public string ClientName{ get; set; } = "--";
-        /// <summary>
-        /// 票号
-        /// </summary>
-        public string TicketNumber{ get; set; } = "--";
-        /// <summary>
-        /// 身份识别代码
-        /// </summary>
-        public string IdentificationCode{ get; set; } = "--";
-        /// <summary>
-        /// 联票
-        /// </summary>
-        public string JointTicket{ get; set; } = "--";
-        /// <summary>
-        /// 出票航空公司
-        /// </summary>
-        public string AirlineCompany{ get; set; } = "--";
-        /// <summary>
-        /// 出票时间
-        /// </summary>
-        public string TimeIssue{ get; set; } = "--";
-        /// <summary>
-        /// 代理人
-        /// </summary>
-        public string DrawingAgent{ get; set; } = "--";
-        /// <summary>
-        /// 航协代码
-        /// </summary>
-        public string NavigationCode{ get; set; } = "--";
-        /// <summary>
-        /// 代理人地址
-        /// </summary>
-        public string AgentsAddress{ get; set; } = "--";
-        /// <summary>
-        /// 代理人电话
-        /// </summary>
-        public string AgentPhone{ get; set; } = "--";
-        /// <summary>
-        /// 代理人传真
-        /// </summary>
-        public string AgentFacsimile{ get; set; } = "--";
-        /// <summary>
-        /// 航班数据
-        /// </summary>
-        public List<AirInfo> AirInfo { get;set; } = new List<AirInfo>();
-    }
-    /// <summary>
-    /// 航班信息
-    /// </summary>
-    public class AirInfo
-    {
-        /// <summary>
-        /// 始发地/到达地
-        /// </summary>
-        public string Destination { get; set; }
-        /// <summary>
-        /// 航班号
-        /// </summary>
-        public string Flight { get; set; }
-        /// <summary>
-        /// 座位等级
-        /// </summary>
-        public string SeatingClass { get; set; }
-        /// <summary>
-        /// 日期
-        /// </summary>
-        public string FlightDate { get; set; }
-        /// <summary>
-        /// 起飞时间
-        /// </summary>
-        public string DepartureTime { get; set; }
-        /// <summary>
-        /// 到达时间
-        /// </summary>
-        public string LandingTime { get; set; }
-        /// <summary>
-        /// 有效期
-        /// </summary>
-        public string ValidityPeriod { get; set; }
-        /// <summary>
-        /// 客票状态
-        /// </summary>
-        public string TicketStatus { get; set; }
-        /// <summary>
-        /// 行李
-        /// </summary>
-        public string Luggage { get; set; }
-        /// <summary>
-        /// 起飞航站楼
-        /// </summary>
-        public string DepartureTerminal { get; set; }
-        /// <summary>
-        /// 到达航站楼
-        /// </summary>
-        public string LandingTerminal { get; set; }
-    }
-
-    public class EntryAndExitTipsView
+    /// <summary>
+    /// 总经理是否审核
+    /// </summary>
+    public int IsAuditGM { get; set; }
+    /// <summary>
+    /// 舱类型
+    /// </summary>
+    public string CTypeName { get; set; }
+    public string PreCurrencyStr { get; set; }
+    public string CurrencyStr { get; set; }
+
+    public string FlightDescription { get; set; }
+    public string ClientNameStr { get; set; }
+    public int IsPay { get; set; }
+}
+
+
+
+
+public class AirGroupCostParameterView
+{
+    /// <summary>
+    /// 经济舱成本
+    /// </summary>
+    public decimal JJCCB { get; set; }
+    /// <summary>
+    /// 经济舱系数
+    /// </summary>
+    public decimal JJCXS { get; set; }
+    /// <summary>
+    /// 经济舱人数
+    /// </summary>
+    public int JJCRS { get; set; }
+    /// <summary>
+    /// 公务舱成本
+    /// </summary>
+    public decimal GWCCB { get; set; }
+    /// <summary>
+    /// 公务舱系数
+    /// </summary>
+    public decimal GWCXS { get; set; }
+    /// <summary>
+    /// 公务舱人数
+    /// </summary>
+    public int GWCRS { get; set; }
+}
+
+public class AirTicketReservationsPayView
+{
+    /// <summary>
+    /// 团组号
+    /// </summary>
+    public string TourCode { get; set; }
+    /// <summary>
+    /// 团组名称
+    /// </summary>
+    public string TeamName { get; set; }
+    /// <summary>
+    /// 城市A-B
+    /// </summary>
+    public string FlightsCity { get; set; }
+    /// <summary>
+    /// 航班说明
+    /// </summary>
+    public string FlightsDescription { get; set; }
+    /// <summary>
+    /// 航班号
+    /// </summary>
+    public string FlightsCode { get; set; }
+    /// <summary>
+    /// 舱位
+    /// </summary>
+    public string CTypeName { get; set; }
+    /// <summary>
+    /// 客人姓名
+    /// </summary>
+    public string ClientName { get; set; }
+    /// <summary>
+    /// 舱位人数
+    /// </summary>
+    public int ClientNum { get; set; }
+    /// <summary>
+    /// 单价
+    /// </summary>
+    public decimal PrePrice { get; set; }
+    /// <summary>
+    /// 总价
+    /// </summary>
+    public decimal Price { 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 DateTime CreateTime { get; set; }
+    /// <summary>
+    /// 收款方
+    /// </summary>
+    public string Payee { get; set; }
+    /// <summary>
+    /// 费用说明
+    /// </summary>
+    public string PriceDescription { get; set; }
+    /// <summary>
+    /// 币种
+    /// </summary>
+    public string CurrencyStr { get; set; }
+}
+
+/// <summary>
+/// 导出行程单数据
+/// </summary>
+public class Itinerary
+{
+    /// <summary>
+    /// 航空公司记录编码
+    /// </summary>
+    public string AirlineRecordCode { get; set; } = "--";
+    /// <summary>
+    /// 订座记录编码
+    /// </summary>
+    public string ReservationRecordCode { get; set; } = "--";
+    /// <summary>
+    /// 旅客姓名
+    /// </summary>
+    public string ClientName { get; set; } = "--";
+    /// <summary>
+    /// 票号
+    /// </summary>
+    public string TicketNumber { get; set; } = "--";
+    /// <summary>
+    /// 身份识别代码
+    /// </summary>
+    public string IdentificationCode { get; set; } = "--";
+    /// <summary>
+    /// 联票
+    /// </summary>
+    public string JointTicket { get; set; } = "--";
+    /// <summary>
+    /// 出票航空公司
+    /// </summary>
+    public string AirlineCompany { get; set; } = "--";
+    /// <summary>
+    /// 出票时间
+    /// </summary>
+    public string TimeIssue { get; set; } = "--";
+    /// <summary>
+    /// 代理人
+    /// </summary>
+    public string DrawingAgent { get; set; } = "--";
+    /// <summary>
+    /// 航协代码
+    /// </summary>
+    public string NavigationCode { get; set; } = "--";
+    /// <summary>
+    /// 代理人地址
+    /// </summary>
+    public string AgentsAddress { get; set; } = "--";
+    /// <summary>
+    /// 代理人电话
+    /// </summary>
+    public string AgentPhone { get; set; } = "--";
+    /// <summary>
+    /// 代理人传真
+    /// </summary>
+    public string AgentFacsimile { get; set; } = "--";
+    /// <summary>
+    /// 航班数据
+    /// </summary>
+    public List<AirInfo> AirInfo { get; set; } = new List<AirInfo>();
+}
+/// <summary>
+/// 航班信息
+/// </summary>
+public class AirInfo
+{
+    /// <summary>
+    /// 始发地/到达地
+    /// </summary>
+    public string Destination { get; set; }
+    /// <summary>
+    /// 航班号
+    /// </summary>
+    public string Flight { get; set; }
+    /// <summary>
+    /// 座位等级
+    /// </summary>
+    public string SeatingClass { get; set; }
+    /// <summary>
+    /// 日期
+    /// </summary>
+    public string FlightDate { get; set; }
+    /// <summary>
+    /// 起飞时间
+    /// </summary>
+    public string DepartureTime { get; set; }
+    /// <summary>
+    /// 到达时间
+    /// </summary>
+    public string LandingTime { get; set; }
+    /// <summary>
+    /// 有效期
+    /// </summary>
+    public string ValidityPeriod { get; set; }
+    /// <summary>
+    /// 客票状态
+    /// </summary>
+    public string TicketStatus { get; set; }
+    /// <summary>
+    /// 行李
+    /// </summary>
+    public string Luggage { get; set; }
+    /// <summary>
+    /// 起飞航站楼
+    /// </summary>
+    public string DepartureTerminal { get; set; }
+    /// <summary>
+    /// 到达航站楼
+    /// </summary>
+    public string LandingTerminal { get; set; }
+}
+
+public class EntryAndExitTipsView
+{
+    public decimal jjcCurrentRate { get; set; }
+    public decimal gwcCurrentRate { get; set; }
+    public decimal tdcCurrentRate { get; set; }
+    public string remark { get; set; }
+    public string TripDesc { get; set; }
+}
+
+#region 2026-05版
+
+/// <summary>
+/// 机票费用列表
+/// </summary>
+public class AirTicketFeeListView
+{
+    public int No { get; set; }
+
+    public int Id { get; set; }
+
+    /// <summary>
+    /// 舱位名称
+    /// </summary>
+    public string CabinName { get; set; }
+
+    /// <summary>
+    /// 航班描述
+    /// </summary>
+    public string FlightDesc { get; set; }
+
+    /// <summary>
+    /// 客户名单
+    /// </summary>
+    public string ClientNameList { get; set; }
+
+    /// <summary>
+    /// 客户数量
+    /// </summary>
+    public int ClientCount { get; set; } = 0;
+
+    /// <summary>
+    /// 机票均价
+    /// </summary>
+    public decimal AvgTicketPrice => ClientCount > 0 ? TotalTicketPrice / ClientCount : 0;
+
+    /// <summary>
+    /// 机票总价
+    /// </summary>
+    public decimal TotalTicketPrice { get; set; }
+
+    /// <summary>
+    /// 使用币种
+    /// </summary>
+    public string Currency { get; set; }
+
+    /// <summary>
+    /// 审核状态
+    /// </summary>
+    public string AuditStatus { get; set; }
+
+    /// <summary>
+    /// 备注
+    /// </summary>
+    public string Remark { get; set; }
+
+    /// <summary>
+    /// 申请人
+    /// </summary>
+    public string Applicant { get; set; }
+
+    /// <summary>
+    /// 申请时间
+    /// </summary>
+    public string ApplyTime { get; set; }
+}
+
+public class AirTicketFeeInfoView
+{
+    public int CurrUserId { get; set; }
+
+    public GroupAirTicketInfo GroupAirInfo { get; set; } = new GroupAirTicketInfo();
+
+    public List<AirTicketFeeInfo> AirTicketFeeInfos { get; set; } = new List<AirTicketFeeInfo>();
+
+}
+
+/// <summary>
+/// 团组&机票 信息
+/// </summary>
+public class GroupAirTicketList : GroupAirInfoView
+{
+    /// <summary>
+    /// 出团时间起
+    /// </summary>
+    public DateTime VisitStartDate { get; set; }
+
+    /// <summary>
+    /// 出团时间止
+    /// </summary>
+    public DateTime VisitEndDate { get; set; }
+
+    /// <summary>
+    /// 出访天数
+    /// </summary>
+    public int VisitDays { get; set; }
+
+    /// <summary>
+    /// 出访人数
+    /// </summary>
+    public int VisitPNumber { get; set; }
+}
+
+
+
+/// <summary>
+/// 团组&机票 信息
+/// </summary>
+public class GroupAirTicketInfo : GroupAirInfoView
+{
+    /// <summary>
+    /// 航班描述
+    /// </summary>
+    public string FlightsDescription { get; set; }
+}
+
+/// <summary>
+/// 机票费用信息
+/// </summary>
+public class AirTicketFeeInfo
+{
+    /// <summary>
+    /// 主键Id
+    /// </summary>
+    public int Id { get; set; }
+
+    /// <summary>
+    /// 当前航段代码描述
+    /// </summary>
+    public string FlightsDescription { get; set; }
+
+    /// <summary>
+    /// 航班基础信息(去程、联程、返程)
+    /// </summary>
+    public List<AirTicketBasicInfo> AirTicketBasicInfos { get; set; } = new List<AirTicketBasicInfo>();
+
+    /// <summary>
+    /// 客人名称
+    /// </summary>
+    public string ClientName { get; set; }
+
+    /// <summary>
+    /// 客人名称
+    /// </summary>
+    public List<int> ClientNameIds { get; set; } = new List<int>();
+
+    /// <summary>
+    /// 费用信息(含退票信息)
+    /// </summary>
+    public List<CustTicketInfo> CustTicketInfos { get; set; } = new List<CustTicketInfo>();
+
+    /// <summary>
+    /// 报价说明
+    /// </summary>
+    public string PriceDescription { get; set; }
+
+    #region Credit Info
+    /// <summary>
+    /// 支付方式
+    /// </summary>
+    public int PayDId { get; set; } = 0;
+    /// <summary>
+    /// 消费方式
+    /// </summary>
+    public string ConsumptionPatterns { get; set; }
+    /// <summary>
+    /// 消费日期
+    /// </summary>
+    public string ConsumptionDate { get; set; }
+    /// <summary>
+    /// 付款金额
+    /// </summary>
+    public decimal PayMoney { get; set; }
+    /// <summary>
+    /// 付款币种 数据类型Id
+    /// </summary>
+    public int PaymentCurrency { get; set; }
+    /// <summary>
+    /// 卡类型
+    /// </summary>
+    public int CTDId { get; set; }
+    /// <summary>
+    /// 银行卡号
+    /// </summary>
+    public string BankNo { get; set; }
+    /// <summary>
+    /// 持卡人姓名
+    /// </summary>
+    public string CardholderName { get; set; }
+    /// <summary>
+    /// 当天汇率
+    /// </summary>
+    public decimal DayRate { get; set; }
+    /// <summary>
+    /// 公司银行卡号
+    /// </summary>
+    public string CompanyBankNo { get; set; }
+    /// <summary>
+    /// 对方开户行
+    /// </summary>
+    public string OtherBankName { get; set; }
+    /// <summary>
+    /// 对方银行账号
+    /// </summary>
+    public string OtherSideNo { get; set; }
+    /// <summary>
+    /// 对方姓名
+    /// </summary>
+    public string OtherSideName { get; set; }
+    /// 收款方
+    /// </summary>
+    public string Payee { get; set; }
+
+    /// <summary>
+    /// 费用标识
+    /// 0 公转 1 私转
+    /// </summary>
+    public int OrbitalPrivateTransfer { get; set; }
+    /// <summary>
+    /// 备注
+    /// </summary>
+    public string Remark { get; set; }
+
+    /// <summary>
+    /// 总经理是否审核  
+    /// 0 未审核 
+    /// 1 已通过 
+    /// 2 未通过 
+    /// 3 自动审核
+    /// </summary>
+    public int IsAuditGM { get; set; } = 0;
+
+    /// <summary>
+    ///  审核状态描述
+    /// </summary>
+    public string IsAuditGMStr => IsAuditGM switch
     {
-        public decimal jjcCurrentRate { get; set; }
-        public decimal gwcCurrentRate { get; set; }
-        public decimal tdcCurrentRate { get; set; }
-        public string remark { get; set; }
-        public string TripDesc { get; set; }
-    }
+        0 => "未审核",
+        1 => "已通过",
+        2 => "未通过",
+        3 => "自动审核",
+        _ => "未知状态"
+    };
+
+    /// <summary>
+    /// 总经理审核人
+    /// </summary>
+    public int AuditGMOperate { get; set; }
+
+    /// <summary>
+    /// 总经理审核时间
+    /// </summary>
+    public string AuditGMDate { get; set; }
+
+    /// <summary>
+    /// 审核描述
+    /// </summary>
+    public string AuditStr { get; set; }
+    #endregion
+
+}
+
+public class AppPushBodyView
+{
+    public string GroupName { get; set; }
+
+    public List<AppPushBodyInfo> AppPushBodyInfos { get; set; }
+}
+
+/// <summary>
+/// 机票费用数据整合详情
+/// </summary>
+public class AirTicketFeeOpInfo : AirTicketFeeInfo
+{
+    /// <summary>
+    /// 团组外键编号
+    /// </summary>
+    public int DIId { get; set; }
+
+    /// <summary>
+    /// 记录类型:0-正常机票 1-退票记录
+    /// </summary>
+    public int RecordType { get; set; } = 0;
+
+    /// <summary>
+    /// 关联的原始机票记录ID(退票记录指向原机票记录)
+    /// </summary>
+    public int OriginalReservationId { get; set; }
+
+    /// <summary>
+    /// 客户人数
+    /// </summary>
+    public int ClientNum { get; set; }
+
+    /// <summary>
+    /// 舱类型(数据类型外键)
+    /// </summary>
+    public int CType { get; set; }
+
+    /// <summary>
+    /// 机票全价
+    /// </summary>
+    public decimal Price { get; set; }
+
+    /// <summary>
+    /// 币种
+    /// </summary>
+    public int Currency { get; set; }
+
 }
+
+/// <summary>
+/// 应用推送参数body
+/// </summary>
+public class AppPushBodyInfo
+{
+    /// <summary>
+    /// 操作类型 
+    /// 1 添加 2 修改 
+    /// </summary>
+    public int OperationType { get; set; }
+
+    /// <summary>
+    /// 机票费用数据Id
+    /// </summary>
+    public int AirTicketId { get; set; }
+
+    /// <summary>
+    /// 信用卡数据Id
+    /// </summary>
+    public int CardId { get; set; }
+
+    /// <summary>
+    /// 信用卡CNY金额
+    /// </summary>
+    public decimal CardCNYAmount { get; set; }
+
+}
+
+#endregion

+ 22 - 29
OASystem/OASystem.Domain/ViewModels/Groups/DelegationInfoView.cs

@@ -1,24 +1,7 @@
-using Google.Protobuf;
-using Google.Protobuf.WellKnownTypes;
-using Microsoft.Extensions.Primitives;
-using Newtonsoft.Json;
-using OASystem.Domain.Dtos;
-using OASystem.Domain.Entities.Groups;
-using OASystem.Domain.ViewModels.QiYeWeChat;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Text;
-using System.Text.Json.Serialization;
-using System.Threading.Tasks;
-using JsonConverter = System.Text.Json.Serialization.JsonConverter;
+using OASystem.Domain.Entities.Groups;
 
 namespace OASystem.Domain.ViewModels.Groups
 {
-
-
-
     /// <summary>
     /// 接团信息
     /// 返回视图
@@ -45,7 +28,7 @@ namespace OASystem.Domain.ViewModels.Groups
         /// 团号
         /// </summary>
         public string? TourCode { get; set; }
-       
+
         /// <summary>
         /// 客户名称
         /// </summary>
@@ -82,11 +65,11 @@ namespace OASystem.Domain.ViewModels.Groups
     /// 查询团组简略详情列表
     /// Page 根据Ctable And User 返回可操作的团 View
     /// </summary>
-    public class GroupListByCTableAndUserIdView 
+    public class GroupListByCTableAndUserIdView
     {
         public int Row_Number { get; set; }
         public int Id { get; set; }
-        
+
         /// <summary>
         /// 团组名
         /// </summary>
@@ -116,7 +99,7 @@ namespace OASystem.Domain.ViewModels.Groups
         /// 出访结束日期
         /// </summary>
         public string? VisitEndDate { get; set; }
-        
+
 
         /// <summary>
         /// 出访天数
@@ -130,8 +113,6 @@ namespace OASystem.Domain.ViewModels.Groups
 
     }
 
-
-
     /// <summary>
     /// 接团信息详情 共享 Web
     /// 返回视图
@@ -231,7 +212,6 @@ namespace OASystem.Domain.ViewModels.Groups
 
     }
 
-
     public class GroupCityBasicSourceView
     {
 
@@ -240,7 +220,6 @@ namespace OASystem.Domain.ViewModels.Groups
         public string Name { get; set; }
     }
 
-
     public class CityTree
     {
         [SqlSugar.SugarColumn(IsPrimaryKey = true)]
@@ -403,7 +382,7 @@ namespace OASystem.Domain.ViewModels.Groups
         /// <summary>
         /// 提成等级说明
         /// </summary>
-        public string?  OpRoyaltyRemark { get; set; }
+        public string? OpRoyaltyRemark { get; set; }
 
         /// <summary>
         ///  公务需求
@@ -425,7 +404,7 @@ namespace OASystem.Domain.ViewModels.Groups
     /// 接团信息列表
     /// 返回视图
     /// </summary>
-    public class DelegationListView 
+    public class DelegationListView
     {
         /// <summary>
         /// 主键Id
@@ -593,7 +572,7 @@ namespace OASystem.Domain.ViewModels.Groups
     /// <summary>
     /// 团组编号
     /// </summary>
-    public class TeamCodeView 
+    public class TeamCodeView
     {
         public string TourCode { get; set; }
     }
@@ -666,4 +645,18 @@ namespace OASystem.Domain.ViewModels.Groups
         public string RouteCity { get; set; }
 
     }
+
+    public class GroupAirInfoView
+    {
+        /// <summary>
+        /// 团组ID
+        /// </summary>
+        public int Id { get; set; }
+        public string TeamName { get; set; }
+        public string TourCode { get; set; }
+        public string ClientName { get; set; }
+        public string VisitCountry { get; set; }
+    }
+
+
 }

+ 54 - 0
OASystem/OASystem.Domain/ViewModels/Groups/Grp_CreditCardPaymentView.cs

@@ -104,6 +104,60 @@ namespace OASystem.Domain.ViewModels.Groups
         //public string TotalStr5 { get; set; }
     }
 
+    /// <summary>
+    /// 费用审核概览
+    /// </summary>
+    public class ExpenseAuditOverview
+    {
+        /// <summary>
+        /// 费用类型ID
+        /// </summary>
+        public int FeeTypeId { get; set; }
+
+        /// <summary>
+        /// 费用类型名称
+        /// </summary>
+        public string FeeTypeName { get; set; }
+
+        /// <summary>
+        /// 总记录数
+        /// </summary>
+        public int TotalRecords { get; set; } = 0;
+
+        /// <summary>
+        /// 待审核数
+        /// </summary>
+        public int PendingCount { get; set; } = 0;
+
+        /// <summary>
+        /// 应付款总金额
+        /// </summary>
+        public List<AuditOverviewFeeInfo> TotalPayableAmounts { get; set; } = new();
+
+        /// <summary>
+        /// 目前剩余尾款总金额
+        /// </summary>
+        public List<AuditOverviewFeeInfo> RemainingBalanceAmounts { get; set; } = new();
+
+        /// <summary>
+        /// 已审费用总额
+        /// </summary>
+        public List<AuditOverviewFeeInfo> ApprovedExpenseAmounts { get; set; } = new();
+
+        /// <summary>
+        /// 未审费用总额
+        /// </summary>
+        public List<AuditOverviewFeeInfo> PendingExpenseAmounts { get; set; } = new();
+    }
+
+
+    public class AuditOverviewFeeInfo
+    {
+        public decimal Amount { get; set; } 
+        public string Currency { get; set; }  
+    }
+
+
     public class Grp_CreditCardPaymentDetailView
     {
         /// <summary>

+ 110 - 0
OASystem/OASystem.Domain/ViewModels/JsonView.cs

@@ -32,6 +32,72 @@ public class JsonView
         Msg = msg;
     }
 
+    /// <summary>
+    /// 成功
+    /// </summary>
+    /// <param name="data"></param>
+    /// <param name="count"></param>
+    /// <returns></returns>
+    public static JsonView Success<T>(T? data, int count=0)
+    {
+        return new JsonView() {
+            Code = 200,
+            Count = count,
+            Data = data,
+            Msg = "操作成功!",
+        };
+    }
+
+    /// <summary>
+    /// 成功
+    /// </summary>
+    /// <param name="data"></param>
+    /// <param name="msg"></param>
+    /// <param name="count"></param>
+    /// <returns></returns>
+    public static JsonView Success<T>(T? data,string msg, int count = 0)
+    {
+        return new JsonView()
+        {
+            Code = 200,
+            Count = count,
+            Data = data,
+            Msg = msg,
+        };
+    }
+
+    /// <summary>
+    /// 失败
+    /// </summary>
+    /// <param name="msg"></param>
+    /// <returns></returns>
+    public static JsonView Fail(string msg)
+    {
+        return new JsonView()
+        {
+            Code = 400,
+            Count = 0,
+            Data = null,
+            Msg = msg,
+        };
+    }
+
+    /// <summary>
+    /// 失败
+    /// </summary>
+    /// <param name="msg"></param>
+    /// <returns></returns>
+    public static JsonView Fail<T>( string msg)
+    {
+        return new JsonView()
+        {
+            Code = 400,
+            Count = 0,
+            Data = default(T),
+            Msg = msg,
+        };
+    }
+
     /// <summary>
     /// 操作成功
     /// </summary>
@@ -48,3 +114,47 @@ public class JsonView
 
 }
 
+/// <summary>
+/// 统一返回结构
+/// </summary>
+public class JsonView<T>
+{
+    /// <summary>
+    /// 状态码
+    /// </summary>
+    public int Code { get; set; } = 400;
+    /// <summary>
+    /// 消息
+    /// </summary>
+    public string? Msg { get; set; } = "";
+    /// <summary>
+    /// 条数
+    /// </summary>
+    public int? Count { get; set; } = 0;
+    /// <summary>
+    /// 数据
+    /// </summary>
+    public T? Data { get; set; }
+
+    public static JsonView<T> Success(string msg = "操作成功!", T data = default, int count = 0)
+    {
+        return new JsonView<T>
+        {
+            Code = 200,
+            Count = count,
+            Data = data,
+            Msg = msg
+        };
+    }
+
+    public static JsonView<T> Fail(string msg, int code = 400)
+    {
+        return new JsonView<T>
+        {
+            Code = code,
+            Count = 0,
+            Data = default,
+            Msg = msg
+        };
+    }
+}

+ 1 - 1
OASystem/OASystem.Infrastructure/Repositories/BaseRepository.cs

@@ -150,7 +150,7 @@
         }
 
         /// <summary>
-        /// 删除单数据,加上删除者Id
+        /// 删除单数据,加上删除者Id
         /// </summary>
         /// <typeparam name="T"></typeparam>
         /// <param name="id"></param>

+ 933 - 3
OASystem/OASystem.Infrastructure/Repositories/Groups/AirTicketResRepository.cs

@@ -1,6 +1,7 @@
 using AutoMapper;
 using Newtonsoft.Json;
 using Newtonsoft.Json.Linq;
+using NPOI.POIFS.Properties;
 using OASystem.Domain;
 using OASystem.Domain.AesEncryption;
 using OASystem.Domain.Dtos.Groups;
@@ -9,16 +10,23 @@ using OASystem.Domain.Entities.Financial;
 using OASystem.Domain.Entities.Groups;
 using OASystem.Domain.Entities.Resource;
 using OASystem.Domain.ViewModels.Groups;
+using OASystem.Domain.ViewModels.JuHeExchangeRate;
+using System.Drawing;
 using System.Security.Cryptography;
+using static Google.Protobuf.Reflection.FieldOptions.Types;
 
 namespace OASystem.Infrastructure.Repositories.Groups
 {
     public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations, Grp_AirTicketReservations>
     {
         private readonly IMapper _mapper;
-        public AirTicketResRepository(SqlSugarClient sqlSugar, IMapper mapper) : base(sqlSugar)
+        private readonly DelegationInfoRepository _groupInfoRep; 
+        private readonly TeamRateRepository _groupRateRep;
+        public AirTicketResRepository(SqlSugarClient sqlSugar, DelegationInfoRepository groupInfoRep, TeamRateRepository groupRateRep, IMapper mapper) : base(sqlSugar)
         {
             _mapper = mapper;
+            _groupInfoRep = groupInfoRep;
+            _groupRateRep = groupRateRep;
         }
 
         public async Task<Result> AirTicketResById(AirTicketResByIdDto dto)
@@ -50,7 +58,6 @@ namespace OASystem.Infrastructure.Repositories.Groups
             catch (Exception)
             {
                 return result = new Result() { Code = -2, Msg = "未知错误" };
-                throw;
             }
         }
 
@@ -436,7 +443,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
                     {
                         Grp_CreditCardPayment grp_CreditCard = _mapper.Map<Grp_CreditCardPayment>(dto.CardPaymentOpData);
 
-                        //2025-04-07 第四次更改 PayDId == 72(刷卡) IsPay == 1
+                        // 2025-04-07 第四次更改 PayDId == 72(刷卡) IsPay == 1
                         if (grp_CreditCard.PayDId == 72) grp_CreditCard.IsPay = 1;
                         else grp_CreditCard.IsPay = 0;
 
@@ -1093,5 +1100,928 @@ namespace OASystem.Infrastructure.Repositories.Groups
             public string VerifyResult { get; set; }
 
         }
+
+        #region 2026版
+
+        /// <summary>
+        /// list 2026版
+        /// </summary>
+        /// <param name="dto">查询参数</param>
+        /// <returns>机票费用列表</returns>
+        public async Task<JsonView> ListAsync(AirTicketFeeListDto dto)
+        {
+            // 1. 参数校验
+            if (!SharingStaticData.PortTypes.Contains(dto.PortType))
+                return JsonView.Fail(MsgTips.Port);
+
+            if (dto.DiId <= 0)
+                return JsonView.Fail("团组ID无效");
+
+            // 2. 获取团组信息
+            var groupInfo = await _groupInfoRep.GetGroupAirInfo(dto.DiId);
+            if (groupInfo == null)
+                return JsonView.Fail("暂无团组数据!");
+
+            // 3. 查询机票费用列表
+            var airTicketFeeList = await _sqlSugar.Queryable<Grp_AirTicketReservations>()
+                .InnerJoin<Grp_CreditCardPayment>((x, y) => x.Id == y.CId && x.DIId == y.DIId)
+                .LeftJoin<Sys_SetData>((x, y, z) => x.CType == z.Id)
+                .LeftJoin<Sys_SetData>((x, y, z, z1) => x.Currency == z1.Id)
+                .LeftJoin<Sys_Users>((x, y, z, z1, u) => x.CreateUserId == u.Id)
+                .Where((x, y, z, z1, u) => x.IsDel == 0 && y.IsDel == 0 && x.DIId == dto.DiId)
+                .WhereIF(dto.IsPaySign != -1, (x, y, z, z1, u) => y.IsPay == dto.IsPaySign)
+                .Select((x, y, z, z1, u) => new AirTicketFeeListView()
+                {
+                    Id = x.Id,
+                    CabinName = z.Name,
+                    FlightDesc = x.FlightsDescription,
+                    ClientNameList = x.ClientName,
+                    ClientCount = x.ClientNum,
+                    TotalTicketPrice = x.Price,
+                    Currency = z1.Name,
+                    AuditStatus = y.IsAuditGM.ToString(),
+                    Remark = y.Remark,
+                    Applicant = u.CnName,
+                    ApplyTime = x.CreateTime.ToString("yyyy-MM-dd HH:mm:ss")
+                })
+                .ToListAsync();
+
+            // 4. 获取黑屏代码(三字码映射)
+            var threeCodes = await _sqlSugar.Queryable<Res_ThreeCode>()
+                .Where(x => x.IsDel == 0)
+                .ToDictionaryAsync(x => x.Three, x => x.AirPort);
+
+            // 5. 获取团组成本预算
+            var groupCostParameter = await _sqlSugar.Queryable<Grp_GroupCostParameter>()
+                .FirstAsync(a => a.DiId == dto.DiId && a.IsDel == 0 && a.CostType == "A" && a.IsShare == 1);
+            var airgroupCostParameter = _mapper.Map<AirGroupCostParameterView>(groupCostParameter);
+
+            // 6. 获取客户信息映射
+            var groupClientIds = await _sqlSugar.Queryable<Grp_TourClientList>()
+                .Where(a => a.DiId == dto.DiId && a.IsDel == 0)
+                .Select(x => x.ClientId)
+                .ToListAsync();
+
+            var clientInfoMap = new Dictionary<int, (string FirstName, string LastName, string Pinyin)>();
+            if (groupClientIds.Any())
+            {
+                var encryptedClients = await _sqlSugar.Queryable<Crm_DeleClient>()
+                    .Where(a => a.IsDel == 0 && groupClientIds.Contains(a.Id))
+                    .Select(x => new { x.Id, x.FirstName, x.LastName, x.Pinyin })
+                    .ToListAsync();
+
+                foreach (var client in encryptedClients)
+                {
+                    clientInfoMap[client.Id] = (
+                        AesEncryptionHelper.Decrypt(client.FirstName),
+                        AesEncryptionHelper.Decrypt(client.LastName),
+                        AesEncryptionHelper.Decrypt(client.Pinyin)
+                    );
+                }
+            }
+
+            // 7. 处理航班描述和客户名称
+            foreach (var item in airTicketFeeList)
+            {
+                // 7.1 处理航班描述
+                if (!string.IsNullOrWhiteSpace(item.FlightDesc))
+                {
+                    var flightInfos = FlightParser.ParseFlights(item.FlightDesc);
+                    var flightDescBuilder = new StringBuilder();
+
+                    foreach (var flightInfo in flightInfos)
+                    {
+                        string departAirport = threeCodes.ContainsKey(flightInfo.DepartureAirport)
+                            ? threeCodes[flightInfo.DepartureAirport]?.ToString() ?? flightInfo.DepartureAirport
+                            : flightInfo.DepartureAirport;
+
+                        string arrivalAirport = threeCodes.ContainsKey(flightInfo.ArrivalAirport)
+                            ? threeCodes[flightInfo.ArrivalAirport]?.ToString() ?? flightInfo.ArrivalAirport
+                            : flightInfo.ArrivalAirport;
+
+                        string desc = $"{flightInfo.SequenceNo}.{departAirport}→{arrivalAirport}({flightInfo.DepartureDate})";
+                        flightDescBuilder.AppendLine(desc);
+                    }
+
+                    item.FlightDesc = flightDescBuilder.ToString().TrimEnd();
+                }
+
+                // 7.2 处理客户名称
+                if (!string.IsNullOrWhiteSpace(item.ClientNameList))
+                {
+                    var clientArr = item.ClientNameList.Split(',')
+                        .Where(x => !string.IsNullOrWhiteSpace(x))
+                        .ToArray();
+
+                    var clientNames = new List<string>();
+
+                    foreach (var clientItem in clientArr)
+                    {
+                        if (int.TryParse(clientItem, out int clientId))
+                        {
+                            if (clientId == -1)
+                            {
+                                clientNames.Add("行程单");
+                            }
+                            else if (clientInfoMap.TryGetValue(clientId, out var clientInfo))
+                            {
+                                clientNames.Add(clientInfo.Pinyin);
+                            }
+                        }
+                        else
+                        {
+                            clientNames.Add(clientItem);
+                        }
+                    }
+
+                    item.ClientNameList = clientNames.Any() ? string.Join(",", clientNames) : string.Empty;
+                }
+            }
+
+            // 8. 根据端口类型返回数据
+            if (dto.PortType == 1)
+            {
+                var data = new
+                {
+                    DelegationInfo = groupInfo,
+                    AirTicketReservations = airTicketFeeList,
+                    AirGroupCostParameter = airgroupCostParameter
+                };
+                return JsonView.Success(data);
+            }
+            else if (dto.PortType == 2 || dto.PortType == 3)
+            {
+                int totalCount = airTicketFeeList.Count;
+                int totalPages = (int)Math.Ceiling((double)totalCount / dto.PageSize);
+                if (totalPages == 0) totalPages = 1;
+
+                // 分页数据
+                var pagedData = airTicketFeeList
+                    .Skip((dto.PageIndex - 1) * dto.PageSize)
+                    .Take(dto.PageSize)
+                    .ToList();
+
+                var rst = new ListViewBase<AirTicketFeeListView>
+                {
+                    DataList = pagedData,
+                    DataCount = totalCount,
+                    CurrPageIndex = dto.PageIndex,
+                    CurrPageSize = dto.PageSize
+                };
+
+                var data = new
+                {
+                    AirData = rst,
+                    AirGroupCostParameter = airgroupCostParameter
+                };
+                return JsonView.Success(data);
+            }
+
+            return JsonView.Fail("暂无团组数据!");
+        }
+        
+        /// <summary>
+        /// 机票费用基础数据 2026版
+        /// </summary>
+        /// <returns></returns>
+        public async Task<JsonView> BasicDataAsync(int userId)
+        {
+            var setDatas = _sqlSugar.Queryable<Sys_SetData>().Where(a => a.IsDel == 0).ToList();
+
+            // 附加服务类型
+            var additionalServiceTypes = setDatas
+                .Where(a => a.STid == 139 && a.IsDel == 0)
+                .Select(a => _mapper.Map<SetDataInfoView>(a))
+                .OrderBy(x => x.RemarkSort)
+                .ToList();
+
+            // 舱位类型
+            var ticketClass = setDatas
+                .Where(a => a.STid == 44 && a.IsDel == 0)
+                .Select(a => _mapper.Map<SetDataInfoView>(a))
+                .ToList();
+
+            // 支付方式
+            var payments = setDatas
+                .Where(a => a.STid == 14 && a.IsDel == 0)
+                .Select(a => _mapper.Map<SetDataInfoView>(a))
+                .ToList();
+
+            // 卡类型
+            var cardTypes = setDatas
+               .Where(a => a.STid == 15 && a.IsDel == 0)
+               .Select(a => _mapper.Map<SetDataInfoView>(a))
+               .ToList();
+
+            // 合作方资料
+            var airTicketAgents = _sqlSugar.Queryable<Res_AirTicketAgent>().Where(a => a.IsDel == 0).ToList();
+            foreach (var item in airTicketAgents)
+            {
+                EncryptionProcessor.DecryptProperties(item);
+            }
+
+            var airTicketAgentsView = airTicketAgents.Select(x => new
+            {
+                x.Id,
+                x.Name,
+                x.Account,
+                x.Bank
+            }).ToList();
+
+
+            #region 团组下拉框
+
+            var permGroupIds = Query<Grp_GroupsTaskAssignment>(a => a.IsDel == 0 && a.UId == userId && a.CTId == 85).Select(a => a.DIId).ToList();
+
+            var teamNames = _sqlSugar.Queryable<Grp_DelegationInfo>()
+                .Where(x => x.IsDel == 0 && permGroupIds.Contains(x.Id))
+                .OrderByDescending(x => x.VisitDate)
+                .Select(x => new
+                {
+                    x.Id,
+                    x.TeamName
+                })
+                .ToList();
+
+            #endregion
+
+            // 获取第一个团组的基本信息
+            var firstGroupInfo = await _groupInfoRep.GetGroupAirInfo(teamNames.FirstOrDefault()?.Id ?? 0);
+
+            return JsonView.Success(new
+            {
+                additionalServiceTypes,
+                ticketClass,
+                payment = payments,
+                cardType = cardTypes,
+                airTicketAgents = airTicketAgentsView,
+                firstGroupInfo,
+                groupName = teamNames
+            });
+        }
+
+        /// <summary>
+        /// 详情 2026版
+        /// </summary>
+        /// <returns></returns>
+        public async Task<JsonView> InfoAsync(int userId, int groupId)
+        {
+            var view = new AirTicketFeeInfoView()
+            {
+                CurrUserId = userId,
+                GroupAirInfo = new GroupAirTicketInfo(),
+                AirTicketFeeInfos = new List<AirTicketFeeInfo>()
+            };
+
+            if (userId < 1) return JsonView.Fail<AirTicketFeeInfoView>("用户ID无效");
+            if (groupId < 1) return JsonView.Fail<AirTicketFeeInfoView>("团组ID无效");
+
+            var groupVaild = Query<Grp_GroupsTaskAssignment>(a => a.IsDel == 0 && a.UId == userId && a.CTId == 85 && a.DIId == groupId).Any();
+            if (!groupVaild) return JsonView.Fail<AirTicketFeeInfoView>("没有查看权限");
+
+            // 获取第一个团组的基本信息
+            var firstGroupInfo = await _groupInfoRep.GetGroupAirInfo(groupId);
+            if (firstGroupInfo == null)
+                return JsonView.Fail<AirTicketFeeInfoView>("团组信息不存在");
+
+            // 团组公共信息
+            var groupInfo = _mapper.Map<GroupAirTicketInfo>(firstGroupInfo);
+
+            // 机票信息集合
+            var airTicketFeeInfos = await _sqlSugar.Queryable<Grp_AirTicketReservations>()
+                .InnerJoin<Grp_CreditCardPayment>((x, y) => x.Id == y.CId && x.DIId == y.DIId && y.CTable == 85)
+                .Where((x, y) => x.IsDel == 0 && y.IsDel == 0 && x.DIId == groupId && x.RecordType == 0)
+                .Select((x, y) => new AirTicketFeeInfo
+                {
+                    // 机票信息
+                    Id = x.Id,
+                    FlightsDescription = x.FlightsDescription,
+                    AirTicketBasicInfos = x.AirTicketBasicInfos,
+                    ClientName = x.ClientName,
+                    CustTicketInfos = x.CustTicketInfos,
+                    PriceDescription = x.PriceDescription,
+                    // 支付信息
+                    PayDId = y.PayDId,
+                    ConsumptionPatterns = y.ConsumptionPatterns,
+                    ConsumptionDate = y.ConsumptionDate,
+                    PayMoney = y.PayMoney,
+                    PaymentCurrency = y.PaymentCurrency,
+                    CTDId = y.CTDId,
+                    BankNo = y.BankNo,
+                    CardholderName = y.CardholderName,
+                    DayRate = y.DayRate,
+                    CompanyBankNo = y.CompanyBankNo,
+                    OtherSideNo = y.OtherSideNo,
+                    OtherSideName = y.OtherSideName,
+                    Payee = y.Payee,
+                    OrbitalPrivateTransfer = y.OrbitalPrivateTransfer,
+                    Remark = y.Remark,
+                    IsAuditGM = y.IsAuditGM,
+                    AuditGMOperate = y.AuditGMOperate,
+                    AuditGMDate = y.AuditGMDate
+                })
+                .ToListAsync();
+
+            // 如果没有数据,返回空结果
+            if (!airTicketFeeInfos.Any())
+            {
+                groupInfo.FlightsDescription = string.Empty;
+                return JsonView.Success(new AirTicketFeeInfoView()
+                {
+                    CurrUserId = userId,
+                    GroupAirInfo = groupInfo,
+                    AirTicketFeeInfos = new List<AirTicketFeeInfo>()
+                });
+            }
+
+            // 航班舱位类型
+            var cabinTypes = await _sqlSugar.Queryable<Sys_SetData>().Where(x => x.IsDel == 0 && x.STid == 44).ToListAsync();
+
+            // 分组统计一个航班行程的信息
+            var groupedByFlights = airTicketFeeInfos
+                .GroupBy(x => x.FlightsDescription)
+                .Select(g =>
+                {
+                    var first = g.First();
+
+                    // 合并航班基础信息(去重)
+                    var airTicketBasicInfos = g.SelectMany(x => x.AirTicketBasicInfos ?? new List<AirTicketBasicInfo>()).Distinct().ToList();
+
+                    // 合并客户名称(去重)
+                    var clientNames = string.Join(",", g.Select(x => x.ClientName).ToList());
+                    var clientNameIds = ParseToIntListSafe(clientNames);
+
+                    // 处理审核状态 
+                    var auditsStr = new StringBuilder();
+                    foreach (var item in g)
+                    {
+                        var auditStatus = item.IsAuditGM switch
+                        {
+                            0 => "未审核",
+                            1 => "审核通过",
+                            2 => "审核驳回",
+                            3 => "自动审核",
+                            _ => "未知状态"
+                        };
+
+                        var cabinTypeName = cabinTypes.FirstOrDefault(c => c.Id == item.CustTicketInfos.FirstOrDefault()?.CType)?.Name ?? "未知舱位";
+
+                        auditsStr.Append($"{cabinTypeName}:{auditStatus};");
+                    }
+
+                    // 处理多舱位审核描述
+                    var newFlight = new AirTicketFeeInfo
+                    {
+                        Id = first.Id,
+                        FlightsDescription = g.Key,
+
+                        // 合并航班基础信息(去重)
+                        AirTicketBasicInfos = airTicketBasicInfos,
+
+                        // 合并客户人员
+                        ClientName = clientNames,
+                        ClientNameIds = clientNameIds,
+
+                        // 合并所有客户的机票信息
+                        CustTicketInfos = g.SelectMany(x => x.CustTicketInfos ?? new List<CustTicketInfo>()).ToList(),
+
+                        PriceDescription = first.PriceDescription,
+
+                        // 付款信息
+                        PayDId = first.PayDId,
+                        ConsumptionPatterns = first.ConsumptionPatterns,
+                        ConsumptionDate = first.ConsumptionDate,
+                        PayMoney = g.Sum(x => x.PayMoney),
+                        PaymentCurrency = first.PaymentCurrency,
+                        CTDId = first.CTDId,
+                        BankNo = first.BankNo,
+                        CardholderName = first.CardholderName,
+                        DayRate = first.DayRate,
+                        CompanyBankNo = first.CompanyBankNo,
+                        OtherSideNo = first.OtherSideNo,
+                        OtherSideName = first.OtherSideName,
+                        Payee = first.Payee,
+                        OrbitalPrivateTransfer = first.OrbitalPrivateTransfer,
+                        Remark = first.Remark,
+                        IsAuditGM = first.IsAuditGM,
+                        AuditGMOperate = first.AuditGMOperate,
+                        AuditGMDate = first.AuditGMDate,
+                        AuditStr = auditsStr.ToString().Trim()
+                    };
+
+                    return newFlight;
+                })
+                .ToList();
+
+            // 按序号排序
+            groupedByFlights = groupedByFlights
+                .OrderBy(x => ExtractSequenceNo(x.FlightsDescription))
+                .ToList();
+
+            // 提取完整的航班行程信息
+            var flightsDescAll = new StringBuilder();
+            foreach (var item in groupedByFlights)
+            {
+                flightsDescAll.AppendLine(item.FlightsDescription);
+
+                // 添加测试数据
+                if (!item.AirTicketBasicInfos.Any())
+                {
+                    item.AirTicketBasicInfos = FlightParser.ParseFlights(item.FlightsDescription)
+                        .Select(x => new AirTicketBasicInfo()
+                        {
+                            No = x.SequenceNo,
+                            FlightsCode = x.FlightNumber,
+                            FlightsCity = $"{x.DepartureAirport}/{x.ArrivalAirport}",
+                            FlightsDate = x.DepartureDate,
+                            FlightsTime = x.DepartureTime,
+                            ArrivedTime = x.ArrivalTime
+                        })
+                        .ToList();
+                }
+
+                // 添加测试费用数据(仅用于测试,生产环境应移除)
+                if (!item.CustTicketInfos.Any())
+                {
+                    item.CustTicketInfos = GetTestCustomerData();
+                }
+            }
+            groupInfo.FlightsDescription = flightsDescAll.ToString();
+
+            return JsonView.Success(new AirTicketFeeInfoView()
+            {
+                CurrUserId = userId,
+                GroupAirInfo = groupInfo,
+                AirTicketFeeInfos = groupedByFlights
+            });
+        }
+
+        #region info 辅助方法
+
+        /// <summary>
+        /// 将逗号分隔的字符串解析为 List<int>(带错误处理)
+        /// </summary>
+        public static List<int> ParseToIntListSafe(string input)
+        {
+            if (string.IsNullOrWhiteSpace(input))
+                return new List<int>();
+
+            var result = new List<int>();
+            var parts = input.Split(',');
+
+            foreach (var part in parts)
+            {
+                if (int.TryParse(part.Trim(), out int value))
+                {
+                    result.Add(value);
+                }
+                // 可选:记录解析失败的值
+            }
+
+            return result;
+        }
+
+        /// <summary>
+        /// 提取序号辅助方法
+        /// </summary>
+        private static int ExtractSequenceNo(string flightsDescription)
+        {
+            if (string.IsNullOrWhiteSpace(flightsDescription))
+                return int.MaxValue;
+
+            var match = Regex.Match(flightsDescription, @"^(\d+)\.");
+            return match.Success ? int.Parse(match.Groups[1].Value) : int.MaxValue;
+        }
+
+        /// <summary>
+        /// 获取测试数据
+        /// </summary>
+        private static List<CustTicketInfo> GetTestCustomerData()
+        {
+            var testData = new CustTicketInfo()
+            {
+                ClientId = 1081,
+                CType = 460,
+                ActualPrice = 1000,
+                TicketCode = "9991234567890",
+                TicketNumber = "ABC123",
+                SelectedServiceIds = new List<int> { 1561, 1563 },
+                AdditionalServices = new List<AdditionalService>()
+                {
+                    new AdditionalService() { ServiceTypeId = 1561, Amount = 100 },
+                    new AdditionalService() { ServiceTypeId = 1563, Amount = 50 }
+                },
+                TotalTicketPrice = 1150,
+                IsRefund = true,
+                RefundRecord = new RefundRecord()
+                {
+                    RefundAmount = -200,
+                    NonRefundableTax = -100,
+                    RefundReason = "航班取消",
+                    RefundTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
+                    RefundAccount = 0,
+                }
+            };
+
+            return new List<CustTicketInfo>
+            {
+                testData,
+                new CustTicketInfo
+                {
+                    ClientId = 1720,
+                    CType = 460,
+                    ActualPrice = 1000,
+                    TicketCode = "9991234567890",
+                    TicketNumber = "ABC123"
+                },
+                new CustTicketInfo
+                {
+                    ClientId = 163,
+                    CType = 460,
+                    ActualPrice = 1000,
+                    TicketCode = "9991234567890",
+                    TicketNumber = "ABC123"
+                }
+            };
+        }
+
+        #endregion
+
+        /// <summary>
+        /// 保存 2026版
+        /// </summary>
+        /// <returns></returns>
+        public async Task<JsonView<AppPushBodyView>> SaveAsync(AirTicketFeeSaveDto dto)
+        {
+            // 1. 基础字段设置
+            var currUserId = dto.CurrUserId;
+            var groupId = dto.GroupAirInfo?.Id ?? 0;
+            var successMsg = new StringBuilder();
+            var appPushBodyInfos = new List<AppPushBodyInfo>();
+            var index = 1;
+
+            // 2. 整理数据
+            var airFeeRecords = await OrganizeAirTicketDataAsync(dto.AirTicketFeeInfos, groupId);
+
+            if (airFeeRecords == null || !airFeeRecords.Any())
+            {
+                return JsonView<AppPushBodyView>.Success("没有需要保存的数据");
+            }
+
+            // 3. 开启事务
+            _sqlSugar.Ado.BeginTran();
+
+            try
+            {
+                foreach (var airInfo in airFeeRecords)
+                {
+                    var airFeeInfo = _mapper.Map<Grp_AirTicketReservations>(airInfo);
+                    var cardPaymentInfo = _mapper.Map<Grp_CreditCardPayment>(airInfo);
+
+                    // 设置公共字段
+                    airFeeInfo.CreateUserId = currUserId;
+                    airFeeInfo.IsDel = 0;
+
+                    cardPaymentInfo.CreateUserId = currUserId;
+                    cardPaymentInfo.DIId = groupId;
+                    cardPaymentInfo.CTable = 85;
+                    cardPaymentInfo.UpdateDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
+                    cardPaymentInfo.IsPay = cardPaymentInfo.PayDId == 72 ? 1 : 0;
+                    cardPaymentInfo.PayThenMoney = cardPaymentInfo.PayMoney;
+                    cardPaymentInfo.RMBPrice = cardPaymentInfo.PayMoney * cardPaymentInfo.DayRate;
+                    cardPaymentInfo.IsDel = 0;
+
+                    // 判断是否存在(通过唯一键)
+                    var existingAir = await _sqlSugar.Queryable<Grp_AirTicketReservations>()
+                        .FirstAsync(x => x.DIId == groupId
+                            && x.FlightsDescription == airFeeInfo.FlightsDescription
+                            && x.CType == airFeeInfo.CType
+                            && x.RecordType == airFeeInfo.RecordType
+                            && x.IsDel == 0);
+
+                    int airId = 0;
+                    int cardId = 0;
+
+                    if (existingAir == null)
+                    {
+                        // ========== 新增 ==========
+                        var (Success, ErrorMessage, AirId, CardId) = await InsertNewRecord(airFeeInfo, cardPaymentInfo, index);
+                        if (!Success)
+                        {
+                            RollbackTran();
+                            return JsonView<AppPushBodyView>.Fail(ErrorMessage);
+                        }
+                        airId = AirId;
+                        cardId = CardId;
+
+                        appPushBodyInfos.Add(new AppPushBodyInfo()
+                        {
+                            OperationType = 1,
+                            AirTicketId = airId,
+                            CardId = cardId,
+                            CardCNYAmount = cardPaymentInfo.RMBPrice
+                        });
+                    }
+                    else
+                    {
+                        // ========== 修改 ==========
+                        var result = await UpdateExistingRecord(airFeeInfo, cardPaymentInfo, existingAir, groupId, index);
+                        if (!result.Success)
+                        {
+                            if (result.IsSkip)
+                            {
+                                successMsg.AppendLine(result.ErrorMessage);
+                                index++;
+                                continue;
+                            }
+                            RollbackTran();
+                            return JsonView<AppPushBodyView>.Fail(result.ErrorMessage);
+                        }
+                        airId = result.AirId;
+                        cardId = result.CardId;
+
+                        appPushBodyInfos.Add(new AppPushBodyInfo()
+                        {
+                            OperationType = 2,
+                            AirTicketId = airId,
+                            CardId = cardId,
+                            CardCNYAmount = cardPaymentInfo.RMBPrice
+                        });
+                    }
+
+                    index++;
+                }
+
+                _sqlSugar.Ado.CommitTran();
+
+                var view = new AppPushBodyView()
+                {
+                    GroupName = dto.GroupAirInfo.TeamName,
+                    AppPushBodyInfos = appPushBodyInfos
+                };
+
+                if (successMsg.Length > 0) successMsg.Append("其他新增成功!");
+                else successMsg.Append("保存成功!");
+
+                return JsonView<AppPushBodyView>.Success(successMsg.ToString(),view);
+            }
+            catch (Exception ex)
+            {
+                RollbackTran();
+                return JsonView<AppPushBodyView>.Fail($"保存失败:{ex.Message}");
+            }
+        }
+
+        #region 保存 辅助方法
+
+        /// <summary>
+        /// 整理数据(按舱位类型分组,分离退票和正常记录)
+        /// </summary>
+        private Task<List<AirTicketFeeInfo>> OrganizeAirTicketDataAsync(List<AirTicketFeeInfo> airTicketFeeInfos, int groupId)
+        {
+            var result = new List<AirTicketFeeInfo>();
+
+            foreach (var airInfo in airTicketFeeInfos)
+            {
+                if (airInfo.CustTicketInfos == null || !airInfo.CustTicketInfos.Any())
+                    continue;
+
+                var custRecord = _mapper.Map<AirTicketFeeOpInfo>(airInfo);
+
+                // 按舱位类型分组
+                var groupByCabinType = airInfo.CustTicketInfos
+                    .GroupBy(x => x.CType)
+                    .ToList();
+
+                foreach (var cabinGroup in groupByCabinType)
+                {
+                    var customers = cabinGroup.ToList();
+
+                    // 有退票的客户
+                    var refundCustomers = customers.Where(x => x.IsRefund).ToList();
+
+                    // 1. 同类型舱位(有退票信息的客户正常存储)- 合并成一条数据
+                    if (customers.Any())
+                    {
+                        custRecord.ClientNum = customers.Count;
+                        custRecord.ClientName = string.Join(",", customers.Select(x => x.ClientId));
+                        custRecord.CustTicketInfos = customers;
+                        custRecord.Price = customers.Sum(x => x.ActualPrice);
+                        custRecord.Currency = airInfo.PaymentCurrency;
+                        custRecord.PayMoney = customers.Sum(x => x.TotalTicketPrice);
+
+                        result.Add(custRecord);
+                    }
+
+                    // 2. 有退票记录的客户信息 - 按舱位类型合并成一条数据
+                    if (refundCustomers.Any())
+                    {
+                        custRecord.RecordType = 1; // 退票记录
+                        custRecord.OriginalReservationId = custRecord.Id; // 关联原始记录
+                        custRecord.ClientNum = refundCustomers.Count;
+                        custRecord.ClientName = string.Join(",", refundCustomers.Select(x => x.ClientId));
+                        custRecord.CustTicketInfos = refundCustomers;
+
+                        // 计算退款金额, 当前金额为负数
+                        decimal totalRefundAmount = refundCustomers.Sum(x => (x.RefundRecord?.RefundAmount ?? 0) + (x.RefundRecord?.NonRefundableTax ?? 0));
+                        custRecord.Price = totalRefundAmount;
+                        custRecord.Currency = airInfo.PaymentCurrency;
+                        custRecord.PayMoney = totalRefundAmount;
+                        custRecord.CTDId = refundCustomers.FirstOrDefault()?.RefundRecord?.RefundAccount ?? 0;
+                        result.Add(custRecord);
+                    }
+                }
+            }
+
+            return Task.FromResult(result);
+        }
+
+        /// <summary>
+        /// 新增记录
+        /// </summary>
+        private async Task<(bool Success, string ErrorMessage, int AirId, int CardId)> InsertNewRecord(
+            Grp_AirTicketReservations airFeeInfo,
+            Grp_CreditCardPayment cardPaymentInfo,
+            int index)
+        {
+            // 如果是退票记录,需要先获取关联的正常机票ID
+            if (airFeeInfo.RecordType == 1)
+            {
+                // 根据唯一键查找关联的正常机票记录
+                var normalAir = await _sqlSugar.Queryable<Grp_AirTicketReservations>()
+                    .FirstAsync(x => x.DIId == airFeeInfo.DIId
+                        && x.FlightsDescription == airFeeInfo.FlightsDescription
+                        && x.CType == airFeeInfo.CType
+                        && x.RecordType == 0
+                        && x.IsDel == 0);
+
+                // 设置关联的正常机票ID
+                airFeeInfo.OriginalReservationId = normalAir?.Id ?? 0;
+            }
+
+            // 插入机票信息
+            var airIdLong = await _sqlSugar.Insertable(airFeeInfo).ExecuteReturnIdentityAsync();
+            var airId = (int)airIdLong;
+
+            if (airId < 1)
+            {
+                return (false, $"第{index}条,机票信息添加失败", 0, 0);
+            }
+
+            // 插入付款信息
+            cardPaymentInfo.CId = airId;
+            var cardIdLong = await _sqlSugar.Insertable(cardPaymentInfo).ExecuteReturnIdentityAsync();
+            var cardId = (int)cardIdLong;
+
+            if (cardId < 1)
+            {
+                return (false, $"第{index}条,机票付款信息添加失败", 0, 0);
+            }
+
+            return (true, string.Empty, airId, cardId);
+        }
+
+        /// <summary>
+        /// 更新记录
+        /// </summary>
+        private async Task<(bool Success, bool IsSkip, string ErrorMessage, int AirId, int CardId)> UpdateExistingRecord(
+            Grp_AirTicketReservations airFeeInfo,
+            Grp_CreditCardPayment cardPaymentInfo,
+            Grp_AirTicketReservations existingAir,
+            int groupId,
+            int index)
+        {
+            var airId = existingAir.Id;
+            airFeeInfo.Id = airId;
+
+            // 如果是退票记录,需要关联正常机票ID
+            if (airFeeInfo.RecordType == 1)
+            {
+                // 如果还没有关联正常机票ID,或者需要更新关联
+                if (airFeeInfo.OriginalReservationId <= 0)
+                {
+                    var normalAir1 = await _sqlSugar.Queryable<Grp_AirTicketReservations>()
+                        .FirstAsync(x => x.DIId == groupId
+                            && x.FlightsDescription == airFeeInfo.FlightsDescription
+                            && x.CType == airFeeInfo.CType
+                            && x.RecordType == 0
+                            && x.IsDel == 0);
+
+                    if (normalAir1 == null)
+                    {
+                        return (false, true, $"第{index}条,退票记录关联的正常机票不存在,请先添加正常机票", 0, 0);
+                    }
+
+                    airFeeInfo.OriginalReservationId = normalAir1.Id;
+                }
+            }
+
+            // 获取付款信息
+            var existingCard = await _sqlSugar.Queryable<Grp_CreditCardPayment>()
+                .FirstAsync(x => x.CId == airId && x.DIId == groupId && x.CTable == 85 && x.IsDel == 0);
+
+            // 审核验证
+            if (existingCard != null)
+            {
+                if (existingCard.IsAuditGM == 1 || existingCard.IsAuditGM == 3)
+                {
+                    return (false, true, $"第{index}条,机票费用已审核,不可操作!", 0, 0);
+                }
+
+                if (existingCard.IsPay == 1)
+                {
+                    return (false, true, $"第{index}条,机票费用已付款,不可操作!", 0, 0);
+                }
+            }
+
+            // 更新机票信息
+            var airUpdateCount = await _sqlSugar.Updateable(airFeeInfo)
+                .IgnoreColumns(ignoreAllNullColumns: true)
+                .Where(x => x.Id == airId)
+                .ExecuteCommandAsync();
+
+            if (airUpdateCount < 1)
+            {
+                return (false, false, $"第{index}条,机票信息保存失败", 0, 0);
+            }
+
+            int cardId = 0;
+
+            // 更新或新增付款信息
+            if (existingCard != null)
+            {
+                cardPaymentInfo.Id = existingCard.Id;
+                var cardUpdateCount = await _sqlSugar.Updateable(cardPaymentInfo)
+                    .IgnoreColumns(ignoreAllNullColumns: true)
+                    .Where(x => x.Id == existingCard.Id)
+                    .ExecuteCommandAsync();
+
+                if (cardUpdateCount < 1)
+                {
+                    return (false, false, $"第{index}条,机票费用信息保存失败", 0, 0);
+                }
+
+                cardId = existingCard.Id;
+            }
+            else
+            {
+                cardPaymentInfo.CId = airId;
+                var cardIdLong = await _sqlSugar.Insertable(cardPaymentInfo).ExecuteReturnIdentityAsync();
+
+                if (cardIdLong < 1)
+                {
+                    return (false, false, $"第{index}条,机票费用信息保存失败", 0, 0);
+                }
+
+                cardId = (int)cardIdLong;
+            }
+
+            return (true, false, string.Empty, airId, cardId);
+        }
+
+
+        #endregion
+
+        /// <summary>
+        /// 单条删除 2026版
+        /// </summary>
+        /// <returns></returns>
+        public async Task<JsonView> SoftDelAsync(int userId, int id)
+        {
+            BeginTran();
+            try
+            {
+                var ar_res = await SoftDeleteByIdAsync<Grp_AirTicketReservations>(id.ToString(), userId);
+                if (!ar_res)
+                {
+
+                    RollbackTran();
+                    return JsonView.Fail("删除失败!");
+                }
+
+                var ccp_res = await _sqlSugar.Updateable<Grp_CreditCardPayment>()
+                    .SetColumns(x => x.IsDel == 1)
+                    .SetColumns(x => x.DeleteUserId == userId)
+                    .SetColumns(x => x.DeleteTime == DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))
+                    .Where(a => a.CId == id && a.CTable == 85)
+                    .ExecuteCommandAsync();
+                if (ccp_res < 1)
+                {
+                    RollbackTran();
+                    return JsonView.Fail("删除失败!");
+                }
+
+                CommitTran();
+                return JsonView.Success("删除成功!");
+
+            }
+            catch (Exception ex)
+            {
+                RollbackTran();
+                return JsonView.Fail("删除失败!Error:" + ex.Message);
+            }
+        }
+
+        #endregion
     }
 }

+ 32 - 11
OASystem/OASystem.Infrastructure/Repositories/Groups/DecreasePaymentsRepository.cs

@@ -90,7 +90,8 @@ namespace OASystem.Infrastructure.Repositories.Groups
                                               From Grp_DecreasePayments as gdp With(Nolock) Left Join Grp_CreditCardPayment as ccp With(Nolock) On gdp.Id = ccp.CId
                                               Left Join Sys_SetData as sd1 On gdp.Currency = sd1.Id
                                               Left Join Sys_Users as su On gdp.CreateUserId = su.Id
-                                              Where gdp.DiId = {0} And ccp.CTable = 98 {1} And ccp.IsDel = 0 And gdp.IsDel = 0 ", dto.DiId, sqlWhere);
+                                              Where gdp.DiId = {0} And ccp.CTable = 98 {1} And ccp.IsDel = 0 And gdp.IsDel = 0 order by gdp.CreateTime desc,gdp.Id desc",
+                                              dto.DiId, sqlWhere);
             var _DecreasePayments = await _sqlSugar.SqlQueryable<DecreasePaymentsView>(sql).ToListAsync();
 
             return new JsonView() { Code = 200, Msg = MsgTips.Succeed, Data = _DecreasePayments };
@@ -269,7 +270,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
             }
 
             // 处理此次 付款金额
-            grp_Decrease.ActualPaymentAmount = grp_Decrease.FeeTotal * grp_Decrease.PaymentPercent / 100m;
+            // grp_Decrease.ActualPaymentAmount = grp_Decrease.FeeTotal * grp_Decrease.PaymentPercent / 100m;
 
             // 获取汇率(复用查询)
             var teamRates = await _teamRateRep.PostGroupTeamRateItemByDiIdAndCTableId(1, dto.DiId, 98);
@@ -354,10 +355,12 @@ namespace OASystem.Infrastructure.Repositories.Groups
 
                     if (grp_Decrease.PaymentType.Contains("预付款"))
                     {
+
                         var balancePercent = 100m - grp_Decrease.PaymentPercent;
+                        grp_Decrease.ParentId = id;
                         grp_Decrease.PaymentType = "尾款";
                         grp_Decrease.PaymentPercent = balancePercent;
-                        grp_Decrease.ActualPaymentAmount = grp_Decrease.FeeTotal * balancePercent / 100m;
+                        grp_Decrease.ActualPaymentAmount = grp_Decrease.FeeTotal - grp_Decrease.ActualPaymentAmount;
 
                         var balanceId = await AddAsyncReturnId(grp_Decrease);
                         if (balanceId < 1)
@@ -525,6 +528,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
                     .Where(a => a.Id == grp_Decrease.Id && a.IsDel == 0)
                     .Select(a => new
                     {
+                        a.ParentId,
                         a.PaymentType,
                         a.PaymentPercent,
                         a.Price,
@@ -533,7 +537,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
                         a.FeeTotal,
                     })
                     .First();
-                if (editValid.PaymentType.Contains("尾款"))
+                if (editValid.ParentId == grp_Decrease.Id && editValid.PaymentType.Contains("尾款"))
                 {
                     // 原付款类型为尾款时,修改时必须包含尾款且付款比例、价格信息不变
                     if (!dto.PaymentType.Contains("尾款"))
@@ -626,17 +630,21 @@ namespace OASystem.Infrastructure.Repositories.Groups
                     {
                         #region 付款类型为预付款时,添加尾款信息
 
-                        if (grp_Decrease.PaymentType.Contains("预付款"))
+                        if (editValid.ParentId == grp_Decrease.Id && grp_Decrease.PaymentType.Contains("预付款"))
                         {
                             // 检查尾款信息是否存在,存在则更新,不存在则添加
                             var balanceInfo = await _sqlSugar.Queryable<Grp_DecreasePayments>()
-                                .Where(x => x.DiId == grp_Decrease.DiId && x.PriceName == grp_Decrease.PriceName && x.PaymentType.Contains("尾款") && x.IsDel == 0)
+                                .Where(x => x.DiId == grp_Decrease.DiId 
+                                            && x.ParentId == grp_Decrease.Id
+                                            && x.PriceName == grp_Decrease.PriceName 
+                                            && x.PaymentType.Contains("尾款") 
+                                            && x.IsDel == 0)
                                 .FirstAsync();
 
                             var balancePercent = 100m - grp_Decrease.PaymentPercent;
                             var paymentTypeName = "尾款";
                             var paymentPercent = balancePercent;
-                            var actualPaymentAmount = grp_Decrease.FeeTotal * balancePercent / 100m;
+                            var actualPaymentAmount = grp_Decrease.FeeTotal - grp_Decrease.ActualPaymentAmount;
 
                             if (balanceInfo != null) // 更新尾款信息
                             {
@@ -714,6 +722,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
                             }
                             else // 添加尾款信息
                             {
+                                grp_Decrease.ParentId = grp_Decrease.Id;
                                 grp_Decrease.PaymentType = paymentTypeName;
                                 grp_Decrease.PaymentPercent = paymentPercent;
                                 grp_Decrease.ActualPaymentAmount = actualPaymentAmount;
@@ -751,7 +760,11 @@ namespace OASystem.Infrastructure.Repositories.Groups
                         if (grp_Decrease.PaymentType.Contains("全款"))
                         {
                             var balanceInfo = await _sqlSugar.Queryable<Grp_DecreasePayments>()
-                                .Where(x => x.DiId == grp_Decrease.DiId && x.PriceName == grp_Decrease.PriceName && x.PaymentType.Contains("尾款") && x.IsDel == 0)
+                                .Where(x => x.DiId == grp_Decrease.DiId 
+                                            && x.ParentId == grp_Decrease.Id
+                                            && x.PriceName == grp_Decrease.PriceName 
+                                            && x.PaymentType.Contains("尾款") 
+                                            && x.IsDel == 0)
                                 .FirstAsync();
 
                             if (balanceInfo != null) {
@@ -917,8 +930,12 @@ namespace OASystem.Infrastructure.Repositories.Groups
                         //检索历史金额
                         var histyPrice = _sqlSugar.Queryable<Grp_DecreasePayments>()
                             .LeftJoin<Grp_CreditCardPayment>((x, a) => a.IsDel == 0 && a.CTable == 98 && a.CId == x.Id)
-                            .Where((x, a) => x.IsDel == 0 && x.DiId == paymentInfo.DiId && x.PriceName == paymentInfo.PriceName && 
-                            !x.PaymentType.Contains("尾款") && x.Id != paymentInfo.Id)
+                            .Where((x, a) => x.IsDel == 0 
+                                            && x.DiId == paymentInfo.DiId
+                                            && x.ParentId == 0
+                                            && x.PriceName == paymentInfo.PriceName 
+                                            && !x.PaymentType.Contains("尾款") 
+                                            && x.Id != paymentInfo.Id)
                             .Select((x, a) => new
                             {
                                 x.FeeTotal,
@@ -963,7 +980,11 @@ namespace OASystem.Infrastructure.Repositories.Groups
 
                         // 处理尾款审核
                         var balanceAuditInfo = _sqlSugar.Queryable<Grp_DecreasePayments>()
-                            .Where(x => x.DiId == paymentInfo.DiId && x.IsDel == 0 && x.PriceName == paymentInfo.PriceName && x.PaymentType.Contains("尾款"))
+                            .Where(x => x.DiId == paymentInfo.DiId 
+                                        && x.IsDel == 0 
+                                        && x.ParentId == paymentInfo.ParentId
+                                        && x.PriceName == paymentInfo.PriceName 
+                                        && x.PaymentType.Contains("尾款"))
                             .First();
                         if (balanceAuditInfo != null)
                         {

+ 34 - 0
OASystem/OASystem.Infrastructure/Repositories/Groups/DelegationInfoRepository.cs

@@ -199,6 +199,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
         }
 
         #endregion
+
         /// <summary>
         /// 获取接团信息Info
         /// </summary>
@@ -233,6 +234,39 @@ namespace OASystem.Infrastructure.Repositories.Groups
 
         #region 团组
 
+        /// <summary>
+        /// 团组&机票 信息
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        public async Task<GroupAirTicketList> GetGroupAirInfo(int groupId)
+        {
+           
+            var groupInfo = await _sqlSugar.Queryable<Grp_DelegationInfo>()
+                .Where(a => a.Id == groupId && a.IsDel == 0)
+                .Select(x => new GroupAirTicketList()
+                {
+                    Id = x.Id,
+                    TeamName = x.TeamName,
+                    TourCode = x.TourCode,
+                    ClientName = x.ClientName,
+                    VisitCountry = x.VisitCountry,
+                    VisitStartDate = x.VisitStartDate,
+                    VisitEndDate = x.VisitEndDate,
+                    VisitDays = x.VisitDays,
+                    VisitPNumber = x.VisitPNumber
+                })
+                .FirstAsync();
+
+            if (groupInfo != null)
+            {
+                groupInfo.VisitCountry = FormartTeamName(groupInfo.VisitCountry);
+            }
+
+            return groupInfo;
+        }
+
+
         /// <summary>
         /// 统一团组国家分割字符
         /// </summary>

+ 12 - 22
OASystem/OASystem.Infrastructure/Repositories/Groups/TeamRateRepository.cs

@@ -1,21 +1,12 @@
 using Newtonsoft.Json;
-using NPOI.SS.Formula.PTG;
 using OASystem.Domain;
 using OASystem.Domain.Dtos.Financial;
 using OASystem.Domain.Dtos.Groups;
 using OASystem.Domain.Entities.Financial;
 using OASystem.Domain.Entities.Groups;
 using OASystem.Domain.ViewModels.Financial;
-using OASystem.Domain.ViewModels.Groups;
 using OASystem.Infrastructure.Repositories.System;
-using Serilog;
-using SqlSugar;
-using System;
-using System.Collections.Generic;
 using System.Diagnostics;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
 namespace OASystem.Infrastructure.Repositories.Groups
 {
@@ -43,7 +34,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
         {
 
             Result result = new() { Code = -2 };
-            Stopwatch stopwatch = Stopwatch.StartNew(); 
+            Stopwatch stopwatch = Stopwatch.StartNew();
 
             GroupNameDto groupNameDto = new GroupNameDto() { PortType = dto.PortType };
             var groups = await _deleInfoRep.GetGroupNameList(groupNameDto);
@@ -200,8 +191,8 @@ namespace OASystem.Infrastructure.Repositories.Groups
 
 
                     rateStr += $"{otherCurrencyData1.Find(it => it.Name.Equals(item1.OtherRateCode))?.Remark ?? "Unknown"}({item1.OtherRateCode}):{item1.OtherPrice}|";
-                    
-                    
+
+
                 }
 
                 if (!rateStr.Contains("CNY"))
@@ -415,7 +406,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
                                                              .WhereColumns(it => it.Id)
                                                              .ExecuteCommand();
                                 }
-                                
+
 
                                 updateCount++;
                             }
@@ -513,7 +504,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
             TeamRateUpdateDto OPDto = new TeamRateUpdateDto();
             OPDto.DiId = diId;
             var teamRates = new List<TeamRateUpdateInfo>();
-            if (_info == null) 
+            if (_info == null)
             {
                 var currData = _sqlSugar.Queryable<Sys_SetData>().Where(it => it.IsDel == 0 && it.STid == 66 && it.Name.Equals(currCode)).First();
                 teamRates.Add(
@@ -521,9 +512,9 @@ namespace OASystem.Infrastructure.Repositories.Groups
                     {
                         Id = 0,
                         CTable = cTableId,
-                        teamRates = new List<TeamRateDescView> 
+                        teamRates = new List<TeamRateDescView>
                         {
-                            new TeamRateDescView() 
+                            new TeamRateDescView()
                             {
                                 CurrencyName = currData?.Remark,
                                 CurrencyCode = currCode,
@@ -562,7 +553,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
         /// </summary>
         /// <param name="currStr"></param>
         /// <returns></returns>
-        public  List<TeamRateDescView> GroupCurrencySplittings(string currStr)
+        public List<TeamRateDescView> GroupCurrencySplittings(string currStr)
         {
             List<TeamRateDescView> _view = new List<TeamRateDescView>();
 
@@ -618,7 +609,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
         /// </summary>
         /// <param name="currStr"></param>
         /// <returns> List<TeamRateDescView> </returns>
-        public TeamRateDescAddCurrencyIdView GroupCurrencySplittingSingle(List<SetDataInfoView> currs,string currStr)
+        public TeamRateDescAddCurrencyIdView GroupCurrencySplittingSingle(List<SetDataInfoView> currs, string currStr)
         {
             TeamRateDescAddCurrencyIdView _view = new TeamRateDescAddCurrencyIdView();
 
@@ -654,7 +645,6 @@ namespace OASystem.Infrastructure.Repositories.Groups
         /// <returns></returns>
         public async Task<TeamRateModelGeneralView> PostGroupTeamRateByDiIdAndCTableId(int portType, int diId, int cTable)
         {
-
             TeamRateModelGeneralView _view = new TeamRateModelGeneralView();
 
             if (diId < 1)
@@ -741,7 +731,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
                     else
                     {
                         teamRateDescViews.Add(GroupCurrencySplittingSingle(currencyDatas, item.Remark));
-                       
+
                     }
 
                     #endregion
@@ -801,7 +791,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
             string sql = string.Format(@"Select * From Sys_SetData Where STid = {0} And IsDel = 0", 66);
             var currencyDatas = _sqlSugar.SqlQueryable<SetDataInfoView>(sql).ToList();
 
-            _view = currencyDatas.Select(it => new CurrencyHot() { CurrencyId = it.Id,  CurrencyCode = it.Name, CurrencyName = it.Remark}).ToList();
+            _view = currencyDatas.Select(it => new CurrencyHot() { CurrencyId = it.Id, CurrencyCode = it.Name, CurrencyName = it.Remark }).ToList();
             #endregion
 
             List<TeamRateDescAddCurrencyIdView> teamRateDatas = new List<TeamRateDescAddCurrencyIdView>();
@@ -847,7 +837,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
 
             if (_view.Count > 0)
             {
-                
+
                 _view = _view.OrderByDescending(it => it.Number).ToList();
             }