소스 검색

1、倒退表 新增字段
2、团组流程 新增 节点操作权限

Lyyyi 3 일 전
부모
커밋
06e3b769da

+ 186 - 12
OASystem/OASystem.Api/Controllers/GroupsController.cs

@@ -1691,7 +1691,7 @@ namespace OASystem.API.Controllers
                     .ExecuteCommandAsync();
 
                 //默认创建倒推表
-                await _invertedListRep._Create(4, dto.Diid, msg);
+                await _invertedListRep.Create(4, dto.Diid, msg);
             }
 
             #endregion
@@ -1770,7 +1770,7 @@ namespace OASystem.API.Controllers
                                 .ExecuteCommandAsync();
 
                             //默认创建倒推表
-                            await _invertedListRep._Create(dto.UserId, dto.Id, msg);
+                            await _invertedListRep.Create(dto.UserId, dto.Id, msg);
                         }
                     }
                     #endregion
@@ -1816,7 +1816,7 @@ namespace OASystem.API.Controllers
                     }
 
                     //默认创建倒推表
-                    await _invertedListRep._Create(dto.UserId, diId);
+                    await _invertedListRep.Create(dto.UserId, diId);
 
                     //默认创建签证流程
                     await _visaProcessRep.Create(dto.UserId, diId);
@@ -2014,7 +2014,7 @@ namespace OASystem.API.Controllers
                     #endregion
 
                     //默认创建倒推表
-                    await _invertedListRep._Create(dto.UserId, diId);
+                    await _invertedListRep.Create(dto.UserId, diId);
 
                     //默认创建签证流程
                     await _visaProcessRep.Create(dto.UserId, diId);
@@ -2220,9 +2220,8 @@ namespace OASystem.API.Controllers
                     }
                     #endregion
 
-
                     //默认创建倒推表
-                    await _invertedListRep._Create(dto.UserId, diId);
+                    await _invertedListRep.Create(dto.UserId, diId);
                 }
                 else if (dto.Status == 2)
                 {
@@ -28790,7 +28789,7 @@ AirHotelPrice
         [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
         public async Task<IActionResult> PostInvertedListInit()
         {
-            var viewData = await _invertedListRep._Init();
+            var viewData = await _invertedListRep.Init();
             if (viewData.Code != 0)
             {
                 return Ok(JsonView(false, viewData.Msg));
@@ -28809,7 +28808,7 @@ AirHotelPrice
         [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
         public async Task<IActionResult> PostInvertedListInfo(InvertedListCreateDto dto)
         {
-            var viewData = await _invertedListRep._Info(dto.PortType, dto.DiId);
+            var viewData = await _invertedListRep.Info(dto.PortType, dto.DiId);
             if (viewData.Code != 0)
             {
                 return Ok(JsonView(false, viewData.Msg));
@@ -28829,7 +28828,7 @@ AirHotelPrice
         [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
         public async Task<IActionResult> PostInvertedListCreate(InvertedListCreateDto dto)
         {
-            var viewData = await _invertedListRep._Create(dto.PortType, dto.DiId);
+            var viewData = await _invertedListRep.Create(dto.PortType, dto.DiId);
             if (viewData.Code != 0)
             {
                 return Ok(JsonView(false, viewData.Msg));
@@ -28848,7 +28847,7 @@ AirHotelPrice
         [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
         public async Task<IActionResult> PostInvertedListUpdate(InvertedListUpdateDto dto)
         {
-            var viewData = await _invertedListRep._Update(dto);
+            var viewData = await _invertedListRep.Update(dto);
             if (viewData.Code != 0)
             {
                 return Ok(JsonView(false, viewData.Msg));
@@ -28868,7 +28867,7 @@ AirHotelPrice
         public async Task<IActionResult> PostInvertedListFileDownload(InvertedListFileDownloadDto dto)
         {
 
-            var info2 = await _invertedListRep._Info(1, dto.DiId);
+            var info2 = await _invertedListRep.Info(1, dto.DiId);
             if (info2.Code != 0)
             {
                 return Ok(JsonView(false, "倒推表数据未生成,请先生成倒退表数据!"));
@@ -29071,7 +29070,6 @@ AirHotelPrice
 
             #endregion
 
-
             var fileDir = AppSettingsHelper.Get("WordBasePath");
             string fileName = $"{teamName}团出行准备流程表{CommonFun.GUID}.doc";
             string filePath = fileDir + $@"InvertedList/{fileName}";
@@ -29080,6 +29078,182 @@ AirHotelPrice
             return Ok(JsonView(true, "操作成功!", Url));
         }
 
+        /// <summary>
+        /// 倒推表 
+        /// File Download
+        /// 客户版
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost]
+        [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
+        public async Task<IActionResult> PostInvertedListClientFileDownload(InvertedListFileDownloadDto dto)
+        {
+            var info2 = await _invertedListRep.Info(1, dto.DiId);
+            if (info2.Code != 0)
+            {
+                return Ok(JsonView(false, "倒推表数据未生成,请先生成倒退表数据!"));
+            }
+            var info1 = info2.Data as InvertedListInfoView;
+            var info = await _invertedListRep._sqlSugar.Queryable<Grp_DelegationInfo>().Where(it => it.Id == dto.DiId).FirstAsync();
+            string teamName = "";
+            if (info != null) teamName = info.TeamName;
+
+
+            var tempPath = $"{AppSettingsHelper.Get("WordBasePath")}Template/倒退表模板20250121.doc";
+            var doc = new Document();
+            var builder = new DocumentBuilder(doc);
+
+            #region New Insert
+
+            string titleFont = $"仿宋",
+                   contentFont = $"仿宋";
+
+            var fontColor = Color.FromArgb(64, 64, 64);
+            //设置标题 团组名称
+            builder.ParagraphFormat.Alignment = ParagraphAlignment.Center;
+            builder.Font.Name = titleFont;
+            builder.Font.Color = fontColor;
+            builder.Font.Size = 12;
+            builder.Font.Bold = true;
+            builder.Writeln(teamName);
+            //设置副标题 团组出访时间
+            builder.Font.Size = 12;
+            builder.Writeln("出发时间:" + info1.AirportdDropOffDt.ConvertToDatetime());
+            builder.Writeln();
+
+            var backgroundColor = Color.FromArgb(199, 199, 199);
+
+            var table = builder.StartTable();
+
+            builder.InsertCell();
+            builder.CellFormat.Shading.BackgroundPatternColor = backgroundColor;
+            builder.CellFormat.Borders.LineStyle = Aspose.Words.LineStyle.Single;
+            builder.CellFormat.Borders.LineWidth = 1;
+            builder.CellFormat.Borders.Color = Color.FromArgb(183, 183, 183);
+
+            table.FirstRow.FirstCell.CellFormat.Width = 20; // 单位为百分比
+            builder.RowFormat.Height = 40;
+            builder.CellFormat.VerticalAlignment = CellVerticalAlignment.Center;
+            builder.Font.Name = contentFont;
+            builder.Font.Size = 12;
+            builder.Font.Bold = true;
+            builder.Font.Color = fontColor;
+            builder.Write("时间");
+
+            builder.InsertCell();
+            table.FirstRow.FirstCell.CellFormat.Width = 30; // 单位为百分比
+            builder.Write("流程");
+
+            builder.InsertCell();
+            table.FirstRow.FirstCell.CellFormat.Width = 50; // 单位为百分比
+            builder.Write("说明");
+            builder.EndRow();
+
+            //报批资料准备
+            if (info1.IsSelectedAirportd)
+            {
+                InvertedListFileDownloadSetRow(builder, contentFont, info1.Client_ApprovalDataDt.ConvertToDatetime(), "报批资料准备", info1.ApprovalDataRemark);
+            }
+            //报批/提供送签资料
+            if (info1.IsSelectedApprovalData)
+            {
+                InvertedListFileDownloadSetRow(builder, contentFont, info1.Client_ApprovalDt.ConvertToDatetime(), "报批/提供送签资料", info1.ApprovalRemark);
+            }
+            //出批件护照办理
+            if (info1.IsSelectedIssueApproval)
+            {
+                InvertedListFileDownloadSetRow(builder, contentFont, info1.Client_IssueApprovalDt.ConvertToDatetime(), "出批件\r\n护照办理", info1.IssueApprovalRemark);
+            }
+            //签证资料准备
+            if (info1.IsSelectedVisaInfo)
+            {
+                InvertedListFileDownloadSetRow(builder, contentFont, info1.Client_VisaInformationDt.ConvertToDatetime(), "签证资料准备", info1.VisaInformationRemark);
+            }
+            //送签
+            if (info1.IsSelectedSendVisa)
+            {
+                InvertedListFileDownloadSetRow(builder, contentFont, info1.Client_SendVisaDt.ConvertToDatetime(), "送签", info1.SendVisaRemark);
+            }
+            //出签
+            if (info1.IsSelectedIssueVisa)
+            {
+                InvertedListFileDownloadSetRow(builder, contentFont, info1.Client_IssueVisaDt.ConvertToDatetime(), "出签", info1.IssueVisaRemark);
+            }
+
+            //机票
+            if (info1.IsSelectedAirTicket)
+            {
+                if (string.IsNullOrEmpty(info1.Client_AirTicketDt)) info1.Client_AirTicketDt = info1.Client_IssueVisaDt;
+
+                InvertedListFileDownloadSetRow(builder, contentFont, info1.Client_AirTicketDt.ConvertToDatetime(), "机票", info1.AirTicketRemark);
+            }
+            //酒店
+            if (info1.IsSelectedHotel)
+            {
+                if (string.IsNullOrEmpty(info1.Client_HotelDt)) info1.Client_HotelDt = info1.Client_IssueVisaDt;
+                InvertedListFileDownloadSetRow(builder, contentFont, info1.Client_HotelDt.ConvertToDatetime(), "酒店", info1.HotelRemark);
+            }
+
+            //开行前会
+            if (info1.IsSelectedPreTripMeeting)
+            {
+                InvertedListFileDownloadSetRow(builder, contentFont, info1.Client_PreTripMeetingDt.ConvertToDatetime(), "开行前会", info1.PreTripMeetingRemark);
+            }
+            //送机
+            if (info1.IsSelectedAirportdDropOff)
+            {
+                InvertedListFileDownloadSetRow(builder, contentFont, info1.Client_AirportdDropOffDt.ConvertToDatetime(), "送机", info1.AirportdDropOffRemark);
+            }
+
+
+            builder.EndTable();
+
+            //table.PreferredWidth = PreferredWidth.FromPercent(100.00);
+            //table.AutoFit(AutoFitBehavior.FixedColumnWidths);
+            for (int i = 0; i < table.Rows.Count; i++)
+            {
+                var row = table.Rows[i];
+                //设置宽度
+                row.Cells[0].CellFormat.Width = 20.00;
+                row.Cells[1].CellFormat.Width = 30.00;
+                row.Cells[2].CellFormat.Width = 50.00;
+                if (i != 0)
+                {
+                    //边框设置
+                    InvertedListFileDownloadSetBorders(row.Cells, 0);
+                    InvertedListFileDownloadSetBorders(row.Cells, 1);
+                    InvertedListFileDownloadSetBorders(row.Cells, 2);
+                }
+            }
+
+            #region 页脚插入图片
+
+            // 创建页脚
+            Aspose.Words.HeaderFooter footer = new Aspose.Words.HeaderFooter(doc, HeaderFooterType.FooterPrimary);
+            doc.FirstSection.HeadersFooters.Add(footer);
+
+            Paragraph para = new Paragraph(doc);
+            para.ParagraphFormat.Alignment = ParagraphAlignment.Right;
+            footer.AppendChild(para);
+
+            builder.MoveTo(para);
+
+            string imagePath = "./Images/FMGJ_Log.png";
+            builder.InsertImage(imagePath);
+
+            #endregion
+
+            #endregion
+
+            var fileDir = AppSettingsHelper.Get("WordBasePath");
+            string fileName = $"{teamName}团出行准备流程表(客户版){CommonFun.GUID}.doc";
+            string filePath = fileDir + $@"InvertedList/{fileName}";
+            doc.Save(filePath);
+            string Url = $@"{AppSettingsHelper.Get("WordBaseUrl")}Office/Word/InvertedList/{fileName}";
+            return Ok(JsonView(true, "操作成功!", Url));
+        }
+
         private void InvertedListFileDownloadSetRow(DocumentBuilder builder, string fontName, string dateTime, string content, string reamrk)
         {
             var fontColor = Color.FromArgb(64, 64, 64);

+ 108 - 71
OASystem/OASystem.Api/Middlewares/ExceptionHandlingMiddleware.cs

@@ -1,5 +1,7 @@
 
 using Microsoft.Data.SqlClient;
+using System.Text.Json;
+using System.Text.Json.Serialization;
 
 namespace OASystem.API.Middlewares
 {
@@ -43,86 +45,121 @@ namespace OASystem.API.Middlewares
         /// <summary>
         /// 自定义全局异常处理方法
         /// </summary>
-        /// <param name="context"></param>
-        /// <param name="exception"></param>
-        /// <param name="db"></param>
+        /// <param name="context">HTTP上下文</param>
+        /// <param name="exception">捕获的异常</param>
+        /// <param name="db">SqlSugar客户端</param>
         /// <returns></returns>
         private async Task HandleExceptionAsync(HttpContext context, Exception exception, SqlSugarClient db)
         {
-            //if (db.Ado != null && db.Ado.Transaction != null)
-            //{
-            //    try
-            //    {
-            //        db.Ado.RollbackTran();
-            //    }
-            //    catch (Exception ex)
-            //    {
-            //        _logger.LogError("Error rolling back transaction: {ErrorMessage}", ex.Message);
-            //    }
-            //}
-
-            // 检查响应是否已开始
-            if (!context.Response.HasStarted)
+            // ========== 1. 事务回滚(恢复并优化) ==========
+            try
             {
-                context.Response.ContentType = "application/json"; // 设置响应内容类型为 JSON
-                var response = context.Response;
-                var errorResponse = new JsonView
+                if (db?.Ado?.Transaction != null)
                 {
-                    Code = StatusCodes.Status500InternalServerError,
-                    Data = ""
-                };
-
-                // 根据异常类型设置不同的响应状态码和消息
-                switch (exception)
-                {
-                    case BusinessException businessEx: // 新增:处理 BusinessException
-                        response.StatusCode = StatusCodes.Status400BadRequest;
-                        errorResponse.Msg = businessEx.Message;
-                        errorResponse.Code = StatusCodes.Status400BadRequest; // 设置正确的 HTTP 状态码
-                        break;
-                    case SqlException sqlEx when sqlEx.Number == -2:
-                        response.StatusCode = StatusCodes.Status503ServiceUnavailable;
-                        errorResponse.Msg = "数据库连接超时,请稍后重试。";
-                        break;
-                    case SqlException sqlEx when sqlEx.Number == -4:
-                        response.StatusCode = StatusCodes.Status503ServiceUnavailable;
-                        errorResponse.Msg = "数据库连接池耗尽,请稍后重试。";
-                        break;
-                    case ApplicationException ex when ex.Message.Contains("Invalid token"):
-                        response.StatusCode = StatusCodes.Status403Forbidden;
-                        errorResponse.Msg = ex.Message;
-                        break;
-                    case ApplicationException ex:
-                        response.StatusCode = StatusCodes.Status400BadRequest;
-                        errorResponse.Msg = ex.Message;
-                        break;
-                    case KeyNotFoundException ex:
-                        response.StatusCode = StatusCodes.Status404NotFound;
-                        errorResponse.Msg = ex.Message;
-                        break;
-                    case SqlSugarException ex:
-                        response.StatusCode = ex.Message.Contains("timeout")
-                            ? StatusCodes.Status503ServiceUnavailable
-                            : StatusCodes.Status500InternalServerError;
-                        errorResponse.Msg = ex.Message.Contains("timeout")
-                            ? "数据库连接超时,请稍后重试。"
-                            : ex.Message;
-                        break;
-                    default:
-                        response.StatusCode = StatusCodes.Status400BadRequest;
-                        errorResponse.Msg = exception.Message; // 不直接暴露异常详细信息
-                        break;
+                    db.Ado.RollbackTran();
+                    _logger.LogWarning("Transaction rolled back due to exception: {ExceptionType}", exception.GetType().Name);
                 }
-
-                _logger.LogError(exception, "An exception occurred."); // 记录异常详细信息
-                var result = JsonConvert.SerializeObject(errorResponse);
-                await context.Response.WriteAsync(result); // 将错误响应写入客户端
             }
-            else
+            catch (Exception rollbackEx)
             {
-                // 如果响应已开始,记录日志但不做进一步处理
-                _logger.LogError(exception, "An exception occurred after response started.");
+                _logger.LogError(rollbackEx, "Failed to rollback transaction");
             }
+
+            // ========== 2. 响应已开始则仅记录日志 ==========
+            if (context.Response.HasStarted)
+            {
+                _logger.LogError(exception, "Exception occurred after response started | RequestId: {RequestId}",
+                    context.TraceIdentifier); // 记录请求ID
+                return;
+            }
+
+            // ========== 3. 构建标准化响应 ==========
+            context.Response.ContentType = "application/json; charset=utf-8";
+            var (statusCode, message, details, logLevel) = exception switch
+            {
+                // 业务异常:返回自定义信息,日志级别为Warning(非错误)
+                BusinessException businessEx => (
+                    businessEx.Code,
+                    businessEx.Msg,
+                    businessEx.Data,
+                    LogLevel.Warning
+                ),
+                // 权限/资源异常:标准化提示,日志Warning
+                UnauthorizedAccessException => (403, "无权访问此资源", null, LogLevel.Warning),
+                KeyNotFoundException => (404, "请求的资源不存在", null, LogLevel.Warning),
+                // 数据库超时:隐藏敏感信息,日志Error
+                SqlException sqlEx when IsDbTimeoutException(sqlEx) => (
+                    503,
+                    "数据库连接超时,请稍后重试。",
+                    null,
+                    LogLevel.Error
+                ),
+                SqlSugarException sugarEx when IsSugarTimeoutException(sugarEx) => (
+                    503,
+                    "数据库连接超时,请稍后重试。",
+                    null,
+                    LogLevel.Error
+                ),
+                // 其他数据库异常:隐藏原始消息,日志Error
+                SqlException => (500, "数据库服务异常", null, LogLevel.Error),
+                SqlSugarException => (500, "数据库服务异常", null, LogLevel.Error),
+                // 应用程序异常(业务逻辑错误):日志Warning
+                ApplicationException appEx => (400, appEx.Message, null, LogLevel.Warning),
+                // 兜底系统异常:隐藏原始消息,日志Critical
+                _ => (500, "服务器内部错误", null, LogLevel.Critical)
+            };
+
+            // ========== 4. 设置HTTP状态码(关键优化) ==========
+            context.Response.StatusCode = 200;
+
+            // ========== 5. 分级记录日志(含上下文) ==========
+            var logMessage = new Dictionary<string, string>
+            {
+                ["RequestId"] = context.TraceIdentifier,
+                ["Path"] = context.Request.Path,
+                ["Method"] = context.Request.Method,
+                ["User"] = context.User?.Identity?.Name ?? "Anonymous",
+                ["ExceptionType"] = exception.GetType().FullName
+            };
+            _logger.Log(logLevel, exception, "Global exception handled | {LogContext}",
+                System.Text.Json.JsonSerializer.Serialize(logMessage));
+
+            // ========== 6. 序列化响应(驼峰命名,隐藏空值) ==========
+            var jsonOptions = new JsonSerializerOptions
+            {
+                PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+                DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
+            };
+            var result = System.Text.Json.JsonSerializer.Serialize(new JsonView
+            {
+                Code = statusCode,
+                Msg = message,
+                Data = details
+            }, jsonOptions);
+
+            await context.Response.WriteAsync(result);
+        }
+
+        // ========== 辅助方法:判断数据库超时异常(可扩展) ==========
+        private bool IsDbTimeoutException(SqlException sqlEx)
+        {
+            // 显式声明long数组,覆盖SQL Server/MySQL常见超时错误码
+            // 错误码说明:
+            // -2/-4:SQL Server 连接超时
+            // 1205:SQL Server 死锁
+            // 4060:SQL Server 无法连接数据库
+            // 2148732164:.NET 封装的数据库超时(对应0x80131904)
+            long[] timeoutErrorCodes = new long[] { -2, -4, 1205, 4060, 2148732164 };
+            // 转换sqlEx.Number为long后再判断(避免类型不匹配)
+            return timeoutErrorCodes.Contains((long)sqlEx.Number);
+        }
+
+        // ========== 辅助方法:判断SqlSugar超时异常 ==========
+        private bool IsSugarTimeoutException(SqlSugarException sugarEx)
+        {
+            var timeoutKeywords = new[] { "timeout", "超时", "deadlock", "死锁" };
+            return timeoutKeywords.Any(k => sugarEx.Message.Contains(k, StringComparison.OrdinalIgnoreCase));
         }
+
     }
 }

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

@@ -558,6 +558,13 @@ builder.Services.TryAddSingleton(typeof(CommonService));
 
 var app = builder.Build();
 
+//// 1. 异常处理器应该在最早的位置(除了日志等)
+//app.UseExceptionHandler(new ExceptionHandlerOptions
+//{
+//    ExceptionHandlingPath = "/Home/Error", 
+//    AllowStatusCode404Response = true
+//});
+
 //自定义异常中间件
 app.UseMiddleware<ExceptionHandlingMiddleware>();
 

+ 1 - 6
OASystem/OASystem.Domain/Dtos/Groups/InvertedListDto.cs

@@ -1,9 +1,4 @@
 using OASystem.Domain.ViewModels.Groups;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
 namespace OASystem.Domain.Dtos.Groups
 {
@@ -48,7 +43,7 @@ namespace OASystem.Domain.Dtos.Groups
     public class InvertedListFileDownloadDto : PortDtoBase
     {
         /// <summary>
-        /// 请求类型
+        /// 团组Id
         /// </summary>
         public int DiId { get; set; }
     }

+ 61 - 8
OASystem/OASystem.Domain/Entities/Groups/Grp_InvertedList.cs

@@ -1,10 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace OASystem.Domain.Entities.Groups
+namespace OASystem.Domain.Entities.Groups
 {
     /// <summary>
     /// 团组倒推表
@@ -26,6 +20,12 @@ namespace OASystem.Domain.Entities.Groups
         [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
         public string ApprovalDataDt { get; set; }
 
+        /// <summary>
+        /// 报批资料准备时间(客户) - A
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
+        public string Client_ApprovalDataDt { get; set; }
+
         /// <summary>
         /// 报批资料准备
         /// 是否完成
@@ -46,6 +46,12 @@ namespace OASystem.Domain.Entities.Groups
         [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
         public string ApprovalDt { get; set; }
 
+        /// <summary>
+        /// 报批时间(客户) - A
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
+        public string Client_ApprovalDt { get; set; }
+
         /// <summary>
         /// 报批时间
         /// 1 预批件 2 正常批件 默认值??-1
@@ -73,6 +79,12 @@ namespace OASystem.Domain.Entities.Groups
         [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
         public string IssueApprovalDt { get; set; }
 
+        /// <summary>
+        /// 出批件时间(客户) - A
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
+        public string Client_IssueApprovalDt { get; set; }
+
         /// <summary>
         /// 出批件
         /// 是否完成
@@ -116,6 +128,12 @@ namespace OASystem.Domain.Entities.Groups
         [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
         public string VisaInformationDt { get; set; }
 
+        /// <summary>
+        /// 签证资料准备时间(客户) - A
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
+        public string Client_VisaInformationDt { get; set; }
+
         /// <summary>
         /// 签证资料准备
         /// 是否完成
@@ -136,6 +154,12 @@ namespace OASystem.Domain.Entities.Groups
         [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
         public string SendVisaDt { get; set; }
 
+        /// <summary>
+        /// 送签时间(客户) - B
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
+        public string Client_SendVisaDt { get; set; }
+
         /// <summary>
         /// 送签
         /// 是否完成
@@ -156,6 +180,12 @@ namespace OASystem.Domain.Entities.Groups
         [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
         public string IssueVisaDt { get; set; }
 
+        /// <summary>
+        /// 出签时间(客户) - B
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
+        public string Client_IssueVisaDt { get; set; }
+
         /// <summary>
         /// 出签
         /// 是否完成
@@ -170,13 +200,18 @@ namespace OASystem.Domain.Entities.Groups
         [SugarColumn(IsNullable = true, ColumnDataType = "varchar(300)")]
         public string IssueVisaRemark { get; set; } = "外办领取签证";
 
-
         /// <summary>
         /// 机票 - B
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
         public string AirTicketDt { get; set; }
 
+        /// <summary>
+        /// 机票(客户) - B
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
+        public string Client_AirTicketDt { get; set; }
+
         /// <summary>
         /// 机票
         /// 是否完成
@@ -197,6 +232,12 @@ namespace OASystem.Domain.Entities.Groups
         [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
         public string HotelDt { get; set; }
 
+        /// <summary>
+        /// 酒店(客户) - B
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
+        public string Client_HotelDt { get; set; }
+
         /// <summary>
         /// 酒店
         /// 是否完成
@@ -217,6 +258,12 @@ namespace OASystem.Domain.Entities.Groups
         [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
         public string PreTripMeetingDt { get; set; }
 
+        /// <summary>
+        /// 行前会(客户) - A
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
+        public string Client_PreTripMeetingDt { get; set; }
+
         /// <summary>
         /// 行前会
         /// 是否完成
@@ -237,6 +284,12 @@ namespace OASystem.Domain.Entities.Groups
         [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
         public string AirportdDropOffDt { get; set; }
 
+        /// <summary>
+        /// 送机(客户) - A
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
+        public string Client_AirportdDropOffDt { get; set; }
+
         /// <summary>
         /// 送机
         /// 是否完成

+ 30 - 4
OASystem/OASystem.Domain/Result.cs

@@ -1,4 +1,6 @@
-namespace OASystem.Domain
+using System.Runtime.Serialization;
+
+namespace OASystem.Domain
 {
     public class Result
     {
@@ -21,9 +23,11 @@
         }
     }
 
+
     /// <summary>
     /// 业务异常基类
     /// </summary>
+    [Serializable] // 核心:添加序列化特性(IIS识别的关键)
     public class BusinessException : Exception
     {
         /// <summary>
@@ -31,17 +35,19 @@
         /// </summary>
         public int Code { get; set; }
 
-
         /// <summary>
         /// 错误消息
         /// </summary>
-        public string Msg{ get; set; }
+        public string Msg { get; set; }
 
         /// <summary>
         /// 错误详情
         /// </summary>
         public new object Data { get; set; }
 
+        // 无参构造函数(序列化必备,即使不直接使用)
+        public BusinessException() : base() { }
+
         public BusinessException(string message) : base(message)
         {
             Code = 400;
@@ -69,11 +75,31 @@
         }
 
         public BusinessException(string message, Exception innerException)
-        : base(message, innerException)
+            : base(message, innerException)
         {
             Code = 400;
             Msg = message;
         }
+
+        // 核心:序列化构造函数(IIS反序列化时必须调用)
+        protected BusinessException(SerializationInfo info, StreamingContext context)
+            : base(info, context)
+        {
+            // 读取序列化的自定义属性(和GetObjectData对应)
+            Code = info.GetInt32(nameof(Code));
+            Msg = info.GetString(nameof(Msg));
+            Data = info.GetValue(nameof(Data), typeof(object));
+        }
+
+        // 核心:重写GetObjectData(序列化自定义属性)
+        public override void GetObjectData(SerializationInfo info, StreamingContext context)
+        {
+            base.GetObjectData(info, context);
+            // 写入自定义属性到序列化流
+            info.AddValue(nameof(Code), Code);
+            info.AddValue(nameof(Msg), Msg);
+            info.AddValue(nameof(Data), Data);
+        }
     }
 
 

+ 49 - 0
OASystem/OASystem.Domain/ViewModels/Groups/InvertedListView.cs

@@ -281,6 +281,55 @@ namespace OASystem.Domain.ViewModels.Groups
         public bool IsSelectedAirportdDropOff { get; set; } = true;
         #endregion
 
+        #region 新增每项客户使用使用时间
+        /// <summary>
+        /// 报批资料准备时间(客户) - A
+        /// </summary>
+        public string Client_ApprovalDataDt { get; set; }
+
+        /// <summary>
+        /// 报批时间(客户) - A
+        /// </summary>
+        public string Client_ApprovalDt { get; set; }
+
+        /// <summary>
+        /// 出批件时间(客户) - A
+        /// </summary>
+        public string Client_IssueApprovalDt { get; set; }
+
+        /// <summary>
+        /// 签证资料准备时间(客户) - A
+        /// </summary>
+        public string Client_VisaInformationDt { get; set; }
+
+        /// <summary>
+        /// 送签时间(客户) - B
+        /// </summary>
+        public string Client_SendVisaDt { get; set; }
+
+        /// <summary>
+        /// 出签时间(客户) - B
+        /// </summary>
+        public string Client_IssueVisaDt { get; set; }
+
+        /// <summary>
+        /// 机票(客户) - B
+        /// </summary>
+        public string Client_AirTicketDt { get; set; }
+        /// <summary>
+        /// 酒店(客户) - B
+        /// </summary>
+        public string Client_HotelDt { get; set; }
+        /// <summary>
+        /// 行前会(客户) - A
+        /// </summary>
+        public string Client_PreTripMeetingDt { get; set; }
+
+        /// <summary>
+        /// 送机(客户) - A
+        /// </summary>
+        public string Client_AirportdDropOffDt { get; set; }
+        #endregion
     }
     /// <summary>
     /// 签证国家信息

+ 3 - 1
OASystem/OASystem.Domain/ViewModels/JsonView.cs

@@ -1,4 +1,6 @@
-namespace OASystem.Domain.ViewModels;
+using System.Text.Json.Serialization;
+
+namespace OASystem.Domain.ViewModels;
 
 /// <summary>
 /// 统一返回结构

+ 36 - 13
OASystem/OASystem.Infrastructure/Repositories/Groups/InvertedListRepository.cs

@@ -35,7 +35,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
         /// 基础数据
         /// </summary>
         /// <returns></returns>
-        public async Task<Result> _Init()
+        public async Task<Result> Init()
         {
             dynamic groupData = null;
             var groupData1 = await _delegationInfoRep.PostShareGroupInfos(1);
@@ -65,7 +65,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
         /// Info
         /// </summary>
         /// <returns></returns>
-        public async Task<Result> _Info(int portTypeId,int diId)
+        public async Task<Result> Info(int portTypeId,int diId)
         {
             #region 参数验证
             if (portTypeId < 1)
@@ -116,7 +116,6 @@ namespace OASystem.Infrastructure.Repositories.Groups
                 string countryDesc = "";
                 foreach (var item in visitCountry)
                 {
-
                     int visaDays = Convert.ToInt32(visaFeeDatas.Find(it => it.VisaCountry.Equals(item.Trim()))?.VisaTime ?? "0");
                     visaCountryInfos.Add(new VisaCountryInfo()
                     {
@@ -152,6 +151,21 @@ namespace OASystem.Infrastructure.Repositories.Groups
 
             int ilId = info.Id;
 
+            #region 新增客户时间字段值不存在使用默认值
+
+            if (string.IsNullOrEmpty(info.Client_ApprovalDataDt)) info.Client_ApprovalDataDt = info.ApprovalDataDt;
+            if (string.IsNullOrEmpty(info.Client_ApprovalDt)) info.Client_ApprovalDt = info.ApprovalDt;
+            if (string.IsNullOrEmpty(info.Client_IssueApprovalDt)) info.Client_IssueApprovalDt = info.IssueApprovalDt;
+            if (string.IsNullOrEmpty(info.Client_VisaInformationDt)) info.Client_VisaInformationDt = info.VisaInformationDt;
+            if (string.IsNullOrEmpty(info.Client_SendVisaDt)) info.Client_SendVisaDt = info.SendVisaDt;
+            if (string.IsNullOrEmpty(info.Client_IssueVisaDt)) info.Client_IssueVisaDt = info.IssueVisaDt;
+            if (string.IsNullOrEmpty(info.Client_AirTicketDt)) info.Client_AirTicketDt = info.AirTicketDt;
+            if (string.IsNullOrEmpty(info.Client_HotelDt)) info.Client_HotelDt = info.HotelDt;
+            if (string.IsNullOrEmpty(info.Client_PreTripMeetingDt)) info.Client_PreTripMeetingDt = info.PreTripMeetingDt;
+            if (string.IsNullOrEmpty(info.Client_AirportdDropOffDt)) info.Client_AirportdDropOffDt = info.AirportdDropOffDt;
+
+            #endregion
+
             string visaSql = string.Format(@$"Select * From Grp_InvertedListVisaCountry Where Isdel={0} And ILId = {ilId}");
             var visaCountryInfo = await _sqlSugar.SqlQueryable<VisaCountryInfo>(visaSql).ToListAsync();
 
@@ -167,7 +181,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
         /// </summary>
         /// <param name="diId"></param>
         /// <returns></returns>
-        public async Task<Result> _Create(int userId,int diId,string remark = "")
+        public async Task<Result> Create(int userId,int diId,string remark = "")
         {
             #region 参数验证
             if (diId < 1)
@@ -258,22 +272,32 @@ namespace OASystem.Infrastructure.Repositories.Groups
                 sendVisa_advanceDays = -12,         //送签
                 issueVisa_advanceDays = -5,         //出签
                 preTripMeeting_advanceDays = -3,    //行前会
-                airportdDropOff_advanceDays = 0;   //送机
+                airportdDropOff_advanceDays = 0;    //送机
 
             var info = new Grp_InvertedList() { 
                 DiId = diId,
                 ApprovalDataDt = visitDt.AddDays(approvalData_advanceDays).ToString("yyyy-MM-dd"),
+                Client_ApprovalDataDt = visitDt.AddDays(approvalData_advanceDays).ToString("yyyy-MM-dd"),
                 ApprovalDt = visitDt.AddDays(approval_advanceDays).ToString("yyyy-MM-dd"),
+                Client_ApprovalDt = visitDt.AddDays(approval_advanceDays).ToString("yyyy-MM-dd"),
                 ApprovalType = -1,
                 IssueApprovalDt = visitDt.AddDays(IssueApproval_advanceDays).ToString("yyyy-MM-dd"),
+                Client_IssueApprovalDt = visitDt.AddDays(IssueApproval_advanceDays).ToString("yyyy-MM-dd"),
                 //ApplyPassportDt = visitDt.AddDays(ApplyPassport_advanceDays).ToString("yyyy-MM-dd"),
                 VisaInformationDt = visitDt.AddDays(VisaInformation_advanceDays).ToString("yyyy-MM-dd"),
+                Client_VisaInformationDt = visitDt.AddDays(VisaInformation_advanceDays).ToString("yyyy-MM-dd"),
                 SendVisaDt = visitDt.AddDays(sendVisa_advanceDays).ToString("yyyy-MM-dd"),
+                Client_SendVisaDt = visitDt.AddDays(sendVisa_advanceDays).ToString("yyyy-MM-dd"),
                 IssueVisaDt = visitDt.AddDays(issueVisa_advanceDays).ToString("yyyy-MM-dd"),
-                AirTicketDt = visitDt.AddDays(issueVisa_advanceDays).ToString("yyyy-MM-dd"), //机票 应国交部要求默认使用出签时间
-                HotelDt = visitDt.AddDays(issueVisa_advanceDays).ToString("yyyy-MM-dd"),     //酒店 应国交部要求默认使用出签时间
+                Client_IssueVisaDt = visitDt.AddDays(issueVisa_advanceDays).ToString("yyyy-MM-dd"),
+                AirTicketDt = visitDt.AddDays(issueVisa_advanceDays).ToString("yyyy-MM-dd"),        //机票 应国交部要求默认使用出签时间
+                Client_AirTicketDt = visitDt.AddDays(issueVisa_advanceDays).ToString("yyyy-MM-dd"), //机票 应国交部要求默认使用出签时间
+                HotelDt = visitDt.AddDays(issueVisa_advanceDays).ToString("yyyy-MM-dd"),            //酒店 应国交部要求默认使用出签时间
+                Client_HotelDt = visitDt.AddDays(issueVisa_advanceDays).ToString("yyyy-MM-dd"),     //酒店 应国交部要求默认使用出签时间
                 PreTripMeetingDt = visitDt.AddDays(preTripMeeting_advanceDays).ToString("yyyy-MM-dd"),
+                Client_PreTripMeetingDt = visitDt.AddDays(preTripMeeting_advanceDays).ToString("yyyy-MM-dd"),
                 AirportdDropOffDt = visitDt.AddDays(airportdDropOff_advanceDays).ToString("yyyy-MM-dd"),
+                Client_AirportdDropOffDt = visitDt.AddDays(airportdDropOff_advanceDays).ToString("yyyy-MM-dd"),
                 CreateUserId = userId,
                 Remark = remark
             };
@@ -290,12 +314,12 @@ namespace OASystem.Infrastructure.Repositories.Groups
                 return _result;
             }
 
-            var _InvertedListVisaCountries = new List<Grp_InvertedListVisaCountry>();
+            var invertedListVisaCountries = new List<Grp_InvertedListVisaCountry>();
             foreach (var item in visitCountry)
             {
                 int days = Convert.ToInt32(visaFeeDatas.Find(x => x.VisaCountry.Equals(item.Trim()))?.VisaTime ?? "0");
-                
-                _InvertedListVisaCountries.Add(new Grp_InvertedListVisaCountry()
+
+                invertedListVisaCountries.Add(new Grp_InvertedListVisaCountry()
                 {
                     ILId = add1,
                     Country = item,
@@ -305,7 +329,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
                 });
             }
 
-            int add2 = await _sqlSugar.Insertable<Grp_InvertedListVisaCountry>(_InvertedListVisaCountries).ExecuteReturnIdentityAsync();
+            int add2 = await _sqlSugar.Insertable<Grp_InvertedListVisaCountry>(invertedListVisaCountries).ExecuteReturnIdentityAsync();
 
             _sqlSugar.CommitTran();
             _result.Code = 0;
@@ -318,7 +342,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
         /// </summary>
         /// <param name="diId"></param>
         /// <returns></returns>
-        public async Task<Result> _Update(InvertedListUpdateDto dto)
+        public async Task<Result> Update(InvertedListUpdateDto dto)
         {
             #region 参数验证
             if (dto.DiId < 1)
@@ -372,6 +396,5 @@ namespace OASystem.Infrastructure.Repositories.Groups
             return _result;
         }
     
-    
     }
 }

+ 46 - 31
OASystem/OASystem.Infrastructure/Repositories/Groups/ProcessOverviewRepository.cs

@@ -71,6 +71,11 @@ namespace OASystem.Infrastructure.Repositories.Groups
                 })
                 .ToListAsync();
 
+            var defaultJobNames = new List<string>()
+            {
+                "OP主管"
+            };
+
             #region 商邀报批流程
 
             string oaNode1Tips = NodeTipsMsg(groupInfo, GroupProcessType.Invitation, 1);
@@ -157,8 +162,13 @@ namespace OASystem.Infrastructure.Repositories.Groups
             string airNode8Tips = NodeTipsMsg(groupInfo, GroupProcessType.AirTicket, 8);
 
             //节点可操作用户列表
+            var airNodeOpJobNames = new List<string>() {
+                "机票"
+            };
+            airNodeOpJobNames.AddRange(defaultJobNames);
+
             var airNodeOpUsers = users.Where(u =>
-                u.JobName != null && u.JobName.Contains("机票")
+                u.JobName != null && airNodeOpJobNames.Contains(u.JobName)
             ).Select(u => u.Id).ToList();
 
             processs.Add(
@@ -179,7 +189,6 @@ namespace OASystem.Infrastructure.Repositories.Groups
             #endregion
 
             #region 酒店流程
-
             string hotelNode1Tips = NodeTipsMsg(groupInfo, GroupProcessType.Hotel, 1);
             string hotelNode2Tips = NodeTipsMsg(groupInfo, GroupProcessType.Hotel, 2);
             string hotelNode3Tips = NodeTipsMsg(groupInfo, GroupProcessType.Hotel, 3);
@@ -187,8 +196,11 @@ namespace OASystem.Infrastructure.Repositories.Groups
             string hotelNode5Tips = NodeTipsMsg(groupInfo, GroupProcessType.Hotel, 5);
 
             //节点可操作用户列表
+            var hotelNodeOpJobNames = new List<string>() { "酒店" };
+            hotelNodeOpJobNames.AddRange(defaultJobNames);
+
             var hotelNodeOpUsers = users.Where(u =>
-                u.JobName != null && u.JobName.Contains("酒店")
+                u.JobName != null && hotelNodeOpJobNames.Contains(u.JobName)
             ).Select(u => u.Id).ToList();
 
             processs.Add(
@@ -215,8 +227,11 @@ namespace OASystem.Infrastructure.Repositories.Groups
             string opNode7Tips = NodeTipsMsg(groupInfo, GroupProcessType.LocalGuide, 7);
 
             //节点可操作用户列表
+            var opNodeOpJobNames = new List<string>() { "OP" };
+            opNodeOpJobNames.AddRange(defaultJobNames);
+
             var opNodeOpUsers = users.Where(u =>
-                u.JobName != null && u.JobName.Equals("OP")
+                u.JobName != null && opNodeOpJobNames.Contains( u.JobName)
             ).Select(u => u.Id).ToList();
 
             processs.Add(
@@ -667,23 +682,25 @@ namespace OASystem.Infrastructure.Repositories.Groups
                 {
                     // 1. 获取节点和流程信息
                     var node = await _sqlSugar.Queryable<Grp_ProcessNode>()
-                        .FirstAsync(n => n.Id == nodeId && n.IsDel == 0)
-                        ?? throw new BusinessException("当前节点不存在或已被删除。");
+                        .FirstAsync(n => n.Id == nodeId && n.IsDel == 0);
+
+                    if (node == null) return new Result { Code = StatusCodes.Status400BadRequest, Msg = "当前节点不存在或已被删除。" };
 
                     // 2. 用户权限验证
                     if (!HasNodeOperationPermission(node, currUserId))
                     {
-                        throw new BusinessException("当前用户没有操作此节点的权限。");
+                        return new Result { Code = StatusCodes.Status400BadRequest, Msg = "当前用户没有操作此节点的权限." };
                     }
 
                     var process = await _sqlSugar.Queryable<Grp_ProcessOverview>()
-                        .FirstAsync(p => p.Id == node.ProcessId && p.IsDel == 0)
-                        ?? throw new BusinessException("关联的流程不存在。");
+                        .FirstAsync(p => p.Id == node.ProcessId && p.IsDel == 0);
+
+                    if (process == null) return new Result { Code = StatusCodes.Status400BadRequest, Msg = "关联的流程不存在。" };
 
                     // 3. 检查是否重复操作
                     if (node.OverallStatus == targetStatus)
                     {
-                        throw new BusinessException($"当前节点已为{GetStatusDescription(targetStatus)}状态,无需重复操作。");
+                        return new Result { Code = StatusCodes.Status400BadRequest, Msg = $"当前节点已为{GetStatusDescription(targetStatus)}状态,无需重复操作。。" };
                     }
 
                     // 4. 存储更新前的值
@@ -1349,18 +1366,20 @@ namespace OASystem.Infrastructure.Repositories.Groups
 
             // 1. 获取并验证节点和流程
             var node = await _sqlSugar.Queryable<Grp_ProcessNode>()
-                .FirstAsync(n => n.Id == nodeId && n.IsDel == 0)
-                ?? throw new BusinessException("当前节点不存在或已被删除。");
+                .FirstAsync(n => n.Id == nodeId && n.IsDel == 0);
+
+            if (node == null) return new Result { Code = 400, Msg = "当前节点不存在或已被删除。" };
 
             // 1.1. 用户权限验证
             if (!HasNodeOperationPermission(node, currUserId))
             {
-                throw new BusinessException("当前用户没有操作此节点的权限。");
+                return new Result { Code = 400, Msg = "当前用户没有操作此节点的权限。" };
             }
 
             var process = await _sqlSugar.Queryable<Grp_ProcessOverview>()
-                .FirstAsync(p => p.Id == node.ProcessId && p.IsDel == 0)
-                ?? throw new BusinessException("当前流程不存在或已被删除。");
+                .FirstAsync(p => p.Id == node.ProcessId && p.IsDel == 0);
+
+            if (process == null) return new Result { Code = 400, Msg = "当前流程不存在或已被删除。" };
 
             // 2.1 存储更新前流程及节点信息
             var nodeBefore = CloneNode(node);
@@ -1374,10 +1393,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
             node.IsAssist = isAssist;
             node.IsFileUp = isFileUp;
             node.IsPart = isPart;
-            if (!string.IsNullOrEmpty(dto.Remark))
-            {
-                node.Remark = dto.Remark;
-            }
+            node.Remark = dto.Remark;
 
             // 3. 保存节点更新
             await _sqlSugar.Updateable(node)
@@ -1412,7 +1428,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
 
             }
 
-            return new Result { Code = 200, Msg = "实际操作时间设置成功。" };
+            return new Result { Code = 200, Msg = "设置成功。" };
         }
 
         #endregion
@@ -1721,7 +1737,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
                     var firstNode = await _sqlSugar.Queryable<Grp_ConfProcessNode>().FirstAsync(x => x.IsDel == 0 && x.ProcessId == existingProcesses.FirstOrDefault().Id);
                     if (!HasConfNodeOperationPermission(firstNode, currUserId))
                     {
-                        throw new BusinessException("当前用户没有操作此节点的权限。");
+                        return new Result { Code = 400, Msg = "当前用户没有操作此节点的权限。" };
                     }
 
                     //团组会流程完成 不可切换模板
@@ -1789,23 +1805,25 @@ namespace OASystem.Infrastructure.Repositories.Groups
                 {
                     // 1. 获取节点和流程信息
                     var node = await _sqlSugar.Queryable<Grp_ConfProcessNode>()
-                        .FirstAsync(n => n.Id == nodeId && n.IsDel == 0)
-                        ?? throw new BusinessException("当前节点不存在或已被删除。");
+                        .FirstAsync(n => n.Id == nodeId && n.IsDel == 0);
+
+                    if (node == null) return new Result { Code = StatusCodes.Status400BadRequest, Msg = "当前节点不存在或已被删除。" };
 
                     // 2. 用户权限验证
                     if (!HasConfNodeOperationPermission(node, currUserId))
                     {
-                        throw new BusinessException("当前用户没有操作此节点的权限。");
+                        return new Result { Code = StatusCodes.Status400BadRequest, Msg = "当前用户没有操作此节点的权限。" };
                     }
 
                     var process = await _sqlSugar.Queryable<Grp_ConfProcessOverview>()
-                        .FirstAsync(p => p.Id == node.ProcessId && p.IsDel == 0)
-                        ?? throw new BusinessException("关联的流程不存在。");
+                        .FirstAsync(p => p.Id == node.ProcessId && p.IsDel == 0);
+
+                    if (process == null) return new Result { Code = StatusCodes.Status400BadRequest, Msg = "关联的流程不存在。" };
 
                     // 3. 检查是否重复操作
                     if (node.OverallStatus == targetStatus)
                     {
-                        throw new BusinessException($"当前节点已为{GetStatusDescription(targetStatus)}状态,无需重复操作。");
+                        return new Result { Code = StatusCodes.Status400BadRequest, Msg = "当前节点已为{GetStatusDescription(targetStatus)}状态,无需重复操作。。" };
                     }
 
                     // 4. 存储更新前的值
@@ -2238,10 +2256,7 @@ namespace OASystem.Infrastructure.Repositories.Groups
 
             node.IsFileUp = isFileUp;
             node.Participators = dto.Participators;
-            if (!string.IsNullOrEmpty(dto.Remark))
-            {
-                node.Remark = dto.Remark;
-            }
+            node.Remark = dto.Remark;
 
             // 3. 保存节点更新
             await _sqlSugar.Updateable(node)

+ 3 - 3
OASystem/OASystem.Infrastructure/Repositories/PersonnelModule/TaskAllocationRepository.cs

@@ -129,12 +129,12 @@ namespace OASystem.Infrastructure.Repositories.PersonnelModule
                 if (taskAudits.Any())
                 {
                     //任务权限
-                    if (taslPerm)
-                    {
+                    //if (taslPerm)
+                    //{
                         taskOpertionAudit.IsAddPerm = true;
                         taskOpertionAudit.IsDelPerm = true;
                         taskOpertionAudit.IsStopPerm = true;
-                    }
+                    //}
 
                     var userIds = taskAudits.Select(it => it.SubUserId).ToList();
                     string userSql = string.Format(@"Select u.Id,u.CnName As Name,c.CompanyName,