Explorar el Código

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

LEIYI hace 2 meses
padre
commit
81afda3182
Se han modificado 22 ficheros con 1037 adiciones y 379 borrados
  1. 2 1
      OASystem/EntitySync/Program.cs
  2. 46 8
      OASystem/OASystem.Api/Controllers/AuthController.cs
  3. 28 9
      OASystem/OASystem.Api/Controllers/FinancialController.cs
  4. 20 2
      OASystem/OASystem.Api/Controllers/PersonnelModuleController.cs
  5. 107 1
      OASystem/OASystem.Api/Controllers/ResourceController.cs
  6. 15 0
      OASystem/OASystem.Api/OAMethodLib/APNs/APNsLib.cs
  7. 185 0
      OASystem/OASystem.Api/OAMethodLib/APNs/APNsService.cs
  8. 23 0
      OASystem/OASystem.Api/OAMethodLib/APNs/IAPNsService.cs
  9. 3 0
      OASystem/OASystem.Api/Program.cs
  10. 356 351
      OASystem/OASystem.Api/appsettings.json
  11. 5 0
      OASystem/OASystem.Domain/Dtos/Financial/Fin_GroupExtraCostDto.cs
  12. 2 0
      OASystem/OASystem.Domain/Dtos/PersonnelModule/TreeNode.cs
  13. 15 0
      OASystem/OASystem.Domain/Dtos/Resource/OfficialActivitiesDto.cs
  14. 19 1
      OASystem/OASystem.Domain/Dtos/UserDto/LoginDto.cs
  15. 5 0
      OASystem/OASystem.Domain/Entities/Financial/Fin_GroupExtraCost.cs
  16. 27 0
      OASystem/OASystem.Domain/Entities/System/Sys_DeviceToken.cs
  17. 6 0
      OASystem/OASystem.Domain/ViewModels/Financial/Fin_GroupExtraCostView.cs
  18. 12 0
      OASystem/OASystem.Domain/ViewModels/LoginView.cs
  19. 7 0
      OASystem/OASystem.Domain/ViewModels/Resource/OfficialActivitiesView.cs
  20. 18 2
      OASystem/OASystem.Infrastructure/Repositories/Financial/ForeignReceivablesRepository.cs
  21. 32 4
      OASystem/OASystem.Infrastructure/Repositories/Groups/CarTouristGuideGroundRepository.cs
  22. 104 0
      OASystem/OASystem.Infrastructure/Repositories/Login/DeviceTokenRepository.cs

+ 2 - 1
OASystem/EntitySync/Program.cs

@@ -141,7 +141,8 @@ db.CodeFirst.SetStringDefaultLength(50).BackupTable().InitTables(new Type[]
     //typeof(Grp_Opinionaire),//团组接待意见调查 
     //typeof(Sys_ExchangeRateRecord),//团组接待意见调查 
     //typeof(Res_TranslatorLibrary),//翻译人员库 
-    typeof(Grp_OfficialDutyLinkTranslator),//公务信息关联翻译人员 
+    //typeof(Grp_OfficialDutyLinkTranslator),//公务信息关联翻译人员 
+    typeof(Sys_DeviceToken)
 
 });
 Console.WriteLine("数据库结构同步完成!");

+ 46 - 8
OASystem/OASystem.Api/Controllers/AuthController.cs

@@ -37,10 +37,12 @@ namespace OASystem.API.Controllers
         private readonly MessageRepository _messageRep;
         private readonly IQiYeWeChatApiService _qiYeWeChatApiServic;
         private readonly IHubContext<ChatHub, IChatClient> _hubContext;
+        private readonly DeviceTokenRepository _deviceTokenRepository;
 
 
-        public AuthController(IConfiguration config, LoginRepository loginRep, IMapper mapper,MessageRepository message,
+        public AuthController(IConfiguration config, LoginRepository loginRep, IMapper mapper, MessageRepository message,
             SystemMenuPermissionRepository systemMenuPermissionRepository, IQiYeWeChatApiService qiYeWeChatApiService, MessageRepository messageRep,
+            DeviceTokenRepository deviceRep,
             IHubContext<ChatHub, IChatClient> hubContext)
         {
             _config = config;
@@ -50,6 +52,7 @@ namespace OASystem.API.Controllers
             _SystemMenuPermissionRepository = systemMenuPermissionRepository;
             _qiYeWeChatApiServic = qiYeWeChatApiService;
             _messageRep = messageRep;
+            _deviceTokenRepository = deviceRep;
             _hubContext = hubContext;
         }
 
@@ -70,7 +73,7 @@ namespace OASystem.API.Controllers
 
             #region 校验用户信息 
             var userData = _loginRep.Login(dto).Result;
-            if (userData.Code != 0)  return Ok(JsonView(false, userData.Msg)); 
+            if (userData.Code != 0) return Ok(JsonView(false, userData.Msg));
 
             #endregion
             Result authData = null;
@@ -286,15 +289,18 @@ namespace OASystem.API.Controllers
 
                     var companyDetails2 = companyDetails.GroupBy(it => it.DepId).Select(it => it.First()).ToList();
                     //部门
-                    depDetailsView = companyDetails2.Where(depIt => depIt.CompanyId == it.CompanyId).Select(depIt => {
+                    depDetailsView = companyDetails2.Where(depIt => depIt.CompanyId == it.CompanyId).Select(depIt =>
+                    {
 
                         DepDetailsView depDetails = new DepDetailsView();
                         List<JobDetailsView> jobDetails = new List<JobDetailsView>();
 
                         //岗位
-                        jobDetails = companyDetails.Where(jobIt => jobIt.DepId == depIt.DepId).Select(jobIt => {
+                        jobDetails = companyDetails.Where(jobIt => jobIt.DepId == depIt.DepId).Select(jobIt =>
+                        {
 
-                            JobDetailsView jobDetail = new JobDetailsView() { 
+                            JobDetailsView jobDetail = new JobDetailsView()
+                            {
                                 JobId = jobIt.JobId,
                                 JobName = jobIt.JobName,
                             };
@@ -375,12 +381,44 @@ namespace OASystem.API.Controllers
             //Result result = new Result();
             //var httpContext = HttpContext.User.Claims.FirstOrDefault(it => it.Type == ClaimTypes.Name)?.Value;
             //Sys_Users sys_Users = _mapper.Map<Sys_Users>(dto);
-            var _view = await _loginRep.ChangePassword(dto.UserId,dto.Password);
-            if (_view.Code == 0) return Ok(JsonView(true,"操作成功!"));
+            var _view = await _loginRep.ChangePassword(dto.UserId, dto.Password);
+            if (_view.Code == 0) return Ok(JsonView(true, "操作成功!"));
 
             return Ok(JsonView(false, _view.Msg));
         }
 
+        /// <summary>
+        /// 保存deviceToken
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("SaveDeviceToken")]
+        [ProducesResponseType(typeof(LoginView), StatusCodes.Status200OK)]
+        public async Task<IActionResult> SaveDeviceToken(SaveDeviceToken dto)
+        {
+            var view = await _deviceTokenRepository.SaveToken(dto);
+
+            if (view.Code == 0) return Ok(JsonView(true, "操作成功!"));
+
+            return Ok(JsonView(false, view.Msg));
+        }
+
+        /// <summary>
+        /// 获取deviceToken
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("GetDeviceToken")]
+        [ProducesResponseType(typeof(LoginView), StatusCodes.Status200OK)]
+        public async Task<IActionResult> GetDeviceToken(GetDeviceToken dto)
+        {
+            var view = await _deviceTokenRepository.GetToken(dto.account);
+
+            if (view.Code == 0) return Ok(JsonView(true, "操作成功!", view.Data));
+
+            return Ok(JsonView(false, view.Msg));
+        }
+
         /// <summary>
         /// 测试auth
         /// </summary>
@@ -826,7 +864,7 @@ namespace OASystem.API.Controllers
 
                 return Ok(JsonView(false, "操作失败!", ex.Message));
             }
-            
+
         }
 
     }

+ 28 - 9
OASystem/OASystem.Api/Controllers/FinancialController.cs

@@ -3267,6 +3267,7 @@ namespace OASystem.API.Controllers
             _entity.Area = dto.Area;
             _entity.SupervisorConfirm = dto.SupervisorConfirm;
             _entity.ManagerConfirm = dto.ManagerConfirm;
+            _entity.SYsupervisorConfirm = dto.SYsupervisorConfirm;
 
             DateTime dt_PriceDt;
             bool b_PriceDt = DateTime.TryParse(dto.PriceDt, out dt_PriceDt);
@@ -3579,8 +3580,9 @@ namespace OASystem.API.Controllers
             string sql_data = string.Format(@"Select * From (	
                                                 Select row_number() over (order by f.Id Desc) as RowNumber,f.Id,f.DiId,
 												f.PriceName,f.PriceType,f.PriceDetailType,CAST(f.Price as varchar)+' '+s.[Name] as PriceStr,f.PriceCount,CAST(f.PriceSum as varchar)+' '+s.[Name] as PriceSumStr,
-                                                f.CreateUserId,f.PriceDt,c.IsAuditGM,f.ManagerConfirm,f.SupervisorConfirm,
-                                                CASE  ManagerConfirm WHEN 1 THEN '已确认' ELSE '未确认' END as 'ManagerConfirmStr' , CASE  SupervisorConfirm WHEN 1 THEN '已确认' ELSE '未确认' END  as 'SupervisorConfirmStr'														
+                                                f.CreateUserId,f.PriceDt,c.IsAuditGM,f.ManagerConfirm,f.SupervisorConfirm,f.Remark,
+                                                CASE  ManagerConfirm WHEN 1 THEN '已确认' ELSE '未确认' END as 'ManagerConfirmStr' , CASE  SupervisorConfirm WHEN 1 THEN '已确认' ELSE '未确认' END  as 'SupervisorConfirmStr',		
+                                                CASE  SYsupervisorConfirm WHEN 1 THEN '已确认' ELSE '未确认' END  as 'SYsupervisorConfirmStr'
                                                 From Fin_GroupExtraCost f
                                                 Inner Join Grp_CreditCardPayment c On f.Id = c.CId
                                                 Inner Join Sys_SetData s On f.PriceCurrency = s.Id
@@ -3589,8 +3591,9 @@ namespace OASystem.API.Controllers
                                                 ) temp Where RowNumber Between {1} and {2}", sqlWhere, startIndex, endIndex);
             string sql_count = string.Format(@"Select Count(1) as DataCount From (	
                                                 Select row_number() over (order by f.Id Desc) as RowNumber,f.Id,f.DiId,
-												f.PriceName,f.PriceType,f.Price,f.FilePath,f.CreateUserId,f.CreateTime,c.IsAuditGM,f.ManagerConfirm,f.SupervisorConfirm,
-                                                CASE  ManagerConfirm WHEN 1 THEN '已确认' ELSE '未确认' END as 'ManagerConfirmStr' , CASE  SupervisorConfirm WHEN 1 THEN '已确认' ELSE '未确认' END  as 'SupervisorConfirmStr'
+												f.PriceName,f.PriceType,f.Price,f.FilePath,f.CreateUserId,f.CreateTime,c.IsAuditGM,f.ManagerConfirm,f.SupervisorConfirm,f.Remark,
+                                                CASE  ManagerConfirm WHEN 1 THEN '已确认' ELSE '未确认' END as 'ManagerConfirmStr' , CASE  SupervisorConfirm WHEN 1 THEN '已确认' ELSE '未确认' END  as 'SupervisorConfirmStr',
+                                                CASE  SYsupervisorConfirm WHEN 1 THEN '已确认' ELSE '未确认' END  as 'SYsupervisorConfirmStr'
                                                 From Fin_GroupExtraCost f
                                                 Inner Join Grp_CreditCardPayment c On f.Id = c.CId
                                                 Inner Join Sys_SetData s On f.PriceCurrency = s.Id
@@ -4029,8 +4032,7 @@ Group by PriceType ", dto.diId);
                     JoinType.Left, s1.Id == l.toCurr && s1.IsDel == 0,
                     JoinType.Left, i.Id == l.DiId && i.IsDel == 0
                 ))
-                .Where((f, c, s, g) => f.IsDel == 0 && f.DiId == dto.diId && 
-                    (f.ManagerConfirm == 1 ||  f.ManagerConfirm == null || f.SupervisorConfirm == 1 || f.SupervisorConfirm == null))
+                .Where((f, c, s, g) => f.IsDel == 0 && f.DiId == dto.diId)
                 .Select((f, c, s, g, r, l, s1, i) => new
                 {
                     c.PaymentCurrency,
@@ -4054,9 +4056,25 @@ Group by PriceType ", dto.diId);
                     f.PriceDt,
                     i.VisitPNumber,
                     f.DiId,
-                    f.PriceCurrency
+                    f.PriceCurrency,
+                    f.SupervisorConfirm,
+                    f.ManagerConfirm,
+                    f.SYsupervisorConfirm,
                 })
                 .ToList()
+                .Where(x=> {
+                    var count = 0;
+                    var stringArr = new string[] { "SYsupervisorConfirm", "SupervisorConfirm", "ManagerConfirm" };
+                    foreach (var item in stringArr)
+                    {
+                        var number = x.GetType()?.GetProperty(item)?.GetValue(x).ObjToInt();
+                        if (number > 0)
+                        {
+                            count++;
+                        }
+                    }
+                    return count > 1;
+                })
                 .Select(x =>
                 {
                     var costPirce = x.Price;
@@ -4101,11 +4119,11 @@ Group by PriceType ", dto.diId);
 
                             if (x.toCurr != x.PaymentCurrency)
                             {
-                                remake += $"{x.PriceDt.ToString("M/d")} {findCheck?.Name} {x.PaymentCurrencyStr} {(int)Math.Round(oldPrice)}( 折算{x.toCurrStr} {(int)Math.Round(costPirce)}) - 财政{cost} * {x.VisitPNumber}  = {x.toCurrStr}{costResultPirce}";
+                                remake += $" {x.PriceDt.ToString("M/d")} {findCheck?.Name} {x.PaymentCurrencyStr} {(int)Math.Round(oldPrice)}( 折算{x.toCurrStr} {(int)Math.Round(costPirce)}) - 财政{cost} * {x.VisitPNumber}  = {x.toCurrStr}{costResultPirce}";
                             }
                             else
                             {
-                                remake += $"{x.PriceDt.ToString("M/d")} {findCheck?.Name}{x.PaymentCurrencyStr} {(int)Math.Round(costPirce)} - 财政{cost} * {x.VisitPNumber}  = {x.PaymentCurrencyStr}{costResultPirce}";
+                                remake += $" {x.PriceDt.ToString("M/d")} {findCheck?.Name}{x.PaymentCurrencyStr} {(int)Math.Round(costPirce)} - 财政{cost} * {x.VisitPNumber}  = {x.PaymentCurrencyStr}{costResultPirce}";
                             }
 
                             costPirce = costResultPirce;
@@ -4315,6 +4333,7 @@ Group by PriceType ", dto.diId);
             {
                 {1 ,"ManagerConfirm" },
                 {2 ,"SupervisorConfirm" },
+                {3 ,"SYsupervisorConfirm" },
             };
             if (dto.DataId < 1 || confirmStatusArr.Keys.Contains(dto.ConfirmId) == false)
             {

+ 20 - 2
OASystem/OASystem.Api/Controllers/PersonnelModuleController.cs

@@ -2,6 +2,7 @@
 using FluentValidation;
 using Microsoft.AspNetCore.SignalR;
 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;
@@ -2161,6 +2162,14 @@ OPTION (MAXRECURSION 0); -- 允许无限递归      ";
         public async Task<IActionResult> AssessmentSettingOperationAsync(PerAssessmentSettingOperationDto dto)
         {
             var jw = JsonView(false);
+
+            if (!ModelState.IsValid)
+            {
+                jw.Msg = "参数错误";
+                jw.Data = ModelState;
+                return Ok(jw);
+            }
+
             try
             {
                 if (dto.ParentId != 0)
@@ -2184,7 +2193,6 @@ OPTION (MAXRECURSION 0); -- 允许无限递归      ";
                 };
 
                 jw.Code = 200;
-                jw.Data = "";
                 if (dto.Id == 0)
                 {
                     entity.CreateUserId = dto.CreateId;
@@ -2202,7 +2210,6 @@ OPTION (MAXRECURSION 0); -- 允许无限递归      ";
             {
                jw.Msg = "Api error " + ex.Message;
                jw.Code = 400;
-               jw.Data = "";
             }
 
             return Ok(jw);
@@ -2331,6 +2338,17 @@ OPTION (MAXRECURSION 0); -- 允许无限递归      ";
             return Ok(jw);
         }
 
+        [HttpPost]
+        public IActionResult TempTest(QueryAssessmentSettingListOffsetAsyncDto Dto)
+        {
+            var jw = JsonView(false);
+
+
+            APNsLib.pushMsg("com.panamerican.oa2024", "db151df1d25823f84323c4597c5672460a983d49ba039c7ff54c4c8e36edd5c6", NotificationType.Alert, "测试", "测试字标题", "测试内容");
+
+            return Ok(jw);
+        }
+
         #endregion
     }
 }

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

@@ -1,5 +1,7 @@
-using Aspose.Words;
+using Aspose.Cells;
+using Aspose.Words;
 using EyeSoft.Extensions;
+using EyeSoft.Reflection;
 using NPOI.SS.Formula.Functions;
 using OASystem.API.OAMethodLib;
 using OASystem.API.OAMethodLib.QiYeWeChatAPI.AppNotice;
@@ -2589,6 +2591,110 @@ Inner Join Sys_Department as d With(Nolock) On u.DepId=d.Id Where m.Id={0} ", _m
             return numStr;
         }
 
+        [HttpPost]
+        public IActionResult ExportOfficialActivitiesInfo(ExportOfficialActivitiesInfoDto dto)
+        {
+            var jw = JsonView(false);
+
+            if (!ModelState.IsValid)
+            {
+                jw.Data = ModelState;
+                return Ok(jw);
+            }
+
+            var startTime_bool = DateTime.TryParse(dto.StartTime, out DateTime startTime_parse);
+            var endTime_bool = DateTime.TryParse(dto.EndTime, out DateTime endTime_parse);
+
+            if(startTime_parse > endTime_parse)
+            {
+                jw.Msg = "开始实际不能大于结束时间!";
+                return Ok(jw);
+            }
+
+            if (startTime_bool && endTime_bool)
+            {
+                //处理时间模板
+                var Query_DB = _sqlSugar.Queryable<Grp_DelegationInfo>()
+                                .LeftJoin<Res_OfficialActivities>((a, b) => a.Id == b.DiId && b.IsDel == 0)
+                                .LeftJoin<Sys_Users>((a, b, c) => b.CreateUserId == c.Id && c.IsDel == 0)
+                                .Where((a, b, c) => a.IsDel == 0 && a.VisitDate >= startTime_parse && a.VisitDate <= endTime_parse)
+                                .WhereIF(dto.State != -1, (a, b, c) => b.ConfirmTheInvitation == dto.State)
+                                .Select((a, b, c) => new
+                                {
+                                    a.TeamName,
+                                    b.Client,
+                                    b.ConfirmTheInvitation,
+                                    b.Contact,
+                                    ConfirmTheInvitationStr = b.ConfirmTheInvitation == 1 ? "是" : "否",
+                                    b.Date,
+                                    b.Nature,
+                                    c.CnName,
+                                    b.Address,
+                                    b.CreateTime
+                                })
+                                .ToList()
+                                .Where(x=>!x.Client.IsNullOrWhiteSpace())
+                                .ToList();
+
+                if (Query_DB.Count > 0)
+                {
+                    //打开excel
+                    var ftpPath = AppSettingsHelper.Get("ExcelFtpPath");
+                    var fileBasePath = AppSettingsHelper.Get("ExcelBasePath");
+                    var fileName = "公务出访信息.docx";
+
+                    //创建数据源Table
+                    DataTable dtSource = new DataTable();
+                    dtSource.TableName = "TB";
+
+                    var firstElem = Query_DB.First();
+                    //遍历firstElem所有属性
+                    foreach (var item in firstElem.GetType().GetProperties())
+                    {
+                        dtSource.Columns.Add(item.Name, item.PropertyType);
+                    }
+
+                    foreach (var item in Query_DB)
+                    {
+                        DataRow dr = dtSource.NewRow();
+                        //遍历dtSource所有列头
+                        foreach (DataColumn column in dtSource.Columns)
+                        {
+                            dr[column.ColumnName] = item.GetType().GetProperty(column.ColumnName)?.GetValue(item)?.ToString();
+                        }
+                        dtSource.Rows.Add(dr);
+                    }
+
+                    WorkbookDesigner designer = new WorkbookDesigner();
+                    designer.Workbook = new Workbook(fileBasePath + ("Template/公务出访信息.xlsx"));
+
+                    var title = startTime_parse.ToString("yyyy年MM月dd日") + " - " + endTime_parse.ToString("yyyy年MM月dd日") + " 公务出访信息";
+                    designer.SetDataSource("title", title);
+                    designer.SetDataSource(dtSource);
+                    designer.Process();
+                    
+                    var exportSerevePath = fileBasePath + "ExportOfficialActivitiesInfo/" + title + ".xlsx";
+                    var exportFtpPaht = ftpPath + "ExportOfficialActivitiesInfo/" + title + ".xlsx";
+
+                    //保存文件
+                    designer.Workbook.Save(exportSerevePath);
+                    jw.Data = AppSettingsHelper.Get("ExcelBaseUrl") + exportFtpPaht;
+                    jw.Code = 200;
+                    jw.Msg = "获取成功!";
+                }
+                else
+                {
+                    jw.Data = "无公务信息!";
+                }
+
+            }
+            else
+            {
+                jw.Data = "时间格式错误!";
+            }
+            return Ok(jw);
+        }
+
         #endregion
 
         #region 请示数据库

+ 15 - 0
OASystem/OASystem.Api/OAMethodLib/APNs/APNsLib.cs

@@ -0,0 +1,15 @@
+
+namespace OASystem.API.OAMethodLib.APNs
+{
+    public static class APNsLib
+    {
+        private static readonly IAPNsService _APNsService = AutofacIocManager.Instance.GetService<IAPNsService>();
+
+        public static bool pushMsg(string apnsTopic, string deviceToken, NotificationType type, string title, string subtitle, string body)
+        {
+            _APNsService.PushNotification(apnsTopic, deviceToken, type, title, subtitle, body);
+
+            return true;
+        }
+    }
+}

+ 185 - 0
OASystem/OASystem.Api/OAMethodLib/APNs/APNsService.cs

@@ -0,0 +1,185 @@
+using System.Security.Claims;
+using System.Security.Cryptography;
+using Microsoft.IdentityModel.Tokens;
+using System.IdentityModel.Tokens.Jwt;
+using static System.Net.Mime.MediaTypeNames;
+using Microsoft.Net.Http.Headers;
+using Microsoft.Extensions.Configuration;
+using NPOI.SS.Formula.Functions;
+
+namespace OASystem.API.OAMethodLib.APNs
+{
+    public enum NotificationType : int
+    {
+        Alert = 0,
+        Sound = 1,
+        Badge = 2,
+        Silent = 3
+    }
+
+    /// <summary>
+    /// APNs 生成 JWT token,添加服务的时候,使用单利
+    /// </summary>
+    public class APNsService : IAPNsService
+    {
+        static string token = null;
+        static string baseUrl = null;
+
+        private readonly IConfiguration _configuration;
+        private readonly IHttpClientFactory _httpClientFactory;
+        public APNsService(IConfiguration configuration, IHttpClientFactory httpClientFactory)
+        {
+            this._configuration = configuration;
+            this._httpClientFactory = httpClientFactory;
+
+            //APNsService.baseUrl = this._configuration["apple:pushNotificationServer"];
+            APNsService.baseUrl = this._configuration["apple:pushNotificationServer_Production"];
+        }
+
+        /// <summary>
+        /// 生成 APNs JWT token
+        /// </summary>
+        /// <returns></returns>
+        public string GetnerateAPNsJWTToken()
+        {
+            return this.GetnerateAPNsJWTToken(APNsService.token);
+        }
+
+        /// <summary>
+        /// 生成 APNs JWT token
+        /// </summary>
+        /// <returns></returns>
+        private string GetnerateAPNsJWTToken(string oldToken)
+        {
+            var tokenHandler = new JwtSecurityTokenHandler();
+            var iat = ((DateTime.UtcNow.Ticks - new DateTime(1970, 1, 1).Ticks) / TimeSpan.TicksPerSecond);
+
+            // 判断原 token 是否超过 50 分钟,如果未超过,直接返回
+            if (string.IsNullOrWhiteSpace(oldToken) == false)
+            {
+                JwtPayload oldPayload = tokenHandler.ReadJwtToken(oldToken).Payload;
+                var oldIat = oldPayload.Claims.FirstOrDefault(c => c.Type == "iat");
+                if (oldIat != null)
+                {
+                    if (long.TryParse(oldIat.Value, out long oldIatValue) == true)
+                    {
+                        // 两次间隔小于 50 分钟,使用原 token
+                        if ((iat - oldIatValue) < (50 * 60))
+                        {
+                            return oldToken;
+                        }
+                    }
+                }
+            }
+
+            var kid = _configuration["apple:kid"];
+            var securityKey = _configuration["apple:securityKey"].Replace("\n", "");
+            var iss = _configuration["apple:iss"];
+
+            var claims = new Claim[]
+            {
+            new Claim("iss", iss),
+            new Claim("iat", iat.ToString())
+            };
+
+            var eCDsa = ECDsa.Create();
+
+            eCDsa.ImportPkcs8PrivateKey(Convert.FromBase64String(securityKey), out _);
+
+            var key = new ECDsaSecurityKey(eCDsa);
+
+            key.KeyId = kid;
+
+            var signingCredentials = new SigningCredentials(key, SecurityAlgorithms.EcdsaSha256);
+            var jwtHeader = new JwtHeader(signingCredentials);
+            var jwtPayload = new JwtPayload(claims);
+
+            var jwtSecurityToken = new JwtSecurityToken(jwtHeader, jwtPayload);
+
+            APNsService.token = tokenHandler.WriteToken(jwtSecurityToken);
+
+            return APNsService.token;
+        }
+
+        /// <summary>
+        /// 发送推送通知
+        /// </summary>
+        /// <param name="apnsTopic">APP Id</param>
+        /// <param name="deviceToken">设备标识</param>
+        /// <param name="type">通知类型</param>
+        /// <param name="title">标题</param>
+        /// <param name="subtitle">子标题</param>
+        /// <param name="body">通知内容</param>
+        /// <returns></returns>
+        public async Task<string> PushNotification(string apnsTopic, string deviceToken, NotificationType type, string title, string subtitle, string body)
+        {
+            var responseData = FailedAPNsReponseData();
+            var token = this.GetnerateAPNsJWTToken();
+            var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, APNsService.baseUrl + deviceToken)
+            {
+                Headers =
+            {
+                { HeaderNames.Authorization, "bearer " +  token },
+                { "apns-topic", apnsTopic },
+                { "apns-expiration", "0" }
+            },
+                Version = new Version(2, 0)
+            };
+
+            var notContent = new
+            {
+                aps = new
+                {
+                    alert = new
+                    {
+                        title = title,
+                        subtitle = subtitle,
+                        body = body
+                    }
+                }
+            };
+            //var content = new StringContent(JsonSerializerTool.SerializeDefault(notContent), System.Text.Encoding.UTF8, Application.Json);
+
+            var content = new StringContent(System.Text.Json.JsonSerializer.Serialize(notContent));
+
+            httpRequestMessage.Content = content;
+
+            var httpClient = _httpClientFactory.CreateClient();
+            try
+            {
+                var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);
+
+                if (httpResponseMessage.IsSuccessStatusCode)
+                {
+                    responseData.Code = 200;
+                    return System.Text.Json.JsonSerializer.Serialize(responseData);
+                }
+                else
+                {
+                    responseData.Data = httpResponseMessage.StatusCode;
+                    return System.Text.Json.JsonSerializer.Serialize(responseData);
+                }
+            }
+            catch (Exception e)
+            {
+                responseData.Data = e.Message;
+                return System.Text.Json.JsonSerializer.Serialize(responseData);
+            }
+        }
+
+        public APNsReponseData FailedAPNsReponseData()
+        {
+            return new APNsReponseData() { Code = 400, Data = "" };
+        }
+    }
+
+    public class APNsReponseData
+    {
+        public int Code { get; set; } = 0;
+
+        public object Data { get; set; } = "";
+
+    }
+
+   
+}

+ 23 - 0
OASystem/OASystem.Api/OAMethodLib/APNs/IAPNsService.cs

@@ -0,0 +1,23 @@
+namespace OASystem.API.OAMethodLib.APNs
+{
+    public interface IAPNsService
+    {
+        /// <summary>
+        /// 生成 APNs JWT token
+        /// </summary>
+        /// <returns></returns>
+        string GetnerateAPNsJWTToken();
+
+        /// <summary>
+        /// 发送推送通知
+        /// </summary>
+        /// <param name="apnsTopic">APP Id</param>
+        /// <param name="deviceToken">设备标识</param>
+        /// <param name="type">通知类型</param>
+        /// <param name="title">标题</param>
+        /// <param name="subtitle">子标题</param>
+        /// <param name="body">通知内容</param>
+        /// <returns></returns>
+        Task<string> PushNotification(string apnsTopic, string deviceToken, NotificationType type, string title, string subtitle, string body);
+    }
+}

+ 3 - 0
OASystem/OASystem.Api/Program.cs

@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core;
 using Microsoft.Extensions.DependencyInjection.Extensions;
 using OASystem.API.Middlewares;
 using OASystem.API.OAMethodLib;
+using OASystem.API.OAMethodLib.APNs;
 using OASystem.API.OAMethodLib.Hub.Hubs;
 using OASystem.API.OAMethodLib.JuHeAPI;
 using OASystem.API.OAMethodLib.QiYeWeChatAPI;
@@ -339,6 +340,8 @@ builder.Services.AddSingleton<IJobFactory, IOCJobFactory>();
 
 #endregion
 
+builder.Services.AddSingleton<IAPNsService,APNsService>();
+
 #region SignalR
 builder.Services.AddSignalR()
                 .AddJsonProtocol(options =>

+ 356 - 351
OASystem/OASystem.Api/appsettings.json

@@ -1,364 +1,369 @@
 {
-  "ConnectionStrings": {
-    "OA2023DB": "server=132.232.92.186;uid=sa;pwd=Yjx@158291;database=OA2023DB;MultipleActiveResultSets=True;",
-    "OA2014DB": "server=132.232.92.186;uid=sa;pwd=Yjx@158291;database=OA2014;MultipleActiveResultSets=True;"
-  },
-  "JwtSecurityKey": "48d3f4fe770940a1068052f581536b81", //jwt密钥
-  "UseSwagger": "true", //启用Swagger
-  "GroupsConfig": {
-    "AutoCreate": "4",
-    "Leader": "149",
-    "ExBeginDays": "3",
-    "ExEndDays": "30",
-    "DefaultUser": "51",
-    "Boss": "21",
-    "FilterUser": "51,180"
-  },
-  "DeleReminderConfig": {
-    "PhoneNumber": "17380669807,13683474118,18780121225",
-    "Test": "18477317582"
-  },
-  "RateCurrency": [
-    {
-      "CurrencyName": "人民币",
-      "CurrencyCode": "CNY"
-    },
-    {
-      "CurrencyName": "美元",
-      "CurrencyCode": "USD"
-    },
-    {
-      "CurrencyName": "欧元",
-      "CurrencyCode": "EUR"
-    },
-    {
-      "CurrencyName": "港币",
-      "CurrencyCode": "HKD"
-    },
-    {
-      "CurrencyName": "日元",
-      "CurrencyCode": "JPY"
-    },
-    {
-      "CurrencyName": "英镑",
-      "CurrencyCode": "GBP"
-    },
-    {
-      "CurrencyName": "澳大利亚元",
-      "CurrencyCode": "AUD"
-    },
-    {
-      "CurrencyName": "加拿大元",
-      "CurrencyCode": "CAD"
-    },
-    {
-      "CurrencyName": "泰国铢",
-      "CurrencyCode": "THB"
-    },
-    {
-      "CurrencyName": "新加坡元",
-      "CurrencyCode": "SGD"
-    },
-    {
-      "CurrencyName": "瑞士法郎",
-      "CurrencyCode": "CHK"
-    },
-    {
-      "CurrencyName": "丹麦克朗",
-      "CurrencyCode": "DKK"
-    },
-    {
-      "CurrencyName": "澳门元",
-      "CurrencyCode": "MOP"
-    },
-    {
-      "CurrencyName": "林吉特",
-      "CurrencyCode": "MYR"
-    },
-    {
-      "CurrencyName": "挪威克朗",
-      "CurrencyCode": "NOK"
-    },
-    {
-      "CurrencyName": "新西兰元",
-      "CurrencyCode": "NZD"
-    },
-    {
-      "CurrencyName": "卢布",
-      "CurrencyCode": "RUB"
-    },
-    {
-      "CurrencyName": "瑞典克朗",
-      "CurrencyCode": "SEK"
-    },
-    {
-      "CurrencyName": "菲律宾比索",
-      "CurrencyCode": "PHP"
-    },
-    {
-      "CurrencyName": "新台币",
-      "CurrencyCode": "TWD"
-    },
-    {
-      "CurrencyName": "巴西雷亚尔",
-      "CurrencyCode": "BRL"
-    },
-    {
-      "CurrencyName": "韩国元",
-      "CurrencyCode": "ZAR"
-    }
-  ],
-  "ExcelBaseUrl": "http://132.232.92.186:24/",
-  "ExcelBasePath": "D:/FTP/File/OA2023/Office/Excel/",
-  "ExcelFtpPath": "Office/Excel/",
-  "OfficeBaseUrl": "http://132.232.92.186:24/",
-  "OfficeTempBasePath": "D:/FTP/File/OA2023/Office/",
+    "ConnectionStrings": {
+        "OA2023DB": "server=132.232.92.186;uid=sa;pwd=Yjx@158291;database=OA2023DB;MultipleActiveResultSets=True;",
+        "OA2014DB": "server=132.232.92.186;uid=sa;pwd=Yjx@158291;database=OA2014;MultipleActiveResultSets=True;"
+    },
+    "JwtSecurityKey": "48d3f4fe770940a1068052f581536b81", //jwt密钥
+    "UseSwagger": "true", //启用Swagger
+    "GroupsConfig": {
+        "AutoCreate": "4",
+        "Leader": "149",
+        "ExBeginDays": "3",
+        "ExEndDays": "30",
+        "DefaultUser": "51",
+        "Boss": "21",
+        "FilterUser": "51,180"
+    },
+    "apple:securityKey": "MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQglyUl7hjI75YJUVMbZLN6TpkiFzuTXUN+UIjuJA7+y8ugCgYIKoZIzj0DAQehRANCAAS8GR7lKNst4KENCp45OXCMyiytvzK0qdRBrx0l+bMaHjiU+Upfox82G+Xy4wd8hI+0wMDh341aNelqEdYUUx3O",
+    "apple:kid": "RMV7Y4KM9V",
+    "apple:iss": "HKF372JSMK",
+    "apple:pushNotificationServer": "https://api.sandbox.push.apple.com:443/3/device/",
+    "apple:pushNotificationServer_Production": "https://api.push.apple.com:443/3/device/",
+    "DeleReminderConfig": {
+        "PhoneNumber": "17380669807,13683474118,18780121225",
+        "Test": "18477317582"
+    },
+    "RateCurrency": [
+        {
+            "CurrencyName": "人民币",
+            "CurrencyCode": "CNY"
+        },
+        {
+            "CurrencyName": "美元",
+            "CurrencyCode": "USD"
+        },
+        {
+            "CurrencyName": "欧元",
+            "CurrencyCode": "EUR"
+        },
+        {
+            "CurrencyName": "港币",
+            "CurrencyCode": "HKD"
+        },
+        {
+            "CurrencyName": "日元",
+            "CurrencyCode": "JPY"
+        },
+        {
+            "CurrencyName": "英镑",
+            "CurrencyCode": "GBP"
+        },
+        {
+            "CurrencyName": "澳大利亚元",
+            "CurrencyCode": "AUD"
+        },
+        {
+            "CurrencyName": "加拿大元",
+            "CurrencyCode": "CAD"
+        },
+        {
+            "CurrencyName": "泰国铢",
+            "CurrencyCode": "THB"
+        },
+        {
+            "CurrencyName": "新加坡元",
+            "CurrencyCode": "SGD"
+        },
+        {
+            "CurrencyName": "瑞士法郎",
+            "CurrencyCode": "CHK"
+        },
+        {
+            "CurrencyName": "丹麦克朗",
+            "CurrencyCode": "DKK"
+        },
+        {
+            "CurrencyName": "澳门元",
+            "CurrencyCode": "MOP"
+        },
+        {
+            "CurrencyName": "林吉特",
+            "CurrencyCode": "MYR"
+        },
+        {
+            "CurrencyName": "挪威克朗",
+            "CurrencyCode": "NOK"
+        },
+        {
+            "CurrencyName": "新西兰元",
+            "CurrencyCode": "NZD"
+        },
+        {
+            "CurrencyName": "卢布",
+            "CurrencyCode": "RUB"
+        },
+        {
+            "CurrencyName": "瑞典克朗",
+            "CurrencyCode": "SEK"
+        },
+        {
+            "CurrencyName": "菲律宾比索",
+            "CurrencyCode": "PHP"
+        },
+        {
+            "CurrencyName": "新台币",
+            "CurrencyCode": "TWD"
+        },
+        {
+            "CurrencyName": "巴西雷亚尔",
+            "CurrencyCode": "BRL"
+        },
+        {
+            "CurrencyName": "韩国元",
+            "CurrencyCode": "ZAR"
+        }
+    ],
+    "ExcelBaseUrl": "http://132.232.92.186:24/",
+    "ExcelBasePath": "D:/FTP/File/OA2023/Office/Excel/",
+    "ExcelFtpPath": "Office/Excel/",
+    "OfficeBaseUrl": "http://132.232.92.186:24/",
+    "OfficeTempBasePath": "D:/FTP/File/OA2023/Office/",
 
-  "WordBaseUrl": "http://132.232.92.186:24/",
-  "WordBasePath": "D:/FTP/File/OA2023/Office/Word/",
-  "WordFtpPath": "Office/Word/",
+    "WordBaseUrl": "http://132.232.92.186:24/",
+    "WordBasePath": "D:/FTP/File/OA2023/Office/Word/",
+    "WordFtpPath": "Office/Word/",
 
-  "GrpFileBaseUrl": "http://132.232.92.186:24/",
-  "GrpFileBasePath": "D:/FTP/File/OA2023/Office/GrpFile/",
-  "GrpFileFtpPath": "Office/GrpFile/",
+    "GrpFileBaseUrl": "http://132.232.92.186:24/",
+    "GrpFileBasePath": "D:/FTP/File/OA2023/Office/GrpFile/",
+    "GrpFileFtpPath": "Office/GrpFile/",
 
-  "ExcelTempPath": "D:/FTP/File/OA2023/Office/Excel/Template/",
-  "GrpListFileBasePath": "D:/FTP/File/OA2023/Office/GrpFile/GroupList/",
-  "GrpListFileFtpPath": "Office/GrpFile/GroupList/",
-  //D:\FTP\File\OA2023\Office\GrpFile
-  "VisaProgressImageBaseUrl": "http://132.232.92.186:24/",
-  "VisaProgressImageBasePath": "D:/FTP/File/OA2023/Image/Visa/",
-  "VisaProgressImageFtpPath": "Image/Visa/",
+    "ExcelTempPath": "D:/FTP/File/OA2023/Office/Excel/Template/",
+    "GrpListFileBasePath": "D:/FTP/File/OA2023/Office/GrpFile/GroupList/",
+    "GrpListFileFtpPath": "Office/GrpFile/GroupList/",
+    //D:\FTP\File\OA2023\Office\GrpFile
+    "VisaProgressImageBaseUrl": "http://132.232.92.186:24/",
+    "VisaProgressImageBasePath": "D:/FTP/File/OA2023/Image/Visa/",
+    "VisaProgressImageFtpPath": "Image/Visa/",
 
-  "WageSheetExcelBaseUrl": "http://132.232.92.186:24/",
-  "WageSheetExcelFptPath": "D:/FTP/File/OA2023/Office/WageSheetFile/",
+    "WageSheetExcelBaseUrl": "http://132.232.92.186:24/",
+    "WageSheetExcelFptPath": "D:/FTP/File/OA2023/Office/WageSheetFile/",
 
-  "WageSheetTaxExcelBaseUrl": "http://132.232.92.186:24/",
-  "WageSheetTaxExcelFptPath": "D:/FTP/File/OA2023/Office/Excel/WageSheetTaxFile/",
+    "WageSheetTaxExcelBaseUrl": "http://132.232.92.186:24/",
+    "WageSheetTaxExcelFptPath": "D:/FTP/File/OA2023/Office/Excel/WageSheetTaxFile/",
 
-  "CTableCorrelationPageDatas": [
-    {
-      "CTableId": 76, //CtableId 酒店预订
-      "PageIdDatas": [ //页面Ids
-        28
-      ]
-    },
-    {
-      "CTableId": 77, //CtableId  行程
-      "PageIdDatas": [ //页面Id
-      ]
-    },
-    {
-      "CTableId": 79, //CtableId 车/导游地接
-      "PageIdDatas": [ //页面Id
-        30
-      ]
-    },
-    {
-      "CTableId": 80, //CtableId  签证
-      "PageIdDatas": [ //页面Id
-      ]
-    },
-    {
-      "CTableId": 81, //CtableId 邀请/公务活动
-      "PageIdDatas": [ //页面Id
-      ]
-    },
-    {
-      "CTableId": 82, //CtableId 团组客户保险
-      "PageIdDatas": [ //页面Id
-      ]
-    },
-    {
-      "CTableId": 85, //CtableId 机票预订
-      "PageIdDatas": [ //页面Id
-      ]
-    },
-    {
-      "CTableId": 98, //CtableId 其他款项
-      "PageIdDatas": [ //页面Id
-        69
-      ]
-    },
-    {
-      "CTableId": 285, //CtableId 其他款项与收款退还
-      "PageIdDatas": [ //页面Id
-        //69
-      ]
-    },
-    {
-      "CTableId": 751, //CtableId 酒店早餐
-      "PageIdDatas": [ //页面Id
-      ]
-    },
-    {
-      "CTableId": 1015, //CtableId 超支费用
-      "PageIdDatas": [ //页面Id
-        174
-      ]
-    }
-  ],
+    "CTableCorrelationPageDatas": [
+        {
+            "CTableId": 76, //CtableId 酒店预订
+            "PageIdDatas": [ //页面Ids
+                28
+            ]
+        },
+        {
+            "CTableId": 77, //CtableId  行程
+            "PageIdDatas": [ //页面Id
+            ]
+        },
+        {
+            "CTableId": 79, //CtableId 车/导游地接
+            "PageIdDatas": [ //页面Id
+                30
+            ]
+        },
+        {
+            "CTableId": 80, //CtableId  签证
+            "PageIdDatas": [ //页面Id
+            ]
+        },
+        {
+            "CTableId": 81, //CtableId 邀请/公务活动
+            "PageIdDatas": [ //页面Id
+            ]
+        },
+        {
+            "CTableId": 82, //CtableId 团组客户保险
+            "PageIdDatas": [ //页面Id
+            ]
+        },
+        {
+            "CTableId": 85, //CtableId 机票预订
+            "PageIdDatas": [ //页面Id
+            ]
+        },
+        {
+            "CTableId": 98, //CtableId 其他款项
+            "PageIdDatas": [ //页面Id
+                69
+            ]
+        },
+        {
+            "CTableId": 285, //CtableId 其他款项与收款退还
+            "PageIdDatas": [ //页面Id
+                //69
+            ]
+        },
+        {
+            "CTableId": 751, //CtableId 酒店早餐
+            "PageIdDatas": [ //页面Id
+            ]
+        },
+        {
+            "CTableId": 1015, //CtableId 超支费用
+            "PageIdDatas": [ //页面Id
+                174
+            ]
+        }
+    ],
 
-  //消息通知类型
-  "MessageNotificationType": [
-    {
-      "TypeId": 1022, //系统公告
-      "MsgTypeIds": [
-        1 // 公告消息
-      ]
-    },
-    {
-      "TypeId": 1021, //操作通知
-      "MsgTypeIds": [
-        2, // 团组流程管控消息
-        3, // 团组业务操作消息
-        4, // 团组费用审核消息
-        5 // 团组签证进度更新消息
-      ]
-    },
-    {
-      "TypeId": 1020, //任务通知
-      "MsgTypeIds": [
-        6 //任务进度更新消息
-      ]
-    }
-  ],
+    //消息通知类型
+    "MessageNotificationType": [
+        {
+            "TypeId": 1022, //系统公告
+            "MsgTypeIds": [
+                1 // 公告消息
+            ]
+        },
+        {
+            "TypeId": 1021, //操作通知
+            "MsgTypeIds": [
+                2, // 团组流程管控消息
+                3, // 团组业务操作消息
+                4, // 团组费用审核消息
+                5 // 团组签证进度更新消息
+            ]
+        },
+        {
+            "TypeId": 1020, //任务通知
+            "MsgTypeIds": [
+                6 //任务进度更新消息
+            ]
+        }
+    ],
 
 
-  //职位默认页面权限
-  "DefaultPostPageData": [
-    {
-      "DepId": -1, //部门: 公司公共页面
-      "PostPageAuths": [
-        {
-          "PostId": -1,
-          "PageIds": [
-            42 //Page: 日常费用付款申请
-            //16 //Page: 员工资料列表
-          ]
-        }
-      ]
-    },
-    {
-      "DepId": 0, //部门:人事部,信息部,策划部(部门页面一致归类在一起)
-      "PostPageAuths": [
-        {
-          "PostId": 0, //职位:普通员工(除经理主管外/公共页面)
-          "PageIds": [
-            149 //Page: 主页(员工)
-          ]
-        }
-      ]
-    },
-    {
-      "DepId": 1, //部门: 总经办
-      "PostPageAuths": [
-        {
-          "PostId": 3, //职位: 总经理助理
-          "PageIds": [
-            150 //Page: 主页(总助)
-          ]
-        }
-      ]
-    },
-    {
-      "DepId": 6, //部门:市场部
-      "PostPageAuths": [
-        {
-          "PostId": 21, //职位:普通员工(除经理主管外)
-          "PageIds": [
-            //153, //Page: 主页(市场部)
-            149, //Page: 主页(员工)
-            118, //Page: 出入境费用明细
-            168, //Page: 出入境国家三公费用标准
-            89 //Page: 公司客户资料
-          ]
-        }
-      ]
-    },
-    {
-      "DepId": 3, //部门:财务部
-      "PostPageAuths": [
-        {
-          "PostId": 10, //职位:会计
-          "PageIds": [
-            151 //Page: 主页(财务)
-          ]
-        }
-      ]
-    },
-    {
-      "DepId": 7, //国交部
-      "PostPageAuths": [
-        {
-          "PostId": -1, //职位:部门公共页面
-          "PageIds": [
-            //154, //Page: 主页(国交)
-            40, //Page: 其他款项
-            174 //Page: 超支费用
-          ]
-        },
-        {
-          "PostId": 0, //职位:经理/主管
-          "PageIds": [
-            154, //Page: 主页(国交)
-            27, //Page: 团组操作
-            104, //Page: 接团客户名单
-            118, //Page: 出入境费用明细
-            168 //Page: 出入境国家三公费用标准
-          ]
-        },
-        {
-          "PostId": 26, //职位:签证
-          "PageIds": [
-            149, //Page: 主页(员工)
-            31, //Page: 签证费用录入
-            158, //Page: OCR识别
-            32 //Page: 保险录入
-          ]
-        },
-        {
-          "PostId": 27, //职位:商邀
-          "PageIds": [
-            149, //Page: 主页(员工)
-            25, //Page: 邀请资料
-            29, //Page: 邀请公务费用
-            166, //Page: 公务出访
-            167 //Page:导出邀请函
-          ]
-        },
-        {
-          "PostId": 28, //职位:OP
-          "PageIds": [
-            149, //Page: 主页(员工)
-            24, //Page: 导游地接资料
-            30, //Page: 地接费用录入
-            122, //Page: OP行程单
-            111 //Page: 车公司资料
-          ]
-        },
-        {
-          "PostId": 24, //职位:机票
-          "PageIds": [
-            149, //Page: 主页(员工)
-            114, //Page: 机票行程代码录
-            120, //Page: 三字码资料表
-            160, //Page: 代理出票合作方
-            161 //Page: 机票费用录入
-          ]
-        },
-        {
-          "PostId": 25, //职位:酒店
-          "PageIds": [
-            149, //Page: 主页(员工)
-            23, //Page: 酒店资料 
-            28 //Page: 酒店预订
-          ]
+    //职位默认页面权限
+    "DefaultPostPageData": [
+        {
+            "DepId": -1, //部门: 公司公共页面
+            "PostPageAuths": [
+                {
+                    "PostId": -1,
+                    "PageIds": [
+                        42 //Page: 日常费用付款申请
+                        //16 //Page: 员工资料列表
+                    ]
+                }
+            ]
+        },
+        {
+            "DepId": 0, //部门:人事部,信息部,策划部(部门页面一致归类在一起)
+            "PostPageAuths": [
+                {
+                    "PostId": 0, //职位:普通员工(除经理主管外/公共页面)
+                    "PageIds": [
+                        149 //Page: 主页(员工)
+                    ]
+                }
+            ]
+        },
+        {
+            "DepId": 1, //部门: 总经办
+            "PostPageAuths": [
+                {
+                    "PostId": 3, //职位: 总经理助理
+                    "PageIds": [
+                        150 //Page: 主页(总助)
+                    ]
+                }
+            ]
+        },
+        {
+            "DepId": 6, //部门:市场部
+            "PostPageAuths": [
+                {
+                    "PostId": 21, //职位:普通员工(除经理主管外)
+                    "PageIds": [
+                        //153, //Page: 主页(市场部)
+                        149, //Page: 主页(员工)
+                        118, //Page: 出入境费用明细
+                        168, //Page: 出入境国家三公费用标准
+                        89 //Page: 公司客户资料
+                    ]
+                }
+            ]
+        },
+        {
+            "DepId": 3, //部门:财务部
+            "PostPageAuths": [
+                {
+                    "PostId": 10, //职位:会计
+                    "PageIds": [
+                        151 //Page: 主页(财务)
+                    ]
+                }
+            ]
+        },
+        {
+            "DepId": 7, //国交部
+            "PostPageAuths": [
+                {
+                    "PostId": -1, //职位:部门公共页面
+                    "PageIds": [
+                        //154, //Page: 主页(国交)
+                        40, //Page: 其他款项
+                        174 //Page: 超支费用
+                    ]
+                },
+                {
+                    "PostId": 0, //职位:经理/主管
+                    "PageIds": [
+                        154, //Page: 主页(国交)
+                        27, //Page: 团组操作
+                        104, //Page: 接团客户名单
+                        118, //Page: 出入境费用明细
+                        168 //Page: 出入境国家三公费用标准
+                    ]
+                },
+                {
+                    "PostId": 26, //职位:签证
+                    "PageIds": [
+                        149, //Page: 主页(员工)
+                        31, //Page: 签证费用录入
+                        158, //Page: OCR识别
+                        32 //Page: 保险录入
+                    ]
+                },
+                {
+                    "PostId": 27, //职位:商邀
+                    "PageIds": [
+                        149, //Page: 主页(员工)
+                        25, //Page: 邀请资料
+                        29, //Page: 邀请公务费用
+                        166, //Page: 公务出访
+                        167 //Page:导出邀请函
+                    ]
+                },
+                {
+                    "PostId": 28, //职位:OP
+                    "PageIds": [
+                        149, //Page: 主页(员工)
+                        24, //Page: 导游地接资料
+                        30, //Page: 地接费用录入
+                        122, //Page: OP行程单
+                        111 //Page: 车公司资料
+                    ]
+                },
+                {
+                    "PostId": 24, //职位:机票
+                    "PageIds": [
+                        149, //Page: 主页(员工)
+                        114, //Page: 机票行程代码录
+                        120, //Page: 三字码资料表
+                        160, //Page: 代理出票合作方
+                        161 //Page: 机票费用录入
+                    ]
+                },
+                {
+                    "PostId": 25, //职位:酒店
+                    "PageIds": [
+                        149, //Page: 主页(员工)
+                        23, //Page: 酒店资料 
+                        28 //Page: 酒店预订
+                    ]
+                }
+            ]
         }
-      ]
-    }
-  ],
+    ],
 
-  //日付类型Data
-  "Dailypayment": "666,667"
+    //日付类型Data
+    "Dailypayment": "666,667"
 }

+ 5 - 0
OASystem/OASystem.Domain/Dtos/Financial/Fin_GroupExtraCostDto.cs

@@ -106,6 +106,11 @@ namespace OASystem.Domain.Dtos.Financial
         /// 经理确认
         /// </summary>
         public int ManagerConfirm { get; set; }
+
+        /// <summary>
+        /// 商邀主管确认
+        /// </summary>
+        public int SYsupervisorConfirm { get; set; }
     }
 
     public class Fin_GroupExtraCostDto_Search : DtoBase

+ 2 - 0
OASystem/OASystem.Domain/Dtos/PersonnelModule/TreeNode.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
@@ -111,6 +112,7 @@ namespace OASystem.Domain.Dtos.PersonnelModule
 
     public class PerAssessmentSettingOperationDto
     {
+        [Required(ErrorMessage = "项名称不能为空")]
         public string Name { get; set; }
         public decimal AssessmentProportion { get; set; }
         public string AssessmentStandard { get; set; }

+ 15 - 0
OASystem/OASystem.Domain/Dtos/Resource/OfficialActivitiesDto.cs

@@ -1,6 +1,7 @@
 using Microsoft.AspNetCore.Http;
 using System;
 using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
@@ -191,6 +192,20 @@ namespace OASystem.Domain.Dtos.Resource
 
     }
 
+    public class ExportOfficialActivitiesInfoDto
+    {
+        [Required(ErrorMessage = "请输入开始时间")]
+        public string StartTime { get; set; }
+        [Required(ErrorMessage = "请输入结束时间")]
+
+        public string EndTime { get; set; }
+
+        /// <summary>
+        /// -1 All 0 未确认 1 已确认
+        /// </summary>
+        public int State { get; set; }
+    }
+
     public class PostOfficialActivitiesReqReqSampleTipsDto
     {
         public string country { get; set; }

+ 19 - 1
OASystem/OASystem.Domain/Dtos/UserDto/LoginDto.cs

@@ -6,7 +6,7 @@ namespace OASystem.Domain.Dtos.UserDto;
 /// 用户登录
 /// 提交DTO
 /// </summary>
-public class LoginDto:PortDtoBase
+public class LoginDto : PortDtoBase
 {
     /// <summary>
     /// 登录账号
@@ -17,3 +17,21 @@ public class LoginDto:PortDtoBase
     /// </summary>
     public string? Password { get; set; }
 }
+
+/// <summary>
+/// 移动设备Token保存
+/// </summary>
+public class SaveDeviceToken : PortDtoBase
+{
+    public string account { get; set; }
+    public string deviceToken { get; set; }
+}
+
+
+/// <summary>
+/// 移动设备Token获取
+/// </summary>
+public class GetDeviceToken : PortDtoBase
+{
+    public string account { get; set; }
+}

+ 5 - 0
OASystem/OASystem.Domain/Entities/Financial/Fin_GroupExtraCost.cs

@@ -94,5 +94,10 @@ namespace OASystem.Domain.Entities.Financial
         /// 主管确认
         /// </summary>
         public int? SupervisorConfirm { get; set; }
+
+        /// <summary>
+        /// 商邀主管确认
+        /// </summary>
+        public int? SYsupervisorConfirm { get; set; }
     }
 }

+ 27 - 0
OASystem/OASystem.Domain/Entities/System/Sys_DeviceToken.cs

@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OASystem.Domain.Entities.System
+{
+    /// <summary>
+    /// DeviceToken
+    /// </summary>
+    [SugarTable("Sys_DeviceToken")]
+    public class Sys_DeviceToken : EntityBase
+    {
+        /// <summary>
+        /// 工号
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(50)")]
+        public string Number { get; set; }
+
+        /// <summary>
+        /// 设备编号
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "nvarchar(256)")]
+        public string DeviceToken { get; set; }
+    }
+}

+ 6 - 0
OASystem/OASystem.Domain/ViewModels/Financial/Fin_GroupExtraCostView.cs

@@ -33,6 +33,12 @@ namespace OASystem.Domain.ViewModels.Financial
 
         public string SupervisorConfirmStr { get; set; }
 
+        public string Remark { get; set; }
+
+        /// <summary>
+        /// 商邀主管确认
+        /// </summary>
+        public string SYsupervisorConfirmStr { get; set; }
     }
 
     public class Fin_GroupExtraCostDetailView

+ 12 - 0
OASystem/OASystem.Domain/ViewModels/LoginView.cs

@@ -37,3 +37,15 @@ public class LoginView
     /// </summary>
     public int AnnouncementUnReadCount { get; set; }
 }
+
+
+public class DeviceTokenView
+{
+    public int Id { get; set; }
+    /// <summary>
+    /// 分类名称
+    /// </summary>
+    public string Number { get; set; }
+
+    public string DeviceToken { get; set; }
+}

+ 7 - 0
OASystem/OASystem.Domain/ViewModels/Resource/OfficialActivitiesView.cs

@@ -36,6 +36,13 @@ namespace OASystem.Domain.ViewModels.Resource
 
         public string CreateUserName { get; set; }
         public string OfficialFormName { get; set; }
+
+        public string ConfirmTheInvitationStr
+        { 
+            get { 
+                return this.ConfirmTheInvitation == 0 ? "未确认" : "已确认";
+            }
+        }
     }
 
     /// <summary>

+ 18 - 2
OASystem/OASystem.Infrastructure/Repositories/Financial/ForeignReceivablesRepository.cs

@@ -11,6 +11,7 @@ using OASystem.Domain.ViewModels.Groups;
 using OASystem.Infrastructure.Repositories.Groups;
 using OASystem.Infrastructure.Repositories.System;
 using SqlSugar;
+using SqlSugar.Extensions;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -535,7 +536,7 @@ Where ffr.IsDel=0 And ffr.Diid={0}", dto.DiId);
             //币种不同则计算rmb值
             var overspList = _sqlSugar.Queryable<Fin_GroupExtraCost, Grp_CreditCardPayment>
                 ((e, c) => c.CTable == 1015 && c.CId == e.Id && c.IsDel == 0).
-                Where((e, c) => e.IsDel == 0 && e.DiId == dto.DiId && (e.SupervisorConfirm == 1 || e.ManagerConfirm == 1)).
+                Where((e, c) => e.IsDel == 0 && e.DiId == dto.DiId).
                 Select((e, c) => new
                 {
                     e.Price,
@@ -547,7 +548,22 @@ Where ffr.IsDel=0 And ffr.Diid={0}", dto.DiId);
                     e.PriceCurrency,
                     e.Remark,
                 })
-                .ToList();
+                .ToList().
+                Where(x =>
+                {
+                    var count = 0;
+                    var stringArr = new string[] { "SYsupervisorConfirm" , "SupervisorConfirm" , "ManagerConfirm" };
+                    foreach (var item in stringArr)
+                    {
+                        var number = x.GetType()?.GetProperty(item)?.GetValue(x).ObjToInt();
+                        if (number > 0)
+                        {
+                            count++;
+                        }
+                    }
+                    return count > 1;
+
+                }).ToList();
             var overspListGroup = overspList.GroupBy(x => x.PriceCurrency).ToList();
             string foreignReceivablesRemake = string.Empty;
             int count = 1;

+ 32 - 4
OASystem/OASystem.Infrastructure/Repositories/Groups/CarTouristGuideGroundRepository.cs

@@ -868,6 +868,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
 
                     var aMeal = cityPrice.FoodCost.ObjToDecimal() / 2; //一顿三公费用
                     var findSetDataCurr = _sqlSugar.Queryable<Sys_SetData>().First(q => q.Id == dto.toCurr && q.IsDel == 0);
+                    var carCurr = _sqlSugar.Queryable<Sys_SetData>().First(q => q.Id == carTouristList.First().Currency && q.IsDel == 0);
 
                     foreach (var item in arr)
                     {
@@ -910,6 +911,10 @@ namespace OASystem.Infrastructure.Repositories.Groups
                     var exitMealCount = 0;
                     var price = 0M;
                     //var day = 0;
+                    var consumerMealStr = string.Empty;
+                    var consumerMealNumber = 0;
+                    var consumerMealAllPrice = 0.00M;
+
                     while (start <= end)
                     {
                         //查看每日每顿是否存在超支
@@ -919,6 +924,22 @@ namespace OASystem.Infrastructure.Repositories.Groups
                         bool isExistsWan = carTouristList.Where(x => x.SId == mealsDic[989] && x.IsDel == 0 && DateTime.Compare(start, x.DatePrice.ObjToDate()) == 0 && x.Price > 0).Count() == 0;
                         var mealPriceWu = carTouristList.Where(x => x.SId == 93 && x.IsDel == 0 && DateTime.Compare(start, x.DatePrice.ObjToDate()) == 0).Sum(x => x.Price * x.Count);
                         var mealPriceWan = carTouristList.Where(x => x.SId == 989 && x.IsDel == 0 && DateTime.Compare(start, x.DatePrice.ObjToDate()) == 0).Sum(x => x.Price * x.Count);
+                        var mealPriceWuChao = carTouristList.Where(x => x.SId == mealsDic[93] && x.IsDel == 0 && DateTime.Compare(start, x.DatePrice.ObjToDate()) == 0).Sum(x => x.Price * x.Count);
+                        var mealPriceWanChao = carTouristList.Where(x => x.SId == mealsDic[989] && x.IsDel == 0 && DateTime.Compare(start, x.DatePrice.ObjToDate()) == 0).Sum(x => x.Price * x.Count);
+
+                        if (mealPriceWu > 0)
+                        {
+                            consumerMealStr += $"{start.ToString("M/d")}午餐消费{mealPriceWu + mealPriceWuChao}{carCurr?.Name},";
+                            consumerMealNumber++;
+                            consumerMealAllPrice += mealPriceWu + mealPriceWuChao;
+                        }
+
+                        if (mealPriceWan > 0)
+                        {
+                            consumerMealStr += $"{start.ToString("M/d")}晚餐消费{mealPriceWan + mealPriceWanChao}{carCurr?.Name},";
+                            consumerMealNumber++;
+                            consumerMealAllPrice += mealPriceWan + mealPriceWanChao;
+                        }
 
                         if (isExistsWu && isExistsWan)
                         {
@@ -949,7 +970,9 @@ namespace OASystem.Infrastructure.Repositories.Groups
                     {
                         remake = remake.TrimEnd(',');
                         remake += $"财政 {findSetDataCurr?.Name} {aMeal} * {di.VisitPNumber} * {exitMealCount}餐";
-                        int priceInt = (int)Math.Round(price / di.VisitPNumber / exitMealCount);
+                        remake += $",[TuT]{consumerMealStr} 共{consumerMealNumber}顿 {consumerMealAllPrice} {carCurr?.Name}";
+
+                        decimal priceInt = price / di.VisitPNumber / exitMealCount;
                         groupExtraCostsArr.Add(new Fin_GroupExtraCost
                         {
                             Coefficient = 1,
@@ -971,12 +994,15 @@ namespace OASystem.Infrastructure.Repositories.Groups
                         });
                     }
 
+                    var needToConfirm = new int[] { 1088, 1050 }; 
+
                     if (groupExtraCostsArr.Count > 0)
                     {
                         foreach (var item in groupExtraCostsArr)
                         {
-                            var ManagerConfirm = item.PriceDetailType == 1088 ? 0 : 1;
-                            var SupervisorConfirm = item.PriceDetailType == 1088 ? 0 : 1;
+                            var ManagerConfirm = needToConfirm.Contains(item.PriceDetailType) ? 0 : 1;
+                            var SupervisorConfirm = needToConfirm.Contains(item.PriceDetailType) ? 0 : 1;
+                            var SYsupervisorConfirm = needToConfirm.Contains(item.PriceDetailType) ? 0 : 1;
                             var QuerySgin = _sqlSugar.Queryable<Fin_GroupExtraCost>()
                                 .Where(x => x.PriceName == item.PriceName && x.DiId == item.DiId && x.IsDel == 0 && x.PriceDt.ToString("yyyy-MM-dd") == item.PriceDt.ToString("yyyy-MM-dd"))
                                 .First();
@@ -1003,7 +1029,8 @@ namespace OASystem.Infrastructure.Repositories.Groups
                                     costSign = dto.OrbitalPrivateTransfer,
                                     Area = item.Area,
                                     ManagerConfirm = ManagerConfirm,
-                                    SupervisorConfirm = SupervisorConfirm
+                                    SupervisorConfirm = SupervisorConfirm,
+                                    SYsupervisorConfirm = SYsupervisorConfirm
                                 });
                                 _sqlSugar.Insertable<Fin_LocalGuideRelevancyOverspend>(new Fin_LocalGuideRelevancyOverspend
                                 {
@@ -1291,6 +1318,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
             _entity.Area = dto.Area;
             _entity.SupervisorConfirm = dto.SupervisorConfirm;
             _entity.ManagerConfirm = dto.ManagerConfirm;
+            _entity.SYsupervisorConfirm = dto.SYsupervisorConfirm;
 
             _daiRep.BeginTran();
             if (dto.editType == 1)

+ 104 - 0
OASystem/OASystem.Infrastructure/Repositories/Login/DeviceTokenRepository.cs

@@ -0,0 +1,104 @@
+using OASystem.Domain.Dtos.UserDto;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using OASystem.Domain;
+using SqlSugar;
+using OASystem.Domain.Entities.System;
+using EyeSoft.Runtime.InteropServices;
+
+namespace OASystem.Infrastructure.Repositories.Login
+{
+    public class DeviceTokenRepository : BaseRepository<Sys_DeviceToken, DeviceTokenView>
+    {
+        public DeviceTokenRepository(SqlSugarClient sqlSugar) : base(sqlSugar)
+        {
+        }
+
+        public async Task<Result> GetToken(string number)
+        {
+            Result result = new Result() { Code = -2 };
+
+            string sql = string.Format("Select * From Sys_DeviceToken With(NoLock) Where Number = '{0}'", number);
+            Sys_DeviceToken deviceTokenEntity = await GetSingleInfoBySqlWithNolockAsync(sql);
+            if (deviceTokenEntity == null)
+            {
+                result.Code = -2;
+                result.Msg = "获取失败!";
+                result.Data = null;
+            }
+            else
+            {
+
+                DeviceTokenView view = new DeviceTokenView()
+                {
+                    Id = deviceTokenEntity.Id,
+                    DeviceToken = deviceTokenEntity.DeviceToken,
+                    Number = number
+                };
+
+                result.Code = 0;
+                result.Msg = "!";
+                result.Data = view;
+            }
+
+            return result;
+        }
+
+        public async Task<Result> SaveToken(SaveDeviceToken dto)
+        {
+            Result result = new Result() { Code = -2 };
+
+            string sql = string.Format("Select * From Sys_DeviceToken With(NoLock) Where Number = '{0}'", dto.account);
+            Sys_DeviceToken deviceTokenEntity = await GetSingleInfoBySqlWithNolockAsync(sql);
+
+            if (deviceTokenEntity == null)
+            {
+                Sys_DeviceToken ist = new Sys_DeviceToken()
+                {
+                    CreateTime = DateTime.Now,
+                    CreateUserId = 234,
+                    DeleteTime = "",
+                    DeleteUserId = 0,
+                    DeviceToken = dto.deviceToken,
+                    IsDel = 0,
+                    Number = dto.account,
+                    Remark = ""
+                };
+
+                var _AddId = await _sqlSugar.Insertable(ist).ExecuteReturnIdentityAsync();
+                if (_AddId < 0)
+                {
+                    result.Code = -2;
+                    result.Msg = "添加失败!";
+                }
+                else
+                {
+                    result.Code = 0;
+                    result.Msg = "操作成功!";
+                }
+            }
+            else
+            {
+                var change = await _sqlSugar.Updateable<Sys_DeviceToken>()
+                                        .SetColumns(it => it.DeviceToken == dto.deviceToken)
+                                        .Where(it => it.Id == deviceTokenEntity.Id)
+                                        .ExecuteCommandAsync();
+                if (change > 0)
+                {
+                    result.Code = 0;
+                    result.Msg = "操作成功!";
+                }
+                else
+                {
+                    result.Code = -2;
+                    result.Msg = "更新失败!";
+                }
+            }
+
+            return result;
+        }
+    }
+}