ソースを参照

Merge branch 'develop' of http://132.232.92.186:3000/XinXiBu/OA2023 into develop

yuanrf 2 週間 前
コミット
579e7921ef

+ 18 - 8
OASystem/EntitySync/Program.cs

@@ -1,4 +1,5 @@
-using OASystem.Domain.Entities.Groups;
+using OASystem.Domain.Entities.Financial;
+using OASystem.Domain.Entities.Groups;
 using OASystem.Domain.Entities.PersonnelModule;
 using OASystem.Domain.Entities.Resource;
 using OASystem.Domain.Entities.System;
@@ -10,7 +11,14 @@ var db = new SqlSugarClient(new ConnectionConfig()
     DbType = DbType.SqlServer,
     IsAutoCloseConnection = true,
     InitKeyType = InitKeyType.Attribute
-});
+},
+    provider =>
+    {
+        provider.Aop.OnLogExecuting = (sql, pars) =>
+        {
+            Console.WriteLine(sql); // 输出生成的 SQL
+        };
+    });
 
 #region Aop
 //db.Aop.OnLogExecuting = (sql, pars) =>
@@ -37,6 +45,7 @@ else
 
 //同步数据表结构
 db.DbMaintenance.CreateDatabase();
+
 db.CodeFirst.SetStringDefaultLength(50).BackupTable().InitTables(new Type[]
 {
     //typeof(Sys_PageFunctionPermission)
@@ -156,12 +165,13 @@ db.CodeFirst.SetStringDefaultLength(50).BackupTable().InitTables(new Type[]
     //typeof(Grp_GroupShareFile),//团组共享文件 
     //typeof(Pm_GoodsAudit),//物品确认审核表 
     
-    typeof(Pm_GoodsReceiveDetails),//物品领用明细表 
+    //typeof(Pm_GoodsReceiveDetails),//物品领用明细表 
     /*审核流程相关表*/
-    typeof(Sys_AuditFlow),//审核流程表 
-    typeof(Sys_AuditRecord),//审核记录表 
-    typeof(Sys_AuditTemplate),//审核模板表 
-    typeof(Sys_AuditTemplateNode),//审核模板节点表 
-    typeof(Sys_AuditTemplateNodeUser),//审核模板节点人员表 
+    //typeof(Sys_AuditFlow),//审核流程表 
+    //typeof(Sys_AuditRecord),//审核记录表 
+    //typeof(Sys_AuditTemplate),//审核模板表 
+    //typeof(Sys_AuditTemplateNode),//审核模板节点表 
+    //typeof(Sys_AuditTemplateNodeUser),//审核模板节点人员表 
+    typeof(Fin_ApplicationLinkGoods),//日服申请单物品关联表 
 }); 
 Console.WriteLine("数据库结构同步完成!");

+ 10 - 10
OASystem/OASystem.Api/Controllers/AuthController.cs

@@ -38,11 +38,11 @@ namespace OASystem.API.Controllers
         private readonly IConfiguration _config;
         private readonly LoginRepository _loginRep;
         private readonly MessageRepository _message;
-        private readonly SystemMenuPermissionRepository _SystemMenuPermissionRepository;
+        private readonly SystemMenuPermissionRepository _sysMenuPermRep;
         private readonly MessageRepository _messageRep;
         private readonly IQiYeWeChatApiService _qiYeWeChatApiServic;
         private readonly IHubContext<ChatHub, IChatClient> _hubContext;
-        private readonly DeviceTokenRepository _deviceTokenRepository;
+        private readonly DeviceTokenRepository _deviceTokenRep;
 
         /// <summary>
         /// 
@@ -51,13 +51,13 @@ namespace OASystem.API.Controllers
         /// <param name="loginRep"></param>
         /// <param name="mapper"></param>
         /// <param name="message"></param>
-        /// <param name="systemMenuPermissionRepository"></param>
+        /// <param name="sysMenuPermRep"></param>
         /// <param name="qiYeWeChatApiService"></param>
         /// <param name="messageRep"></param>
         /// <param name="deviceRep"></param>
         /// <param name="hubContext"></param>
         public AuthController(IConfiguration config, LoginRepository loginRep, IMapper mapper, MessageRepository message,
-            SystemMenuPermissionRepository systemMenuPermissionRepository, IQiYeWeChatApiService qiYeWeChatApiService, MessageRepository messageRep,
+            SystemMenuPermissionRepository sysMenuPermRep, IQiYeWeChatApiService qiYeWeChatApiService, MessageRepository messageRep,
             DeviceTokenRepository deviceRep,
             IHubContext<ChatHub, IChatClient> hubContext)
         {
@@ -65,10 +65,10 @@ namespace OASystem.API.Controllers
             _loginRep = loginRep;
             _mapper = mapper;
             _message = message;
-            _SystemMenuPermissionRepository = systemMenuPermissionRepository;
+            _sysMenuPermRep = sysMenuPermRep;
             _qiYeWeChatApiServic = qiYeWeChatApiService;
             _messageRep = messageRep;
-            _deviceTokenRepository = deviceRep;
+            _deviceTokenRep = deviceRep;
             _hubContext = hubContext;
         }
 
@@ -127,7 +127,7 @@ namespace OASystem.API.Controllers
                 uName = (userData.Data as UserLoginInfoView).CnName;
                 depName = (userData.Data as UserLoginInfoView).DepName;
                 role = (userData.Data as UserLoginInfoView).JobName;
-                authData = _SystemMenuPermissionRepository.QueryMenuLoad(uId, dto.PortType);
+                authData = _sysMenuPermRep.QueryMenuLoad(uId, dto.PortType);
                 unReadCount = await _messageRep.GetUnReadCount(uId);
                 announcementUnReadCount = await _messageRep.GetAnnouncementUnReadCount(uId);
             }
@@ -228,7 +228,7 @@ namespace OASystem.API.Controllers
                 uId = (userData.Data as UserLoginInfoView).UserId;
                 uName = (userData.Data as UserLoginInfoView).CnName;
                 role = (userData.Data as UserLoginInfoView).JobName;
-                authData = _SystemMenuPermissionRepository.MobileMenuLoad(uId, dto.PortType);
+                authData = _sysMenuPermRep.MobileMenuLoad(uId, dto.PortType);
                 unReadCount = await _messageRep.GetUnReadCount(uId);
                 announcementUnReadCount = await _messageRep.GetAnnouncementUnReadCount(uId);
             }
@@ -434,7 +434,7 @@ namespace OASystem.API.Controllers
         [ProducesResponseType(typeof(LoginView), StatusCodes.Status200OK)]
         public async Task<IActionResult> SaveDeviceToken(SaveDeviceToken dto)
         {
-            var view = await _deviceTokenRepository.SaveToken(dto);
+            var view = await _deviceTokenRep.SaveToken(dto);
 
             if (view.Code == 0) return Ok(JsonView(true, "操作成功!"));
 
@@ -450,7 +450,7 @@ namespace OASystem.API.Controllers
         [ProducesResponseType(typeof(LoginView), StatusCodes.Status200OK)]
         public async Task<IActionResult> GetDeviceToken(GetDeviceToken dto)
         {
-            var view = await _deviceTokenRepository.GetToken(dto.account);
+            var view = await _deviceTokenRep.GetToken(dto.account);
 
             if (view.Code == 0) return Ok(JsonView(true, "操作成功!", view.Data));
 

+ 16 - 3
OASystem/OASystem.Api/Controllers/FinancialController.cs

@@ -40,6 +40,7 @@ namespace OASystem.API.Controllers
     {
         private readonly IMapper _mapper;
         private readonly IConfiguration _config;
+        private readonly ILogger<FinancialController> _logger;
         private readonly SqlSugarClient _sqlSugar;
         private readonly HttpClient _httpClient;
         private readonly SetDataTypeRepository _setDataTypeRep;
@@ -55,13 +56,25 @@ namespace OASystem.API.Controllers
         /// <summary>
         /// 初始化
         /// </summary>
-        public FinancialController(IMapper mapper, IConfiguration configuration, DailyFeePaymentRepository daiRep, SqlSugarClient sqlSugar, SetDataTypeRepository setDataTypeRep,
-            TeamRateRepository teamRateRep, ForeignReceivablesRepository ForForeignReceivablesRep, ProceedsReceivedRepository proceedsReceivedRep,
-            PaymentRefundAndOtherMoneyRepository paymentRefundAndOtherMoneyRep, HttpClient httpClient, DelegationInfoRepository delegationInfoRep, SetDataRepository setDataRep,
+        public FinancialController(
+            IMapper mapper, 
+            IConfiguration configuration,
+            ILogger<FinancialController> logger,
+            DailyFeePaymentRepository daiRep, 
+            SqlSugarClient sqlSugar, 
+            SetDataTypeRepository setDataTypeRep,
+            TeamRateRepository teamRateRep, 
+            ForeignReceivablesRepository ForForeignReceivablesRep, 
+            ProceedsReceivedRepository proceedsReceivedRep,
+            PaymentRefundAndOtherMoneyRepository paymentRefundAndOtherMoneyRep, 
+            HttpClient httpClient, 
+            DelegationInfoRepository delegationInfoRep, 
+            SetDataRepository setDataRep,
             ForeignReceivablesRepository foreignReceivablesRepository)
         {
             _mapper = mapper;
             _config = configuration;
+            _logger = logger;
             _daiRep = daiRep;
             _sqlSugar = sqlSugar;
             _setDataTypeRep = setDataTypeRep;

+ 64 - 114
OASystem/OASystem.Api/Controllers/GroupsController.cs

@@ -4,6 +4,7 @@ using Aspose.Words.Drawing;
 using Aspose.Words.Tables;
 using DiffMatchPatch;
 using Microsoft.AspNetCore.SignalR;
+using Microsoft.Extensions.Logging;
 using NPOI.HSSF.UserModel;
 using NPOI.SS.UserModel;
 using NPOI.SS.Util;
@@ -29,7 +30,6 @@ using OASystem.Domain.Entities.Groups;
 using OASystem.Domain.ViewModels.Financial;
 using OASystem.Domain.ViewModels.Groups;
 using OASystem.Domain.ViewModels.OCR;
-using OASystem.Domain.ViewModels.Statistics;
 using OASystem.Infrastructure.Repositories.CRM;
 using OASystem.Infrastructure.Repositories.Financial;
 using OASystem.Infrastructure.Repositories.Groups;
@@ -40,7 +40,6 @@ using System.Data;
 using System.Diagnostics;
 using System.Globalization;
 using System.IO.Compression;
-using System.Text.Json.Nodes;
 using System.Web;
 using static OASystem.API.OAMethodLib.JWTHelper;
 using static OASystem.Infrastructure.Repositories.Groups.AirTicketResRepository;
@@ -802,7 +801,8 @@ namespace OASystem.API.Controllers
             if (dto.UserId < 1) return Ok(JsonView(false, "员工Id为空"));
             if (dto.PageId < 1) return Ok(JsonView(false, "页面Id为空"));
 
-            PageFunAuthViewBase pageFunAuthView = new PageFunAuthViewBase();
+            PageFunAuthViewBase 
+                pageFunAuthView = new PageFunAuthViewBase();
             #region 页面操作权限验证
             pageFunAuthView = await GeneralMethod.PostUserPageFuncDatas(dto.UserId, dto.PageId);
 
@@ -4256,17 +4256,13 @@ FROM
                 int dataId = groupData.Data.GetType().GetProperty("dataId").GetValue(groupData.Data, null);
 
                 //自动审核
-                var autoAdit = await _feeAuditRep.FeeAutomaticAudit(5, diId, dataId); //机票费用合计方式
-
-                #region 舱位单价方式
-               
-
-
-
-                #endregion
-
-
+                //var autoAdit1 = await _feeAuditRep.FeeAutomaticAudit(5, diId, dataId); //机票费用合计方式
+                (bool status,string auditMsg) = await AirTicketResAutoAudit(diId, dataId); //KIMI-AI识别INIT方式
 
+                _logger.LogInformation("【机票费用自动审核(KIMI-AI识别INIT)】请求参数:{RequstData} 返回结果:是否执行:{IsExecute}; 结果消息:{ResponseMsg};",
+                    $"GroupId:{diId};airId:{dataId};",
+                    status,
+                    auditMsg);
 
                 await AppNoticeLibrary.SendChatMsg_GroupStatus_ApplyFee(ccpId, sign, QiyeWeChatEnum.GuoJiaoLeaderChat);
 
@@ -4297,27 +4293,34 @@ FROM
         /// 机票费用自动审核 AI识别方式
         /// </summary>
         /// <returns></returns>
-        private async Task<(bool, string)> AirTicketResAutoAudit(int diId,int dataId)
+        private async Task<(bool, string)> AirTicketResAutoAudit(int diId, int dataId)
         {
-            var auditFeeTypeIds = new List<int>() {
-                    457,   //头等舱
-                    458,   //公务舱
+            var ecoTypeIds = new List<int>() {
                     459,   //超经舱
                     460,   //经济舱
                     1430,  //公务舱(实际经济舱)
-                    1431,  //头等舱(实际公务舱)
                     1432,  //头等舱(实际经济舱)
                 };
+            var bizTypeIds = new List<int>() {
+                    457,   //头等舱
+                    458,   //公务舱
+                    1431,  //头等舱(实际公务舱)
+                };
+
+            var auditFeeTypeIds = new List<int>() {};
+            auditFeeTypeIds.AddRange(ecoTypeIds);
+            auditFeeTypeIds.AddRange(bizTypeIds);
 
             int currModule = 85;
             var airInfo = await _sqlSugar.Queryable<Grp_AirTicketReservations>()
                 .InnerJoin<Grp_CreditCardPayment>((x, y) => x.Id == y.CId && y.CTable == currModule && y.IsDel == 0)
                 .Where((x, y) => x.Id == dataId && x.IsDel == 0 && auditFeeTypeIds.Contains(x.CType))
-                .Select((x, y) => new {
+                .Select((x, y) => new
+                {
                     x.Id,
                     CcpId = y.Id,
                     x.FlightsDate,
-                    x.Price,
+                    x.PrePrice,
                     x.CType,
                     x.ClientNum,
                     x.DIId
@@ -4339,6 +4342,7 @@ FROM
 
             //处理 成本详细信息日期为空 填装费用信息 
             var airFeeLabel = new StringBuilder();
+            airFeeLabel.AppendLine("费用成本:");
             for (int i = 0; i < groupCostContents.Count; i++)
             {
                 if (string.IsNullOrEmpty(groupCostContents[i].Date))
@@ -4359,10 +4363,10 @@ FROM
 
                 var item = groupCostContents[i];
                 airFeeLabel.AppendLine($"{item.Date}");
-                airFeeLabel.AppendLine($"{item.ITIN.Replace("\n"," ")}");
+                airFeeLabel.AppendLine($"{item.ITIN.Replace("\n", " ")}");
             }
             airFeeLabel.AppendLine($"注:费用:位置:航班号或者航班时间后面; 组成:经济舱/公务舱;");
-            airFeeLabel.AppendLine($"取出{airInfo.FlightsDate}费用金额,按照经济舱/公务舱格式返回,如果不存在的话则返回0.00/0.00,只返回金额格式文本;不需要其他文字描述;");
+            airFeeLabel.AppendLine($"取出{airInfo.FlightsDate}费用金额,按照经济舱/公务舱格式返回,如果不存在的话则返回0.00/0.00,只返回金额格式文本;不需要其他文字描述;");
 
             //访问Kimi AI 返回价格结果
             var msgs = new List<SeedMessages>()
@@ -4379,10 +4383,46 @@ FROM
             var kimiApiResult_JObject = JObject.Parse(kimiApiResult);
             string clients = kimiApiResult_JObject["content"].ToString();
 
+            decimal ecoPri_kimiAI = 0.00M,
+                    bizPri_kimiAI = 0.00M;
+            //单个价格: 经济舱价格
+            if (decimal.TryParse(clients, out ecoPri_kimiAI)) { }
+            //价格文本包含/:/前经济舱价格 /后公务舱价格
+            if (clients.Contains('/'))
+            {
+                var priArray = clients.Split('/');
+                decimal.TryParse(priArray[0], out ecoPri_kimiAI); //
+                decimal.TryParse(priArray[1], out bizPri_kimiAI); //经济舱价格
+            }
+            else return (false, "价格文本不存在价格或KIMI-AI未识别出价格,不可自动审核!");
 
+            var isAutoAudit = false;
+            //价格验证处理
+            //经济舱
+            if (ecoTypeIds.Contains(airInfo.CType))
+            {
+                if (airInfo.PrePrice <= ecoPri_kimiAI) isAutoAudit = true;
+            }
+            //公务舱
+            else if (bizTypeIds.Contains(airInfo.CType))
+            {
+                if (airInfo.PrePrice <= bizPri_kimiAI) isAutoAudit = true;
+            }
+            else return (false, "该舱位类型不在自动审核范围内,不可自动审核!");
 
+            //是否执行自动审核
+            if (isAutoAudit)
+            {
+                var auditStatus = await _sqlSugar.Updateable<Grp_CreditCardPayment>()
+                    .SetColumns(x => x.IsAuditGM == 3)
+                    .SetColumns(x => x.AuditGMOperate == 4)
+                    .SetColumns(x => x.AuditGMDate == DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))
+                    .Where(x => x.Id == airInfo.CcpId)
+                    .ExecuteCommandAsync();
+                if (auditStatus > 0 ) return (true, "机票自动审核(KIMI-AI识别方式)执行成功!");
+            }
 
-            return (false,"不可自动审核!");
+            return (false, "当前费用超过成本费用,不可自动审核!");
         }
 
         /// <summary>
@@ -4396,7 +4436,7 @@ FROM
         [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
         public async Task<IActionResult> AirTicketResAutoAuditTest(int groupId, int Id)
         {
-            (bool status,string msg) = await AirTicketResAutoAudit(groupId, Id);
+            (bool status, string msg) = await AirTicketResAutoAudit(groupId, Id);
 
             return Ok(JsonView(status, msg));
         }
@@ -25592,96 +25632,6 @@ ORDER BY
         }
         #endregion
 
-        //        /// <summary>
-        //        /// 
-        //        /// </summary>
-        //        /// <param name="_dto"></param>
-        //        /// <returns></returns>
-        //        [HttpPost]
-        //        [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
-        //        public async Task<IActionResult> Test_DataChange(PostTourClientListDownloadFile _dto)
-        //        {
-        //            _airTicketResRep.ChangeDataBase(DBEnum.OA2014DB);
-
-        //            string sql = string.Format(@" Select * From Visa With(Nolock) ");
-        //            List<OA2014_Visa> list_visa = _airTicketResRep._sqlSugar.SqlQueryable<OA2014_Visa>(sql).ToList();
-
-        //            _airTicketResRep.ChangeDataBase(DBEnum.OA2023DB);
-        //            Dictionary<string, int> dic_psg = new Dictionary<string, int>();
-        //            dic_psg.Add("客人", 974);
-        //            dic_psg.Add("司机", 975);
-        //            dic_psg.Add("导游", 976);
-        //            dic_psg.Add("公司内部人员", 977);
-        //            dic_psg.Add("司机/导游/公司内部人员", 978);
-
-        //            foreach (var item in list_visa)
-        //            {
-        //                Grp_VisaInfo temp = new Grp_VisaInfo();
-
-        //                temp.Id = item.Id;
-        //                temp.DIId = item.DIId;
-        //                temp.VisaClient = item.VisaClient;
-        //                temp.VisaPrice = Convert.ToDecimal(item.VisaPrice);
-        //                temp.VisaCurrency = item.VisaCurrency;
-
-        //                temp.IsThird = item.IsThird;
-        //                if (dic_psg.ContainsKey(item.PassengerType))
-        //                {
-        //                    temp.PassengerType = dic_psg[item.PassengerType];
-        //                }
-        //                else {
-        //                    temp.PassengerType = -1;
-        //                }
-        //                temp.VisaNumber = item.VisaNumber;
-        //                temp.VisaFreeNumber = item.VisaFreeNumber;
-        //                temp.CreateUserId = item.Operators;
-        //                DateTime dt_ct;
-        //                bool b_ct = DateTime.TryParse(item.OperatorsDate, out dt_ct);
-        //                if (b_ct)
-        //                {
-        //                    temp.CreateTime = dt_ct;
-        //                }
-        //                else
-        //                {
-        //                    temp.CreateTime = DateTime.Now;
-        //                }
-        //                temp.DeleteTime = "";
-        //                temp.DeleteUserId = 0;
-        //                temp.Remark = item.Remark;
-        //                if (string.IsNullOrEmpty(temp.Remark)) {
-        //                    temp.Remark = "";
-        //                }
-        //                temp.IsDel = item.IsDel;
-        //                temp.VisaDescription = item.VisaAttachment;
-        //                string sqlInsert = string.Format(@" INSERT INTO [dbo].[Grp_VisaInfo]
-        //([Id]
-        //           ,[DIId]
-        //           ,[VisaClient]
-        //           ,[VisaPrice]
-        //           ,[VisaCurrency]
-        //           ,[IsThird]
-        //           ,[PassengerType]
-        //           ,[VisaNumber]
-        //           ,[VisaFreeNumber]
-        //           ,[CreateUserId]
-        //           ,[CreateTime]
-        //           ,[DeleteTime]
-        //           ,[DeleteUserId]
-        //           ,[Remark]
-        //           ,[IsDel]
-        //           ,[visaDescription])
-        //     VALUES
-        //           ({0},{1},'{2}',{3},{4}
-        //,{5},{6},{7},{8},{9}
-        //,'{10}','{11}',{12},'{13}',{14},'{15}') ",temp.Id,temp.DIId,temp.VisaClient,temp.VisaPrice,temp.VisaCurrency,
-        //temp.IsThird,temp.PassengerType,temp.VisaNumber,temp.VisaNumber,temp.CreateUserId,
-        //temp.CreateTime, temp.DeleteTime, temp.DeleteUserId, temp.Remark,temp.IsDel,temp.VisaDescription
-        //);
-        //               await _airTicketResRep.ExecuteCommandAsync(sqlInsert);
-        //            }
-        //            return Ok(JsonView(true, "操作成功!"));
-        //        }
-
         /// <summary>
         /// 123132123
         /// </summary>

+ 17 - 10
OASystem/OASystem.Api/Controllers/PersonnelModuleController.cs

@@ -1,9 +1,7 @@
 using Aspose.Cells;
 using FluentValidation;
 using Microsoft.AspNetCore.SignalR;
-using NPOI.XSSF.Model;
 using OASystem.API.OAMethodLib;
-using OASystem.API.OAMethodLib.APNs;
 using OASystem.API.OAMethodLib.Hub.HubClients;
 using OASystem.API.OAMethodLib.Hub.Hubs;
 using OASystem.API.OAMethodLib.QiYeWeChatAPI;
@@ -14,14 +12,12 @@ using OASystem.Domain.Entities.Groups;
 using OASystem.Domain.Entities.PersonnelModule;
 using OASystem.Domain.ViewModels.PersonnelModule;
 using OASystem.Domain.ViewModels.QiYeWeChat;
-using OASystem.Domain.ViewModels.Statistics;
 using OASystem.Infrastructure.Repositories.Groups;
 using OASystem.Infrastructure.Repositories.PersonnelModule;
 using System.Data;
 using System.Diagnostics;
 using System.Globalization;
 using static OASystem.API.OAMethodLib.JWTHelper;
-using static OpenAI.GPT3.ObjectModels.SharedModels.IOpenAiModels;
 
 namespace OASystem.API.Controllers
 {
@@ -2050,7 +2046,7 @@ WHERE
             if (dto.IsExcelDownload)
             {
                 if (userId < 1) return Ok(JsonView(false, "excel导出时,需传入正确的当前操作人UserId!"));
-;
+                ;
                 #region 页面操作权限验证
                 PageFunAuthViewBase pageFunAuthView = await GeneralMethod.PostUserPageFuncDatas(userId, pageId);
                 if (pageFunAuthView.FilesDownloadAuth == 0) return Ok(JsonView(false, "您未分配文件下载权限!"));
@@ -2135,6 +2131,7 @@ WHERE
                 ReceiveDetails = receiveDetails,
                 Reason = dto.Reason,
                 Remark = dto.Remark,
+                IsReplace = dto.IsReplace,
                 CurrUserId = currUserInfo.UserId,
             };
 
@@ -2186,7 +2183,7 @@ WHERE
             var view = await _goodsRep.GoodsReceiveAudit(idArray, currUserInfo.UserId, dto.AuditEnum);
             //var view = new JsonView() { Code = StatusCodes.Status200OK};
             //TODO:出库成功 并且是团组相关物资 向团组其他款项添加信息
-            #region 出库成功并且是团组相关物资 向团组其他款项添加信息
+            #region 出库成功 && 不需要归还的物资(IsReplace == false) && 团组相关物资 向团组其他款项添加信息
 
             if (view.Code != StatusCodes.Status200OK) return Ok(view);
 
@@ -2197,6 +2194,12 @@ WHERE
             var receiveInfo = await _sqlSugar.Queryable<Pm_GoodsReceive>().FirstAsync(x => x.IsDel == 0 && x.Id == receiveId);
             if (receiveInfo == null) return Ok(view);
 
+            if (receiveInfo.IsReplace)
+            {
+                view.Msg += $"\r\n 需要归还的物资不向团组其他款项添加信息!";
+                return Ok(view);
+            }
+
             var auditEnums = new List<GoodsAuditEnum>() { GoodsAuditEnum.OutConfirmed, GoodsAuditEnum.OutRejected };
             if (!auditEnums.Contains(receiveInfo.AuditStatus)) return Ok(view);
 
@@ -2352,7 +2355,7 @@ WHERE
                 //物资领用通知
                 await AppNoticeLibrary.SendUserMsg_CompanyShare_ToHR(sign, dto.GroupId, dto.CurrUserId);
             }
-            
+
             return Ok(res);
         }
 
@@ -2427,12 +2430,16 @@ WHERE
                 await AppNoticeLibrary.SendUserMsg_CompanyShare_ToHR(receiveId, receiveInfo.GroupId, receiveInfo.CreateUserId);
             }
 
-
-            //TODO:出库成功 并且是团组相关物资 向团组其他款项添加信息
-            #region 出库成功并且是团组相关物资 向团组其他款项添加信息
+            #region 出库成功 && 不需要归还的物资(IsReplace == false) && 团组相关物资 向团组其他款项添加信息
 
             //if (!auditEnums.Contains(receiveInfo.AuditStatus)) return Ok(view);
 
+            if (receiveInfo.IsReplace)
+            {
+                view.Msg += $"\r\n 需要归还的物资不向团组其他款项添加信息!";
+                return Ok(view);
+            }
+
             var groupInfo = await _sqlSugar.Queryable<Grp_DelegationInfo>().FirstAsync(x => x.IsDel == 0 && x.Id == receiveInfo.GroupId);
             if (groupInfo == null) return Ok(view);
 

+ 63 - 0
OASystem/OASystem.Api/HealthChecks/ExampleHealthCheck.cs

@@ -0,0 +1,63 @@
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+
+namespace OASystem.API.HealthChecks
+{
+    public class ExampleHealthCheck : IHealthCheck
+    {
+        public Task<HealthCheckResult> CheckHealthAsync(
+            HealthCheckContext context,
+            CancellationToken cancellationToken = default)
+        {
+            // Here you would check the health of your dependencies
+            // For example, check database connection, external services, etc.
+            var isHealthy = true; // Replace with actual health check logic
+
+            if (isHealthy)
+            {
+                return Task.FromResult(
+                    HealthCheckResult.Healthy("A healthy result."));
+            }
+
+            return Task.FromResult(
+                new HealthCheckResult(
+                    context.Registration.FailureStatus, "An unhealthy result."));
+        }
+    }
+
+    public class DatabaseHealthCheck : IHealthCheck
+    {
+        public async Task<HealthCheckResult> CheckHealthAsync(
+            HealthCheckContext context,
+            CancellationToken cancellationToken = default)
+        {
+            try
+            {
+                // 模拟数据库检查
+                await Task.Delay(100, cancellationToken);
+                return HealthCheckResult.Healthy("Database is OK");
+            }
+            catch (Exception ex)
+            {
+                return HealthCheckResult.Unhealthy("Database failure", ex);
+            }
+        }
+    }
+
+    public class ApiHealthCheck : IHealthCheck
+    {
+        private readonly HttpClient _httpClient;
+
+        public ApiHealthCheck(HttpClient httpClient) => _httpClient = httpClient;
+
+        public async Task<HealthCheckResult> CheckHealthAsync(
+            HealthCheckContext context,
+            CancellationToken cancellationToken = default)
+        {
+            var response = await _httpClient.GetAsync("ping", cancellationToken);
+            return response.IsSuccessStatusCode
+                ? HealthCheckResult.Healthy("API is responsive")
+                : HealthCheckResult.Degraded($"API response: {response.StatusCode}");
+        }
+    }
+
+}

+ 8 - 1
OASystem/OASystem.Api/OASystem.API.csproj

@@ -29,6 +29,9 @@
 
   <ItemGroup>
     <PackageReference Include="AlibabaCloud.SDK.Dysmsapi20170525" Version="2.0.23" />
+    <PackageReference Include="AspNetCore.HealthChecks.UI" Version="6.0.1" />
+    <PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="6.0.2" />
+    <PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="6.0.2" />
     <PackageReference Include="Aspose.Cells" Version="23.4.0" />
     <PackageReference Include="Autofac" Version="6.4.0" />
     <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="8.0.0" />
@@ -42,6 +45,7 @@
     <PackageReference Include="Flurl.Http" Version="3.2.4" />
     <PackageReference Include="JsonDiffPatch.Net" Version="2.3.0" />
     <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.11" />
+    <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="6.0.0" />
     <PackageReference Include="NodaTime" Version="3.2.0" />
     <PackageReference Include="NPOI" Version="2.7.1" />
     <PackageReference Include="PinYinConverterCore" Version="1.0.2" />
@@ -49,7 +53,10 @@
     <PackageReference Include="Quartz" Version="3.6.2" />
     <PackageReference Include="QuartzUI.Extension.AspNetCore" Version="1.1.8" />
     <PackageReference Include="RestSharp" Version="106.15.0" />
-    <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
+    <PackageReference Include="Serilog" Version="4.0.0" />
+    <PackageReference Include="Serilog.AspNetCore" Version="8.0.0" />
+    <PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
+    <PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
     <PackageReference Include="SqlSugarCore" Version="5.1.3.35" />
     <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
     <PackageReference Include="System.Data.OleDb" Version="8.0.0" />

+ 104 - 23
OASystem/OASystem.Api/Program.cs

@@ -1,7 +1,10 @@
+using HealthChecks.UI.Client;
+using Microsoft.AspNetCore.Diagnostics.HealthChecks;
 using Microsoft.AspNetCore.Http.Connections;
 using Microsoft.AspNetCore.Http.Features;
 using Microsoft.AspNetCore.Server.Kestrel.Core;
 using Microsoft.Extensions.DependencyInjection.Extensions;
+using OASystem.API.HealthChecks;
 using OASystem.API.Middlewares;
 using OASystem.API.OAMethodLib;
 using OASystem.API.OAMethodLib.AMapApi;
@@ -15,6 +18,7 @@ using Quartz;
 using Quartz.Impl;
 using Quartz.Spi;
 using QuzrtzJob.Factory;
+using Serilog.Events;
 
 Console.Title = $"FMGJ OASystem Server";
 var builder = WebApplication.CreateBuilder(args);
@@ -121,13 +125,13 @@ builder.Services.AddScoped(options =>
 {
     return new SqlSugarClient(new List<ConnectionConfig>()
     {
-        new ConnectionConfig() {
+        new() {
             ConfigId = DBEnum.OA2023DB,
             ConnectionString = _config.GetConnectionString("OA2023DB"),
             DbType = DbType.SqlServer,
             IsAutoCloseConnection = true
         },
-        new ConnectionConfig()
+        new()
         {
             ConfigId = DBEnum.OA2014DB,
             ConnectionString = _config.GetConnectionString("OA2014DB"),
@@ -289,10 +293,47 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
 #endregion
 
 #region 初始化日志
+var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
 Log.Logger = new LoggerConfiguration()
-       .MinimumLevel.Debug()
+        //不记录定时访问API
+        .Filter.ByIncludingOnly(logEvent =>
+        {
+            if (logEvent.Properties.TryGetValue("RequestPath", out var pathValue))
+            {
+                var path = pathValue.ToString().Trim('"');
+                return !path.StartsWith("/api/System/PotsMessageUnreadTotalCount");
+            }
+            return true;
+        })
+       .MinimumLevel.Information()
+       .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
+       .MinimumLevel.Override("System", LogEventLevel.Warning)
+       .Enrich.FromLogContext()
+       .WriteTo.Console()
        .WriteTo.File(Path.Combine("Logs", @"Log.txt"), rollingInterval: RollingInterval.Day)
        .CreateLogger();
+
+// 配置Serilog为Log;
+builder.Host.UseSerilog();
+#endregion
+
+#region 健康检查服务
+
+//// Add health checks
+//builder.Services.AddHealthChecks()
+//    .AddCheck<ExampleHealthCheck>("example_health_check")
+//    .AddCheck<DatabaseHealthCheck>("database")
+//    .AddCheck<ApiHealthCheck>("external_api",
+//        tags: new[] { "infrastructure" });
+
+//builder.Services.AddHealthChecksUI(options =>
+//{
+//    options.AddHealthCheckEndpoint("API", "/health");
+//    options.AddHealthCheckEndpoint("External Service", "https://other-service/health"); // 外部服务
+//}).AddInMemoryStorage();
+
+//builder.Services.AddEndpointsApiExplorer();
+
 #endregion
 
 #region 引入注册Autofac Module
@@ -370,25 +411,50 @@ builder.Services.AddSignalR()
 builder.Services.TryAddSingleton(typeof(CommonService));
 #endregion
 
-#region 健康检查服务
+var app = builder.Build();
 
-//// 
-//builder.Services.AddHealthChecks()
-//    .AddCheck("ExampleHealthCheck", () =>
-//        HealthCheckResult.Healthy("Everything is OK!"), tags: new[] { "example" });
+//serilog日志 请求中间管道
+app.UseSerilogRequestLogging(options =>
+{
+    //options.MessageTemplate = "HTTP {RequestMethod} {RequestPath} from {ClientIP} (UA: {UserAgent}, Referer: {Referer}) - {StatusCode} in {Elapsed} ms";
 
-//// 添加 HealthChecks UI 服务
-//builder.Services.AddHealthChecksUI(setup =>
-//{
-//    setup.SetEvaluationTimeInSeconds(60); // 每 60 秒检查一次
-//    setup.MaximumHistoryEntriesPerEndpoint(50); // 每个端点最多保存 50 条历史记录
-//    setup.SetApiMaxActiveRequests(1); // UI API 的最大并发请求数
-//})
-//.AddInMemoryStorage(); // 使用内存存储健康检查结果
+    options.MessageTemplate = "HTTP {RequestMethod} {RequestPath} from {ClientIP} (UA: {UserAgent}) - {StatusCode} in {Elapsed} ms";
 
-#endregion
+    // 自定义日志级别
+    options.GetLevel = (httpContext, elapsed, ex) =>
+    {
+        if (ex != null) return LogEventLevel.Error;
+        if (httpContext.Response.StatusCode > 499) return LogEventLevel.Error;
 
-var app = builder.Build();
+        // 对健康检查等端点使用更低级别
+        if (httpContext.Request.Path.StartsWithSegments("/health"))
+            return LogEventLevel.Debug;
+
+        return LogEventLevel.Information;
+    };
+
+    // 丰富日志上下文
+    options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
+    {
+        // 获取客户端IP(处理代理情况)
+        var ipAddress = CommonFun.GetClientIpAddress(httpContext);
+        var userAgent = CommonFun.DetectOS(httpContext.Request.Headers.UserAgent.ToString());
+
+        // 添加IP和其他有用信息到日志上下文
+        diagnosticContext.Set("ClientIP", ipAddress);
+        diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value);
+        diagnosticContext.Set("UserAgent", userAgent);
+        diagnosticContext.Set("Referer", httpContext.Request.Headers.Referer.ToString());
+
+        // 对于API请求添加额外信息
+        if (httpContext.Request.Path.StartsWithSegments("/api"))
+        {
+            diagnosticContext.Set("RequestContentType", httpContext.Request.ContentType);
+            diagnosticContext.Set("RequestContentLength", httpContext.Request.ContentLength ?? 0);
+        }
+    };
+
+});
 
 AutofacIocManager.Instance.Container = app.UseHostFiltering().ApplicationServices.GetAutofacRoot();//AutofacIocManager
 
@@ -472,15 +538,30 @@ app.MapHub<ChatHub>("/ChatHub", options =>
 
 #region 配置健康检查端点
 
-// 配置健康检查端点
 //app.MapHealthChecks("/health", new HealthCheckOptions
 //{
-//    Predicate = _ => true, // 包含所有健康检查
-//    ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse // 使用 UI 的响应格式
+//    ResponseWriter = async (context, report) =>
+//    {
+//        var result = new
+//        {
+//            Status = report.Status.ToString(),
+//            Checks = report.Entries.Select(e => new {
+//                Name = e.Key,
+//                Status = e.Value.Status.ToString(),
+//                Description = e.Value.Description
+//            }),
+//            Duration = report.TotalDuration
+//        };
+//        await context.Response.WriteAsJsonAsync(result);
+//    }
 //});
 
-
-
+//// 健康检查UI仪表板(可视化界面)
+//app.MapHealthChecksUI(options =>
+//{
+//    options.UIPath = "/health-ui"; // 访问路径
+//    options.ApiPath = "/health-ui-api"; // API路径(供UI前端调用)
+//});
 #endregion
 
 app.MapControllerRoute(

+ 10 - 0
OASystem/OASystem.Domain/Dtos/PersonnelModule/GoodsDTO.cs

@@ -340,6 +340,11 @@ namespace OASystem.Domain.Dtos.PersonnelModule
         /// </summary>
         public string? Reason { get; set; }
 
+        /// <summary>
+        /// 是否归还
+        /// </summary>
+        public bool IsReplace { get; set; } = false;
+
         /// <summary>
         /// 备注
         /// </summary>
@@ -398,6 +403,11 @@ namespace OASystem.Domain.Dtos.PersonnelModule
         /// </summary>
         public string? Reason { get; set; }
 
+        /// <summary>
+        /// 是否归还
+        /// </summary>
+        public bool IsReplace { get; set; } = false;
+
         /// <summary>
         /// 备注
         /// </summary>

+ 66 - 0
OASystem/OASystem.Domain/Entities/Financial/Fin_ApplicationLinkGoods.cs

@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OASystem.Domain.Entities.Financial
+{
+    /// <summary>
+    /// 日服申请单物品关联表
+    /// </summary>
+    [SugarTable(tableName: "Fin_ApplicationLinkGoods", tableDescription: "日服申请单物品关联表")]
+    public class Fin_ApplicationLinkGoods : EntityBase
+    {
+        /// <summary>
+        /// 日付申请Id(Fin_DailyFeePayment)
+        /// </summary>
+        [SugarColumn(ColumnName = "DailyId", ColumnDescription = "日付申请Id(Fin_DailyFeePayment)", IsNullable = true, ColumnDataType = "int")]
+        public int DailyId { get; private set; }
+
+        /// <summary>
+        /// 物品Id(Pm_GoodsInfo)
+        /// </summary>
+        [SugarColumn(ColumnName = "GoodsId", ColumnDescription = "物品Id(Pm_GoodsInfo)", IsNullable = true, ColumnDataType = "int")]
+        public int GoodsId { get; private set; }
+
+        /// <summary>
+        /// 物品入库Id(Pm_GoodsStorage)
+        /// </summary>
+        [SugarColumn(ColumnName = "GoodsStorageId", ColumnDescription = "物品入库Id(Pm_GoodsStorage)", IsNullable = true, ColumnDataType = "int")]
+        public int GoodsStorageId { get; private set; }
+
+        public Fin_ApplicationLinkGoods() { }
+
+        /// <summary>
+        /// info
+        /// 构造函数
+        /// </summary>
+        /// <param name="id"></param>
+        /// <param name="dailyId"></param>
+        /// <param name="goodsId"></param>
+        /// <param name="goodsStorageId"></param>
+        /// <param name="remark"></param>
+        /// <param name="userId"></param>
+        public Fin_ApplicationLinkGoods(int id, int dailyId, int goodsId, int goodsStorageId, string remark, int userId)
+        {
+            Id = id;
+            DailyId = dailyId;
+            GoodsId = goodsId;
+            GoodsStorageId = goodsStorageId;
+            Remark = remark;
+            CreateUserId = userId;
+            CreateTime = DateTime.Now;
+        }
+
+        /// <summary>
+        /// 删除赋值构造函数
+        /// </summary>
+        /// <param name="userId"></param>
+        public Fin_ApplicationLinkGoods(int userId)
+        {
+            DeleteUserId = userId;
+            DeleteTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
+        }
+    }
+}

+ 5 - 0
OASystem/OASystem.Domain/Entities/PersonnelModule/Pm_GoodsReceive.cs

@@ -78,5 +78,10 @@ namespace OASystem.Domain.Entities.PersonnelModule
         public string StatusDesc { get; set; }
         // = string.Format("领用确认:状态:待确认  审核人:-  审核时间:-;<br/>人事部:状态:待确认  审核人:-  审核时间:-;<br/>财务部:状态:待确认  审核人:-  审核时间:-;");
 
+        /// <summary>
+        /// 是否归还
+        /// </summary>
+        [SugarColumn(ColumnDescription = "是否归还", IsNullable = true, ColumnDataType = "bit")]
+        public bool IsReplace { get; set; } = false;
     }
 }

+ 6 - 0
OASystem/OASystem.Domain/Entities/System/Sys_SystemMenuPermission.cs

@@ -55,5 +55,11 @@ namespace OASystem.Domain.Entities.System
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "varchar(100)")]
         public string Icon { get; set; }
+
+        /// <summary>
+        /// 排序
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+        public int SortOrder { get; set; }
     }
 }

+ 2 - 0
OASystem/OASystem.Domain/ViewModels/Financial/Fin_DailyFeePaymentView.cs

@@ -226,6 +226,7 @@ namespace OASystem.Domain.ViewModels.Financial
                 if (FAudit == 0) str = "未审核";
                 else if (FAudit == 1) str = "已通过";
                 else if (FAudit == 2) str = "未通过";
+                else if (FAudit == 3) str = "自动审核";
                 return str;
             }
         }
@@ -243,6 +244,7 @@ namespace OASystem.Domain.ViewModels.Financial
                 if (MAudit == 0) str = "未审核";
                 else if (MAudit == 1) str = "已通过";
                 else if (MAudit == 2) str = "未通过";
+                else if (MAudit == 3) str = "自动审核";
                 return str;
             }
         }

+ 1 - 0
OASystem/OASystem.Domain/ViewModels/System/MenuLoadView.cs

@@ -23,5 +23,6 @@ namespace OASystem.Domain.ViewModels.System
         public string AndroidUrl { get; set; }
         public string IosUrl { get; set; }
         public string icon { get; set; }
+        public int SortOrder { get; set; }
     }
 }

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

@@ -25,6 +25,7 @@
     <PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
     <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
     <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.0" />
+    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
     <PackageReference Include="NPOI" Version="2.6.0" />
     <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
     <PackageReference Include="SqlSugarCore" Version="5.1.3.32" />

+ 207 - 60
OASystem/OASystem.Infrastructure/Repositories/Financial/DailyFeePaymentRepository.cs

@@ -1,19 +1,16 @@
 using AutoMapper;
-using NPOI.SS.Formula.Functions;
+using Microsoft.Extensions.Logging;
+using Serilog;
+using Newtonsoft.Json;
 using OASystem.Domain;
 using OASystem.Domain.Dtos;
 using OASystem.Domain.Dtos.Financial;
+using OASystem.Domain.Dtos.PersonnelModule;
 using OASystem.Domain.Entities.Financial;
-using OASystem.Domain.Entities.Groups;
+using OASystem.Domain.Entities.PersonnelModule;
 using OASystem.Domain.ViewModels.Financial;
+using OASystem.Infrastructure.Repositories.PersonnelModule;
 using OASystem.Infrastructure.Repositories.System;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.Design;
-using System.Linq;
-using System.Runtime.Intrinsics.Arm;
-using System.Text;
-using System.Threading.Tasks;
 
 namespace OASystem.Infrastructure.Repositories.Financial
 {
@@ -23,9 +20,11 @@ namespace OASystem.Infrastructure.Repositories.Financial
     public class DailyFeePaymentRepository : BaseRepository<Fin_DailyFeePayment, Fin_DailyFeePaymentView>
     {
         private readonly IMapper _mapper;
-        private readonly System.SetDataTypeRepository _setDataTypeRep;
-        private readonly System.UsersRepository _UsersRep;
-        private readonly System.CompanyRepository _CompanyRep;
+        private readonly SetDataTypeRepository _setDataTypeRep;
+        private readonly UsersRepository _UsersRep;
+        private readonly CompanyRepository _CompanyRep;
+        private readonly GoodsRepository _goodsRep;
+        private readonly ILogger<DailyFeePaymentRepository> _logger;
 
         /// <summary>
         /// 
@@ -35,17 +34,23 @@ namespace OASystem.Infrastructure.Repositories.Financial
         /// <param name="setDataTypeRep"></param>
         /// <param name="usersRep"></param>
         /// <param name="companyRep"></param>
-        public DailyFeePaymentRepository(SqlSugarClient sqlSugar, 
-            IMapper mapper, 
-            System.SetDataTypeRepository setDataTypeRep,
-            UsersRepository usersRep, 
-            CompanyRepository companyRep)
+        /// <param name="goodsRep"></param>
+        /// <param name="logger"></param>
+        public DailyFeePaymentRepository(SqlSugarClient sqlSugar,
+            IMapper mapper,
+            SetDataTypeRepository setDataTypeRep,
+            UsersRepository usersRep,
+            CompanyRepository companyRep,
+            GoodsRepository goodsRep,
+            ILogger<DailyFeePaymentRepository> logger)
             : base(sqlSugar)
         {
-            this._mapper = mapper;
-            this._setDataTypeRep = setDataTypeRep;
-            this._UsersRep = usersRep;
-            this._CompanyRep = companyRep;
+            _mapper = mapper;
+            _setDataTypeRep = setDataTypeRep;
+            _UsersRep = usersRep;
+            _CompanyRep = companyRep;
+            _goodsRep = goodsRep;
+            _logger = logger;
         }
 
         /// <summary>
@@ -114,9 +119,9 @@ namespace OASystem.Infrastructure.Repositories.Financial
                 else
                 {
                     userData.Insert(0, new { Id = -1, CnName = "全部", DepId = 0, DepName = "", JobPostId = 0, JobName = "" });
-                }                     
-                                      
-            }                         
+                }
+
+            }
             var userData1 = userData.Select(x => new { x.Id, x.CnName }).ToList();
 
             //62 公转 63 私转        
@@ -136,7 +141,7 @@ namespace OASystem.Infrastructure.Repositories.Financial
 
             if (dto.PortType == 1) //web
             {
-                DailyFeePaymentList = new 
+                DailyFeePaymentList = new
                 {
                     FeeTypeData = priceTypeData,
                     FeeSubTypeData = priceSubTypeData,
@@ -169,6 +174,7 @@ namespace OASystem.Infrastructure.Repositories.Financial
         /// 90 Parent 各部门基础固定费用明细  
         /// 104 Parent 团组费用
         /// 672 Sub 办公费用-信息部申请费用
+        /// 1433 Sub 团组/会务储备物资采购
         /// </summary>
         /// <param name="typeId"></param>
         /// <returns></returns>
@@ -176,14 +182,15 @@ namespace OASystem.Infrastructure.Repositories.Financial
         {
             if (typeId < 1) return false;
 
-            var parentIds = new List<int>() 
-                {   
+            var parentIds = new List<int>()
+                {
                     90,  // 各部门基础固定费用明细
                     104, // 团组费用
                 };
             var subIds = new List<int>()
                 {
-                    672, //办公费用-信息部申请费用
+                    672,  //办公费用-信息部申请费用
+                    1433, //团组/会务储备物资采购
                 };
 
             var parentTypeDatas = await _sqlSugar.Queryable<Sys_SetData>().Where(x => x.IsDel == 0 && parentIds.Contains(x.STid)).ToListAsync();
@@ -206,7 +213,7 @@ namespace OASystem.Infrastructure.Repositories.Financial
         /// <returns></returns>
         public async Task<Result> GetPriceTypeAddData(PortDtoBase dto)
         {
-            Result result = new Result() { Code = -2 };
+            Result result = new() { Code = -2 };
 
             dynamic? DailyFeePaymentList = null;
 
@@ -420,7 +427,7 @@ namespace OASystem.Infrastructure.Repositories.Financial
             _fee.GroupId = dto.GroupId;
 
             //自动审核验证
-            var auditPer  = await GMAutoApprovalType(dto.PriceTypeId);
+            var auditPer = await GMAutoApprovalType(dto.PriceTypeId);
             var priceTypeInfo = await _sqlSugar.Queryable<Sys_SetData>().FirstAsync(x => x.IsDel == 0 && x.Id == dto.PriceTypeId);
             if (auditPer)
             {
@@ -558,13 +565,13 @@ namespace OASystem.Infrastructure.Repositories.Financial
             Result result = new Result() { Code = -2 };
 
             int? delFeeStatus = await _sqlSugar.Updateable<Fin_DailyFeePayment>()
-                                  .Where(a => a.Id == dto.Id)
-                                  .SetColumns(a => new Fin_DailyFeePayment
-                                  {
-                                      IsDel = 1,
-                                      DeleteUserId = dto.Id,
-                                      DeleteTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
-                                  }).ExecuteCommandAsync();
+                .Where(a => a.Id == dto.Id)
+                .SetColumns(a => new Fin_DailyFeePayment
+                {
+                    IsDel = 1,
+                    DeleteUserId = dto.UserId,
+                    DeleteTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
+                }).ExecuteCommandAsync();
 
             result.Code = 0;
             return result;
@@ -581,36 +588,176 @@ namespace OASystem.Infrastructure.Repositories.Financial
 
             if (dto.AuditType == 1) //财务审核
             {
-                int? auditStatus = await _sqlSugar.Updateable<Fin_DailyFeePayment>()
-                                  .Where(a => a.Id == dto.Id)
-                                  .SetColumns(a => new Fin_DailyFeePayment
-                                  {
-                                      FAudit = dto.AuditStatus,
-                                      FAuditDate = DateTime.Now,
-                                  }).ExecuteCommandAsync();
-                if (auditStatus != null && auditStatus > 0)
-                    result.Code = 0;
-                else
-                    result.Msg = "财务审核操作失败";
+                int auditStatus = await _sqlSugar.Updateable<Fin_DailyFeePayment>()
+                    .Where(a => a.Id == dto.Id)
+                    .SetColumns(a => new Fin_DailyFeePayment
+                    {
+                        FAudit = dto.AuditStatus,
+                        FAuditDate = DateTime.Now,
+                    }).ExecuteCommandAsync();
+                if (auditStatus > 0) result.Code = 0;
+                else result.Msg = "财务审核操作失败";
             }
             else if (dto.AuditType == 2) //总经理
             {
-                int? auditStatus = await _sqlSugar.Updateable<Fin_DailyFeePayment>()
-                                  .Where(a => a.Id == dto.Id)
-                                  .SetColumns(a => new Fin_DailyFeePayment
-                                  {
-                                      MAudit = dto.AuditStatus,
-                                      MAuditDate = DateTime.Now,
-                                  }).ExecuteCommandAsync();
-
-                if (auditStatus != null && auditStatus > 0)
-                    result.Code = 0;
-                else
-                    result.Msg = "总经理审核操作失败";
+                int auditStatus = await _sqlSugar.Updateable<Fin_DailyFeePayment>()
+                    .Where(a => a.Id == dto.Id)
+                    .SetColumns(a => new Fin_DailyFeePayment
+                    {
+                        MAudit = dto.AuditStatus,
+                        MAuditDate = DateTime.Now,
+                    }).ExecuteCommandAsync();
+                if (auditStatus > 0) result.Code = 0;
+                else result.Msg = "总经理审核操作失败";
             }
+
+            #region 审核完全通过 指定类型 向物资添加信息
+            //费用类型
+            var feeTypeIds = new List<int>() {
+                1433,    //团组/会务储备物资采购
+                1435,    //公司固定物资采购
+            };
+
+            //审核状态集合
+            var auditStatuses = new List<int>() {
+                1,  //审核通过
+                3,  //自动审核 
+            };
+
+            var dailyInfo = await _sqlSugar.Queryable<Fin_DailyFeePayment>()
+                .FirstAsync(x => x.Id == dto.Id && x.IsDel == 0 &&
+                                 auditStatuses.Contains(x.MAudit) &&
+                                 auditStatuses.Contains(x.FAudit) &&
+                                 feeTypeIds.Contains(x.PriceTypeId));
+            if (dailyInfo != null)
+            {
+                //执行物资添加、入库、关联信息操作
+                var linkOpRes = await ApplicationLinkGoodsAddAsync(dailyInfo.Id);
+
+                // Updated the logging statement to ensure consistent message templates
+                _logger.LogInformation("【日付申请单关联物品】请求参数:{DailyInfoId}\r\n返回参数:{LinkOpResData}\r\n返回消息:{LinkOpResMsg}",
+                    dailyInfo.Id,
+                    JsonConvert.SerializeObject(linkOpRes.Data),
+                    linkOpRes.Msg);
+            }
+
+            #endregion
+
             return result;
         }
 
 
+        /// <summary>
+        /// 购买申请单关联物品
+        /// </summary>
+        /// <param name="applicatId">日付申请Id</param>
+        /// <returns></returns>
+        public async Task<JsonView> ApplicationLinkGoodsAddAsync(int applicatId)
+        {
+            if (applicatId < 1) return new JsonView() { Code = StatusCodes.Status400BadRequest, Msg = $"日付申请单ID不正确!" };
+            //if (currUserId < 1) return new JsonView() { Code = StatusCodes.Status400BadRequest, Msg = $"当前登陆人ID不正确!" };
+
+            var dailyInfoRes = await GetSearchById(new Domain.Dtos.Financial.SearchDailyFeePaymentDto() { PortType = 1, Id = applicatId });
+            if (dailyInfoRes.Code != 0) return new JsonView() { Code = StatusCodes.Status400BadRequest, Msg = dailyInfoRes?.Msg ?? "日付申请单信息不存在!" };
+
+            string dynamicJson = JsonConvert.SerializeObject(dailyInfoRes.Data);
+            Fin_DailyFeePaymentInfolView dailyInfo = JsonConvert.DeserializeObject<Fin_DailyFeePaymentInfolView>(dynamicJson);
+
+            if (dailyInfo == null || !dailyInfo.FeeContents.Any())
+            {
+                return new JsonView() { Code = StatusCodes.Status400BadRequest, Msg = $"付申请单信息不存在!!" };
+            }
+
+            //审核验证 总经理 && 财务 审核通过 才可进行 物品添加、入库、关联信息 操作
+            if (dailyInfo.MAudit == 0 || dailyInfo.MAudit == 2 || dailyInfo.FAudit == 0 || dailyInfo.FAudit == 2)
+            {
+                return new JsonView() { Code = StatusCodes.Status400BadRequest, Msg = $"审核未通过,不可进行物资关联信息!" };
+            }
+
+            _sqlSugar.BeginTran();
+
+            //1.验证物品信息
+            var currGoodsTypeId = dailyInfo.PriceTypeId switch
+            {
+                1433 => 1424, //团组物资
+                1435 => 1426, //固定资产
+                _ => 0
+            };
+
+            var paras = new List<dynamic>();
+
+            foreach (var item in dailyInfo.FeeContents)
+            {
+                var goodsInfo = await _sqlSugar.Queryable<Pm_GoodsInfo>().FirstAsync(x => x.Name.Equals(item.PriceName) && x.IsDel == 0)
+                    ?? new Pm_GoodsInfo()
+                    {
+                        Id = 0,
+                        Name = item.PriceName,
+                        Type = currGoodsTypeId,
+                        Unit = "-",
+                        CreateUserId = dailyInfo.CreateUserId,
+                        Remark = $"日付申请模块新增物品信息(需自行更改单位):\n{item.Remark}",
+                    };
+                //物品不存在执行添加
+                if (goodsInfo.Id == 0)
+                {
+                    goodsInfo.Id = await _sqlSugar.Insertable(goodsInfo).ExecuteReturnIdentityAsync();
+                    if (goodsInfo.Id < 1)
+                    {
+                        _sqlSugar.RollbackTran();
+                        return new JsonView() { Code = StatusCodes.Status400BadRequest, Msg = $"物品添加失败!" };
+                    }
+                }
+
+                //执行入库信息添加
+                var storageRes = await _goodsRep.GoodsStorageOp(new GoodsStorageOpDto()
+                {
+                    GoodsId = goodsInfo.Id,
+                    Quantity = item.Quantity,
+                    UnitPrice = item.Price,
+                    TotalPrice = item.ItemTotal,
+                    SupplierName = "-",
+                    SupplierTel = "-",
+                    SupplierAddress = "-",
+                    SupplierSource = "-",
+                    StorageUserId = 374, //固定前台ID为入库人
+                    StorageTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
+                    Remark = $"日付申请模块新增物品入库信息:{item.Remark}",
+                }, 374);
+
+                if (storageRes.Code != StatusCodes.Status200OK)
+                {
+                    _sqlSugar.RollbackTran();
+                    return new JsonView() { Code = StatusCodes.Status400BadRequest, Msg = $"物品入库信息添加失败!" };
+                }
+                var storageId = Convert.ToInt32(storageRes.Data.GetType().GetProperty("sign").GetValue(storageRes.Data, null) ?? 0);
+
+                //执行关联表添加
+                var addStatus = await _sqlSugar.Insertable(new Fin_ApplicationLinkGoods(
+                    id: 0,
+                    dailyId: dailyInfo.Id,
+                    goodsId: goodsInfo.Id,
+                    goodsStorageId: storageId,
+                    remark: "",
+                    userId: dailyInfo.CreateUserId
+                    )).ExecuteCommandAsync();
+                if (addStatus < 1)
+                {
+                    _sqlSugar.RollbackTran();
+                    return new JsonView() { Code = StatusCodes.Status400BadRequest, Msg = $"日付、物品入库信息关联信息添加失败!" };
+                }
+
+                paras.Add(new
+                {
+                    dailyId = dailyInfo.Id,
+                    goodsId = goodsInfo.Id,
+                    GoodsStorageId = storageId,
+                    LinkTableId = addStatus
+                });
+            }
+            _sqlSugar.CommitTran();
+
+            return new JsonView() { Code = StatusCodes.Status200OK, Data = paras, Msg = $"操作成功!" };
+        }
     }
 }

+ 17 - 7
OASystem/OASystem.Infrastructure/Repositories/PersonnelModule/GoodsRepository.cs

@@ -1,12 +1,16 @@
 using Aspose.Cells;
 using AutoMapper;
 using EyeSoft.Collections.Generic;
+using EyeSoft.Reflection;
 using Newtonsoft.Json;
 using OASystem.Domain;
 using OASystem.Domain.Dtos.PersonnelModule;
+using OASystem.Domain.Entities.Financial;
 using OASystem.Domain.Entities.Groups;
 using OASystem.Domain.Entities.PersonnelModule;
+using OASystem.Domain.ViewModels.Financial;
 using OASystem.Domain.ViewModels.PersonnelModule;
+using OASystem.Infrastructure.Repositories.Financial;
 using OASystem.Infrastructure.Repositories.System;
 using OASystem.Infrastructure.Tools;
 
@@ -24,7 +28,10 @@ namespace OASystem.Infrastructure.Repositories.PersonnelModule
         private string _excelPath;
         private List<int> _goodsTypeIds; //多部门审核物品类型Id     
         private readonly ApprovalProcessRepository _approvalProcessRep;
-        public GoodsRepository(SqlSugarClient sqlSugar, IMapper mapper, ApprovalProcessRepository approvalProcessRep) : base(sqlSugar)
+        public GoodsRepository(SqlSugarClient sqlSugar, 
+            IMapper mapper,
+            ApprovalProcessRepository approvalProcessRep) 
+            : base(sqlSugar)
         {
             _mapper = mapper;
             _jv = new JsonView() { Code = StatusCodes.Status400BadRequest, Msg = "操作失败!" };
@@ -501,6 +508,7 @@ namespace OASystem.Infrastructure.Repositories.PersonnelModule
             info.CreateUserId = currUserId;
             info.BatchNo = DateTime.Now.ToString("yyyyMMddHHmmssfff");
 
+            var sign = dto.Id;
             _sqlSugar.BeginTran();
             if (info.Id > 0) //修改
             {
@@ -558,8 +566,8 @@ namespace OASystem.Infrastructure.Repositories.PersonnelModule
             {
                 info.ConfirmStatus = GoodsConfirmEnum.WaitConfirm;
 
-                var storageAddId = await _sqlSugar.Insertable(info).ExecuteReturnIdentityAsync();
-                if (storageAddId < 1)
+                sign = await _sqlSugar.Insertable(info).ExecuteReturnIdentityAsync();
+                if (sign < 1)
                 {
                     _sqlSugar.RollbackTran();
                     _jv.Msg = $"添加失败!";
@@ -567,7 +575,7 @@ namespace OASystem.Infrastructure.Repositories.PersonnelModule
                 }
 
                 //入库确认默认状态
-                List<Pm_GoodsAudit> auditInfos = GoodsStorageConfirm(1, storageAddId, info.CreateUserId);
+                List<Pm_GoodsAudit> auditInfos = GoodsStorageConfirm(1, sign, info.CreateUserId);
                 if (auditInfos.Any())
                 {
                     //验证添加多条或者单条审核状态
@@ -583,6 +591,7 @@ namespace OASystem.Infrastructure.Repositories.PersonnelModule
             }
 
             _sqlSugar.CommitTran();
+            _jv.Data = new { sign };
             _jv.Msg = $"操作成功!";
             _jv.Code = StatusCodes.Status200OK;
             return _jv;
@@ -2986,7 +2995,6 @@ namespace OASystem.Infrastructure.Repositories.PersonnelModule
             return _jv;
         }
 
-
         #region New 物品领用、审核
 
         /// <summary>
@@ -3244,6 +3252,7 @@ FROM
                 Reason = dto.Reason,
                 Remark = dto.Remark,
                 AuditStatus = GoodsAuditEnum.Pending,
+                IsReplace = dto.IsReplace,
                 CreateUserId = currUserId,
             };
             var receiveDetails = _mapper.Map<Pm_GoodsReceiveDetails[]>(dto.ReceiveDetails);
@@ -3251,7 +3260,6 @@ FROM
 
             _sqlSugar.BeginTran();
 
-
             //审核状态验证 false:其他物品 true:贵重物品
             var isBatchVail = false;
 
@@ -3514,7 +3522,7 @@ FROM
                 374, // 罗颖
                 233, // 刘华举
                 208, // 雷怡
-                343, //  陈湘
+                343, // 陈湘
             };
             var isShowAllPeople = showAllPeopleIds.Any(x => x == currUserId);
 
@@ -3600,6 +3608,7 @@ FROM
                 Id = receiveInfo.Id,
                 GroupId = receiveInfo.GroupId,
                 Reason = receiveInfo.Reason,
+                IsReplace = receiveInfo.IsReplace,
                 Remark = receiveInfo.Remark,
             };
             info.ReceiveDetails = await _sqlSugar
@@ -4200,5 +4209,6 @@ FROM
         }
 
         #endregion
+
     }
 }

+ 5 - 15
OASystem/OASystem.Infrastructure/Repositories/System/SystemMenuPermissionRepository.cs

@@ -78,7 +78,7 @@ namespace OASystem.Infrastructure.Repositories.System
                 string sql = $@"
                      select a.UId,a.SmId,b.Id as Funid,b.FunctionName,b.FunctionCode,c.Id as modulid,
                       c.Name as modulName,c.STid,d.Id as pageid ,d.Name as PageName,d.SystemMenuCode,d.webUrl,d.AndroidUrl,d.icon,
-                      d.IosUrl
+                      d.IosUrl,d.SortOrder
                       from Sys_UserAuthority a inner join Sys_PageFunctionPermission b on a.FId = b.Id 
                       inner join Sys_SystemMenuPermission d on a.SmId = d.Id inner join Sys_SetData c on c.Id = d.Mid
                       where uid = {uid} and a.IsDel= 0  and b.IsDel = 0 
@@ -96,7 +96,7 @@ namespace OASystem.Infrastructure.Repositories.System
                     sql += $@" and d.IsEnable = 1 group by 
                       a.UId,a.SmId,b.Id,b.FunctionName,b.FunctionCode,c.Id,
                       c.Name,c.STid,d.Id,d.Name,d.SystemMenuCode,d.webUrl,d.AndroidUrl,d.icon,
-                      d.IosUrl";
+                      d.IosUrl,d.SortOrder";
                     DBData = _sqlSugar.SqlQueryable<MenuLoadView>(sql).ToList();
                 }
                 else if (PortType == 2)
@@ -104,7 +104,7 @@ namespace OASystem.Infrastructure.Repositories.System
                     sql += $@" and d.phoneIsEnable = 1 group by 
                       a.UId,a.SmId,b.Id,b.FunctionName,b.FunctionCode,c.Id,
                       c.Name,c.STid,d.Id,d.Name,d.SystemMenuCode,d.webUrl,d.AndroidUrl,d.icon,
-                      d.IosUrl ";
+                      d.IosUrl,d.SortOrder ";
                     DBData = _sqlSugar.SqlQueryable<MenuLoadView>(sql).ToList();
                 }
 
@@ -127,23 +127,15 @@ namespace OASystem.Infrastructure.Repositories.System
                             {
                                 it.Uid,
                                 it.SmId,
-                                //it.Funid,
-                                //it.FunctionName,
-                                //it.FunctionCode,
-                                //it.modulid,
-                                //it.modulName,
-                                //it.STid,
                                 it.pageid,
                                 it.PageName,
                                 PageAuth = item.Where(x => x.pageid == it.pageid).Select(x => new { x.Funid, x.FunctionCode, x.FunctionName }),
                                 it.SystemMenuCode,
                                 it.webUrl,
-                                //it.AndroidUrl,
-                                //it.IosUrl,
-                                //it.icon,
+                                it.SortOrder
                             });
 
-                            pageData = pageData.GroupBy(x => x.SmId).Select(y => y.First());
+                            pageData = pageData.GroupBy(x => x.SmId).Select(y => y.First()).OrderBy(x => x.SortOrder).ToList();
 
                             string Remark = setDataArr.Find(x => x.Id == item.FirstOrDefault()?.modulid)?.Remark ?? "";
                             
@@ -154,9 +146,7 @@ namespace OASystem.Infrastructure.Repositories.System
                                 pageList = pageData,
                                 Remark
                             });
-                           
                         }
-
                     }
                     else if (PortType == 2)
                     {

+ 111 - 0
OASystem/OASystem.Infrastructure/Tools/CommonFun.cs

@@ -213,6 +213,117 @@ public static class CommonFun
 
         return ip;
     }
+
+    /// <summary>
+    /// IP地址获取方法(serilog 单独处理)
+    /// </summary>
+    /// <param name="httpContext"></param>
+    /// <returns></returns>
+    public static string GetClientIpAddress(HttpContext httpContext)
+    {
+        // 1. 尝试从X-Forwarded-For获取(处理代理/负载均衡情况)
+        if (httpContext.Request.Headers.TryGetValue("X-Forwarded-For", out var forwardedFor))
+        {
+            // X-Forwarded-For可能包含逗号分隔的IP列表(客户端,代理1,代理2,...)
+            var ip = forwardedFor.FirstOrDefault()?.Split(',').FirstOrDefault()?.Trim();
+            if (!string.IsNullOrEmpty(ip))
+                return ip;
+        }
+
+        // 2. 尝试从X-Real-IP获取
+        if (httpContext.Request.Headers.TryGetValue("X-Real-IP", out var realIp))
+        {
+            var ip = realIp.FirstOrDefault();
+            if (!string.IsNullOrEmpty(ip))
+                return ip;
+        }
+
+        // 3. 使用连接远程IP地址
+        var remoteIp = httpContext.Connection.RemoteIpAddress;
+
+        // 处理IPv6映射的IPv4地址
+        if (remoteIp != null && remoteIp.IsIPv4MappedToIPv6)
+        {
+            remoteIp = remoteIp.MapToIPv4();
+        }
+
+        return remoteIp?.ToString() ?? "unknown";
+    }
+
+    #endregion
+
+    #region UserAgent
+    /// <summary>
+    /// 分层检测策略实现操作系统识别
+    /// </summary>
+    /// <param name="userAgent"></param>
+    /// <returns></returns>
+    public static string DetectOS(string userAgent)
+    {
+        if (string.IsNullOrEmpty(userAgent)) return "Unknown";
+
+        // 移动端优先检测
+        if (IsMobile(userAgent))
+        {
+            if (userAgent.Contains("iPhone")) return ParseIOSVersion(userAgent);
+            if (userAgent.Contains("Android")) return ParseAndroidVersion(userAgent);
+            if (userAgent.Contains("Windows Phone")) return "Windows Mobile";
+        }
+
+        // PC端检测
+        if (userAgent.Contains("Windows NT")) return ParseWindowsVersion(userAgent);
+        if (userAgent.Contains("Macintosh")) return "macOS";
+        if (userAgent.Contains("Linux")) return "Linux";
+
+        return "Other";
+    }
+
+    /// <summary>
+    /// 移动设备判断
+    /// </summary>
+    /// <param name="userAgent"></param>
+    /// <returns></returns>
+    private static bool IsMobile(string userAgent)
+    {
+        string[] mobileKeywords = { "iPhone", "Android", "Windows Phone", "iPad", "iPod" };
+        return mobileKeywords.Any(key => userAgent.Contains(key));
+    }
+
+    /// <summary>
+    /// iOS版本解析
+    /// </summary>
+    /// <param name="userAgent"></param>
+    /// <returns></returns>
+    private static string ParseIOSVersion(string userAgent)
+    {
+        var match = Regex.Match(userAgent, @"iPhone OS (\d+_\d+)");
+        return match.Success ? $"iOS {match.Groups[1].Value.Replace('_', '.')}" : "iOS";
+    }
+
+    /// <summary>
+    ///  Android版本解析
+    /// </summary>
+    /// <param name="userAgent"></param>
+    /// <returns></returns>
+    private static string ParseAndroidVersion(string userAgent)
+    {
+        var match = Regex.Match(userAgent, @"Android (\d+)");
+        return match.Success ? $"Android {match.Groups[1].Value}" : "Android";
+    }
+
+    /// <summary>
+    /// Windows版本解析
+    /// </summary>
+    /// <param name="userAgent"></param>
+    /// <returns></returns>
+    private static string ParseWindowsVersion(string userAgent)
+    {
+        if (userAgent.Contains("Windows NT 10.0")) return "Windows 10/11";
+        if (userAgent.Contains("Windows NT 6.3")) return "Windows 8.1";
+        if (userAgent.Contains("Windows NT 6.2")) return "Windows 8";
+        return "Windows";
+    }
+
     #endregion
 
     #region 随机数