Explorar o código

定时任务:
1、团组流程提示、预警 企业微信消息通知 每天早上九点触发

Lyyyi hai 2 días
pai
achega
6dbdcef5bc

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

@@ -1675,11 +1675,12 @@ di.VisitPNumber);
         /// 团组流程节点任务通知
         /// 接收者:审核节点相关人 
         /// </summary>
+        /// <param name="title"></param>
         /// <param name="groupName"></param>
         /// <param name="msgs"></param>
         /// <param name="qwUserIds"></param>
         /// <returns></returns>
-        public static async Task<bool> SendUserMsg_GroupProcNode_Notif(string groupName,List<string> msgs, List<string> qwUserIds)
+        public static async Task<bool> SendUserMsg_GroupProcNode_Notif(string title, string groupName,List<string> msgs, List<string> qwUserIds)
         {
             //消息体 或者 接收人 集合为空,则不发送
             if (!msgs.Any() || !qwUserIds.Any())
@@ -1689,15 +1690,16 @@ di.VisitPNumber);
 
             var msgLabel = string.Join("\n", msgs);
 
-            string resMsg = string.Format(@" ` 团组任务通知 `  
+            string resMsg = string.Format(@" ` {0} `  
 
-> ` {0} `: 
+> ` {1} `: 
 
-{1}
+> {2}
 
->通知时间:<font color='comment'>{2}</font> 
+>通知时间:<font color='comment'>{3}</font> 
 
 [详细信息请前往OA系统查看](http://oa.pan-american-intl.com:4399/)   ",
+title,
 groupName,
 msgLabel,
 DateTime.Now.ToString("yyyy-MM-dd HH:mm"));

+ 0 - 1
OASystem/OASystem.Api/OAMethodLib/QiYeWeChatAPI/QiYeWeChatApiService.cs

@@ -1714,7 +1714,6 @@ namespace OASystem.API.OAMethodLib.QiYeWeChatAPI
                 touser = userIds
             };
 
-
             string url = string.Format("/cgi-bin/message/send?access_token={0}&debug=1", access_Token.access_token);
             var json = System.Text.Json.JsonSerializer.Serialize(reqJson);
             var content = new StringContent(json, Encoding.UTF8, "application/json");

+ 80 - 24
OASystem/OASystem.Api/OAMethodLib/Quartz/Business/GroupProcessNode.cs

@@ -1,5 +1,4 @@
-using OASystem.API.OAMethodLib.QiYeWeChatAPI;
-using OASystem.API.OAMethodLib.QiYeWeChatAPI.AppNotice;
+using OASystem.API.OAMethodLib.QiYeWeChatAPI.AppNotice;
 using OASystem.Domain.Entities.Groups;
 using OASystem.Infrastructure.Repositories.Groups;
 using static OASystem.Infrastructure.Repositories.Groups.ProcessOverviewRepository;
@@ -12,9 +11,30 @@ namespace OASystem.API.OAMethodLib.Quartz.Business
     public static class GroupProcessNode
     {
         private readonly static DelegationInfoRepository _grpDeleRep = AutofacIocManager.Instance.GetService<DelegationInfoRepository>();
-        private static readonly ProcessOverviewRepository _procOverviewRep = AutofacIocManager.Instance.GetService<ProcessOverviewRepository>();
+        private readonly static ProcessOverviewRepository _procOverviewRep = AutofacIocManager.Instance.GetService<ProcessOverviewRepository>();
         //private static readonly IQiYeWeChatApiService _qiYeWeChatApiService = AutofacIocManager.Instance.GetService<IQiYeWeChatApiService>();
 
+        /// <summary>
+        /// 默认通知人用户ID集合 团组流程
+        /// </summary>
+        private readonly static HashSet<string> defaultNotifyUserIds_groupProc = new HashSet<string>
+            {
+                "johnny.yang@pan-american-intl.com",   // 杨俊霄 5
+                "Roy.lei", // 雷怡 208
+                "js"       // 江姗 327
+            };
+
+        /// <summary>
+        /// 默认通知人用户ID集合 费用结算流程
+        /// </summary>
+        private readonly static HashSet<string> defaultNotifyUserIds_feeProc = new HashSet<string>
+            {
+                "johnny.yang@pan-american-intl.com", // 杨俊霄 5
+                "amy.zhu@pan-american-intl.com",     // 朱成梅 22
+                "Roy.lei",          // 雷怡 208
+            };
+
+
         /// <summary>
         /// 企微通知
         /// </summary>
@@ -22,22 +42,17 @@ namespace OASystem.API.OAMethodLib.Quartz.Business
         {
             var currDt = DateTime.Now;
 
-            // 默认通知人
-            var defaultNotifyUserIds = new HashSet<int>
-            {
-                5,   // 雷怡
-                208, // 杨俊霄
-                327  // 江姗
-            };
-            var defaultNotifyQiyeChatUserIds = await _grpDeleRep._sqlSugar.Queryable<Sys_Users>()
-               .Where(x => defaultNotifyUserIds.Contains(x.Id) && x.IsDel == 0)
-               .Select(x => x.QiyeChatUserId)
-               .ToListAsync();
-
             var groupAllNodeInfos = await _procOverviewRep.GetGroupAllProcessNodeInfoAsync();
             if (groupAllNodeInfos == null) return;
 
-            var notifyList = groupAllNodeInfos.FindAll(x => x.PromptTime?.Date == currDt.Date);
+            var notifyList = groupAllNodeInfos.FindAll(x =>
+                x.IsPrompt &&
+                x.PromptTime?.Date == currDt.Date &&
+                (x.GroupType != 248 || // 条件1:非248团组
+                 x.GroupType == 248 && // 条件2:248团组
+                 (x.ProcType == GroupProcessType.Visa ||
+                  x.ProcType == GroupProcessType.Invitation)) // 条件3:只允许签证和商邀
+            );
             if (notifyList == null) return;
 
             // 按照团组ID和流程枚举分组
@@ -58,15 +73,19 @@ namespace OASystem.API.OAMethodLib.Quartz.Business
                 if (info == null) continue;
 
                 var msgs = info.ProcType == GroupProcessType.Invitation
-                   ? group.Value.Select(x => $"- {x.NodeName}开始了。").ToList()
-                    : new List<string> { $"- {info.NodeName}开始了。" };
+                   ? group.Value.Select(x => $"任务:{x.NodeName},开始了。").ToList()
+                    : new List<string> { $"任务:{info.NodeName},开始了。" };
 
                 var qiWeiChatUserIds = new List<string>();
-                qiWeiChatUserIds.AddRange(defaultNotifyQiyeChatUserIds);
+
+                //根据流程 通知不同的人  费用结算 通知财务主管;其他类型 通知国交经理
+                if (info.ProcType == GroupProcessType.FeeSettle) qiWeiChatUserIds.AddRange(defaultNotifyUserIds_feeProc);
+                else qiWeiChatUserIds.AddRange(defaultNotifyUserIds_groupProc);
+
                 qiWeiChatUserIds.AddRange(getPromptPersonQiyeChatUserIds(info.PromptPerson));
 
                 // 企微通知
-                await AppNoticeLibrary.SendUserMsg_GroupProcNode_Notif(info.GroupName, msgs, qiWeiChatUserIds);
+                await AppNoticeLibrary.SendUserMsg_GroupProcNode_Notif("团组任务通知", info.GroupName, msgs, qiWeiChatUserIds);
             }
         }
 
@@ -77,14 +96,51 @@ namespace OASystem.API.OAMethodLib.Quartz.Business
         {
             var currDt = DateTime.Now;
 
-            var groupInfos = await _grpDeleRep._sqlSugar.Queryable<Grp_DelegationInfo>()
-                .Where(x => x.IsDel == 0)
-                .ToListAsync();
+            var groupAllNodeInfos = await _procOverviewRep.GetGroupAllProcessNodeInfoAsync();
+            if (groupAllNodeInfos == null) return;
 
+            var notifyList = groupAllNodeInfos.FindAll(x =>
+                x.IsAlert &&
+                x.AlertTime?.Date == currDt.Date &&
+                (x.GroupType != 248 || // 条件1:非248团组
+                 x.GroupType == 248 && // 条件2:248团组
+                 (x.ProcType == GroupProcessType.Visa ||
+                  x.ProcType == GroupProcessType.Invitation)) // 条件3:只允许签证和商邀
+            );
+            if (notifyList == null) return;
 
+            // 按照团组ID和流程枚举分组
+            var groupedByGroupIdAndProcType = notifyList.GroupBy(x => new { x.GroupId, x.ProcType })
+               .ToDictionary(
+                    group => group.Key,
+                    group => group.ToList()
+                );
 
+            // 提取获取提示人员企业微信用户ID的逻辑
+            Func<List<UserAndQiWeiUserIdView>, List<string>> getPromptPersonQiyeChatUserIds = promptPersons =>
+                promptPersons?.Select(x => x.QiyeChatUserId).ToList() ?? new List<string>();
 
-        }
+            // 按照团分组发送通知
+            foreach (var group in groupedByGroupIdAndProcType)
+            {
+                    var info = group.Value.FirstOrDefault();
+                if (info == null) continue;
 
+                var msgs = info.ProcType == GroupProcessType.Invitation
+                   ? group.Value.Select(x => $"你操作的 {x.NodeName} 任务进度用时已过半,请抓紧时间完成任务!如需帮助,请立即联系上级领导!").ToList()
+                    : new List<string> { $"你操作的 {info.NodeName} 任务进度用时已过半,请抓紧时间完成任务!如需帮助,请立即联系上级领导!" };
+
+                var qiWeiChatUserIds = new List<string>();
+
+                //根据流程 通知不同的人  费用结算 通知财务主管;其他类型 通知国交经理
+                if (info.ProcType == GroupProcessType.FeeSettle) qiWeiChatUserIds.AddRange(defaultNotifyUserIds_feeProc);
+                else qiWeiChatUserIds.AddRange(defaultNotifyUserIds_groupProc);
+
+                qiWeiChatUserIds.AddRange(getPromptPersonQiyeChatUserIds(info.PromptPerson));
+
+                // 企微通知
+                await AppNoticeLibrary.SendUserMsg_GroupProcNode_Notif("团组任务预警",info.GroupName, msgs, qiWeiChatUserIds);
+            }
+        }
     }
 }

+ 5 - 1
OASystem/OASystem.Api/OAMethodLib/Quartz/Jobs/GroupProcessNodeJob.cs

@@ -23,8 +23,12 @@ namespace OASystem.API.OAMethodLib.Quartz.Jobs
 
             try
             {
-                //在此处编写任务业务代码
+                //务业务代码
+                //团组流程节点提示通知-企微通知
                 GroupProcessNode.QiYeWeChatNotifyAsync();
+
+                //团组流程节点预警通知-企微通知
+                GroupProcessNode.QiYeWeChatWarnNotifyAsync();
             }
             catch (Exception ex)
             {

+ 2 - 4
OASystem/OASystem.Api/OAMethodLib/Quartz/QuartzFactory.cs

@@ -64,10 +64,8 @@ namespace QuzrtzJob.Factory
             await _scheduler.ScheduleJob(teamCurrencyJobDetail, teamCurrencyTrigger);
             await _scheduler.ScheduleJob(performanceJobDetail, performanceTrigger);
 
-            //每天早上九点触发
-            //await CreateAndScheduleJob<GroupProcessNodeJob>("job5", "group", CreateTrigger("0 0 9 * * ?"));
-            await CreateAndScheduleJob<GroupProcessNodeJob>("job6", "group", CreateTrigger("30 43 11 * * ?"));
-
+            // 团组流程提示、预警 企业微信消息通知 每天早上九点触发
+            await CreateAndScheduleJob<GroupProcessNodeJob>("job6", "group", CreateTrigger("0 0 9 * * ?"));
 
             return await Task.FromResult("将触发器和任务器绑定到调度器中完成");
         }

+ 1 - 1
OASystem/OASystem.Api/Program.cs

@@ -533,13 +533,13 @@ builder.Services.AddScoped(typeof(DynamicSearchService<>));
 #endregion
 
 #region Quartz
-
 builder.Services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();
 builder.Services.AddSingleton<QuartzFactory>();
 builder.Services.AddSingleton<ALiYunPostMessageJob>();
 builder.Services.AddSingleton<TaskJob>();
 builder.Services.AddSingleton<TaskNewsFeedJob>();
 builder.Services.AddSingleton<PerformanceJob>();
+builder.Services.AddSingleton<GroupProcessNodeJob>();
 //# new business
 builder.Services.AddControllersWithViews();
 builder.Services.AddSingleton<IAPNsService, APNsService>();

+ 145 - 39
OASystem/OASystem.Infrastructure/Repositories/Groups/ProcessOverviewRepository.cs

@@ -526,6 +526,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
         {
             var nodeDetails = new List<GroupProcFullNodeDetails>();
             int groupId = groupInfo.Id;
+            int groupType = groupInfo.TeamDid;
             string groupName = groupInfo.TeamName;
             groupInfo.VisitDate = groupInfo.VisitDate.AddDays(1); //第二天开始计算
 
@@ -543,42 +544,42 @@ namespace OASystem.Infrastructure.Repositories.Groups
                 if (groupInfo.StepOperationTime.HasValue) xy_timeBase1 = groupInfo.StepOperationTime.Value;
             }
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, oa_proc, 1, xy_timeBase1, 4, true, oa_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, oa_proc, 1, xy_timeBase1, 4, true, oa_users)
             );
 
             //2. 7个工作日,所有报批机构前部联系,邀请机构一个国家不少于4家进行重点对接(4家机构中,其中3家机构需有效对接,其中1家可为付费机构备选))
             DateTime? xy_timeBase2 = groupInfo.VisitDate;
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, oa_proc, 2, xy_timeBase2, 7, true, oa_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, oa_proc, 2, xy_timeBase2, 7, true, oa_users)
             );
 
             //3. 10个工作日,根据最新情况,联系公务机构1/3取得回应;邀请机构基本明确。
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, oa_proc, 3, null, 0, true, oa_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, oa_proc, 3, null, 0, true, oa_users)
             );
 
             //4. 正式名单下放后2周内(含非工作日)。如团组前期准备时间已经较长,则按客户要求尽快提供。 加急团组备注特殊情况。
             DateTime? xy_timeBase4 = null;
             if (custInfo != null) xy_timeBase4 = custInfo.CreateTime;
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, oa_proc, 4, xy_timeBase4, 14, false, oa_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, oa_proc, 4, xy_timeBase4, 14, false, oa_users)
             );
 
             //5. 团组出发前,5个工作日完成所有公务确认工作。
             DateTime? xy_timeBase5 = groupInfo.VisitDate.AddDays(-1); //时间倒推,调整回原始出发日期
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, oa_proc, 5, xy_timeBase5, -5, true, oa_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, oa_proc, 5, xy_timeBase5, -5, true, oa_users)
             );
 
             //6. 
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, oa_proc, 6, null, 0, true, oa_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, oa_proc, 6, null, 0, true, oa_users)
             );
 
             //7. 如果需要上传请在团组结束前完成
             DateTime? xy_timeBase7 = groupInfo.VisitEndDate; 
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, oa_proc, 7, xy_timeBase7, 0, true, oa_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, oa_proc, 7, xy_timeBase7, 0, true, oa_users)
             );
             #endregion
 
@@ -591,13 +592,13 @@ namespace OASystem.Infrastructure.Repositories.Groups
 
             //1. 
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, visa_proc, 1, null, 0, true, visa_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, visa_proc, 1, null, 0, true, visa_users)
             );
 
             //2. 按进度实际签证办理落实情况,团组出发前上传票据。
             DateTime? qz_timeBase2 = groupInfo.VisitDate;
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, visa_proc, 2, qz_timeBase2, 7, true, visa_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, visa_proc, 2, qz_timeBase2, 7, true, visa_users)
             );
             #endregion
 
@@ -616,45 +617,45 @@ namespace OASystem.Infrastructure.Repositories.Groups
             }
 
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, air_proc, 1, air_timeBase1, 0, true, air_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, air_proc, 1, air_timeBase1, 0, true, air_users)
             );
 
             //2. 
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, visa_proc, 2, null, 0, true, visa_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, air_proc, 2, null, 0, true, visa_users)
             );
 
             //3. 完成机票采购确认(含预算核对、出票确认等)
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, visa_proc, 3, null, 0, true, visa_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, air_proc, 3, null, 0, true, visa_users)
             );
 
             //4. 
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, visa_proc, 4, null, 0, true, visa_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, air_proc, 4, null, 0, true, visa_users)
             );
 
             //5. 团组出发前2个工作日 
             DateTime? air_timeBase5 = groupInfo.VisitDate;
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, visa_proc, 5, air_timeBase5, -2, true, visa_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, air_proc, 5, air_timeBase5, -2, true, visa_users)
             );
 
             //6.  
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, visa_proc, 6, null, 2, true, visa_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, air_proc, 6, null, 2, true, visa_users)
             );
 
             //7. 机票蓝联打票及上传机票超支费用账单,团组归国后5个工作日内
             DateTime? air_timeBase7 = groupInfo.VisitEndDate;
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, visa_proc, 7, air_timeBase7, 5, true, visa_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, air_proc, 7, air_timeBase7, 5, true, visa_users)
             );
 
             //8.  团组归国后10个工作日内 *按机票报价*0.999折扣出具机票报销蓝联、行程单及机票说明
             DateTime? air_timeBase8 = groupInfo.VisitEndDate;
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, visa_proc, 8, air_timeBase8, 10, true, visa_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, air_proc, 8, air_timeBase8, 10, true, visa_users)
             );
             #endregion
 
@@ -672,63 +673,63 @@ namespace OASystem.Infrastructure.Repositories.Groups
                 if (groupInfo.StepOperationTime.HasValue) hotel_timeBase1 = groupInfo.StepOperationTime.Value;
             }
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, hotel_proc, 1, hotel_timeBase1, 2, true, hotel_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, hotel_proc, 1, hotel_timeBase1, 2, true, hotel_users)
             );
 
             //2. 
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, hotel_proc, 2, null, 0, true, hotel_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, hotel_proc, 2, null, 0, true, hotel_users)
             );
 
             //3. 
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, hotel_proc, 3, null, 0, true, hotel_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, hotel_proc, 3, null, 0, true, hotel_users)
             );
 
             //4. 团组出发前2个工作日
             DateTime? hotel_timeBase4 = groupInfo.VisitDate;
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, hotel_proc, 4, hotel_timeBase4, -2, true, hotel_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, hotel_proc, 4, hotel_timeBase4, -2, true, hotel_users)
             );
 
             //5. 团组结束后5个工作日内 
             DateTime? hotel_timeBase5 = groupInfo.VisitEndDate;
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, hotel_proc, 5, hotel_timeBase5, 5, true, hotel_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, hotel_proc, 5, hotel_timeBase5, 5, true, hotel_users)
             );
 
             #endregion
 
             #region 地接
 
-            var dj_proc = GroupProcessType.LocalGuide;
+            var localGuide_proc = GroupProcessType.LocalGuide;
 
             //用户可操作权限
-            var dj_users = NodeOpUserTpl(users)[dj_proc];
+            var localGuide_users = NodeOpUserTpl(users)[localGuide_proc];
 
             //1. 机票行程代码最后一段录入后1个工作日内。
             DateTime? dj_timeBase1 = null;
             if (airTripCodeInfo != null) dj_timeBase1 = airTripCodeInfo.CreateTime;
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, dj_proc, 1, dj_timeBase1, 1, true, dj_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, localGuide_proc, 1, dj_timeBase1, 1, true, localGuide_users)
             );
 
             //2. 团组出行前20个工作日
             DateTime? dj_timeBase2 = groupInfo.VisitDate;
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, dj_proc, 2, dj_timeBase2, -20, true, dj_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, localGuide_proc, 2, dj_timeBase2, -20, true, localGuide_users)
             );
 
             //3. 上一步往后3个工作日内
             DateTime? dj_timeBase3 = groupInfo.VisitDate;
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, dj_proc, 3, dj_timeBase3, -17, true, dj_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, localGuide_proc, 3, dj_timeBase3, -17, true, localGuide_users)
             );
 
             //4. 上一步往后2个工作日内
             DateTime? dj_timeBase4 = groupInfo.VisitDate;
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, dj_proc, 4, dj_timeBase4, -15, true, dj_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, localGuide_proc, 4, dj_timeBase4, -15, true, localGuide_users)
             );
 
             //5. 1.制定最终《行程单》及《出行手册》 \r\n2. 倒推表里开行前会 -3天。
@@ -741,48 +742,48 @@ namespace OASystem.Infrastructure.Repositories.Groups
                 }
             }
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, dj_proc, 5, dj_timeBase5, -3, true, dj_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, localGuide_proc, 5, dj_timeBase5, -3, true, localGuide_users)
             );
 
             //6. 
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, dj_proc, 6, null, 0, true, dj_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, localGuide_proc, 6, null, 0, true, localGuide_users)
             );
 
             //7. 团组归国后5个工作日内) *上传最终报批行程,确定城市间交通最终版报价分配;地接账单(清楚标注超时及其他项超支费用)、地接交通费用原始票据、城市间交通明细表;
             DateTime? dj_timeBase7 = groupInfo.VisitEndDate;
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, dj_proc, 7, dj_timeBase7, 5, true, dj_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, localGuide_proc, 7, dj_timeBase7, 5, true, localGuide_users)
             );
 
             #endregion
 
             #region 费用计算
 
-            var fyjs_proc = GroupProcessType.FeeSettle;
+            var feeSettle_proc = GroupProcessType.FeeSettle;
 
             //用户可操作权限
-            var fyjsfyjs_users = NodeOpUserTpl(users)[fyjs_proc];
+            var feeSettle_users = NodeOpUserTpl(users)[feeSettle_proc];
 
             //1.
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, fyjs_proc, 1, null, 0, true, fyjsfyjs_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, feeSettle_proc, 1, null, 0, true, feeSettle_users)
             );
 
             //2. 
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, fyjs_proc, 2, null, 0, true, fyjsfyjs_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, feeSettle_proc, 2, null, 0, true, feeSettle_users)
             );
 
             //3. 团组归国后12个工作日内
             DateTime? fyjs_timeBase3 = groupInfo.VisitEndDate;
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, fyjs_proc, 3, fyjs_timeBase3, 12, true, fyjsfyjs_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, feeSettle_proc, 3, fyjs_timeBase3, 12, true, feeSettle_users)
             );
 
             //4.
             nodeDetails.Add(
-                GroupProcFullNodeDetails.Create(groupId, groupName, fyjs_proc, 4, null, 0, true, fyjsfyjs_users)
+                GroupProcFullNodeDetails.Create(groupId, groupName, groupType, feeSettle_proc, 4, null, 0, true, feeSettle_users)
             );
 
             #endregion
@@ -802,7 +803,15 @@ namespace OASystem.Infrastructure.Repositories.Groups
         {
             var processs = new List<GroupProcFullNodeDetails>();
 
-            var groupInfos = await _sqlSugar.Queryable<Grp_DelegationInfo>().Where(x => x.IsDel == 0).ToListAsync();
+            var groupTypeIds = new List<int>() {
+                38,    // 政府团
+                39,    // 企业团
+                40,    // 散客团
+                1048,  // 高校团
+                248,   // 非团组
+            };
+
+            var groupInfos = await _sqlSugar.Queryable<Grp_DelegationInfo>().Where(x => x.IsDel == 0 && groupTypeIds.Contains(x.TeamDid)).ToListAsync();
 
             if (groupInfos.Any())
             {
@@ -987,6 +996,8 @@ namespace OASystem.Infrastructure.Repositories.Groups
 
             public string GroupName { get; set; }
 
+            public int GroupType { get; set; }
+
             /// <summary>
             /// 流程类型
             /// </summary>
@@ -1230,6 +1241,26 @@ namespace OASystem.Infrastructure.Repositories.Groups
             /// </summary>
             public bool IsWorkday { get; set; } = false;
 
+            /// <summary>
+            /// 是否提示(包含条件检查)
+            /// </summary>
+            public bool IsPrompt
+            {
+                get
+                {
+                    // 基础检查:提示时间不为空
+                    if (!PromptTime.HasValue)
+                        return false;
+
+                    // 额外条件
+                    // 提示时间不能是未来时间
+                    if (PromptTime > DateTime.Now)
+                        return false;
+
+                    return true;
+                }
+            }
+
             /// <summary>
             /// 提示时间
             /// </summary>
@@ -1254,6 +1285,80 @@ namespace OASystem.Infrastructure.Repositories.Groups
                 }
             }
 
+            /// <summary>
+            /// 是否预警(基于时间计算规则)
+            /// 业务规则:
+            /// 1:商邀流程只在第三个节点预警
+            /// 2:预警时间 ≤ 提示时间(当两者都存在时)
+            /// </summary>
+            public bool IsAlert
+            {
+                get
+                {
+                    // 基本规则:有预警时间
+                    if (!AlertTime.HasValue)
+                        return false;
+
+                    // 业务规则1:商邀流程只在第三个节点预警
+                    if (ProcType == GroupProcessType.Invitation && NodeOrder != 3)
+                    {
+                        return false; // 商邀流程非第三个节点,不预警
+                    }
+
+                    // 业务规则2:预警时间 ≤ 提示时间(当两者都存在时)
+                    if (PromptTime.HasValue && AlertTime.Value > PromptTime.Value)
+                        return false;
+
+                    return true;
+                }
+            }
+
+            /// <summary>
+            /// 预警时间(基于提示时间计算)
+            /// 规则:
+            /// 1. Days > 0:基准时间向前推(未来)
+            /// 2. Days < 0:基准时间向后推(过去)
+            /// 3. Days = 0:不计算预警时间
+            /// 4. |Days| = 1:预警时间等于提示时间
+            /// 5. |Days| > 1:预警天数 = 提示天数的一半(向下取整)
+            /// </summary>
+            public DateTime? AlertTime
+            {
+                get
+                {
+                    if (!PromptTimeBase.HasValue || Days == 0)
+                        return null;
+
+                    // 获取天数的绝对值用于计算比例
+                    int absDays = Math.Abs(Days);
+                    int actualDays;
+
+                    if (absDays == 1)
+                    {
+                        // |Days| = 1 时,预警时间等于提示时间
+                        actualDays = Days; // 保持原方向(正/负)
+                    }
+                    else
+                    {
+                        // |Days| > 1 时,预警天数 = 提示天数的一半
+                        int alertDays = Math.Max(1, absDays / 2);
+
+                        // 保持原方向(正/负),但天数是原天数的一半
+                        actualDays = (Days > 0 ? alertDays : -alertDays);
+                    }
+
+                    // 统一计算逻辑
+                    if (IsWorkday)
+                    {
+                        return AddWeekdays(PromptTimeBase.Value, actualDays);
+                    }
+                    else
+                    {
+                        return PromptTimeBase.Value.AddDays(actualDays);
+                    }
+                }
+            }
+
             /// <summary>
             /// 提示人
             /// </summary>
@@ -1261,12 +1366,13 @@ namespace OASystem.Infrastructure.Repositories.Groups
 
             public GroupProcFullNodeDetails() { }
 
-            public static GroupProcFullNodeDetails Create(int groupId, string groupName, GroupProcessType procType, int nodeOrder, DateTime? promptTimeBase,
+            public static GroupProcFullNodeDetails Create(int groupId, string groupName, int groupType, GroupProcessType procType, int nodeOrder, DateTime? promptTimeBase,
                int days, bool isWorkday, List<UserAndQiWeiUserIdView> promptPerson)
             {
                 return new GroupProcFullNodeDetails
                 {
                     GroupId = groupId,
+                    GroupType = groupType,
                     GroupName = groupName,
                     ProcType = procType,
                     NodeOrder = nodeOrder,