Преглед на файлове

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

yuanrf преди 1 ден
родител
ревизия
e9c6d29362

Файловите разлики са ограничени, защото са твърде много
+ 786 - 311
OASystem/OASystem.Api/Controllers/GroupsController.cs


+ 19 - 0
OASystem/OASystem.Domain/Dtos/Groups/VisaProcessDtos.cs

@@ -70,6 +70,25 @@ namespace OASystem.Domain.Dtos.Groups
         public List<VisaProcessNode> VisaSubNodes { get; set; }
     }
 
+
+    public class GroupProcessSetActualDoneDto
+    {
+        /// <summary>
+        /// 节点Id
+        /// </summary>
+        public int NodeId { get; set; }
+
+        /// <summary>
+        /// 实际完成时间
+        /// </summary>
+        public DateTime ActualDone { get; set; }
+
+        /// <summary>
+        /// 当前用户Id  
+        /// </summary>
+        public int CurrUserId { get; set; }
+    }
+
     public class VisaProcessSetCompletedDto
     {
         public int StepId { get; set; }

+ 6 - 0
OASystem/OASystem.Domain/Entities/Groups/Grp_DelegationInfo.cs

@@ -259,6 +259,12 @@ namespace OASystem.Domain.Entities.Groups
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "int")]
         public int Step { get; set; } = 1;
+
+        /// <summary>
+        /// 步骤操作时间
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "datetime")]
+        public DateTime? StepOperationTime { get; set; }
     }
 
 }

+ 52 - 141
OASystem/OASystem.Domain/Entities/Groups/Grp_ProcessOverview.cs

@@ -68,139 +68,18 @@ namespace OASystem.Domain.Entities.Groups
 
         public Grp_ProcessOverview() { }
 
-        /// <summary>
-        /// 流程进度默认模板
-        /// </summary>
-        /// <param name="groupId"></param>
-        /// <param name="currUserId"></param>
-        /// <param name="visaCountries"></param>
-        /// <returns></returns>
-        public static List<Grp_ProcessOverview> ProcessInit(int groupId, int currUserId, List<string> visaCountries)
+        public static Grp_ProcessOverview Create(int groupId, int processOrder, GroupProcessType processType, ProcessStatus overallStatus, int currUserId, List<Grp_ProcessNode> nodes)
         {
-            //单独处理签证流程节点
-            var visaNodes = new List<Grp_ProcessNode>();
-
-            if (visaCountries != null && visaCountries.Count > 0)
-            {
-                var visaDefualtNodes = new List<VisaProcessNode>();
-                for (int i = 1; i < visaCountries.Count + 1; i++)
-                {
-                    visaDefualtNodes.Add(VisaProcessNode.Info(i, visaCountries[i - 1].ToString()));
-                }
-
-                visaNodes.Add(new Grp_ProcessNode()
-                {
-                    NodeOrder = 1,
-                    NodeName = "签证流程",
-                    OverallStatus = ProcessStatus.InProgress,
-                    IsCurrent = true,
-                    CreateUserId = currUserId,
-                    Remark = JsonConvert.SerializeObject(visaDefualtNodes)
-                });
-            }
-
-            return new List<Grp_ProcessOverview>
+            return new Grp_ProcessOverview
             {
-                //商邀报批流程
-                new()
-                {
-                    GroupId = groupId,
-                    ProcessOrder = 1,
-                    ProcessType = GroupProcessType.Invitation,
-                    OverallStatus = ProcessStatus.InProgress,
-                    StartTime = DateTime.Now,
-                    UpdatedUserId = currUserId,
-                    CreateUserId = currUserId,
-                    Nodes = new List<Grp_ProcessNode>()
-                    {
-                        new(){NodeOrder = 1, NodeName="报批资料准备", OverallStatus=ProcessStatus.InProgress, IsCurrent=true,CreateUserId = currUserId },
-                        new(){NodeOrder = 2, NodeName="开始报批", OverallStatus=ProcessStatus.UnStarted, IsCurrent=false,CreateUserId = currUserId },
-                        new(){NodeOrder = 3, NodeName="报批中", OverallStatus=ProcessStatus.UnStarted, IsCurrent=false,CreateUserId = currUserId },
-                        new(){NodeOrder = 4, NodeName="报批已出", OverallStatus=ProcessStatus.UnStarted, IsCurrent=false,CreateUserId = currUserId }
-                    }
-                },
-
-                //签证流程
-                new()
-                {
-                    GroupId = groupId,
-                    ProcessOrder = 2,
-                    ProcessType = GroupProcessType.Visa,
-                    OverallStatus = ProcessStatus.InProgress,
-                    StartTime = DateTime.Now,
-                    UpdatedUserId = currUserId,
-                    CreateUserId = currUserId,
-                    Nodes = visaNodes
-                },
-
-                //机票流程
-                new()
-                {
-                    GroupId = groupId,
-                    ProcessOrder = 3,
-                    ProcessType = GroupProcessType.AirTicket,
-                    OverallStatus = ProcessStatus.InProgress,
-                    StartTime = DateTime.Now,
-                    UpdatedUserId = currUserId,
-                    CreateUserId = currUserId,
-                    Nodes = new List<Grp_ProcessNode>()
-                    {
-                        new(){NodeOrder = 1, NodeName="机票占位中", OverallStatus=ProcessStatus.InProgress, IsCurrent=true,CreateUserId = currUserId },
-                        new(){NodeOrder = 2, NodeName="机票已出", OverallStatus=ProcessStatus.UnStarted, IsCurrent=false,CreateUserId = currUserId },
-                    }
-                },
-
-                //酒店流程
-                new()
-                {
-                    GroupId = groupId,
-                    ProcessOrder = 4,
-                    ProcessType = GroupProcessType.Hotel,
-                    OverallStatus = ProcessStatus.InProgress,
-                    StartTime = DateTime.Now,
-                    UpdatedUserId = currUserId,
-                    CreateUserId = currUserId,
-                    Nodes = new List<Grp_ProcessNode>()
-                    {
-                        new(){NodeOrder = 1, NodeName="酒店占房中", OverallStatus=ProcessStatus.InProgress, IsCurrent=true,CreateUserId = currUserId },
-                        new(){NodeOrder = 2, NodeName="酒店已订", OverallStatus=ProcessStatus.UnStarted, IsCurrent=false,CreateUserId = currUserId },
-                    }
-                },
-
-                //地接流程
-                new()
-                {
-                    GroupId = groupId,
-                    ProcessOrder = 5,
-                    ProcessType = GroupProcessType.LocalGuide,
-                    OverallStatus = ProcessStatus.InProgress,
-                    StartTime = DateTime.Now,
-                    UpdatedUserId = currUserId,
-                    CreateUserId = currUserId,
-                    Nodes = new List<Grp_ProcessNode>()
-                    {
-                        new(){NodeOrder = 1, NodeName="地接对接中", OverallStatus=ProcessStatus.InProgress, IsCurrent=true,CreateUserId = currUserId },
-                        new(){NodeOrder = 2, NodeName="地接已安排好", OverallStatus=ProcessStatus.UnStarted, IsCurrent=false,CreateUserId = currUserId },
-                        new(){NodeOrder = 2, NodeName="出行物资准备完毕", OverallStatus=ProcessStatus.UnStarted, IsCurrent=false,CreateUserId = currUserId },
-                    }
-                },
-
-                //费用结算流程
-                new()
-                {
-                    GroupId = groupId,
-                    ProcessOrder = 6,
-                    ProcessType = GroupProcessType.FeeSettle,
-                    OverallStatus = ProcessStatus.InProgress,
-                    StartTime = DateTime.Now,
-                    UpdatedUserId = currUserId,
-                    CreateUserId = currUserId,
-                    Nodes = new List<Grp_ProcessNode>()
-                    {
-                        new(){NodeOrder = 1, NodeName="费用结算中", OverallStatus=ProcessStatus.InProgress, IsCurrent=true,CreateUserId = currUserId },
-                        new(){NodeOrder = 2, NodeName="费用结算完毕", OverallStatus=ProcessStatus.UnStarted, IsCurrent=false,CreateUserId = currUserId },
-                    }
-                }
+                GroupId = groupId,
+                ProcessOrder = processOrder,
+                ProcessType = processType,
+                OverallStatus = ProcessStatus.InProgress,
+                StartTime = DateTime.Now,
+                UpdatedUserId = currUserId,
+                CreateUserId = currUserId,
+                Nodes = nodes
             };
         }
     }
@@ -223,6 +102,12 @@ namespace OASystem.Domain.Entities.Groups
         [SugarColumn(ColumnName = "NodeName", ColumnDescription = "节点名称", ColumnDataType = "varchar(100)")]
         public string NodeName { get; set; }
 
+        /// <summary>
+        /// 节点描述提示
+        /// </summary>
+        [SugarColumn(ColumnName = "NodeDescTips", ColumnDescription = "节点描述提示", ColumnDataType = "varchar(300)")]
+        public string NodeDescTips { get; set; }
+
         /// <summary>
         /// 节点顺序 
         /// </summary>
@@ -247,6 +132,12 @@ namespace OASystem.Domain.Entities.Groups
         [SugarColumn(ColumnName = "OperationTime", ColumnDescription = "操作时间", IsNullable = true, ColumnDataType = "datetime")]
         public DateTime? OperationTime { get; set; }
 
+        /// <summary>
+        /// 实际完成时间 
+        /// </summary>
+        [SugarColumn(ColumnName = "ActualDone", ColumnDescription = "实际完成时间", IsNullable = true, ColumnDataType = "datetime")]
+        public DateTime? ActualDone { get; set; }
+
         ///// <summary>
         ///// 国家
         ///// </summary>
@@ -260,12 +151,32 @@ namespace OASystem.Domain.Entities.Groups
         [DefaultValue(false)]
         public bool IsCurrent { get; set; }
 
-        ///// <summary>
-        ///// 流程信息 - 关联的流程信息(导航属性)
-        ///// </summary>
-        //[Ignore]
-        //[Navigate(NavigateType.OneToOne, nameof(ProcessId))]
-        //public Grp_ProcessOverview Process { get; set; }
+        public Grp_ProcessNode() { }
+
+        /// <summary>
+        /// Create
+        /// </summary>
+        /// <param name="nodeOrder"></param>
+        /// <param name="nodeName"></param>
+        /// <param name="nodeDescTips"></param>
+        /// <param name="status"></param>
+        /// <param name="isCurrent"></param>
+        /// <param name="currUserId"></param>
+        /// <param name="remark"></param>
+        /// <returns></returns>
+        public static Grp_ProcessNode Create(int nodeOrder, string nodeName, string nodeDescTips,ProcessStatus status, bool isCurrent, int currUserId, string remark=null)
+        {
+            return new Grp_ProcessNode
+            {
+                NodeOrder = nodeOrder,
+                NodeName = nodeName,
+                NodeDescTips = nodeDescTips,
+                OverallStatus = status,
+                IsCurrent = isCurrent,
+                CreateUserId = currUserId,
+                Remark = remark
+            };
+        }
     }
 
     public class VisaProcessNode
@@ -281,17 +192,17 @@ namespace OASystem.Domain.Entities.Groups
         public int OrderNo { get; set; }
 
         /// <summary>
-        /// 收集资料 - 节点名称
+        /// 收集资料 -> 签证材料收集和准备 - 节点名称
         /// </summary>
-        public string NodeName1 { get; set; } = "收集资料";
+        public string NodeName1 { get; set; } = "签证材料收集和准备";
 
         /// <summary>
-        /// 收集资料 - 是否办理
+        /// 收集资料 -> 签证材料收集和准备 - 是否办理
         /// </summary>
         public bool IsHandle1 { get; set; } = false;
 
         /// <summary>
-        /// 收集资料 - 办理时间
+        /// 收集资料 -> 签证材料收集和准备 - 办理时间
         /// </summary>
         public string HandleTime1 { get; set; } = "";
 

+ 2 - 2
OASystem/OASystem.Domain/Enums/GroupProcessType.cs

@@ -14,9 +14,9 @@ namespace OASystem.Domain.Enums
     public enum GroupProcessType
     {
         /// <summary>
-        /// 商邀报批
+        /// 商邀
         /// </summary> 
-        [Description("商邀报批")]
+        [Description("商邀")]
         Invitation = 1,
         /// <summary>
         /// 签证

+ 308 - 56
OASystem/OASystem.Infrastructure/Repositories/Groups/ProcessOverviewRepository.cs

@@ -3,6 +3,7 @@ using Newtonsoft.Json;
 using OASystem.Domain;
 using OASystem.Domain.Dtos.Groups;
 using OASystem.Domain.Entities.Groups;
+using OASystem.Domain.Entities.Resource;
 using System.Reflection;
 
 namespace OASystem.Infrastructure.Repositories.Groups
@@ -20,6 +21,180 @@ namespace OASystem.Infrastructure.Repositories.Groups
             _groupRep = groupRep;
         }
 
+
+        /// <summary>
+        /// 基础数据初始化-团组流程
+        /// </summary>
+        /// <param name="groupId"></param>
+        /// <param name="currUserId"></param>
+        /// <returns></returns>
+        public async Task<List<Grp_ProcessOverview>> ProcessDataInitAsync(int groupId, int currUserId, List<string> visaCountries)
+        {
+            var processs = new List<Grp_ProcessOverview>();
+
+            //团组验证
+            var groupInfo = await _sqlSugar.Queryable<Grp_DelegationInfo>().FirstAsync(g => g.Id == groupId);
+            if (groupInfo == null) return processs;
+
+            // 检查是否已存在流程
+            var existingProcesses = await _sqlSugar.Queryable<Grp_ProcessOverview>()
+                .Where(p => p.IsDel == 0 && p.GroupId == groupId)
+                .ToListAsync();
+            if (existingProcesses.Any()) return processs;
+
+            #region 商邀报批流程
+            var custInfo = await _sqlSugar.Queryable<Grp_TourClientList>()
+                .Where(c => c.DiId == groupId && c.IsDel == 0)
+                .OrderByDescending(c => c.CreateTime)
+                .FirstAsync();
+            string oaNode2Tips = "客户提供完整名单后,2周内取得邀请函(翻译件)。";
+            if (custInfo != null)
+            {
+                oaNode2Tips = $"请于{custInfo.CreateTime.AddDays(14):yyyy年MM月dd日}内完成该项工作(客户提供完整名单后,2周内取得邀请函(翻译件)。)";
+            }
+
+            var oaNode4Tips = $"请于{groupInfo.VisitDate.AddDays(-5):yyyy年MM月dd日}内完成该项工作(按进度实际公务活动落实情况,出发前5日落实公务。)";
+            processs.Add(
+                Grp_ProcessOverview.Create(groupId, 1, GroupProcessType.Invitation, ProcessStatus.InProgress, currUserId,
+                new List<Grp_ProcessNode>()
+                    {
+                        Grp_ProcessNode.Create(1, "报批基础资料准备","更新报批行程和请示,提供其他报批所需材料,4个工作日内完成。",ProcessStatus.InProgress, true,currUserId),
+                        Grp_ProcessNode.Create(2, "报批邀请函资料准备",oaNode2Tips, ProcessStatus.InProgress, false,currUserId),
+                        Grp_ProcessNode.Create(3, "获得批件","提供完整的报批全套资源。",ProcessStatus.InProgress, false, currUserId ),
+                        Grp_ProcessNode.Create(4, "对接公务",oaNode4Tips,ProcessStatus.InProgress, false, currUserId),
+                        Grp_ProcessNode.Create(5, "参与翻译对接","",ProcessStatus.InProgress, false, currUserId),
+                        Grp_ProcessNode.Create(6, "商邀文案配合","",ProcessStatus.InProgress, false, currUserId),
+                    }));
+            #endregion
+
+            #region 签证流程
+
+            //单独处理签证流程节点
+            var visaNodes = new List<Grp_ProcessNode>();
+
+            if (visaCountries != null && visaCountries.Count > 0)
+            {
+                var visaDefualtNodes = new List<VisaProcessNode>();
+                for (int i = 1; i < visaCountries.Count + 1; i++)
+                {
+                    visaDefualtNodes.Add(VisaProcessNode.Info(i, visaCountries[i - 1].ToString()));
+                }
+
+                visaNodes.Add(Grp_ProcessNode.Create(1, "签证", "", ProcessStatus.InProgress, true, currUserId, JsonConvert.SerializeObject(visaDefualtNodes)));
+            }
+
+            processs.Add(Grp_ProcessOverview.Create(groupId, 2, GroupProcessType.Visa, ProcessStatus.InProgress, currUserId, visaNodes));
+            #endregion
+
+            #region 机票流程
+            string airNode1Tips = "建团后打勾确认出团的时候开始24小时内。";
+            if (groupInfo.Step == 1 || groupInfo.Step == 2)
+            {
+                if (groupInfo.StepOperationTime.HasValue)
+                {
+                    airNode1Tips = $"请于{groupInfo.StepOperationTime.Value.AddDays(1):yyyy年MM月dd日}内完成该项工作(建团后打勾确认出团的时候开始24小时内)";
+                }
+            }
+            
+            var airNode5Tips = $"请于{groupInfo.VisitDate.AddDays(-5):yyyy年MM月dd日}内完成该项工作(团组出发前5日)";
+            processs.Add(
+                Grp_ProcessOverview.Create(groupId, 3, GroupProcessType.AirTicket, ProcessStatus.InProgress, currUserId,
+                new List<Grp_ProcessNode>()
+                    {
+                        Grp_ProcessNode.Create(1, "初步拟定航程方案及价格",   airNode1Tips, ProcessStatus.InProgress, true,currUserId ),
+                        Grp_ProcessNode.Create(2, "机票占位、续位",   "", ProcessStatus.UnStarted, false,currUserId ),
+                        Grp_ProcessNode.Create(3, "完成机票采购确认(含预算核对、出票确认等)",  "", ProcessStatus.UnStarted,false,currUserId),
+                        Grp_ProcessNode.Create(4, "进行出票操作并核查信息", "", ProcessStatus.UnStarted, false, currUserId),
+                        Grp_ProcessNode.Create(5, "机票已出",  airNode5Tips, ProcessStatus.UnStarted, false, currUserId),
+                        Grp_ProcessNode.Create(6, "完成机票选座",  "", ProcessStatus.UnStarted, false,currUserId)
+                    }
+                )
+             );
+            #endregion
+
+            #region 酒店流程
+
+            string hotelNode1Tips = "建团后打勾确认出团的时候开始2个工作日。";
+            if (groupInfo.Step == 1 || groupInfo.Step == 2)
+            {
+                if (groupInfo.StepOperationTime.HasValue)
+                {
+                    hotelNode1Tips = $"请于{groupInfo.StepOperationTime.Value.AddDays(2):yyyy年MM月dd日}内完成该项工作(建团后打勾确认出团的时候开始2个工作日)";
+                }
+            }
+
+            var hotelNode4Tips = $"请于{groupInfo.VisitDate.AddDays(-5):yyyy年MM月dd日}内完成该项工作(团组出发前5天)";
+            var hotelNode5Tips = $"请于{groupInfo.VisitEndDate.AddDays(5):yyyy年MM月dd日}内完成该项工作(团组结束后5天内)";
+
+            processs.Add(
+                Grp_ProcessOverview.Create(groupId, 4, GroupProcessType.Hotel, ProcessStatus.InProgress, currUserId,
+                     new List<Grp_ProcessNode>()
+                    {
+                        Grp_ProcessNode.Create(1, "筛选并按照预算标准,对目标酒店进行询价、比价、谈价", hotelNode1Tips, ProcessStatus.InProgress, true, currUserId),
+                        Grp_ProcessNode.Create(2, "获取酒店确认函与入住名单核对", "", ProcessStatus.UnStarted, false, currUserId ),
+                        Grp_ProcessNode.Create(3, "预订酒店并录入OA",  "", ProcessStatus.UnStarted,false,currUserId ),
+                        Grp_ProcessNode.Create(4, "行前再次确认酒店订单、付款状态及入住安排",  hotelNode4Tips,ProcessStatus.UnStarted, false,currUserId ),
+                        Grp_ProcessNode.Create(5, "行程结束后整理酒店发票与结算",  hotelNode5Tips, ProcessStatus.UnStarted, false, currUserId ),
+                    }
+                )
+            );
+            #endregion
+
+            #region 地接流程
+            var airTripCodeInfo = await _sqlSugar.Queryable<Air_TicketBlackCode>()
+                .Where(x => x.IsDel == 0 && x.DiId == groupId)
+                .OrderByDescending(x => x.CreateTime)
+                .FirstAsync();
+            string opNode1Tips = $"机票行程代码最后一段录入后1个工作日内。";
+            if (airTripCodeInfo != null)
+            {
+                opNode1Tips = $"请于{airTripCodeInfo.CreateTime.AddDays(1):yyyy年MM月dd日}内完成该项工作(机票行程代码最后一段录入后1个工作日内)";
+            }
+
+            string opNode2Tips = $"请于{groupInfo.CreateTime.AddDays(7):yyyy年MM月dd日}内完成该项工作(建团完成后7个工作日内)";
+            string opNode3Tips = $"请于{groupInfo.CreateTime.AddDays(10):yyyy年MM月dd日}内完成该项工作(上一步往后3个工作日内)";
+            string opNode4Tips = $"请于{groupInfo.CreateTime.AddDays(12):yyyy年MM月dd日}内完成该项工作(上一步往后2个工作日内)";
+
+            var backListInfo = await _sqlSugar.Queryable<Grp_InvertedList>().Where(x => x.DiId == groupId && x.IsDel == 0).FirstAsync();
+            string opNode5Tips = $"倒推表里开行前会 -3天。";
+            if (backListInfo != null) {
+
+                if (DateTime.TryParse(backListInfo.PreTripMeetingDt,out DateTime dateTime))
+                {
+                    opNode5Tips = $"请于{dateTime.AddDays(-3):yyyy年MM月dd日}内完成该项工作(倒推表里开行前会 -3天)";
+                }
+            }
+            processs.Add(
+                Grp_ProcessOverview.Create(groupId, 5, GroupProcessType.LocalGuide, ProcessStatus.InProgress, currUserId,
+                     new List<Grp_ProcessNode>()
+                    {
+                        Grp_ProcessNode.Create(1,"根据机票方案出框架行程", opNode1Tips,ProcessStatus.InProgress, true, currUserId ),
+                        Grp_ProcessNode.Create(2,"联系并询价地接、餐厅、用车、景点等供应商", opNode2Tips,ProcessStatus.UnStarted, false, currUserId ),
+                        Grp_ProcessNode.Create(3,"提交供应商报价及比价表", opNode3Tips, ProcessStatus.UnStarted, false, currUserId),
+                        Grp_ProcessNode.Create(4,"执行采购流程", opNode4Tips, ProcessStatus.UnStarted, false, currUserId),
+                        Grp_ProcessNode.Create(5,"制定最终《行程表》及《出行手册》", opNode5Tips, ProcessStatus.UnStarted, false, currUserId ),
+                        Grp_ProcessNode.Create(6,"送机", "", ProcessStatus.UnStarted, false, currUserId ),
+                    }
+                )
+            );
+
+            #endregion
+
+            #region 费用结算流程
+            processs.Add(
+                Grp_ProcessOverview.Create(groupId, 6, GroupProcessType.FeeSettle, ProcessStatus.InProgress, currUserId,
+                    new List<Grp_ProcessNode>()
+                    {
+                        Grp_ProcessNode.Create(1, "费用结算中", "", ProcessStatus.InProgress, true,currUserId ),
+                        Grp_ProcessNode.Create(2, "费用结算完毕", "", ProcessStatus.UnStarted, false,currUserId ),
+                    }
+                )
+            );
+            #endregion
+
+            return processs;
+        }
+
         /// <summary>
         /// 团组流程初始化
         /// </summary>
@@ -35,7 +210,9 @@ namespace OASystem.Infrastructure.Repositories.Groups
             }
 
             // 检查是否已存在流程
-            var existingProcesses = await _sqlSugar.Queryable<Grp_ProcessOverview>().Where(p => p.GroupId == groupId).ToListAsync();
+            var existingProcesses = await _sqlSugar.Queryable<Grp_ProcessOverview>()
+                .Where(p => p.IsDel == 0 && p.GroupId == groupId)
+                .ToListAsync();
             if (existingProcesses.Any())
             {
                 return new Result { Code = 400, Msg = "该团组的流程已存在" };
@@ -45,7 +222,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
             var visaCountries = _groupRep.GroupSplitCountry(groupInfo.VisitCountry);
 
             // 定义默认的流程节点
-            var processs = Grp_ProcessOverview.ProcessInit(groupId, currUserId, visaCountries);
+            var processs = await ProcessDataInitAsync(groupId, currUserId, visaCountries);
 
             _sqlSugar.BeginTran();
 
@@ -68,6 +245,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
                     NodeName = nodeDto.NodeName,
                     NodeOrder = nodeDto.NodeOrder,
                     OverallStatus = nodeDto.OverallStatus,
+                    NodeDescTips = nodeDto.NodeDescTips,
                     //Country = nodeDto.Country,
                     IsCurrent = nodeDto.IsCurrent,
                     Remark = nodeDto.Remark
@@ -106,7 +284,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
             }
 
             // 检查是否已存在流程
-            var existingProcesses = await _sqlSugar.Queryable<Grp_ProcessOverview>().Where(p => p.GroupId == groupId).ToListAsync();
+            var existingProcesses = await _sqlSugar.Queryable<Grp_ProcessOverview>().Where(p => p.IsDel == 0 && p.GroupId == groupId).ToListAsync();
             if (!existingProcesses.Any())
             {
                 //新建团组流程
@@ -117,7 +295,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
                 }
             }
 
-            var users = await _sqlSugar.Queryable<Sys_Users>().ToListAsync();
+            var users = await _sqlSugar.Queryable<Sys_Users>().Select(x => new {x.Id,x.CnName }).ToListAsync();
 
             var processData = await _sqlSugar.Queryable<Grp_ProcessOverview>()
                 .Where(p => p.GroupId == groupId && p.IsDel == 0)
@@ -152,6 +330,8 @@ namespace OASystem.Infrastructure.Repositories.Groups
                         StatusText = n.OverallStatus.GetEnumDescription(),
                         Operator = users.FirstOrDefault(u => u.Id == n.Operator)?.CnName ?? "-",
                         OpeateTime = n.OperationTime.HasValue ? n.OperationTime.Value.ToString("yyyy-MM-dd HH:mm:ss") : "-",
+                        ActualDone = n.ActualDone?.ToString("yyyy-MM-dd HH:mm:ss") ?? "",
+                        n.NodeDescTips,
                         //节点类型为签证时使用
                         visaSubNodes
                     };
@@ -184,11 +364,21 @@ namespace OASystem.Infrastructure.Repositories.Groups
                         throw new BusinessException("当前节点不存在或已被删除。");
                     }
 
-                    // 新增验证:当前节点已完成,不可操作
+                    // 2. 获取流程信息,检查ProcessType
+                    var process = await _sqlSugar.Queryable<Grp_ProcessOverview>()
+                        .FirstAsync(p => p.Id == node.ProcessId && p.IsDel == 0);
+
+                    if (process == null)
+                    {
+                        throw new BusinessException("关联的流程不存在。");
+                    }
+
+                    // 3. 节点操作验证
                     ValidateNodeOperation(node, processStatus);
 
-                    //存储更新前的值
-                    var before = new Grp_ProcessNode() { 
+                    // 4. 存储更新前的值
+                    var before = new Grp_ProcessNode()
+                    {
                         Id = node.Id,
                         ProcessId = node.ProcessId,
                         NodeName = node.NodeName,
@@ -199,7 +389,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
                         IsCurrent = node.IsCurrent,
                     };
 
-                    // 2. 更新节点状态
+                    // 5. 更新节点状态
                     node.OverallStatus = processStatus;
                     node.Operator = currUserId;
                     node.OperationTime = DateTime.Now;
@@ -218,11 +408,12 @@ namespace OASystem.Infrastructure.Repositories.Groups
                         throw new BusinessException("节点状态更新失败。");
                     }
 
-                    //记录节点日志
+                    // 6. 记录节点日志
                     await LogNodeOpAsync(before, node, "Update", currUserId);
 
-                    // 3. 如果是完成当前节点,处理流程流转
-                    if (processStatus == ProcessStatus.Completed && node.IsCurrent)
+                    // 7. 如果是完成当前节点,处理流程流转
+                    //    当前节点或者流程类型为商邀可进入状态流转
+                    if (processStatus == ProcessStatus.Completed && (node.IsCurrent || process.ProcessType == GroupProcessType.Invitation))
                     {
                         await ProcessCurrentNodeCompletionAsync(node, currUserId);
                     }
@@ -262,10 +453,10 @@ namespace OASystem.Infrastructure.Repositories.Groups
             }
 
             // 验证状态流转是否合法(可选)
-            if (targetStatus != ProcessStatus.Completed)
-            {
-                throw new BusinessException("未开始或者进行中的节点只能重新完成,不可进行其他操作。");
-            }
+            //if (targetStatus != ProcessStatus.Completed)
+            //{
+            //    throw new BusinessException("未开始或者进行中的节点只能重新完成,不可进行其他操作。");
+            //}
 
             // 验证是否尝试将已完成节点改为其他状态
             if (node.OverallStatus == ProcessStatus.Completed && targetStatus != ProcessStatus.Completed)
@@ -320,59 +511,76 @@ namespace OASystem.Infrastructure.Repositories.Groups
             // 2.1 记录节点日志 取消当前节点状态
             await LogNodeOpAsync(before, currentNode, "Update", currUserId);
 
-            // 3. 查找并激活下一个节点
-            var nextNode = await _sqlSugar.Queryable<Grp_ProcessNode>()
+            // 3. 查找并激活下一个节点 商邀节点单独处理
+            if (process.ProcessType == GroupProcessType.Invitation)
+            {
+                var invitaNodeStatus = await _sqlSugar.Queryable<Grp_ProcessNode>()
+                    .Where(x => x.IsDel == 0 && x.ProcessId == currentNode.ProcessId)
+                    .ToListAsync();
+
+                int completedCount = invitaNodeStatus.Count(n => n.OverallStatus == ProcessStatus.Completed);
+                int nodeCount = invitaNodeStatus.Count;
+                if (completedCount == nodeCount) //全部子节点完成,该流程完成
+                {
+                    process.OverallStatus = ProcessStatus.Completed;
+                    process.EndTime = DateTime.Now;
+                }
+            }
+            else
+            {
+                var nextNode = await _sqlSugar.Queryable<Grp_ProcessNode>()
                 .Where(n => n.ProcessId == currentNode.ProcessId
                          && n.NodeOrder == currentNode.NodeOrder + 1
                          && n.IsDel == 0)
                 .FirstAsync();
 
-            if (nextNode != null)
-            {
-                var nextNodeBefore = new Grp_ProcessNode()
+                if (nextNode != null)
                 {
-                    Id = nextNode.Id,
-                    ProcessId = nextNode.ProcessId,
-                    NodeName = nextNode.NodeName,
-                    NodeOrder = nextNode.NodeOrder,
-                    OverallStatus = nextNode.OverallStatus,
-                    Operator = nextNode.Operator,
-                    OperationTime = nextNode.OperationTime,
-                    IsCurrent = nextNode.IsCurrent,
-                };
+                    var nextNodeBefore = new Grp_ProcessNode()
+                    {
+                        Id = nextNode.Id,
+                        ProcessId = nextNode.ProcessId,
+                        NodeName = nextNode.NodeName,
+                        NodeOrder = nextNode.NodeOrder,
+                        OverallStatus = nextNode.OverallStatus,
+                        Operator = nextNode.Operator,
+                        OperationTime = nextNode.OperationTime,
+                        IsCurrent = nextNode.IsCurrent,
+                    };
 
-                // 激活下一个节点
-                nextNode.IsCurrent = true;
-                nextNode.OverallStatus = ProcessStatus.InProgress;
-                //nextNode.Operator = currUserId;
-                //nextNode.OperationTime = DateTime.Now;
+                    // 激活下一个节点
+                    nextNode.IsCurrent = true;
+                    nextNode.OverallStatus = ProcessStatus.InProgress;
+                    //nextNode.Operator = currUserId;
+                    //nextNode.OperationTime = DateTime.Now;
 
-                var updateCount = await _sqlSugar.Updateable(nextNode)
-                    .UpdateColumns(n => new
+                    var updateCount = await _sqlSugar.Updateable(nextNode)
+                        .UpdateColumns(n => new
+                        {
+                            n.IsCurrent,
+                            n.OverallStatus,
+                            n.Operator,
+                            n.OperationTime
+                        })
+                        .ExecuteCommandAsync();
+
+                    if (updateCount == 0)
                     {
-                        n.IsCurrent,
-                        n.OverallStatus,
-                        n.Operator,
-                        n.OperationTime
-                    })
-                    .ExecuteCommandAsync();
+                        throw new BusinessException("激活下一节点失败");
+                    }
+
+                    // 1.1 记录节点日志 激活下一节点当前节点状态
+                    await LogNodeOpAsync(nextNodeBefore, nextNode, "Start", currUserId);
 
-                if (updateCount == 0)
+                    // 更新流程状态为进行中
+                    process.OverallStatus = ProcessStatus.InProgress;
+                }
+                else
                 {
-                    throw new BusinessException("激活下一节点失败");
+                    // 下一节点不存在,整个流程完成
+                    process.OverallStatus = ProcessStatus.Completed;
+                    process.EndTime = DateTime.Now;
                 }
-
-                // 1.1 记录节点日志 激活下一节点当前节点状态
-                await LogNodeOpAsync(nextNodeBefore, nextNode, "Start", currUserId);
-
-                // 更新流程状态为进行中
-                process.OverallStatus = ProcessStatus.InProgress;
-            }
-            else
-            {
-                // 下一节点不存在,整个流程完成
-                process.OverallStatus = ProcessStatus.Completed;
-                process.EndTime = DateTime.Now;
             }
 
             // 4. 更新流程信息
@@ -492,6 +700,50 @@ namespace OASystem.Infrastructure.Repositories.Groups
             return new Result { Code = 200, Msg = "节点信息更新成功。" };
         }
 
+        /// <summary>
+        /// 更新签证节点信息及状态
+        /// </summary>
+        /// <param name="dto">签证节点更新数据传输对象</param>
+        /// <returns>操作结果</returns>
+        public async Task<Result> SetActualDoneAsync(int nodeId, DateTime dt, int currUserId)
+        {
+            // 1. 获取并验证节点和流程
+            var node = await _sqlSugar.Queryable<Grp_ProcessNode>()
+                .FirstAsync(n => n.Id == nodeId && n.IsDel == 0)
+                ?? throw new BusinessException("当前节点不存在或已被删除。");
+
+            var process = await _sqlSugar.Queryable<Grp_ProcessOverview>()
+                .FirstAsync(p => p.Id == node.ProcessId && p.IsDel == 0)
+                ?? throw new BusinessException("当前流程不存在或已被删除。");
+
+            // 2.1 存储更新前流程及节点信息
+            var nodeBefore = new Grp_ProcessNode()
+            {
+                Id = node.Id,
+                ProcessId = node.ProcessId,
+                NodeName = node.NodeName,
+                NodeOrder = node.NodeOrder,
+                OverallStatus = node.OverallStatus,
+                Operator = node.Operator,
+                OperationTime = node.OperationTime,
+                IsCurrent = node.IsCurrent,
+            };
+
+            node.ActualDone = dt;
+
+            // 3. 保存节点更新
+            await _sqlSugar.Updateable(node)
+                .UpdateColumns(n => new
+                {
+                    n.ActualDone
+                })
+                .ExecuteCommandAsync();
+            //记录节点日志
+            await LogNodeOpAsync(nodeBefore, node, "Update", currUserId);
+
+            return new Result { Code = 200, Msg = "实际操作时间设置成功。" };
+        }
+
         #region 操作日志
 
         /// <summary>