Lyyyi 8 luni în urmă
părinte
comite
cb8483651d

+ 49 - 0
OASystem/OASystem.Api/OAMethodLib/Quartz/Business/GroupProcessNode.cs

@@ -0,0 +1,49 @@
+using OASystem.API.OAMethodLib.QiYeWeChatAPI;
+using OASystem.Domain.Entities.Groups;
+using OASystem.Infrastructure.Repositories.Groups;
+
+namespace OASystem.API.OAMethodLib.Quartz.Business
+{
+    /// <summary>
+    /// 团组流程节点提示通知
+    /// </summary>
+    public static class GroupProcessNode
+    {
+        private readonly static DelegationInfoRepository _grpDeleRep = AutofacIocManager.Instance.GetService<DelegationInfoRepository>();
+        private static readonly ProcessOverviewRepository _procOverviewRep = AutofacIocManager.Instance.GetService<ProcessOverviewRepository>();
+        private static readonly IQiYeWeChatApiService _qiYeWeChatApiService = AutofacIocManager.Instance.GetService<IQiYeWeChatApiService>();
+
+        /// <summary>
+        /// 企微通知
+        /// </summary>
+        public static async void QiYeWeChatNotifyAsync() 
+        {
+            var currDt = DateTime.Now;
+
+            var groupInfos = await _grpDeleRep._sqlSugar.Queryable<Grp_DelegationInfo>()
+                .Where(x => x.IsDel == 0)
+                .ToListAsync();
+
+
+
+
+        }
+
+        /// <summary>
+        /// 企微预警通知
+        /// </summary>
+        public static async void QiYeWeChatWarnNotifyAsync()
+        {
+            var currDt = DateTime.Now;
+
+            var groupInfos = await _grpDeleRep._sqlSugar.Queryable<Grp_DelegationInfo>()
+                .Where(x => x.IsDel == 0)
+                .ToListAsync();
+
+
+
+
+        }
+
+    }
+}

+ 0 - 24
OASystem/OASystem.Api/OAMethodLib/Quartz/Business/GrpNodeAlert.cs

@@ -1,24 +0,0 @@
-using OASystem.API.OAMethodLib.QiYeWeChatAPI;
-using OASystem.Infrastructure.Repositories.Groups;
-
-namespace OASystem.API.OAMethodLib.Quartz.Business
-{
-    /// <summary>
-    /// 团组流程节点预警
-    /// </summary>
-    public static class GrpNodeAlert
-    {
-        private readonly static DelegationInfoRepository _grpDeleRep = AutofacIocManager.Instance.GetService<DelegationInfoRepository>();
-        private static readonly IQiYeWeChatApiService _qiYeWeChatApiService = AutofacIocManager.Instance.GetService<IQiYeWeChatApiService>();
-
-        /// <summary>
-        /// 商邀流程节点预警消息推送
-        /// 
-        /// </summary>
-        public static async void PostMessage() 
-        {
-
-        }
-
-    }
-}

+ 893 - 293
OASystem/OASystem.Infrastructure/Repositories/Groups/ProcessOverviewRepository.cs

@@ -3,12 +3,15 @@ using AutoMapper;
 using EyeSoft.Extensions;
 using Microsoft.Extensions.Logging;
 using Newtonsoft.Json;
+using NPOI.SS.Formula.Functions;
 using OASystem.Domain;
 using OASystem.Domain.Dtos.Groups;
 using OASystem.Domain.Entities.Groups;
 using OASystem.Domain.Entities.Resource;
 using OASystem.Domain.ViewModels.Groups;
+using OASystem.Domain.ViewModels.Statistics;
 using Org.BouncyCastle.Asn1.X500;
+using System;
 using System.Diagnostics;
 using System.Drawing;
 using System.Reflection;
@@ -54,70 +57,49 @@ namespace OASystem.Infrastructure.Repositories.Groups
                 .ToListAsync();
             if (existingProcesses.Any()) return processs;
 
-            var users = await _sqlSugar.Queryable<Sys_Users>()
-                .LeftJoin<Sys_Company>((u, c) => u.CompanyId == c.Id)
-                .LeftJoin<Sys_Department>((u, c, d) => u.DepId == d.Id)
-                .LeftJoin<Sys_JobPost>((u, c, d, jp) => u.JobPostId == jp.Id)
-                .Where((u, c, d, jp) => u.IsDel == 0)
-                .Select((u, c, d, jp) => new {
-                    u.Id,
-                    u.CnName,
-                    u.CompanyId,
-                    c.CompanyName,
-                    u.DepId,
-                    d.DepName,
-                    u.JobPostId,
-                    jp.JobName,
-                })
-                .ToListAsync();
+            //基础数据
+            var users = await GetUersAsync();
 
-            var defaultJobNames = new List<string>()
-            {
-                "OP主管"
-            };
+            var custInfo = _sqlSugar.Queryable<Grp_TourClientList>()
+                .Where(c => c.DiId == groupId && c.IsDel == 0)
+                .OrderByDescending(c => c.CreateTime)
+                .First();
 
-            #region 商邀报批流程
+            var airTripCodeInfo = _sqlSugar.Queryable<Air_TicketBlackCode>()
+                .Where(x => x.IsDel == 0 && x.DiId == groupId)
+                .OrderByDescending(x => x.CreateTime)
+                .First();
 
-            string oaNode1Tips = NodeTipsMsg(groupInfo, GroupProcessType.Invitation, 1);
-            string oaNode2Tips = NodeTipsMsg(groupInfo, GroupProcessType.Invitation, 2);
-            string oaNode3Tips = NodeTipsMsg(groupInfo, GroupProcessType.Invitation, 3);
-            string oaNode4Tips = NodeTipsMsg(groupInfo, GroupProcessType.Invitation, 4);
-            string oaNode5Tips = NodeTipsMsg(groupInfo, GroupProcessType.Invitation, 5);
-            string oaNode6Tips = NodeTipsMsg(groupInfo, GroupProcessType.Invitation, 6);
-            string oaNode7Tips = NodeTipsMsg(groupInfo, GroupProcessType.Invitation, 7);
+            var backListInfo = _sqlSugar.Queryable<Grp_InvertedList>().Where(x => x.DiId == groupId && x.IsDel == 0).First();
 
-            //节点可操作用户列表
-            var oaJobNames = new List<string>() {
-                "商邀主管",
-                "商邀"
-            };
-            var oaNodeOpUsers = users.Where(u =>
-                u.JobName != null && oaJobNames.Contains(u.JobName)
-            ).Select(u => u.Id)
-            .ToList();
+            #region 商邀报批流程
+            var oa_proc = GroupProcessType.Invitation;
+            var oaNode1 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, oa_proc, 1);
+            var oaNode2 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, oa_proc, 2);
+            var oaNode3 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, oa_proc, 3);
+            var oaNode4 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, oa_proc, 4);
+            var oaNode5 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, oa_proc, 5);
+            var oaNode6 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, oa_proc, 6);
+            var oaNode7 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, oa_proc, 7);
 
             processs.Add(
-                Grp_ProcessOverview.Create(groupId, 1, GroupProcessType.Invitation, ProcessStatus.InProgress, currUserId,
+                Grp_ProcessOverview.Create(groupId, 1, oa_proc, ProcessStatus.InProgress, currUserId,
                 new List<Grp_ProcessNode>()
                     {
-                        Grp_ProcessNode.Create(1, "初期报批文字材料",oaNode1Tips,ProcessStatus.InProgress, true,false,false,false,currUserId,oaNodeOpUsers),
-                        Grp_ProcessNode.Create(2, "第一轮对接",oaNode2Tips, ProcessStatus.InProgress, false,false,false,false,currUserId,oaNodeOpUsers),
-                        Grp_ProcessNode.Create(3, "第二轮对接",oaNode3Tips,ProcessStatus.InProgress, false,false,false,false, currUserId ,oaNodeOpUsers),
-                        Grp_ProcessNode.Create(4, "取得邀请函",oaNode4Tips,ProcessStatus.InProgress, false,false,false,false, currUserId,oaNodeOpUsers),
-                        Grp_ProcessNode.Create(5, "公务等事项确认",oaNode5Tips,ProcessStatus.InProgress, false,false,false,true, currUserId,oaNodeOpUsers),
-                        Grp_ProcessNode.Create(6, "公务邀请数据有效录入",oaNode6Tips,ProcessStatus.InProgress, false,false,false,true, currUserId,oaNodeOpUsers),
-                        Grp_ProcessNode.Create(7, "文件上传",oaNode7Tips,ProcessStatus.InProgress, false,false,true,false, currUserId,oaNodeOpUsers),
+                        Grp_ProcessNode.Create(1, oaNode1.NodeName,oaNode1.NodeTips,ProcessStatus.InProgress, true,false,false,false,currUserId,oaNode1.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(2, oaNode2.NodeName,oaNode2.NodeTips, ProcessStatus.InProgress, false,false,false,false,currUserId,oaNode2.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(3, oaNode3.NodeName,oaNode3.NodeTips,ProcessStatus.InProgress, false,false,false,false, currUserId ,oaNode3.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(4, oaNode4.NodeName,oaNode4.NodeTips,ProcessStatus.InProgress, false,false,false,false, currUserId,oaNode4.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(5, oaNode5.NodeName,oaNode5.NodeTips,ProcessStatus.InProgress, false,false,false,true, currUserId,oaNode5.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(6, oaNode6.NodeName,oaNode6.NodeTips,ProcessStatus.InProgress, false,false,false,true, currUserId,oaNode6.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(7, oaNode7.NodeName,oaNode7.NodeTips,ProcessStatus.InProgress, false,false,true,false, currUserId,oaNode7.PromptPerson.Select(x => x.Id).ToList()),
                     }
                 ));
             #endregion
 
             #region 签证流程
 
-            //节点可操作用户列表
-            var visaNodeOpUsers = users.Where(u =>
-                u.JobName != null && u.JobName.Contains("签证")
-            ).Select(u => u.Id).ToList();
-
+            var visa_proc = GroupProcessType.Visa;
             //单独处理签证流程节点
             var visaNodes = new List<Grp_ProcessNode>();
 
@@ -129,109 +111,86 @@ namespace OASystem.Infrastructure.Repositories.Groups
                     visaDefualtNodes.Add(VisaProcessNode.Info(i, visaCountries[i - 1].ToString()));
                 }
 
-                string visaNode1Tips = NodeTipsMsg(groupInfo, GroupProcessType.Visa, 1);
-                string visaNode2Tips = NodeTipsMsg(groupInfo, GroupProcessType.Visa, 2);
-                visaNodes.Add(Grp_ProcessNode.Create(1, "签证信息", visaNode1Tips, ProcessStatus.InProgress, true, false, false, false, currUserId, visaNodeOpUsers, JsonConvert.SerializeObject(visaDefualtNodes)));
-                visaNodes.Add(Grp_ProcessNode.Create(2, "票据上传(明细表、费用票据、保单及超支费用账单)", visaNode2Tips, ProcessStatus.InProgress, false, false, true, false, currUserId, oaNodeOpUsers));
+                var visaNode1 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, visa_proc, 1);
+                var visaNode2 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, visa_proc, 2);
+                visaNodes.Add(Grp_ProcessNode.Create(1, visaNode1.NodeName, visaNode1.NodeTips, ProcessStatus.InProgress, true, false, false, false, currUserId, visaNode1.PromptPerson.Select(x => x.Id).ToList(), JsonConvert.SerializeObject(visaDefualtNodes)));
+                visaNodes.Add(Grp_ProcessNode.Create(2, visaNode2.NodeName, visaNode2.NodeTips, ProcessStatus.InProgress, false, false, true, false, currUserId, visaNode2.PromptPerson.Select(x => x.Id).ToList()));
             }
 
-            processs.Add(Grp_ProcessOverview.Create(groupId, 2, GroupProcessType.Visa, ProcessStatus.UnStarted, currUserId, visaNodes));
+            processs.Add(Grp_ProcessOverview.Create(groupId, 2, visa_proc, ProcessStatus.UnStarted, currUserId, visaNodes));
             #endregion
 
             #region 机票流程
-            string airNode1Tips = NodeTipsMsg(groupInfo, GroupProcessType.AirTicket, 1);
-            string airNode2Tips = NodeTipsMsg(groupInfo, GroupProcessType.AirTicket, 2);
-            string airNode3Tips = NodeTipsMsg(groupInfo, GroupProcessType.AirTicket, 3);
-            string airNode4Tips = NodeTipsMsg(groupInfo, GroupProcessType.AirTicket, 4);
-            string airNode5Tips = NodeTipsMsg(groupInfo, GroupProcessType.AirTicket, 5);
-            string airNode6Tips = NodeTipsMsg(groupInfo, GroupProcessType.AirTicket, 6);
-            string airNode7Tips = NodeTipsMsg(groupInfo, GroupProcessType.AirTicket, 7);
-            string airNode8Tips = NodeTipsMsg(groupInfo, GroupProcessType.AirTicket, 8);
-
-            //节点可操作用户列表
-            var airNodeOpJobNames = new List<string>() {
-                "机票"
-            };
-            airNodeOpJobNames.AddRange(defaultJobNames);
-
-            var airNodeOpUsers = users.Where(u =>
-                u.JobName != null && airNodeOpJobNames.Contains(u.JobName)
-            ).Select(u => u.Id).ToList();
+            var air_proc = GroupProcessType.AirTicket;
+            var airNode1 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, air_proc, 1);
+            var airNode2 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, air_proc, 2);
+            var airNode3 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, air_proc, 3);
+            var airNode4 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, air_proc, 4);
+            var airNode5 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, air_proc, 5);
+            var airNode6 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, air_proc, 6);
+            var airNode7 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, air_proc, 7);
+            var airNode8 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, air_proc, 8);
 
             processs.Add(
-                Grp_ProcessOverview.Create(groupId, 3, GroupProcessType.AirTicket, ProcessStatus.InProgress, currUserId,
+                Grp_ProcessOverview.Create(groupId, 3, air_proc, ProcessStatus.InProgress, currUserId,
                 new List<Grp_ProcessNode>()
                     {
-                        Grp_ProcessNode.Create(1, "初步拟定航程方案及价格", airNode1Tips, ProcessStatus.InProgress, true,false,false,false, currUserId ,airNodeOpUsers),
-                        Grp_ProcessNode.Create(2, "机票占位、续位", airNode2Tips, ProcessStatus.InProgress, false,false,false,false,currUserId,airNodeOpUsers ),
-                        Grp_ProcessNode.Create(3, "完成机票采购确认", airNode3Tips, ProcessStatus.InProgress,false,false,false,false, currUserId, airNodeOpUsers),
-                        Grp_ProcessNode.Create(4, "进行出票操作并核查信息", airNode4Tips, ProcessStatus.InProgress, false,false,false,false, currUserId, airNodeOpUsers),
-                        Grp_ProcessNode.Create(5, "机票已出", airNode5Tips, ProcessStatus.InProgress, false,false,false,false, currUserId, airNodeOpUsers),
-                        Grp_ProcessNode.Create(6, "完成机票选座", airNode6Tips, ProcessStatus.InProgress, false,false,false,false,currUserId, airNodeOpUsers),
-                        Grp_ProcessNode.Create(7, "票据上传(机票超支费用账单)", airNode7Tips, ProcessStatus.InProgress, false,false,true,false, currUserId,airNodeOpUsers),
-                        Grp_ProcessNode.Create(8, "票据上传", airNode8Tips, ProcessStatus.InProgress, false,false,true,false, currUserId, airNodeOpUsers)
+                        Grp_ProcessNode.Create(1, airNode1.NodeName, airNode1.NodeTips, ProcessStatus.InProgress, true,false,false,false, currUserId ,airNode1.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(2, airNode2.NodeName, airNode2.NodeTips, ProcessStatus.InProgress, false,false,false,false,currUserId ,airNode2.PromptPerson.Select(x => x.Id).ToList() ),
+                        Grp_ProcessNode.Create(3, airNode3.NodeName, airNode3.NodeTips, ProcessStatus.InProgress, false,false,false,false, currUserId ,airNode3.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(4, airNode4.NodeName, airNode4.NodeTips, ProcessStatus.InProgress, false,false,false,false, currUserId ,airNode4.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(5, airNode5.NodeName, airNode5.NodeTips, ProcessStatus.InProgress, false,false,false,false, currUserId ,airNode5.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(6, airNode6.NodeName, airNode6.NodeTips, ProcessStatus.InProgress, false,false,false,false,currUserId ,airNode6.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(7, airNode7.NodeName, airNode7.NodeTips, ProcessStatus.InProgress, false,false,true,false, currUserId ,airNode7.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(8, airNode8.NodeName, airNode8.NodeTips, ProcessStatus.InProgress, false,false,true,false, currUserId ,airNode8.PromptPerson.Select(x => x.Id).ToList())
                     }
                 )
              );
             #endregion
 
             #region 酒店流程
-            string hotelNode1Tips = NodeTipsMsg(groupInfo, GroupProcessType.Hotel, 1);
-            string hotelNode2Tips = NodeTipsMsg(groupInfo, GroupProcessType.Hotel, 2);
-            string hotelNode3Tips = NodeTipsMsg(groupInfo, GroupProcessType.Hotel, 3);
-            string hotelNode4Tips = NodeTipsMsg(groupInfo, GroupProcessType.Hotel, 4);
-            string hotelNode5Tips = NodeTipsMsg(groupInfo, GroupProcessType.Hotel, 5);
-
-            //节点可操作用户列表
-            var hotelNodeOpJobNames = new List<string>() { "酒店" };
-            hotelNodeOpJobNames.AddRange(defaultJobNames);
-
-            var hotelNodeOpUsers = users.Where(u =>
-                u.JobName != null && hotelNodeOpJobNames.Contains(u.JobName)
-            ).Select(u => u.Id).ToList();
+            var hotel_proc = GroupProcessType.Hotel;
+            var hotelNode1 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, hotel_proc, 1);
+            var hotelNode2 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, hotel_proc, 2);
+            var hotelNode3 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, hotel_proc, 3);
+            var hotelNode4 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, hotel_proc, 4);
+            var hotelNode5 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, hotel_proc, 5);
 
             processs.Add(
-                Grp_ProcessOverview.Create(groupId, 4, GroupProcessType.Hotel, ProcessStatus.InProgress, currUserId,
+                Grp_ProcessOverview.Create(groupId, 4, hotel_proc, ProcessStatus.InProgress, currUserId,
                      new List<Grp_ProcessNode>()
                     {
-                        Grp_ProcessNode.Create(1, "按照预算,询价、比价、谈价", hotelNode1Tips, ProcessStatus.InProgress, true, false, false, false, currUserId, hotelNodeOpUsers),
-                        Grp_ProcessNode.Create(2, "获取酒店确认函与入住名单核对", hotelNode2Tips, ProcessStatus.InProgress, false, false, false,false, currUserId , hotelNodeOpUsers),
-                        Grp_ProcessNode.Create(3, "预订酒店并录入OA", hotelNode3Tips, ProcessStatus.InProgress,false, false, false,false,currUserId , hotelNodeOpUsers),
-                        Grp_ProcessNode.Create(4, "行前再次确认酒店相关情况",  hotelNode4Tips,ProcessStatus.InProgress, false, false, false,false,currUserId , hotelNodeOpUsers),
-                        Grp_ProcessNode.Create(5, "行程结束后整理酒店发票与结算",  hotelNode5Tips, ProcessStatus.InProgress, false, false, true,false, currUserId ,hotelNodeOpUsers),
+                        Grp_ProcessNode.Create(1, hotelNode1.NodeName, hotelNode1.NodeTips, ProcessStatus.InProgress, true, false, false, false, currUserId, hotelNode1.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(2, hotelNode2.NodeName, hotelNode2.NodeTips, ProcessStatus.InProgress, false, false, false,false, currUserId, hotelNode2.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(3, hotelNode3.NodeName, hotelNode3.NodeTips, ProcessStatus.InProgress,false, false, false,false, currUserId, hotelNode3.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(4, hotelNode4.NodeName, hotelNode4.NodeTips, ProcessStatus.InProgress, false, false, false,false,currUserId, hotelNode4.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(5, hotelNode5.NodeName, hotelNode5.NodeTips, ProcessStatus.InProgress, false, false, true,false, currUserId, hotelNode5.PromptPerson.Select(x => x.Id).ToList()),
                     }
                 )
             );
             #endregion
 
             #region 地接流程
-            string opNode1Tips = NodeTipsMsg(groupInfo, GroupProcessType.LocalGuide, 1);
-            string opNode2Tips = NodeTipsMsg(groupInfo, GroupProcessType.LocalGuide, 2);
-            string opNode3Tips = NodeTipsMsg(groupInfo, GroupProcessType.LocalGuide, 3);
-            string opNode4Tips = NodeTipsMsg(groupInfo, GroupProcessType.LocalGuide, 4);
-            string opNode5Tips = NodeTipsMsg(groupInfo, GroupProcessType.LocalGuide, 5);
-            string opNode6Tips = NodeTipsMsg(groupInfo, GroupProcessType.LocalGuide, 6);
-            string opNode7Tips = NodeTipsMsg(groupInfo, GroupProcessType.LocalGuide, 7);
-
-            //节点可操作用户列表
-            var opNodeOpJobNames = new List<string>() { "OP" };
-            opNodeOpJobNames.AddRange(defaultJobNames);
-
-            var opNodeOpUsers = users.Where(u =>
-                u.JobName != null && opNodeOpJobNames.Contains( u.JobName)
-            ).Select(u => u.Id).ToList();
+            var op_proc = GroupProcessType.LocalGuide;
+            var opNode1 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, op_proc, 1);
+            var opNode2 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, op_proc, 2);
+            var opNode3 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, op_proc, 3);
+            var opNode4 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, op_proc, 4);
+            var opNode5 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, op_proc, 5);
+            var opNode6 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, op_proc, 6);
+            var opNode7 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, op_proc, 7);
 
             processs.Add(
-                Grp_ProcessOverview.Create(groupId, 5, GroupProcessType.LocalGuide, ProcessStatus.InProgress, currUserId,
+                Grp_ProcessOverview.Create(groupId, 5, op_proc, ProcessStatus.InProgress, currUserId,
                      new List<Grp_ProcessNode>()
                     {
-                        Grp_ProcessNode.Create(1,"根据机票方案出框架行程", opNode1Tips,ProcessStatus.InProgress, true, false, false,false,currUserId,opNodeOpUsers),
-                        Grp_ProcessNode.Create(2,"联系并询价地接相关的供应商", opNode2Tips,ProcessStatus.InProgress, false, false, false,false, currUserId,opNodeOpUsers),
-                        Grp_ProcessNode.Create(3,"提交供应商报价及比价表", opNode3Tips, ProcessStatus.InProgress, false, false, false, false,currUserId,opNodeOpUsers),
-                        Grp_ProcessNode.Create(4,"执行采购流程", opNode4Tips, ProcessStatus.InProgress, false, false, false,false, currUserId,opNodeOpUsers),
-                        Grp_ProcessNode.Create(5,"制定最终行程单及出行手册", opNode5Tips, ProcessStatus.InProgress, false, false, false,false, currUserId ,opNodeOpUsers),
-                        Grp_ProcessNode.Create(6,"送机", opNode6Tips, ProcessStatus.InProgress, false, false, false,false, currUserId,opNodeOpUsers ),
-                        Grp_ProcessNode.Create(7,"最终版报批行程、票据上传", opNode7Tips, ProcessStatus.InProgress, false, false, true, false,currUserId,opNodeOpUsers )
+                        Grp_ProcessNode.Create(1, opNode1.NodeName, opNode1.NodeTips, ProcessStatus.InProgress, true, false, false,false, currUserId, opNode1.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(2, opNode2.NodeName, opNode2.NodeTips, ProcessStatus.InProgress, false, false, false,false, currUserId, opNode2.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(3, opNode3.NodeName, opNode3.NodeTips, ProcessStatus.InProgress, false, false, false, false, currUserId, opNode3.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(4, opNode4.NodeName, opNode4.NodeTips, ProcessStatus.InProgress, false, false, false,false, currUserId, opNode4.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(5, opNode5.NodeName, opNode5.NodeTips, ProcessStatus.InProgress, false, false, false,false, currUserId, opNode5.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(6, opNode6.NodeName, opNode6.NodeTips, ProcessStatus.InProgress, false, false, false,false, currUserId, opNode6.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(7, opNode7.NodeName, opNode7.NodeTips, ProcessStatus.InProgress, false, false, true, false, currUserId, opNode7.PromptPerson.Select(x => x.Id).ToList())
                     }
                 )
             );
@@ -239,23 +198,20 @@ namespace OASystem.Infrastructure.Repositories.Groups
             #endregion
 
             #region 费用结算流程
-            var feeNode1Tips = NodeTipsMsg(groupInfo, GroupProcessType.FeeSettle, 1);
-            var feeNode2Tips = NodeTipsMsg(groupInfo, GroupProcessType.FeeSettle, 2);
-            var feeNode3Tips = NodeTipsMsg(groupInfo, GroupProcessType.FeeSettle, 3);
-            var feeNode4Tips = NodeTipsMsg(groupInfo, GroupProcessType.FeeSettle, 4);
-            //节点可操作用户列表
-            var feeNodeOpUsers = users.Where(u =>
-                u.JobName != null && u.JobName.Contains("会计") && u.CnName.Equals("曾艳")
-            ).Select(u => u.Id).ToList();
+            var fee_proc = GroupProcessType.FeeSettle;
+            var feeNode1 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, fee_proc, 1);
+            var feeNode2 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, fee_proc, 2);
+            var feeNode3 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, fee_proc, 3);
+            var feeNode4 = GetNodeDetailsByProcAndNode(groupInfo, users, custInfo, airTripCodeInfo, backListInfo, fee_proc, 4);
 
             processs.Add(
-                Grp_ProcessOverview.Create(groupId, 6, GroupProcessType.FeeSettle, ProcessStatus.InProgress, currUserId,
+                Grp_ProcessOverview.Create(groupId, 6, fee_proc, ProcessStatus.InProgress, currUserId,
                     new List<Grp_ProcessNode>()
                     {
-                        Grp_ProcessNode.Create(1, "城市间交通报批金额核定", feeNode1Tips, ProcessStatus.InProgress, true, true, false,false,currUserId,feeNodeOpUsers),
-                        Grp_ProcessNode.Create(2, "团组城市间交通及国际机票数据分配的合理性核对", feeNode2Tips, ProcessStatus.InProgress, false, false, false,false,currUserId,feeNodeOpUsers),
-                        Grp_ProcessNode.Create(3, "整理统计相关财务资料给到各单位", feeNode3Tips, ProcessStatus.InProgress, false, false, false,false,currUserId,feeNodeOpUsers),
-                        Grp_ProcessNode.Create(4, "费用结算完毕", feeNode4Tips, ProcessStatus.InProgress, false, false, false,false, currUserId , feeNodeOpUsers),
+                        Grp_ProcessNode.Create(1, feeNode1.NodeName, feeNode1.NodeTips,  ProcessStatus.InProgress, true, true, false,false,currUserId, feeNode1.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(2, feeNode2.NodeName, feeNode2.NodeTips, ProcessStatus.InProgress, false, false, false,false,currUserId, feeNode2.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(3, feeNode3.NodeName, feeNode3.NodeTips, ProcessStatus.InProgress, false, false, false,false,currUserId, feeNode3.PromptPerson.Select(x => x.Id).ToList()),
+                        Grp_ProcessNode.Create(4, feeNode4.NodeName, feeNode4.NodeTips, ProcessStatus.InProgress, false, false, false,false, currUserId , feeNode4.PromptPerson.Select(x => x.Id).ToList()),
                     }
                 )
             );
@@ -371,6 +327,21 @@ namespace OASystem.Infrastructure.Repositories.Groups
                 }
             }
 
+            //基础数据
+            var opUsers = await GetUersAsync();
+
+            var custInfo = _sqlSugar.Queryable<Grp_TourClientList>()
+                .Where(c => c.DiId == groupId && c.IsDel == 0)
+                .OrderByDescending(c => c.CreateTime)
+                .First();
+
+            var airTripCodeInfo = _sqlSugar.Queryable<Air_TicketBlackCode>()
+                .Where(x => x.IsDel == 0 && x.DiId == groupId)
+                .OrderByDescending(x => x.CreateTime)
+                .First();
+
+            var backListInfo = _sqlSugar.Queryable<Grp_InvertedList>().Where(x => x.DiId == groupId && x.IsDel == 0).First();
+
             var users = await _sqlSugar.Queryable<Sys_Users>().Select(x => new { x.Id, x.CnName }).ToListAsync();
 
             var processData = await _sqlSugar.Queryable<Grp_ProcessOverview>()
@@ -440,7 +411,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
                             operatorName = name;
                         }
 
-                        string nodeTipsMsg = NodeTipsMsg(groupInfo, p.ProcessType, n.NodeOrder);
+                        string nodeTipsMsg = GetNodeDetailsByProcAndNode(groupInfo, opUsers, custInfo, airTripCodeInfo, backListInfo, p.ProcessType, n.NodeOrder)?.NodeTips;
 
                         return new ProcessNodeDetailsView()
                         {
@@ -454,7 +425,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
                             OpeateTime = n.OperationTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? "-",
                             ActualDone = n.ActualDone?.ToString("yyyy-MM-dd HH:mm:ss") ?? "",
                             OpUserList = n.OpUserList,       //可操作用户列表
-                            NodeDescTips = nodeTipsMsg,      
+                            NodeDescTips = nodeTipsMsg,
                             IsEnaAssistBtn = isEnaAssistBtn, // 是否启用财务流程首节点协助按钮
                             IsAssist = n.IsAssist,           // 财务流程首节点 存储值
                             IsEnaFileUpBtn = isEnaFileUpBtn, // 是否启用上传文件按钮
@@ -509,193 +480,822 @@ namespace OASystem.Infrastructure.Repositories.Groups
         }
 
         /// <summary>
-        /// 节点提示消息
+        /// 团组流程单节点详情
+        /// </summary>
+        /// <param name="groupInfo"></param>
+        /// <param name="users"></param>
+        /// <param name="custInfo"></param>
+        /// <param name="airTripCodeInfo"></param>
+        /// <param name="backListInfo"></param>
+        /// <returns></returns>
+        public static GroupProcFullNodeDetails GetNodeDetailsByProcAndNode(
+            Grp_DelegationInfo groupInfo,
+            List<NodeOpUserInfo> users,
+            Grp_TourClientList custInfo,
+            Air_TicketBlackCode airTripCodeInfo,
+            Grp_InvertedList backListInfo,
+            GroupProcessType procType,
+            int nodeOrder
+            )
+        { 
+            return GetNodeDetails(
+                groupInfo,
+                users,
+                custInfo,
+                airTripCodeInfo,
+                backListInfo
+                ).FirstOrDefault(x => x.ProcType == procType && x.NodeOrder == nodeOrder);
+        }
+
+        /// <summary>
+        /// 团组流程节点详情计算
         /// </summary>
         /// <param name="groupInfo"></param>
-        /// <param name="procType"></param>
-        /// <param name="nodeOrder"></param>
+        /// <param name="users"></param>
+        /// <param name="custInfo"></param>
+        /// <param name="airTripCodeInfo"></param>
+        /// <param name="backListInfo"></param>
         /// <returns></returns>
-        private string NodeTipsMsg(Grp_DelegationInfo groupInfo, GroupProcessType procType, int nodeOrder)
+        public static List<GroupProcFullNodeDetails> GetNodeDetails(
+            Grp_DelegationInfo groupInfo, 
+            List<NodeOpUserInfo> users, 
+            Grp_TourClientList custInfo,
+            Air_TicketBlackCode airTripCodeInfo,
+            Grp_InvertedList backListInfo
+            )
         {
-            string msg = string.Empty;
+            var nodeDetails = new List<GroupProcFullNodeDetails>();
             int groupId = groupInfo.Id;
+            string groupName = groupInfo.TeamName;
             groupInfo.VisitDate = groupInfo.VisitDate.AddDays(1); //第二天开始计算
-            switch (procType)
+
+            #region 商邀
+
+            var oa_proc = GroupProcessType.Invitation;
+
+            //用户可操作权限
+            var oa_users = NodeOpUserTpl(users)[oa_proc];
+
+            //1. 完成基础请示、报批日程初稿”,4个工作日(仍需根据客户意见和联系情况及时修改补充所需其他材料,例如印证文件、成果表等,直至终稿)
+            DateTime? xy_timeBase1 = null;
+            if (groupInfo.Step == 1 || groupInfo.Step == 2)
             {
-                case GroupProcessType.Invitation:
-                    switch (nodeOrder)
-                    {
-                        case 1:
-                            msg = "“完成基础请示、报批日程初稿”,4个工作日(仍需根据客户意见和联系情况及时修改补充所需其他材料,例如印证文件、成果表等,直至终稿)";
+                if (groupInfo.StepOperationTime.HasValue) xy_timeBase1 = groupInfo.StepOperationTime.Value;
+            }
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, oa_proc, 1, xy_timeBase1, 4, true, oa_users)
+            );
 
-                            if (groupInfo.Step == 1 || groupInfo.Step == 2)
-                            {
-                                if (groupInfo.StepOperationTime.HasValue)
-                                {
-                                    msg = $"请于{AddWeekdays(groupInfo.StepOperationTime.Value, 4):yyyy年MM月dd日}内完成该项工作(“完成基础请示、报批日程初稿”,4个工作日(仍需根据客户意见和联系情况及时修改补充所需其他材料,例如印证文件、成果表等,直至终稿))";
-                                }
-                            }
+            //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)
+            );
 
-                            break;
-                        case 2:
-                            msg = $"请于{AddWeekdays(groupInfo.VisitDate, 7):yyyy年MM月dd日}内完成该项工作(7个工作日,所有报批机构前部联系,邀请机构一个国家不少于4家进行重点对接(4家机构中,其中3家机构需有效对接,其中1家可为付费机构备选))";
-                            break;
-                        case 3:
-                            msg = "10个工作日,根据最新情况,联系公务机构1/3取得回应;邀请机构基本明确。";
-                            break;
-                        case 4:
-                            var custInfo = _sqlSugar.Queryable<Grp_TourClientList>()
-                                .Where(c => c.DiId == groupId && c.IsDel == 0)
-                                .OrderByDescending(c => c.CreateTime)
-                                .First();
+            //3. 10个工作日,根据最新情况,联系公务机构1/3取得回应;邀请机构基本明确。
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, oa_proc, 3, null, 0, true, oa_users)
+            );
 
-                            msg = $"正式名单下放后2周内(含非工作日)。如团组前期准备时间已经较长,则按客户要求尽快提供。 加急团组备注特殊情况。";
+            //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)
+            );
 
-                            if (custInfo != null)
-                            {
-                                msg = $"请于{custInfo.CreateTime.AddDays(14):yyyy年MM月dd日}内完成该项工作(正式名单下放后2周内(含非工作日)。如团组前期准备时间已经较长,则按客户要求尽快提供。 加急团组备注特殊情况。)";
-                            }
-                            break;
-                        case 5:
-                            groupInfo.VisitDate = groupInfo.VisitDate.AddDays(-1); //时间倒推,调整回原始出发日期
-                            msg = $"请于{AddWeekdays(groupInfo.VisitDate,-5):yyyy年MM月dd日}内完成该项工作(团组出发前,5个工作日完成所有公务确认工作。";
+            //5. 团组出发前,5个工作日完成所有公务确认工作。
+            DateTime? xy_timeBase5 = groupInfo.VisitDate.AddDays(-1); //时间倒推,调整回原始出发日期
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, oa_proc, 5, xy_timeBase5, -5, true, oa_users)
+            );
 
-                            break;
-                        case 6:
-                            break;
-                        case 7:
-                            msg = $"请于{groupInfo.VisitEndDate:yyyy年MM月dd日}内完成该项工作(如果需要上传请在团组结束前完成)";
-                            break;
-                    }
-                    break;
-                case GroupProcessType.Visa:
-                    switch (nodeOrder)
+            //6. 
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, 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)
+            );
+            #endregion
+
+            #region 签证
+
+            var visa_proc = GroupProcessType.Visa;
+
+            //用户可操作权限
+            var visa_users = NodeOpUserTpl(users)[visa_proc];
+
+            //1. 
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, 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)
+            );
+            #endregion
+
+            #region 机票
+
+            var air_proc = GroupProcessType.AirTicket;
+
+            //用户可操作权限
+            var air_users = NodeOpUserTpl(users)[air_proc];
+
+            //1. 建团后打勾确认出团的时候开始24小时内。
+            DateTime? air_timeBase1 = null;
+            if (groupInfo.Step == 1 || groupInfo.Step == 2)
+            {
+                if (groupInfo.StepOperationTime.HasValue) air_timeBase1 = groupInfo.StepOperationTime.Value;
+            }
+
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, air_proc, 1, air_timeBase1, 0, true, air_users)
+            );
+
+            //2. 
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, visa_proc, 2, null, 0, true, visa_users)
+            );
+
+            //3. 完成机票采购确认(含预算核对、出票确认等)
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, visa_proc, 3, null, 0, true, visa_users)
+            );
+
+            //4. 
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, visa_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)
+            );
+
+            //6.  
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, visa_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)
+            );
+
+            //8.  团组归国后10个工作日内 *按机票报价*0.999折扣出具机票报销蓝联、行程单及机票说明
+            DateTime? air_timeBase8 = groupInfo.VisitEndDate;
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, visa_proc, 8, air_timeBase8, 10, true, visa_users)
+            );
+            #endregion
+
+            #region 酒店
+
+            var hotel_proc = GroupProcessType.Hotel;
+
+            //用户可操作权限
+            var hotel_users = NodeOpUserTpl(users)[hotel_proc];
+
+            //1. 筛选并按照预算标准,对目标酒店进行询价、比价、谈价 \r\n2. 建团后打勾确认出团的时候开始2个工作日。
+            DateTime? hotel_timeBase1 = null;
+            if (groupInfo.Step == 1 || groupInfo.Step == 2)
+            {
+                if (groupInfo.StepOperationTime.HasValue) hotel_timeBase1 = groupInfo.StepOperationTime.Value;
+            }
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, hotel_proc, 1, hotel_timeBase1, 2, true, hotel_users)
+            );
+
+            //2. 
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, hotel_proc, 2, null, 0, true, hotel_users)
+            );
+
+            //3. 
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, 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)
+            );
+
+            //5. 团组结束后5个工作日内 
+            DateTime? hotel_timeBase5 = groupInfo.VisitEndDate;
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, hotel_proc, 5, hotel_timeBase5, 5, true, hotel_users)
+            );
+
+            #endregion
+
+            #region 地接
+
+            var dj_proc = GroupProcessType.LocalGuide;
+
+            //用户可操作权限
+            var dj_users = NodeOpUserTpl(users)[dj_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)
+            );
+
+            //2. 团组出行前20个工作日
+            DateTime? dj_timeBase2 = groupInfo.VisitDate;
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, dj_proc, 2, dj_timeBase2, -20, true, dj_users)
+            );
+
+            //3. 上一步往后3个工作日内
+            DateTime? dj_timeBase3 = groupInfo.VisitDate;
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, dj_proc, 3, dj_timeBase3, -17, true, dj_users)
+            );
+
+            //4. 上一步往后2个工作日内
+            DateTime? dj_timeBase4 = groupInfo.VisitDate;
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, dj_proc, 4, dj_timeBase4, -15, true, dj_users)
+            );
+
+            //5. 1.制定最终《行程单》及《出行手册》 \r\n2. 倒推表里开行前会 -3天。
+            DateTime? dj_timeBase5 = null;
+            if (backListInfo != null)
+            {
+                if (DateTime.TryParse(backListInfo.PreTripMeetingDt, out DateTime dateTime))
+                {
+                    dj_timeBase5 = dateTime;
+                }
+            }
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, dj_proc, 5, dj_timeBase5, -3, true, dj_users)
+            );
+
+            //6. 
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, dj_proc, 6, null, 0, true, dj_users)
+            );
+
+            //7. 团组归国后5个工作日内) *上传最终报批行程,确定城市间交通最终版报价分配;地接账单(清楚标注超时及其他项超支费用)、地接交通费用原始票据、城市间交通明细表;
+            DateTime? dj_timeBase7 = groupInfo.VisitEndDate;
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, dj_proc, 7, dj_timeBase7, 5, true, dj_users)
+            );
+
+            #endregion
+
+            #region 费用计算
+
+            var fyjs_proc = GroupProcessType.FeeSettle;
+
+            //用户可操作权限
+            var fyjsfyjs_users = NodeOpUserTpl(users)[fyjs_proc];
+
+            //1.
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, fyjs_proc, 1, null, 0, true, fyjsfyjs_users)
+            );
+
+            //2. 
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, fyjs_proc, 2, null, 0, true, fyjsfyjs_users)
+            );
+
+            //3. 团组归国后12个工作日内
+            DateTime? fyjs_timeBase3 = groupInfo.VisitEndDate;
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, fyjs_proc, 3, fyjs_timeBase3, 12, true, fyjsfyjs_users)
+            );
+
+            //4.
+            nodeDetails.Add(
+                GroupProcFullNodeDetails.Create(groupId, groupName, fyjs_proc, 4, null, 0, true, fyjsfyjs_users)
+            );
+
+            #endregion
+
+            return nodeDetails;
+        }
+
+        #region 计算所有团时间节点基础数据
+
+        /// <summary>
+        /// 团组流程 - 数据初始化 ALL
+        /// </summary>
+        /// <param name="groupId"></param>
+        /// <param name="currUserId"></param>
+        /// <returns></returns>
+        public async Task<List<GroupProcFullNodeDetails>> GroupProcFulllNodeTimeInitAsync()
+        {
+            var processs = new List<GroupProcFullNodeDetails>();
+
+            var groupInfos = await _sqlSugar.Queryable<Grp_DelegationInfo>().Where(x => x.IsDel == 0).ToListAsync();
+
+            if (groupInfos.Any())
+            {
+                var groupIds = groupInfos.Select(x => x.Id).ToList();
+
+                //基础数据
+                var opUsers = await GetUersAsync();
+
+                var custInfos = await _sqlSugar.Queryable<Grp_TourClientList>()
+                    .Where(x => x.IsDel == 0 && groupIds.Contains( x.DiId))
+                    .OrderByDescending(c => c.CreateTime)
+                    .ToListAsync();
+
+                var airTripCodeInfos = await _sqlSugar.Queryable<Air_TicketBlackCode>()
+                    .Where(x => x.IsDel == 0 && groupIds.Contains(x.DiId))
+                    .OrderByDescending(x => x.CreateTime)
+                    .ToListAsync();
+
+                var backListInfos = await _sqlSugar.Queryable<Grp_InvertedList>().Where(x => x.IsDel == 0 && groupIds.Contains(x.DiId)).ToListAsync();
+
+                foreach (var groupInfo in groupInfos)
+                {
+                    var custInfo = custInfos.FirstOrDefault(x => x.DiId == groupInfo.Id);
+                    var airTripCodeInfo = airTripCodeInfos.FirstOrDefault(x => x.DiId == groupInfo.Id);
+                    var backListInfo = backListInfos.FirstOrDefault(x => x.DiId == groupInfo.Id);
+
+                    var groupNodeDetails = GetNodeDetails(
+                        groupInfo,
+                        opUsers,
+                        custInfo,
+                        airTripCodeInfo,
+                        backListInfo
+                        );
+                    processs.AddRange(groupNodeDetails);
+                }
+            }
+
+            return processs;
+        }
+
+        /// <summary>
+        /// 获取用户信息
+        /// </summary>
+        /// <returns></returns>
+        public async Task<List<NodeOpUserInfo>> GetUersAsync()
+        {
+            return await _sqlSugar.Queryable<Sys_Users>()
+                .LeftJoin<Sys_Company>((u, c) => u.CompanyId == c.Id)
+                .LeftJoin<Sys_Department>((u, c, d) => u.DepId == d.Id)
+                .LeftJoin<Sys_JobPost>((u, c, d, jp) => u.JobPostId == jp.Id)
+                .Where((u, c, d, jp) => u.IsDel == 0)
+                .Select((u, c, d, jp) => new NodeOpUserInfo()
+                {
+                    Id = u.Id,
+                    CnName = u.CnName,
+                    CompanyId = u.CompanyId,
+                    CompanyName = c.CompanyName,
+                    DepId = u.DepId,
+                    DepName = d.DepName,
+                    JobPostId = u.JobPostId,
+                    JobName = jp.JobName,
+                    QiyeChatUserId = u.QiyeChatUserId,
+                })
+                .ToListAsync();
+        }
+
+        /// <summary>
+        /// 节点可操作人员
+        /// </summary>
+        /// <returns></returns>
+        public static Dictionary<GroupProcessType, List<UserAndQiWeiUserIdView>> NodeOpUserTpl(List<NodeOpUserInfo> users)
+        {
+            var result = new Dictionary<GroupProcessType, List<UserAndQiWeiUserIdView>>();
+
+            var defaultJobNames = new List<string>()
+            {
+                "OP主管"
+            };
+
+            #region 商邀报批流程
+
+            //节点可操作用户列表
+            var oaJobNames = new List<string>() {
+                "商邀主管",
+                "商邀"
+            };
+            var oaNodeOpUsers = users.Where(u =>
+                    u.JobName != null && oaJobNames.Contains(u.JobName)
+                )
+                .Select(u => new UserAndQiWeiUserIdView() { Id = u.Id, Name = u.CnName, QiyeChatUserId = u.QiyeChatUserId })
+            .ToList();
+
+            result.Add(GroupProcessType.Invitation, oaNodeOpUsers);
+            #endregion
+
+            #region 签证流程
+
+            //节点可操作用户列表
+            var visaNodeOpUsers = users.Where(u =>
+                u.JobName != null && u.JobName.Contains("签证")
+                )
+                .Select(u => new UserAndQiWeiUserIdView() { Id = u.Id, Name = u.CnName, QiyeChatUserId = u.QiyeChatUserId })
+            .ToList();
+
+            result.Add(GroupProcessType.Visa, visaNodeOpUsers);
+            #endregion
+
+            #region 机票流程
+            //节点可操作用户列表
+            var airNodeOpJobNames = new List<string>() {
+                "机票"
+            };
+            airNodeOpJobNames.AddRange(defaultJobNames);
+
+            var airNodeOpUsers = users.Where(u =>
+                u.JobName != null && airNodeOpJobNames.Contains(u.JobName)
+                )
+                .Select(u => new UserAndQiWeiUserIdView() { Id = u.Id, Name = u.CnName, QiyeChatUserId = u.QiyeChatUserId })
+            .ToList();
+
+            result.Add(GroupProcessType.AirTicket, airNodeOpUsers);
+            #endregion
+
+            #region 酒店流程
+            //节点可操作用户列表
+            var hotelNodeOpJobNames = new List<string>() { "酒店" };
+            hotelNodeOpJobNames.AddRange(defaultJobNames);
+
+            var hotelNodeOpUsers = users.Where(u =>
+                u.JobName != null && hotelNodeOpJobNames.Contains(u.JobName)
+                )
+                .Select(u => new UserAndQiWeiUserIdView() { Id = u.Id, Name = u.CnName, QiyeChatUserId = u.QiyeChatUserId })
+            .ToList();
+
+            result.Add(GroupProcessType.Hotel, hotelNodeOpUsers);
+            #endregion
+
+            #region 地接流程
+            //节点可操作用户列表
+            var opNodeOpJobNames = new List<string>() { "OP" };
+            opNodeOpJobNames.AddRange(defaultJobNames);
+
+            var opNodeOpUsers = users.Where(u =>
+                u.JobName != null && opNodeOpJobNames.Contains(u.JobName)
+                )
+                .Select(u => new UserAndQiWeiUserIdView() { Id = u.Id, Name = u.CnName, QiyeChatUserId = u.QiyeChatUserId })
+            .ToList();
+
+            result.Add(GroupProcessType.LocalGuide, opNodeOpUsers);
+            #endregion
+
+            #region 费用结算流程
+            //节点可操作用户列表
+            var feeNodeOpUsers = users.Where(u =>
+                u.JobName != null && u.JobName.Contains("会计") && u.CnName.Equals("曾艳")
+                )
+                .Select(u => new UserAndQiWeiUserIdView() { Id = u.Id, Name = u.CnName, QiyeChatUserId = u.QiyeChatUserId })
+            .ToList();
+
+            result.Add(GroupProcessType.FeeSettle, feeNodeOpUsers);
+            #endregion
+
+            return result;
+        }
+
+        public class NodeOpUserInfo
+        {
+            public int Id { get; set; }
+            public string CnName { get; set; }
+            public int CompanyId { get; set; }
+            public string CompanyName { get; set; }
+            public int DepId { get; set; }
+            public string DepName { get; set; }
+            public int JobPostId { get; set; }
+            public string JobName { get; set; }
+            public string QiyeChatUserId { get; set; }
+        }
+
+        public class GroupProcFullNodeDetails
+        {
+            public int GroupId { get; set; }
+
+            public string GroupName { get; set; }
+
+            /// <summary>
+            /// 流程类型
+            /// </summary>
+            public GroupProcessType ProcType { get; set; }
+
+            /// <summary>
+            /// 流程顺序
+            /// </summary>
+            public int ProcOrder
+            {
+                get
+                {
+                    return (int)ProcType;
+                }
+            }
+
+            public int NodeOrder { get; set; }
+
+            public string NodeName
+            {
+                get
+                {
+                    return ProcType switch
                     {
-                        case 1:
-                            break;
-                        case 2:
-                            msg = $"请于{groupInfo.VisitDate:yyyy年MM月dd日}内完成该项工作(按进度实际签证办理落实情况,团组出发前上传票据。)";
-                            break;
-                    }
-                    break;
-                case GroupProcessType.AirTicket:
-                    switch (nodeOrder)
+                        GroupProcessType.Invitation => NodeOrder switch
+                        {
+                            1 => "初期报批文字材料",
+                            2 => "第一轮对接",
+                            3 => "第二轮对接",
+                            4 => "取得邀请函",
+                            5 => "公务等事项确认",
+                            6 => "公务邀请数据有效录入",
+                            7 => "文件上传",
+                            _ => ""
+                        },
+                        GroupProcessType.Visa => NodeOrder switch
+                        {
+                            1 => "签证信息",
+                            2 => "票据上传(明细表、费用票据、保单及超支费用账单)",
+                            _ => ""
+                        },
+                        GroupProcessType.AirTicket => NodeOrder switch
+                        {
+                            1 => "初步拟定航程方案及价格",
+                            2 => "机票占位、续位",
+                            3 => "完成机票采购确认",
+                            4 => "进行出票操作并核查信息",
+                            5 => "机票已出",
+                            6 => "完成机票选座",
+                            7 => "票据上传(机票超支费用账单)",
+                            8 => "票据上传",
+                            _ => ""
+                        },
+                        GroupProcessType.Hotel => NodeOrder switch
+                        {
+                            1 => "按照预算,询价、比价、谈价",
+                            2 => "获取酒店确认函与入住名单核对",
+                            3 => "预订酒店并录入OA",
+                            4 => "行前再次确认酒店相关情况",
+                            5 => "行程结束后整理酒店发票与结算",
+                            _ => ""
+                        },
+                        GroupProcessType.LocalGuide => NodeOrder switch
+                        {
+                            1 => "根据机票方案出框架行程",
+                            2 => "联系并询价地接相关的供应商",
+                            3 => "提交供应商报价及比价表",
+                            4 => "执行采购流程",
+                            5 => "制定最终行程单及出行手册",
+                            6 => "送机",
+                            7 => "最终版报批行程、票据上传",
+                            _ => ""
+                        },
+                        GroupProcessType.FeeSettle => NodeOrder switch
+                        {
+                            1 => "城市间交通报批金额核定",
+                            2 => "团组城市间交通及国际机票数据分配的合理性核对",
+                            3 => "整理统计相关财务资料给到各单位",
+                            4 => "费用结算完毕",
+                            _ => ""
+                        },
+                        _ => ""
+                    };
+                }
+            }
+
+            public string NodeTips
+            {
+                get
+                {
+                    string msg = string.Empty;
+                    string promptMessage = GetPromptMessage();
+
+                    switch (ProcType)
                     {
-                        case 1:
-                            msg = "建团后打勾确认出团的时候开始24小时内。";
-                            if (groupInfo.Step == 1 || groupInfo.Step == 2)
+                        case GroupProcessType.Invitation:
+                            switch (NodeOrder)
                             {
-                                if (groupInfo.StepOperationTime.HasValue)
-                                {
-                                    msg = $"请于{groupInfo.StepOperationTime.Value.AddDays(1):yyyy年MM月dd日}内完成该项工作(建团后打勾确认出团的时候开始24小时内)";
-                                }
+                                case 1:
+                                    msg = "“完成基础请示、报批日程初稿”,4个工作日(仍需根据客户意见和联系情况及时修改补充所需其他材料,例如印证文件、成果表等,直至终稿)";
+                                    break;
+                                case 2:
+                                    msg = "7个工作日,所有报批机构前部联系,邀请机构一个国家不少于4家进行重点对接(4家机构中,其中3家机构需有效对接,其中1家可为付费机构备选)";
+                                    break;
+                                case 3:
+                                    msg = "10个工作日,根据最新情况,联系公务机构1/3取得回应;邀请机构基本明确。";
+                                    break;
+                                case 4:
+                                    msg = "正式名单下放后2周内(含非工作日)。如团组前期准备时间已经较长,则按客户要求尽快提供。 加急团组备注特殊情况。";
+                                    break;
+                                case 5:
+                                    msg = "团组出发前,5个工作日完成所有公务确认工作。";
+                                    break;
+                                case 6:
+                                    break;
+                                case 7:
+                                    msg = "如果需要上传请在团组结束前完成";
+                                    break;
                             }
                             break;
-                        case 2:
-                            break;
-                        case 3:
-                            msg = $"完成机票采购确认(含预算核对、出票确认等)";
-                            break;
-                        case 4:
-                            break;
-                        case 5:
-                            msg = $"请于{AddWeekdays(groupInfo.VisitDate,2):yyyy年MM月dd日}内完成该项工作(团组出发前2个工作日)";
-                            break;
-                        case 6:
-                            break;
-                        case 7:
-                            msg = $"请于{AddWeekdays(groupInfo.VisitEndDate,5):yyyy年MM月dd日}内完成该项工作(机票蓝联打票及上传机票超支费用账单,团组归国后5个工作日内)";
-                            break;
-                        case 8:
-                            msg = $"1. 票据上传(机票报销蓝联、行程单及机票说明) \r\n 2. 请于{AddWeekdays(groupInfo.VisitEndDate,10):yyyy年MM月dd日}内完成该项工作(团组归国后10个工作日内) *按机票报价*0.999折扣出具机票报销蓝联、行程单及机票说明";
-                            break;
-                    }
-                    break;
-                case GroupProcessType.Hotel:
-                    switch (nodeOrder)
-                    {
-                        case 1:
-                            msg = "1. 筛选并按照预算标准,对目标酒店进行询价、比价、谈价 \r\n2. 建团后打勾确认出团的时候开始2个工作日。";
-                            if (groupInfo.Step == 1 || groupInfo.Step == 2)
+                        case GroupProcessType.Visa:
+                            switch (NodeOrder)
                             {
-                                if (groupInfo.StepOperationTime.HasValue)
-                                {
-                                    msg = $"请于{AddWeekdays(groupInfo.StepOperationTime.Value, 2):yyyy年MM月dd日}内完成该项工作(建团后打勾确认出团的时候开始2个工作日)";
-                                }
+                                case 1:
+                                    break;
+                                case 2:
+                                    msg = "按进度实际签证办理落实情况,团组出发前上传票据。";
+                                    break;
                             }
                             break;
-                        case 2:
-                            break;
-                        case 3:
-                            break;
-                        case 4:
-                            msg = $"1.行前再次确认酒店订单、付款状态及入住安排 \r\n 2.请于{AddWeekdays(groupInfo.VisitDate,-2):yyyy年MM月dd日}内完成该项工作(团组出发前2个工作日)";
-                            break;
-                        case 5:
-                            msg = $"1.行程结束后整理酒店发票(含超支费用发票)与结算 \r\n 2.请于{AddWeekdays(groupInfo.VisitEndDate,5):yyyy年MM月dd日}内完成该项工作(团组结束后5个工作日内)";
-                            break;
-                    }
-                    break;
-                case GroupProcessType.LocalGuide:
-                    switch (nodeOrder)
-                    {
-                        case 1:
-                            var airTripCodeInfo = _sqlSugar.Queryable<Air_TicketBlackCode>()
-                                .Where(x => x.IsDel == 0 && x.DiId == groupId)
-                                .OrderByDescending(x => x.CreateTime)
-                                .First();
-                            msg = $"机票行程代码最后一段录入后1个工作日内。";
-                            if (airTripCodeInfo != null)
+                        case GroupProcessType.AirTicket:
+                            switch (NodeOrder)
                             {
-                                msg = $"请于{AddWeekdays(airTripCodeInfo.CreateTime, 1):yyyy年MM月dd日}内完成该项工作(机票行程代码最后一段录入后1个工作日内)";
+                                case 1:
+                                    msg = "建团后打勾确认出团的时候开始24小时内。";
+                                    break;
+                                case 2:
+                                    break;
+                                case 3:
+                                    msg = "完成机票采购确认(含预算核对、出票确认等)";
+                                    break;
+                                case 4:
+                                    break;
+                                case 5:
+                                    msg = "团组出发前2个工作日";
+                                    break;
+                                case 6:
+                                    break;
+                                case 7:
+                                    msg = "机票蓝联打票及上传机票超支费用账单,团组归国后5个工作日内";
+                                    break;
+                                case 8:
+                                    msg = "1. 票据上传(机票报销蓝联、行程单及机票说明) \r\n 2. 团组归国后10个工作日内 *按机票报价*0.999折扣出具机票报销蓝联、行程单及机票说明";
+                                    break;
                             }
                             break;
-                        case 2:
-                            msg = $"1.联系并询价地接、餐厅、用车、景点等供应商 \r\n 2. 请于{AddWeekdays(groupInfo.VisitDate, 20):yyyy年MM月dd日}内完成该项工作(团组出行前20个工作日)";
-                            break;
-                        case 3:
-                            msg = $"请于{AddWeekdays(groupInfo.CreateTime, 10):yyyy年MM月dd日}内完成该项工作(上一步往后3个工作日内)";
-                            break;
-                        case 4:
-                            msg = $"请于{AddWeekdays(groupInfo.CreateTime, 12):yyyy年MM月dd日}内完成该项工作(上一步往后2个工作日内)";
-                            break;
-                        case 5:
-                            var backListInfo = _sqlSugar.Queryable<Grp_InvertedList>().Where(x => x.DiId == groupId && x.IsDel == 0).First();
-                            msg = $"1.制定最终《行程单》及《出行手册》 \r\n2. 倒推表里开行前会 -3天。";
-                            if (backListInfo != null)
+                        case GroupProcessType.Hotel:
+                            switch (NodeOrder)
                             {
-                                if (DateTime.TryParse(backListInfo.PreTripMeetingDt, out DateTime dateTime))
-                                {
-                                    msg = $"请于{dateTime.AddDays(-3):yyyy年MM月dd日}内完成该项工作(倒推表里开行前会 -3天)";
-                                }
+                                case 1:
+                                    msg = "1. 筛选并按照预算标准,对目标酒店进行询价、比价、谈价 \r\n2. 建团后打勾确认出团的时候开始2个工作日。";
+                                    break;
+                                case 2:
+                                    break;
+                                case 3:
+                                    break;
+                                case 4:
+                                    msg = "1.行前再次确认酒店订单、付款状态及入住安排 \r\n 2.团组出发前2个工作日";
+                                    break;
+                                case 5:
+                                    Days = 5;
+                                    msg = "1.行程结束后整理酒店发票(含超支费用发票)与结算 \r\n 2.团组结束后5个工作日内";
+                                    break;
                             }
                             break;
-                        case 6:
+                        case GroupProcessType.LocalGuide:
+                            switch (NodeOrder)
+                            {
+                                case 1:
+                                    msg = "机票行程代码最后一段录入后1个工作日内。";
+                                    break;
+                                case 2:
+                                    msg = "1.联系并询价地接、餐厅、用车、景点等供应商 \r\n 2. 团组出行前20个工作日";
+                                    break;
+                                case 3:
+                                    msg = "上一步往后3个工作日内";
+                                    break;
+                                case 4:
+                                    msg = "上一步往后2个工作日内";
+                                    break;
+                                case 5:
+                                    msg = "1.制定最终《行程单》及《出行手册》 \r\n2. 倒推表里开行前会 -3天。";
+                                    break;
+                                case 6:
+                                    break;
+                                case 7:
+                                    msg = "团组归国后5个工作日内 *上传最终报批行程,确定城市间交通最终版报价分配;地接账单(清楚标注超时及其他项超支费用)、地接交通费用原始票据、城市间交通明细表;";
+                                    break;
+                            }
                             break;
-                        case 7:
-                            msg = $"请于{AddWeekdays( groupInfo.VisitEndDate,5):yyyy年MM月dd日}内完成该项工作(团组归国后5个工作日内) *上传最终报批行程,确定城市间交通最终版报价分配;地接账单(清楚标注超时及其他项超支费用)、地接交通费用原始票据、城市间交通明细表;";
+                        case GroupProcessType.FeeSettle:
+                            switch (NodeOrder)
+                            {
+                                case 1:
+                                    msg = "团组报批前";
+                                    break;
+                                case 2:
+                                    msg = "团组报批前三公费用表";
+                                    break;
+                                case 3:
+                                    msg = "1.整理统计团组超支费用、三公报销资料给到各单位 \r\n 2. 团组归国后12个工作日内";
+                                    break;
+                                case 4:
+                                    break;
+                            }
                             break;
                     }
-                    break;
-                case GroupProcessType.FeeSettle:
-                    switch (nodeOrder)
+
+                    if (!string.IsNullOrEmpty(promptMessage))
                     {
-                        case 1:
-                            msg = "团组报批前";
-                            break;
-                        case 2:
-                            msg = "团组报批前三公费用表";
-                            break;
-                        case 3:
-                            msg = $"1.整理统计团组超支费用、三公报销资料给到各单位 \r\n 2. 请于{AddWeekdays(groupInfo.VisitEndDate,12):yyyy年MM月dd日}内完成该项工作(团组归国后12个工作日内)";
-                            break;
-                        case 4:
-                            break;
+                        msg = $"{promptMessage}({msg})";
                     }
-                    break;
+
+                    return msg;
+                }
             }
-            return msg;
+
+            /// <summary>
+            /// 提示时间 - 基数
+            /// </summary>
+            public DateTime? PromptTimeBase { get; set; }
+
+            /// <summary>
+            /// 提前提醒天数
+            /// </summary>
+            public int Days { get; set; }
+
+            /// <summary>
+            /// 是否是工作日
+            /// true 工作日 false 自然日
+            /// </summary>
+            public bool IsWorkday { get; set; } = false;
+
+            /// <summary>
+            /// 提示时间
+            /// </summary>
+            public DateTime? PromptTime
+            {
+                get
+                {
+                    DateTime? dt = null;
+
+                    if (PromptTimeBase.HasValue)
+                    {
+                        if (IsWorkday)
+                        {
+                            dt = AddWeekdays(PromptTimeBase.Value, Days);
+                        }
+                        else
+                        {
+                            dt = PromptTimeBase.Value.AddDays(Days);
+                        }
+                    }
+                    return dt;
+                }
+            }
+
+            /// <summary>
+            /// 提示人
+            /// </summary>
+            public List<UserAndQiWeiUserIdView> PromptPerson { get; set; }
+
+            public GroupProcFullNodeDetails() { }
+
+            public static GroupProcFullNodeDetails Create(int groupId, string groupName, GroupProcessType procType, int nodeOrder, DateTime? promptTimeBase,
+               int days, bool isWorkday, List<UserAndQiWeiUserIdView> promptPerson)
+            {
+                return new GroupProcFullNodeDetails
+                {
+                    GroupId = groupId,
+                    GroupName = groupName,
+                    ProcType = procType,
+                    NodeOrder = nodeOrder,
+                    PromptTimeBase = promptTimeBase,
+                    Days = days,
+                    IsWorkday = isWorkday,
+                    PromptPerson = promptPerson
+                };
+            }
+
+            private string GetPromptMessage()
+            {
+                if (PromptTime.HasValue)
+                {
+                    return $"请于{PromptTime.Value:yyyy年MM月dd日}内完成该项工作";
+                }
+                return string.Empty;
+            }
+        }
+
+        
+        public class UserAndQiWeiUserIdView
+        {
+            public int Id { get; set; }
+            public string Name { get; set; }
+            public string QiyeChatUserId { get; set; }
         }
+        #endregion
+
 
         #region 状态变更 可操作任意节点 进行中<-->已完成 双向变更