Browse Source

Merge branch 'master' of http://git.12345lm.cn/Fengwo/hotline

Dun.Jason 1 năm trước cách đây
mục cha
commit
7a7f996dfe

+ 12 - 2
src/Hotline.Api/Controllers/Bi/BiOrderController.cs

@@ -72,7 +72,7 @@ namespace Hotline.Api.Controllers.Bi
                 {
                     OrgName = o.Name,
                     OrgId = x.ActualHandleOrgCode,
-                    HandlerExtendedNum = SqlFunc.AggregateSum(SqlFunc.IIF((int)x.Status >= 300 && x.ExpiredTime > x.FiledTime, 1, 0)),
+                    HandlerExtendedNum = SqlFunc.AggregateSum(SqlFunc.IIF((int)x.Status >= 300 && x.ExpiredTime < x.FiledTime, 1, 0)),
                     NoHandlerExtendedNum = SqlFunc.AggregateSum(SqlFunc.IIF((int)x.Status < 300 && x.ExpiredTime < SqlFunc.GetDate(), 1, 0)),
                 }).MergeTable();
             var queryCountersign = _workflowCountersignRepository.Queryable()
@@ -264,7 +264,17 @@ namespace Hotline.Api.Controllers.Bi
 						   ChainRate = t1_t2.Select(x => x.ChainNum).FirstOrDefault() > 0 ?
 						   ((double.Parse(t1.Num.ToString()) - double.Parse(t1_t2.Select(x => x.ChainNum).FirstOrDefault().ToString())) / double.Parse(t1_t2.Select(x => x.ChainNum).FirstOrDefault().ToString()) * 100).ToString("F2") + "%" : "100.00%",
 					   }).ToList();
-			return res;
+			var total = new
+			{
+				Id = "0",
+				Name = "合计",
+				Num = res.Sum(x => x.Num),
+				Sublevel = false,
+				Children = new List<HotspotDataLsitVo>(),
+				ChainNum = res.Sum(x => x.ChainNum),
+				ChainRate = res.Sum(x => x.ChainNum) > 0 ? ((double.Parse(res.Sum(x => x.Num).ToString()) - double.Parse(res.Sum(x => x.ChainNum).ToString())) / double.Parse(res.Sum(x => x.ChainNum).ToString()) * 100).ToString("F2") + "%" : "100.00%"
+			};
+			return new {List =res ,Total = total };
         }
 
 

+ 172 - 160
src/Hotline.Api/Controllers/OrderController.cs

@@ -97,7 +97,7 @@ public class OrderController : BaseController
     private readonly ITypedCache<EnterpriseVo> _cacheResponse;
     private readonly IRepository<OrderSendBackAudit> _orderSendBackAuditRepository;
 
-	public OrderController(
+    public OrderController(
         IOrderDomainService orderDomainService,
         IOrderRepository orderRepository,
         IWorkflowApplication workflowApplication,
@@ -144,7 +144,7 @@ public class OrderController : BaseController
         ILogger<OrderController> logger,
         ITypedCache<EnterpriseVo> cacheResponse,
         IRepository<OrderSendBackAudit> orderSendBackAuditRepository
-		)
+        )
     {
         _orderDomainService = orderDomainService;
         _orderRepository = orderRepository;
@@ -193,7 +193,7 @@ public class OrderController : BaseController
         _cacheResponse = cacheResponse;
         _orderSendBackAuditRepository = orderSendBackAuditRepository;
 
-	}
+    }
 
     #region 工单发布
 
@@ -732,16 +732,17 @@ public class OrderController : BaseController
                         }, cancellationToken: HttpContext.RequestAborted);
 
                 //推门户
-                await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderVisitedWeb, new PublishVisitAllDto(){ 
-                     Id = visit.Id,
-                     Order = orderDto,
-                     OrderVisitDetails = _mapper.Map<List<VisitDetailDto>>(visit.OrderVisitDetails),
-                     VisitName = _sessionContext.UserName,
-                     VisitTime = visit.VisitTime,
-                     VisitType = visit.VisitType,
-                     VisitState=visit.VisitState,
-                     PublishTime = visit.PublishTime,
-                },cancellationToken:HttpContext.RequestAborted);
+                await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderVisitedWeb, new PublishVisitAllDto()
+                {
+                    Id = visit.Id,
+                    Order = orderDto,
+                    OrderVisitDetails = _mapper.Map<List<VisitDetailDto>>(visit.OrderVisitDetails),
+                    VisitName = _sessionContext.UserName,
+                    VisitTime = visit.VisitTime,
+                    VisitType = visit.VisitType,
+                    VisitState = visit.VisitState,
+                    PublishTime = visit.PublishTime,
+                }, cancellationToken: HttpContext.RequestAborted);
             }
 
             if (first != null)
@@ -1137,7 +1138,7 @@ public class OrderController : BaseController
             if (count >= int.Parse(setting?.SettingValue[0]))
                 throw UserFriendlyException.SameMessage("延期申请已超过系统预定设置,不能申请");
         }
-        
+
         var model = _mapper.Map<OrderDelay>(delaydto);
         model.EmployeeId = _sessionContext.RequiredUserId;
         model.EmployeeName = _sessionContext.UserName;
@@ -1150,7 +1151,7 @@ public class OrderController : BaseController
         if (model.BeforeDelay != null)
         {
             model.AfterDelay = _timeLimitDomainService
-                .CalcEndTime(model.BeforeDelay.Value, delaydto.DelayUnit, delaydto.DelayNum, false,order.AcceptTypeCode)?.EndTime; //todo
+                .CalcEndTime(model.BeforeDelay.Value, delaydto.DelayUnit, delaydto.DelayNum, false, order.AcceptTypeCode)?.EndTime; //todo
         }
 
         model.ApplyDelayTime = DateTime.Now;
@@ -1184,7 +1185,7 @@ public class OrderController : BaseController
     [HttpGet("delay")]
     public async Task<PagedDto<OrderDelayDto>> DelayList([FromQuery] DelayListDto dto)
     {
-        var (total, items) = await _orderDelayRepository.Queryable(workflowFilter:false)
+        var (total, items) = await _orderDelayRepository.Queryable(workflowFilter: false)
             .Includes(x => x.Order)
             .Includes(x => x.Workflow)
             .WhereIF(!string.IsNullOrEmpty(dto.Keyword),
@@ -1206,30 +1207,28 @@ public class OrderController : BaseController
     [HttpGet("wait_delay")]
     public async Task<PagedDto<OrderDelayDto>> WaitDelayList([FromQuery] DelayListDto dto)
     {
-	    var (total, items) = await _orderDelayRepository.Queryable()
-		    .Includes(x => x.Order)
-		    .Includes(x => x.Workflow)
-		    .WhereIF(!string.IsNullOrEmpty(dto.Keyword),
-			    d => d.Order.Title.Contains(dto.Keyword!) || d.Order.No.Contains(dto.Keyword!))
-		    .WhereIF(dto.IsApply == true, x => x.DelayState != EDelayState.Examining)
-		    .WhereIF(dto.IsApply == false, x => x.DelayState == EDelayState.Examining)
-		    .WhereIF(dto.DelayState != null, x => x.DelayState == dto.DelayState)
-		    .OrderByDescending(x => x.ApplyDelayTime)
-		    .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
-
-	    return new PagedDto<OrderDelayDto>(total, _mapper.Map<IReadOnlyList<OrderDelayDto>>(items));
-    }
-
-	/// <summary>
-	/// 延期详情
-	/// </summary>
-	/// <param name="id"></param>
-	/// <returns></returns>
-	//[Permission(EPermission.DelayEntity)]
-	[HttpGet("delay/{id}")]
+        var (total, items) = await _orderDelayRepository.Queryable()
+            .WhereIF(!string.IsNullOrEmpty(dto.Keyword),
+                d => d.Order.Title.Contains(dto.Keyword!) || d.No.Contains(dto.Keyword!))
+            .WhereIF(dto.IsApply == true, x => x.DelayState != EDelayState.Examining)
+            .WhereIF(dto.IsApply == false, x => x.DelayState == EDelayState.Examining)
+            .WhereIF(dto.DelayState != null, x => x.DelayState == dto.DelayState)
+            .OrderByDescending(x => x.ApplyDelayTime)
+            .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+
+        return new PagedDto<OrderDelayDto>(total, _mapper.Map<IReadOnlyList<OrderDelayDto>>(items));
+    }
+
+    /// <summary>
+    /// 延期详情
+    /// </summary>
+    /// <param name="id"></param>
+    /// <returns></returns>
+    //[Permission(EPermission.DelayEntity)]
+    [HttpGet("delay/{id}")]
     public async Task<OrderDelayDto> DelayEntity(string id)
     {
-        var model = await _orderDelayRepository.Queryable(workflowFilter:false)
+        var model = await _orderDelayRepository.Queryable(workflowFilter: false)
             .Includes(x => x.Order)
             .Includes(x => x.Workflow)
             .FirstAsync(x => x.Id == id);
@@ -1251,7 +1250,7 @@ public class OrderController : BaseController
     [HttpPost("delay/calc-endtime")]
     public async Task<TimeResult?> DelayCalcEndTime([FromBody] DelayCalcEndTimeDto dto)
     {
-        return _timeLimitDomainService.CalcEndTime(dto.BeginTime, dto.DelayUnit, dto.DelayNum, false,0);
+        return _timeLimitDomainService.CalcEndTime(dto.BeginTime, dto.DelayUnit, dto.DelayNum, false, 0);
     }
 
     /// <summary>
@@ -1340,7 +1339,7 @@ public class OrderController : BaseController
     [HttpGet("screen")]
     public async Task<PagedDto<OrderScreenListDto>> ScreenList([FromQuery] ScreenListDto dto)
     {
-        var query = _orderScreenRepository.Queryable(workflowFilter:false)
+        var query = _orderScreenRepository.Queryable(workflowFilter: false)
             .Includes(x => x.Order)
             .Includes(x => x.VisitDetail)
             .Includes(x => x.Visit, d => d.Order)
@@ -1350,20 +1349,20 @@ public class OrderController : BaseController
         if (dto.Status is EScreenStatus.Apply)
         {
             query.Where(x => (x.Status == EScreenStatus.Apply || x.Status == EScreenStatus.Approval)
-                            && ((SqlFunc.JsonArrayLength(x.Workflow.HandlerUsers) > 0 
-                            &&  SqlFunc.JsonListObjectAny(x.Workflow.HandlerUsers, "Key",_sessionContext.RequiredUserId)) 
-                            ||(SqlFunc.JsonArrayLength(x.Workflow.HandlerUsers)  == 0
-	                        && SqlFunc.JsonListObjectAny(x.Workflow.HandlerOrgs, "Key",
-		                        _sessionContext.RequiredOrgId))));
+                            && ((SqlFunc.JsonArrayLength(x.Workflow.HandlerUsers) > 0
+                            && SqlFunc.JsonListObjectAny(x.Workflow.HandlerUsers, "Key", _sessionContext.RequiredUserId))
+                            || (SqlFunc.JsonArrayLength(x.Workflow.HandlerUsers) == 0
+                            && SqlFunc.JsonListObjectAny(x.Workflow.HandlerOrgs, "Key",
+                                _sessionContext.RequiredOrgId))));
         }
         if (dto.Status.HasValue && dto.Status == EScreenStatus.MyHandle)
         {
             query.Where(x => (x.Status != EScreenStatus.Apply)
-							  && ((SqlFunc.JsonArrayLength(x.Workflow.HandlerUsers) > 0
-							       && SqlFunc.JsonListObjectAny(x.Workflow.HandlerUsers, "Key", _sessionContext.RequiredUserId))
-							      || (SqlFunc.JsonArrayLength(x.Workflow.HandlerUsers) == 0
-							          && SqlFunc.JsonListObjectAny(x.Workflow.HandlerOrgs, "Key",
-								          _sessionContext.RequiredOrgId))));
+                              && ((SqlFunc.JsonArrayLength(x.Workflow.HandlerUsers) > 0
+                                   && SqlFunc.JsonListObjectAny(x.Workflow.HandlerUsers, "Key", _sessionContext.RequiredUserId))
+                                  || (SqlFunc.JsonArrayLength(x.Workflow.HandlerUsers) == 0
+                                      && SqlFunc.JsonListObjectAny(x.Workflow.HandlerOrgs, "Key",
+                                          _sessionContext.RequiredOrgId))));
         }
         var (total, items) = await query
             //.WhereIF(dto.Status.HasValue && dto.Status == EScreenStatus.MyHandle,
@@ -1400,7 +1399,7 @@ public class OrderController : BaseController
         var visit = await _orderVisitRepository.GetAsync(dto.Data.VisitId, HttpContext.RequestAborted);
         setting = _systemSettingCacheManager.GetSetting(SettingConstants.ScreenApplyEndTime);
         var endTime = _timeLimitDomainService
-            .CalcEndTime(visit.VisitTime.Value, ETimeType.WorkDay, int.Parse(setting?.SettingValue[0]), false,0).EndTime;
+            .CalcEndTime(visit.VisitTime.Value, ETimeType.WorkDay, int.Parse(setting?.SettingValue[0]), false, 0).EndTime;
         if (DateTime.Now > endTime)
             throw UserFriendlyException.SameMessage("甄别申请时限已超过系统预定设置,不能申请");
 
@@ -1438,35 +1437,35 @@ public class OrderController : BaseController
     }
 
 
-	/// <summary>
-	/// 工单甄别修改后下一步流程
-	/// </summary>
-	[HttpPost("screen/initial_nextFlow")]
-	[LogFilter("开始工单甄别流程")]
-	public async Task InitialNextFlow([FromBody] ScreenNextFlowDto dto)
-	{
+    /// <summary>
+    /// 工单甄别修改后下一步流程
+    /// </summary>
+    [HttpPost("screen/initial_nextFlow")]
+    [LogFilter("开始工单甄别流程")]
+    public async Task InitialNextFlow([FromBody] ScreenNextFlowDto dto)
+    {
         var screen = await _orderScreenRepository.GetAsync(dto.Data.Id);
         _mapper.Map(dto.Data, screen);
-		if (dto.Data.Files.Any())
-			screen.FileJson = await _fileRepository.AddFileAsync(dto.Data.Files, screen.Id, "", HttpContext.RequestAborted);
-		await _orderScreenRepository.UpdateAsync(screen, HttpContext.RequestAborted);
-		try
-		{
+        if (dto.Data.Files.Any())
+            screen.FileJson = await _fileRepository.AddFileAsync(dto.Data.Files, screen.Id, "", HttpContext.RequestAborted);
+        await _orderScreenRepository.UpdateAsync(screen, HttpContext.RequestAborted);
+        try
+        {
             dto.NextWorkflow.WorkflowId = screen.WorkflowId;
-			await _workflowApplication.NextAsync(dto.NextWorkflow,
-					cancellationToken: HttpContext.RequestAborted);
-		}
-		catch (Exception e)
-		{
-			throw new UserFriendlyException($"工单甄别下一步流程失败!, {e.Message}", "工单甄别下一步流程失败");
-		}
-	}
-
-	/// <summary>
-	/// 查询工单甄别流程开启参数
-	/// </summary>
-	/// <returns></returns>
-	[HttpGet("screen/startflow")]
+            await _workflowApplication.NextAsync(dto.NextWorkflow,
+                    cancellationToken: HttpContext.RequestAborted);
+        }
+        catch (Exception e)
+        {
+            throw new UserFriendlyException($"工单甄别下一步流程失败!, {e.Message}", "工单甄别下一步流程失败");
+        }
+    }
+
+    /// <summary>
+    /// 查询工单甄别流程开启参数
+    /// </summary>
+    /// <returns></returns>
+    [HttpGet("screen/startflow")]
     public async Task<NextStepsDto> GetScreenFlowStartOptionsAsync()
     {
         //return await _workflowApplication.GetStartOptionsAsync(WorkflowModuleConsts.OrderScreen,
@@ -1510,15 +1509,15 @@ public class OrderController : BaseController
     [HttpGet("screen/{id}")]
     public async Task<OrderScreenListDto> ScreenEntity(string id)
     {
-        var model = await _orderScreenRepository.Queryable(workflowFilter:false)
-            .Includes(x=> x.Order)
+        var model = await _orderScreenRepository.Queryable(workflowFilter: false)
+            .Includes(x => x.Order)
             .Includes(x => x.Workflow)
             .Includes(x => x.Visit, d => d.Order)
             .FirstAsync(x => x.Id == id);
         var rspModel = _mapper.Map<OrderScreenListDto>(model);
         rspModel.IsCanHandle = model.Workflow.CanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId);
         rspModel.Handle = await _workflowDomainService.CheckCurrentIsStartStepAsync(rspModel.WorkflowId, _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, HttpContext.RequestAborted);
-		if (rspModel.FileJson != null && rspModel.FileJson.Any())
+        if (rspModel.FileJson != null && rspModel.FileJson.Any())
         {
             var ids = rspModel.FileJson.Select(x => x.Id).ToList();
             rspModel.Files = await _fileRepository.GetFilesAsync(ids, HttpContext.RequestAborted);
@@ -2344,29 +2343,41 @@ public class OrderController : BaseController
         var setting = _systemSettingCacheManager.GetSetting(SettingConstants.RepeatableEventNum);
         return count >= int.Parse(setting?.SettingValue[0] ?? "0");
     }
-	#endregion
+    #endregion
 
-	#region 工单待办
+    #region 工单待办
 
-	/// <summary>
-	/// 查询待办工单
-	/// </summary>
-	[HttpGet("waited")]
+    /// <summary>
+    /// 查询待办工单
+    /// </summary>
+    [HttpGet("waited")]
     public async Task<PagedDto<OrderDto>> QueryWaited([FromQuery] QueryOrderWaitedDto dto)
     {
-       var (total, items) = await _orderRepository.Queryable(workflowFilter: false)
-            .Includes(d => d.Workflow, x => x.Steps)
-            .Where(d => SqlFunc.JsonListObjectAny(d.Workflow.HandlerUsers, "Key", _sessionContext.RequiredUserId) ||
-                        SqlFunc.JsonListObjectAny(d.Workflow.HandlerOrgs, "Key", _sessionContext.RequiredOrgId))
+        var query = dto.IsHandled.HasValue && !dto.IsHandled.Value
+            ? _orderRepository.Queryable().Where(d =>
+                SqlFunc.JsonListObjectAny(d.Workflow.HandlerUsers, "Key", _sessionContext.RequiredUserId) ||
+                SqlFunc.JsonListObjectAny(d.Workflow.HandlerOrgs, "Key", _sessionContext.RequiredOrgId))
+            : _orderRepository.Queryable();
+
+        var (total, items) = await query
             .WhereIF(dto.IsProvince.HasValue, d => d.IsProvince == dto.IsProvince)
-            .WhereIF(dto.IsHandled.HasValue && dto.IsHandled.Value, d => d.Workflow.Steps.Any(x => x.Status == EWorkflowStepStatus.Handled))
-            .WhereIF(dto.IsHandled.HasValue && !dto.IsHandled.Value, d => d.Workflow.Steps.Any(x => x.Status != EWorkflowStepStatus.Handled))
             .WhereIF(!string.IsNullOrEmpty(dto.Keyword),
                 d => d.No.Contains(dto.Keyword) || d.Title.Contains(dto.Keyword))
             .OrderByDescending(d => d.StartTime)
             .ToPagedListAsync(dto, HttpContext.RequestAborted);
 
 
+        //var (total, items) = await _orderRepository.Queryable()
+        //     .Includes(d => d.Workflow, x => x.Steps)
+        //     .WhereIF(dto.IsProvince.HasValue, d => d.IsProvince == dto.IsProvince)
+        //     .WhereIF(dto.IsHandled.HasValue && dto.IsHandled.Value, d => d.Workflow.Steps.Any(x => x.Status == EWorkflowStepStatus.Handled))
+        //     .WhereIF(dto.IsHandled.HasValue && !dto.IsHandled.Value, d => d.Workflow.Steps.Any(x => x.Status != EWorkflowStepStatus.Handled))
+        //     .WhereIF(!string.IsNullOrEmpty(dto.Keyword),
+        //         d => d.No.Contains(dto.Keyword) || d.Title.Contains(dto.Keyword))
+        //     .OrderByDescending(d => d.StartTime)
+        //     .ToPagedListAsync(dto, HttpContext.RequestAborted);
+
+
         return new PagedDto<OrderDto>(total, _mapper.Map<IReadOnlyList<OrderDto>>(items));
     }
 
@@ -2397,18 +2408,18 @@ public class OrderController : BaseController
         return new PagedDto<OrderDto>(total, _mapper.Map<IReadOnlyList<OrderDto>>(items));
     }
 
-	#endregion
+    #endregion
 
-	#region 业务工单退回
+    #region 业务工单退回
 
-	/// <summary>
-	/// 工单退回退回(返回前一节点)
-	/// </summary>
-	[HttpPost("order_previous")]
-	public async Task Previous([FromBody] PreviousWorkflowDto dto)
-	{
-		var oneSendBack = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.OneOrgSendBack)?.SettingValue[0]);
-		var twoSendBack = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.TwoOrgSendBack)?.SettingValue[0]);
+    /// <summary>
+    /// 工单退回退回(返回前一节点)
+    /// </summary>
+    [HttpPost("order_previous")]
+    public async Task Previous([FromBody] PreviousWorkflowDto dto)
+    {
+        var oneSendBack = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.OneOrgSendBack)?.SettingValue[0]);
+        var twoSendBack = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.TwoOrgSendBack)?.SettingValue[0]);
         if (oneSendBack || twoSendBack)
         {
             var workflow =
@@ -2443,15 +2454,16 @@ public class OrderController : BaseController
                 await _orderSendBackAuditRepository.AddAsync(audit, HttpContext.RequestAborted);
             }
             //中心内部流转  不走审批流程
-            if (sendBackOrg.IsCenter &&  applyOrg.IsCenter)
+            if (sendBackOrg.IsCenter && applyOrg.IsCenter)
             {
-				await _workflowApplication.PreviousAsync(dto, HttpContext.RequestAborted);
-			}
+                await _workflowApplication.PreviousAsync(dto, HttpContext.RequestAborted);
+            }
+        }
+        else
+        {
+            await _workflowApplication.PreviousAsync(dto, HttpContext.RequestAborted);
         }
-        else {
-			await _workflowApplication.PreviousAsync(dto, HttpContext.RequestAborted);
-		}
-	}
+    }
 
     /// <summary>
     /// 工单业务退回审批
@@ -2459,52 +2471,52 @@ public class OrderController : BaseController
     /// <param name="dto"></param>
     /// <returns></returns>
 	[Permission(EPermission.OrderPreviousAudit)]
-	[HttpPost("order_previous_audit")]
-	public async Task Audit([FromBody] AuditSendBackDto dto)
-	{
-		if (dto.State == ESendBackAuditState.Refuse && string.IsNullOrEmpty(dto.AuditContent))
-			throw UserFriendlyException.SameMessage("退回拒绝,请填写审批拒绝原因");
-		//验证是否存在退回
-		var sendBack = await _orderSendBackAuditRepository.GetAsync(dto.Id, HttpContext.RequestAborted);
-		if (sendBack is null)
-			throw UserFriendlyException.SameMessage("无效退回");
-		if (sendBack.State != ESendBackAuditState.Apply)
-			throw UserFriendlyException.SameMessage("退回已审批,请勿重复审批");
-
-		_mapper.Map(dto, sendBack);
-		sendBack.AuditId = _sessionContext.UserId;
-		sendBack.AuditUser = _sessionContext.UserName;
-		sendBack.AuditTime = DateTime.Now;
-
-		//执行退回
-		if (sendBack.State ==  ESendBackAuditState.End)
-		{
-			await _workflowApplication.OrderPreviousAsync(sendBack.SendBackData,sendBack.WorkflowUserId, HttpContext.RequestAborted);
-		}
-		await _orderSendBackAuditRepository.UpdateAsync(sendBack, HttpContext.RequestAborted);
-	}
-    
+    [HttpPost("order_previous_audit")]
+    public async Task Audit([FromBody] AuditSendBackDto dto)
+    {
+        if (dto.State == ESendBackAuditState.Refuse && string.IsNullOrEmpty(dto.AuditContent))
+            throw UserFriendlyException.SameMessage("退回拒绝,请填写审批拒绝原因");
+        //验证是否存在退回
+        var sendBack = await _orderSendBackAuditRepository.GetAsync(dto.Id, HttpContext.RequestAborted);
+        if (sendBack is null)
+            throw UserFriendlyException.SameMessage("无效退回");
+        if (sendBack.State != ESendBackAuditState.Apply)
+            throw UserFriendlyException.SameMessage("退回已审批,请勿重复审批");
+
+        _mapper.Map(dto, sendBack);
+        sendBack.AuditId = _sessionContext.UserId;
+        sendBack.AuditUser = _sessionContext.UserName;
+        sendBack.AuditTime = DateTime.Now;
+
+        //执行退回
+        if (sendBack.State == ESendBackAuditState.End)
+        {
+            await _workflowApplication.OrderPreviousAsync(sendBack.SendBackData, sendBack.WorkflowUserId, HttpContext.RequestAborted);
+        }
+        await _orderSendBackAuditRepository.UpdateAsync(sendBack, HttpContext.RequestAborted);
+    }
+
     /// <summary>
     /// 工单业务退回审批列表
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-	[Permission(EPermission.OrderPreviousList)]
-	[HttpGet("order_previous_list")]
-	public async Task<PagedDto<SendBackDto>> AuditList([FromQuery] SendBackListDto dto)
-	{
-		var (total, items) = await _orderSendBackAuditRepository.Queryable()
-			.Includes(x => x.Order)
-			.WhereIF(!string.IsNullOrEmpty(dto.Keyword),
-				d => d.Order.Title.Contains(dto.Keyword!) || d.Order.No.Contains(dto.Keyword!))
-			.WhereIF(dto.CreationTimeStart.HasValue, d => d.CreationTime >= dto.CreationTimeStart)
-			.WhereIF(dto.CreationTimeEnd.HasValue, d => d.CreationTime <= dto.CreationTimeEnd)
-			.WhereIF(dto.State.HasValue, d => d.State == dto.State)
-			.OrderByDescending(x => x.CreationTime)
-			.ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+    [Permission(EPermission.OrderPreviousList)]
+    [HttpGet("order_previous_list")]
+    public async Task<PagedDto<SendBackDto>> AuditList([FromQuery] SendBackListDto dto)
+    {
+        var (total, items) = await _orderSendBackAuditRepository.Queryable()
+            .Includes(x => x.Order)
+            .WhereIF(!string.IsNullOrEmpty(dto.Keyword),
+                d => d.Order.Title.Contains(dto.Keyword!) || d.Order.No.Contains(dto.Keyword!))
+            .WhereIF(dto.CreationTimeStart.HasValue, d => d.CreationTime >= dto.CreationTimeStart)
+            .WhereIF(dto.CreationTimeEnd.HasValue, d => d.CreationTime <= dto.CreationTimeEnd)
+            .WhereIF(dto.State.HasValue, d => d.State == dto.State)
+            .OrderByDescending(x => x.CreationTime)
+            .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
 
-		return new PagedDto<SendBackDto>(total, _mapper.Map<IReadOnlyList<SendBackDto>>(items));
-	}
+        return new PagedDto<SendBackDto>(total, _mapper.Map<IReadOnlyList<SendBackDto>>(items));
+    }
 
     /// <summary>
     /// 退回详情
@@ -2514,21 +2526,21 @@ public class OrderController : BaseController
     [HttpGet("order_previous/{id}")]
     public async Task<OrderSendBackAudit> OrderSendBackEntity(string id)
     {
-	    return await _orderSendBackAuditRepository.Queryable()
-		    .Includes(x => x.Order)
-		    .FirstAsync(x => x.Id == id);
+        return await _orderSendBackAuditRepository.Queryable()
+            .Includes(x => x.Order)
+            .FirstAsync(x => x.Id == id);
     }
 
-	#endregion
+    #endregion
 
-	#region 省工单退回
+    #region 省工单退回
 
-	/// <summary>
-	/// 工单退回列表
-	/// </summary>
-	/// <param name="dto"></param>
-	/// <returns></returns>
-	[Permission(EPermission.SendBackOrderList)]
+    /// <summary>
+    /// 工单退回列表
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [Permission(EPermission.SendBackOrderList)]
     [HttpGet("send_back")]
     public async Task<PagedDto<OrderSendBackDto>> UrgeList([FromQuery] OrderSendBackListDto dto)
     {

+ 9 - 5
src/Hotline.Application/FlowEngine/WorkflowApplication.cs

@@ -915,7 +915,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
     {
         int orgLevel;
         List<Kv> items;
-        var levelOneOrgCode = _sessionContext.RequiredOrgId.GetHigherOrgId();
+        string upperOrgId;
         switch (policy)
         {
             case EDynamicPolicy.OrgUpCenterTop:
@@ -931,8 +931,10 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 }
                 else
                 {
+                    //上级部门Id
+                    upperOrgId = _sessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
                     items = await _organizeRepository.Queryable()
-                        .Where(d => !d.IsCenter && d.Level == orgLevel && d.Id.StartsWith(levelOneOrgCode))
+                        .Where(d => d.Id == upperOrgId)
                         .Select(d => new Kv { Key = d.Id, Value = d.Name })
                         .ToListAsync(cancellationToken);
                 }
@@ -941,8 +943,10 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             case EDynamicPolicy.OrgUp:
                 orgLevel = _sessionContext.OrgLevel - 1;
                 if (orgLevel <= 0) orgLevel = 1;
+                //上级部门Id
+                upperOrgId = _sessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
                 items = await _organizeRepository.Queryable()
-                    .Where(d => d.Level == orgLevel && d.Id.StartsWith(levelOneOrgCode))
+                    .Where(d => d.Id == upperOrgId)
                     .Select(d => new Kv { Key = d.Id, Value = d.Name })
                     .ToListAsync(cancellationToken);
                 break;
@@ -959,7 +963,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 {
                     orgLevel = _sessionContext.OrgLevel + 1;
                     items = await _organizeRepository.Queryable()
-                        .Where(d => !d.IsCenter && d.Level == orgLevel && d.Id.StartsWith(levelOneOrgCode))
+                        .Where(d => !d.IsCenter && d.Level == orgLevel && d.Id.StartsWith(_sessionContext.RequiredOrgId))
                         .Select(d => new Kv { Key = d.Id, Value = d.Name })
                         .ToListAsync(cancellationToken);
                 }
@@ -968,7 +972,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             case EDynamicPolicy.OrgDown:
                 orgLevel = _sessionContext.OrgLevel + 1;
                 items = await _organizeRepository.Queryable()
-                    .Where(d => d.Level == orgLevel && d.Id.StartsWith(levelOneOrgCode))
+                    .Where(d => d.Level == orgLevel && d.Id.StartsWith(_sessionContext.RequiredOrgId))
                     .Select(d => new Kv { Key = d.Id, Value = d.Name })
                     .ToListAsync(cancellationToken);
                 break;

+ 13 - 13
src/Hotline.Share/Dtos/Push/MessageDto.cs

@@ -9,10 +9,20 @@ namespace Hotline.Share.Dtos.Push
         /// </summary>
         public EPushBusiness PushBusiness { get; set; }
 
+        /// <summary>
+        /// 接收姓名
+        /// </summary>
+        public string Name { get; set; }
+
+        /// <summary>
+        /// 接收手机号码
+        /// </summary>
+        public string TelNumber { get; set; }
+
         /// <summary>
         /// 外部业务唯一编号
         /// </summary>
-        public string ExternalId { get; set; }
+        public string? ExternalId { get; set; }
 
         /// <summary>
         /// 关联工单编号
@@ -22,7 +32,7 @@ namespace Hotline.Share.Dtos.Push
         /// <summary>
         /// 推送平台
         /// </summary>
-        public EPushPlatform PushPlatform { get; set; }
+        public EPushPlatform? PushPlatform { get; set; } = EPushPlatform.Sms;
 
         /// <summary>
         /// 模板
@@ -39,19 +49,9 @@ namespace Hotline.Share.Dtos.Push
         /// </summary>
         public string? Remark { get; set; }
 
-        /// <summary>
-        /// 接收姓名
-        /// </summary>
-        public string Name { get; set; }
-
-        /// <summary>
-        /// 接收手机号码
-        /// </summary>
-        public string TelNumber { get; set; }
-
         /// <summary>
         /// 参数
         /// </summary>
-        public List<string> Params { get; set; }
+        public List<string>? Params { get; set; }
     }
 }

+ 1 - 0
src/Hotline/FlowEngine/Workflows/Workflow.cs

@@ -480,6 +480,7 @@ public partial class Workflow
     /// </summary>
     public void UpdateActualOption()
     {
+        if(FlowType is EFlowType.Review) return;
         var step = Steps.FirstOrDefault(d => d.Id == ActualHandleStepId);
         if (step is null)
             throw new UserFriendlyException($"未查询到对应实际办理子节点,workflowId: {Id}");

+ 8 - 7
src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs

@@ -285,13 +285,6 @@ namespace Hotline.FlowEngine.Workflows
 
             var counterSignType = GetCounterSignType(currentStep.BusinessType);
 
-            await HandleStepAsync(currentStep, workflow, dto, flowAssignInfo.FlowAssignType, counterSignType,
-                cancellationToken);
-
-            currentStep.IsActualHandled = CheckIsActualHandle(workflow, currentStep, nextStepDefine, dto);
-
-            _mapper.Map(dto, workflow);
-
             var updateSteps = new List<WorkflowStep> { currentStep };
 
             //结束当前会签流程
@@ -326,6 +319,13 @@ namespace Hotline.FlowEngine.Workflows
                 }
             }
 
+            await HandleStepAsync(currentStep, workflow, dto, flowAssignInfo.FlowAssignType, counterSignType,
+                cancellationToken);
+
+            currentStep.IsActualHandled = CheckIsActualHandle(workflow, currentStep, nextStepDefine, dto);
+
+            _mapper.Map(dto, workflow);
+
             //操作为回到会签汇总时,更新开始会签节点的会签办理状态
             if (currentStep.IsInCountersign() && dto.BackToCountersignEnd)
             {
@@ -1262,6 +1262,7 @@ namespace Hotline.FlowEngine.Workflows
             newStep.Status = EWorkflowStepStatus.WaitForAccept;
             newStep.PrevStepId = step.PrevStepId;
             newStep.IsMain = step.IsMain;
+            newStep.IsOrigin = step.IsOrigin;
             //newStep.ParentId = step.ParentId;
             newStep.Handlers = step.Handlers;
             newStep.StartCountersignId = step.StartCountersignId;

+ 4 - 4
src/Hotline/Push/FWMessage/Message.cs

@@ -21,8 +21,8 @@ namespace Hotline.Push.FWMessage
         /// <summary>
         /// 外部业务唯一编号
         /// </summary>
-        [SugarColumn(ColumnDescription = "外部业务唯一编号", ColumnDataType = "varchar(50)")]
-        public string ExternalId { get; set; }
+        [SugarColumn(ColumnDescription = "外部业务唯一编号", ColumnDataType = "varchar(50)", IsNullable = true)]
+        public string? ExternalId { get; set; }
 
         /// <summary>
         /// 业务系统短信ID
@@ -33,8 +33,8 @@ namespace Hotline.Push.FWMessage
         /// <summary>
         /// 推送平台
         /// </summary>
-        [SugarColumn(ColumnDescription = "推送平台")]
-        public EPushPlatform PushPlatform { get; set; }
+        [SugarColumn(ColumnDescription = "推送平台"), IsNullable = true]
+        public EPushPlatform? PushPlatform { get; set; } = EPushPlatform.Sms;
 
         /// <summary>
         /// 推送状态

+ 18 - 12
src/Hotline/Push/FWMessage/PushDomainService.cs

@@ -1,4 +1,6 @@
 using DotNetCore.CAP;
+using Hotline.Orders;
+using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.Push;
 using Hotline.Share.Dtos.Push.FWMessage;
 using Hotline.Share.Enums.Push;
@@ -69,7 +71,7 @@ public class PushDomainService : IPushDomainService, IScopeDependency
 
         #region 替换模板内容
         //如果模板为空,参数为空则直接用短信内容
-        if (!string.IsNullOrEmpty(messageDto.TemplateCode) && messageDto.Params.Count > 0)
+        if (!string.IsNullOrEmpty(messageDto.TemplateCode))
         {
             //查询模板信息
             var messageTemplate = await _messageTemplateRepository.GetAsync(p => p.Code == messageDto.TemplateCode, cancellation);
@@ -77,19 +79,23 @@ public class PushDomainService : IPushDomainService, IScopeDependency
                 throw UserFriendlyException.SameMessage("未找到模板短信!");
 
             string Template = messageTemplate.Content;
-            //正则查询模板中需要替换的内容
-            Regex regex = new(@"\{[a-zA-Z0-9]{1,}\}");
-            var matches = regex.Matches(Template);
-            if (matches != null && matches.Count != messageDto.Params.Count)
-                //参数与需要替换的字符数不匹配
-                throw UserFriendlyException.SameMessage("模板需要参数与实际传递参数个数不匹配!");
-            //reason = "模板需要参数与实际传递参数个数不匹配!";
-
-            //根据正则查询出来的匹配项替换内容
-            for (int i = 0; i < matches.Count; i++)
-                Template = Template.Replace(matches[i].ToString(), messageDto.Params[i]);
+            if (messageDto.Params != null && messageDto.Params.Count > 0)
+            {
+                //正则查询模板中需要替换的内容
+                Regex regex = new(@"\{[a-zA-Z0-9]{1,}\}");
+                var matches = regex.Matches(Template);
+                if (matches != null && matches.Count != messageDto.Params.Count)
+                    //参数与需要替换的字符数不匹配
+                    throw UserFriendlyException.SameMessage("模板需要参数与实际传递参数个数不匹配!");
+                //reason = "模板需要参数与实际传递参数个数不匹配!";
+
+                //根据正则查询出来的匹配项替换内容
+                for (int i = 0; i < matches.Count; i++)
+                    Template = Template.Replace(matches[i].ToString(), messageDto.Params[i]);
 
+            }
             messageDto.Content = Template;
+
         }
         #endregion
         var message = _mapper.Map<Message>(messageDto);

+ 2 - 1
src/XF.Domain.Repository/Entity.cs

@@ -263,7 +263,8 @@ public abstract class PositionEntity : FullStateEntity
 
 public abstract class PositionWorkflowEntity : PositionEntity, IWorkflow
 {
-    [SugarColumn(IsNullable = true)] public string? WorkflowId { get; set; }
+    [SugarColumn(IsNullable = true)] 
+    public string? WorkflowId { get; set; }
 
     /// <summary>
     /// 过期时间配置id