ソースを参照

Merge branch 'release' of http://110.188.24.182:10023/Fengwo/hotline into release

tangjiang 9 ヶ月 前
コミット
9d1c85f045

+ 9 - 17
src/Hotline.Api/Controllers/CommonPController.cs

@@ -156,39 +156,31 @@ namespace Hotline.Api.Controllers
             //今日待办 tasksOkNum
             //var time = DateTime.Parse(tadayTime);
             //工单
-            var order = await _orderRepository.Queryable()
-                .Where(o => SqlFunc.JsonListObjectAny(o.HandlerUsers, "Key", _sessionContext.RequiredUserId) ||
-                            SqlFunc.JsonListObjectAny(o.HandlerOrgs, "Key", _sessionContext.RequiredOrgId))
-                .GroupBy(o => o.Id).MergeTable()
+            var order = await _orderRepository.Queryable(hasHandled: true)
+                .GroupBy(o => new { o.Id,o.Status }).MergeTable()
                 .Select(o => new
                 {
                     aboutExpire = SqlFunc.AggregateSum(SqlFunc.IIF(DateTime.Now > o.NearlyExpiredTime!.Value && DateTime.Now < o.ExpiredTime!.Value,
                         1, 0)),
-                    havExpired = SqlFunc.AggregateSum(SqlFunc.IIF(DateTime.Now > o.ExpiredTime!.Value, 1, 0)),
+                    havExpired = SqlFunc.AggregateSum(SqlFunc.IIF(DateTime.Now > o.ExpiredTime!.Value && o.Status < EOrderStatus.Filed, 1, 0)),
                     countersignHandle = SqlFunc.AggregateSum(SqlFunc.IIF(o.CounterSignType.HasValue, 1, 0)),
                 }).FirstAsync();
             var aboutExpire = order?.aboutExpire ?? 0;
             var havExpired = order?.havExpired ?? 0;
             var countersignHandle = order?.countersignHandle ?? 0;
             //延期
-            var delay = await _orderDelayRepository.Queryable()
-                .Includes(x => x.Workflow)
-                .Where(x => SqlFunc.JsonListObjectAny(x.HandlerUsers, "Key", _sessionContext.RequiredUserId) ||
-                            SqlFunc.JsonListObjectAny(x.HandlerOrgs, "Key", _sessionContext.RequiredOrgId))
-                .Where(x => x.DelayState == EDelayState.Examining).CountAsync();
+            var delay = await _orderDelayRepository.Queryable(hasHandled: true)
+                .Where(d => d.DelayState == EDelayState.Examining).CountAsync();
             //甄别
-            var screenAudit = await _orderScreenRepository.Queryable()
-                .Includes(x => x.Workflow)
-                .Where(x => SqlFunc.JsonListObjectAny(x.HandlerUsers, "Key", _sessionContext.RequiredUserId) ||
-                            SqlFunc.JsonListObjectAny(x.HandlerOrgs, "Key", _sessionContext.RequiredOrgId))
-                .Where(x => x.Status == EScreenStatus.Apply)
+            var screenAudit = await _orderScreenRepository.Queryable(hasHandled: true)
+                .Where(d => d.Status == EScreenStatus.Apply)
                 .CountAsync();
-            var workTime = _timeLimitDomainService.CalcWorkTimeReduce(DateTime.Now, 5);
+            //var workTime = _timeLimitDomainService.CalcWorkTimeReduce(DateTime.Now, 5);
             var screenHandle = await _orderVisitedDetailRepository.Queryable(false, true)
                 .Includes(x => x.OrderVisit)
                 .LeftJoin<OrderScreen>((x, s) => x.Id == s.VisitDetailId && s.Status < EScreenStatus.End && s.IsDeleted == false)
                 .Where((x, s) => s.Id == null)
-                .Where(x => x.OrderVisit.VisitTime < DateTime.Now && x.OrderVisit.VisitTime > workTime)
+                //.Where(x => x.OrderVisit.VisitTime < DateTime.Now && x.OrderVisit.VisitTime > workTime)
                 .Where((x, s) => x.OrderVisit.VisitState == EVisitState.Visited && x.OrderVisit.IsCanHandle)
                 .Where((x, s) => x.VisitTarget == EVisitTarget.Org && x.VisitOrgCode == _sessionContext.OrgId && (
                     SqlFunc.JsonField(x.OrgProcessingResults, "Key") == "1" || SqlFunc.JsonField(x.OrgProcessingResults, "Key") == "2" ||

+ 22 - 25
src/Hotline.Api/Controllers/OrderController.cs

@@ -2665,27 +2665,17 @@ public class OrderController : BaseController
         dto.RepeatableEventDetails = repeatables;
         if (!string.IsNullOrEmpty(dto.WorkflowId))
         {
-
             var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withSteps: true,
                 cancellationToken: HttpContext.RequestAborted);
-            //  var centerOpinion = workflow.Steps.Where(x => x.HandlerOrgId == OrgSeedData.CenterId).OrderByDescending(x => x.CreationTime).Select(x => x.Opinion).FirstOrDefault();
             var centerOpinion = workflow.Steps.Where(x => x.HandlerOrgId == OrgSeedData.CenterId).MaxBy(d => d.CreationTime)?.Opinion ?? string.Empty;
-
-            dto.CenterOpinion = centerOpinion;//string.IsNullOrEmpty(centerOpinion) ? string.Empty : centerOpinion;
+            dto.CenterOpinion = centerOpinion;
             List<OrderRemarksDto> remarks = workflow.Steps.Where(x => !string.IsNullOrEmpty(x.Remark)).Select(x => new OrderRemarksDto { Remark = x.Remark, RemarkTime = x.CreationTime, RemarkUser = x.CreatorName }).ToList();
             dto.OrderRemarks = remarks;
-
-            var sendBack = await _orderSendBackAuditRepository.Queryable().Where(x => x.OrderId == dto.Id).OrderByDescending(x => x.CreationTime).FirstAsync();
-            dto.SendBackOpinion = sendBack is { Id: not null } && !string.IsNullOrEmpty(sendBack.Content) ? sendBack.Content : string.Empty;
-
-            //var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withSteps: true,
-            //    cancellationToken: HttpContext.RequestAborted);
-            //var centerOpinion = workflow.Steps.Where(x => x.HandlerOrgId == OrgSeedData.CenterId).OrderByDescending(x => x.CreationTime).Select(x => x.Opinion).First();
-            //dto.CenterOpinion = string.IsNullOrEmpty(centerOpinion) ? string.Empty : centerOpinion;
-            //var sendBack = await _orderSendBackAuditRepository.Queryable().Where(x => x.OrderId == dto.Id).OrderByDescending(x => x.CreationTime).FirstAsync();
-            //dto.SendBackOpinion = sendBack is { Id: not null } && !string.IsNullOrEmpty(sendBack.Content) ? sendBack.Content : string.Empty;
-            //List<OrderRemarksDto> remarks = workflow.Steps.Where(x => !string.IsNullOrEmpty(x.Remark)).Select(x => new OrderRemarksDto { Remark = x.Remark, RemarkTime = x.CreationTime, RemarkUser = x.CreatorName }).ToList();
-            //dto.OrderRemarks = remarks;
+            if (order.Status == EOrderStatus.SendBack || order.Status == EOrderStatus.SendBackAudit)
+            {
+				var sendBack = await _orderSendBackAuditRepository.Queryable().Where(x => x.OrderId == dto.Id).OrderByDescending(x => x.CreationTime).FirstAsync();
+				dto.SendBackOpinion = sendBack is { Id: not null } && !string.IsNullOrEmpty(sendBack.Content) ? sendBack.Content : string.Empty;
+			}
         }
 
         return dto;
@@ -3155,7 +3145,8 @@ public class OrderController : BaseController
         var order = await _orderRepository.GetAsync(d => d.WorkflowId == workflow.Id, HttpContext.RequestAborted);
         if (order is null)
             throw new UserFriendlyException($"工单未开启流程, workflowId: {workflow.Id}");
-        _mapper.Map(workflow, order);
+        order.UpdateHandlingStatus(workflow.IsInCountersign);
+		_mapper.Map(workflow, order);
         await _orderRepository.UpdateAsync(order, HttpContext.RequestAborted);
     }
 
@@ -3697,14 +3688,16 @@ public class OrderController : BaseController
                     TraceId = currentStep.Id
                 };
                 await _orderSendBackAuditRepository.AddAsync(audit, HttpContext.RequestAborted);
-            }
+                await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { Status = EOrderStatus.SendBackAudit })
+	                .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
+			}
             else
             {
                 var flowDirection = await _workflowApplication.PreviousAsync(dto, HttpContext.RequestAborted);
                 var processType = flowDirection == EFlowDirection.OrgToCenter || flowDirection == EFlowDirection.CenterToCenter
                     ? EProcessType.Zhiban
                     : EProcessType.Jiaoban;
-                await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { ProcessType = processType })
+                await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { ProcessType = processType,Status = EOrderStatus.SendBack })
                     .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
                 //发送短信TODO
             }
@@ -3715,7 +3708,7 @@ public class OrderController : BaseController
             var processType = flowDirection == EFlowDirection.OrgToCenter || flowDirection == EFlowDirection.CenterToCenter
                 ? EProcessType.Zhiban
                 : EProcessType.Jiaoban;
-            await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { ProcessType = processType })
+            await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { ProcessType = processType, Status = EOrderStatus.SendBack })
                 .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
             //发送短信TODO
         }
@@ -3757,7 +3750,7 @@ public class OrderController : BaseController
                               flowDirection == EFlowDirection.CenterToCenter
                 ? EProcessType.Zhiban
                 : EProcessType.Jiaoban;
-            await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { ProcessType = processType })
+            await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { ProcessType = processType, Status = EOrderStatus.SendBack })
                 .Where(o => o.Id == sendBack.OrderId).ExecuteCommandAsync(HttpContext.RequestAborted);
             //发送短信TODO
             await _orderSendBackAuditRepository.UpdateAsync(sendBack, HttpContext.RequestAborted);
@@ -3797,7 +3790,7 @@ public class OrderController : BaseController
                 var processType = flowDirection == EFlowDirection.OrgToCenter || flowDirection == EFlowDirection.CenterToCenter
                     ? EProcessType.Zhiban
                     : EProcessType.Jiaoban;
-                await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { ProcessType = processType })
+                await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { ProcessType = processType, Status = EOrderStatus.SendBack })
                     .Where(o => o.Id == sendBack.OrderId).ExecuteCommandAsync(HttpContext.RequestAborted);
                 //发送短信TODO
             }
@@ -4144,7 +4137,7 @@ public class OrderController : BaseController
             //var expiredTime = _timeLimitDomainService.CalcEndTime(DateTime.Now,
             //	ETimeType.WorkDay,
             //	dto.TimeLimit.Value, order.AcceptTypeCode);
-            await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { ExpiredTime = expiredTime.ExpiredTime, NearlyExpiredTime = expiredTime.NearlyExpiredTime, NearlyExpiredTimeOne = expiredTime.NearlyExpiredTimeOne, ProcessType = processType })
+            await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { ExpiredTime = expiredTime.ExpiredTime, NearlyExpiredTime = expiredTime.NearlyExpiredTime, NearlyExpiredTimeOne = expiredTime.NearlyExpiredTimeOne, ProcessType = processType,Status = EOrderStatus.Special })
                 .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
             var orderDto = _mapper.Map<OrderDto>(order);
             await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderExpiredTimeUpdate, orderDto,
@@ -4194,6 +4187,10 @@ public class OrderController : BaseController
                 //}
             }
         }
+        else {
+			await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { Status = EOrderStatus.SpecialAudit })
+				.Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
+		}
     }
 
     /// <summary>
@@ -4365,7 +4362,7 @@ public class OrderController : BaseController
             var processType = special.FlowDirection is EFlowDirection.OrgToCenter or EFlowDirection.CenterToCenter or EFlowDirection.FiledToCenter
                 ? EProcessType.Zhiban
                 : EProcessType.Jiaoban;
-            await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { ExpiredTime = expiredTime.ExpiredTime, NearlyExpiredTime = expiredTime.NearlyExpiredTime, NearlyExpiredTimeOne = expiredTime.NearlyExpiredTimeOne, ProcessType = processType })
+            await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { ExpiredTime = expiredTime.ExpiredTime, NearlyExpiredTime = expiredTime.NearlyExpiredTime, NearlyExpiredTimeOne = expiredTime.NearlyExpiredTimeOne, ProcessType = processType,Status = EOrderStatus.Special })
                 .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
             var orderDto = _mapper.Map<OrderDto>(order);
             await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderExpiredTimeUpdate, orderDto,
@@ -4467,7 +4464,7 @@ public class OrderController : BaseController
                 var processType = special.FlowDirection is EFlowDirection.OrgToCenter or EFlowDirection.CenterToCenter or EFlowDirection.FiledToCenter
                     ? EProcessType.Zhiban
                     : EProcessType.Jiaoban;
-                await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { ExpiredTime = expiredTime.ExpiredTime, NearlyExpiredTime = expiredTime.NearlyExpiredTime, NearlyExpiredTimeOne = expiredTime.NearlyExpiredTimeOne, ProcessType = processType })
+                await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { ExpiredTime = expiredTime.ExpiredTime, NearlyExpiredTime = expiredTime.NearlyExpiredTime, NearlyExpiredTimeOne = expiredTime.NearlyExpiredTimeOne, ProcessType = processType,Status = EOrderStatus.Special })
                     .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
                 var orderDto = _mapper.Map<OrderDto>(order);
                 await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderExpiredTimeUpdate, orderDto,

+ 8 - 8
src/Hotline.Api/StartupHelper.cs

@@ -263,14 +263,14 @@ namespace Hotline.Api
                 switch (callCenterConfiguration.CallCenterType)
                 {
                     case AppDefaults.CallCenterType.XingTang:
-                        // var getCallsJobKey = new JobKey(nameof(XingTangCallsSyncJob));
-                        // d.AddJob<XingTangCallsSyncJob>(getCallsJobKey);
-                        // d.AddTrigger(t => t
-                        //     .WithIdentity("get-callsxt-trigger")
-                        //     .ForJob(getCallsJobKey)
-                        //     .StartNow()
-                        //     .WithCronSchedule("0/5 * * * * ?")
-                        // );
+                        var getCallsJobKey = new JobKey(nameof(XingTangCallsSyncJob));
+                        d.AddJob<XingTangCallsSyncJob>(getCallsJobKey);
+                        d.AddTrigger(t => t
+                            .WithIdentity("get-callsxt-trigger")
+                            .ForJob(getCallsJobKey)
+                            .StartNow()
+                            .WithCronSchedule("0/5 * * * * ?")
+                        );
 
                         var getOperationsJobKey = new JobKey(nameof(XingTangTelOperationSyncJob));
                         d.AddJob<XingTangTelOperationSyncJob>(getOperationsJobKey);

+ 2 - 2
src/Hotline.Api/config/appsettings.Development.json

@@ -28,7 +28,7 @@
     }
   },
   "ConnectionStrings": {
-    "Hotline": "PORT=5432;DATABASE=hotline;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;"
+    "Hotline": "PORT=5432;DATABASE=hotline_dev;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;"
     //"Redis": "110.188.24.182:50179",
     //"MongoDB": "mongodb://192.168.100.121:27017",
     //"Wex": "server=222.212.82.225;Port=4509;Database=fs_kft;Uid=root;Pwd=Wex@12345;"
@@ -37,7 +37,7 @@
     "Host": "110.188.24.182",
     "Port": 50179,
     "Password": "fengwo123!$!$",
-    "Database": 3 //release:3, dev:5
+    "Database": 5 //release:3, dev:5
   },
   "Swagger": true,
   "Cors": {

+ 2 - 2
src/Hotline.Api/config/appsettings.json

@@ -28,7 +28,7 @@
     }
   },
   "ConnectionStrings": {
-    "Hotline": "PORT=5432;DATABASE=hotline;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;"
+    "Hotline": "PORT=5432;DATABASE=hotline_dev;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;"
     //"Redis": "110.188.24.182:50179,password=fengwo22@@",
     //"MongoDB": "mongodb://192.168.100.121:27017",
     //"Wex": "server=222.212.82.225;Port=4509;Database=fs_kft;Uid=root;Pwd=Wex@12345;"
@@ -37,7 +37,7 @@
     "Host": "110.188.24.182",
     "Port": 50179,
     "Password": "fengwo123!$!$",
-    "Database": 3
+    "Database": 5 
   },
   "Swagger": true,
   "Cors": {

+ 17 - 33
src/Hotline.Application/CallCenter/DefaultCallApplication.cs

@@ -217,7 +217,7 @@ public abstract class DefaultCallApplication : ICallApplication
             .WhereIF(dto.Direction != null, d => d.Direction == dto.Direction)
             .WhereIF(dto.WaitDurationStart != null && dto.WaitDurationStart > 0, d => d.WaitDuration >= dto.WaitDurationStart)
             .WhereIF(dto.WaitDurationEnd != null && dto.WaitDurationEnd > 0, d => d.WaitDuration <= dto.WaitDurationEnd)
-            .OrderByDescending(d=>d.Id)
+            .OrderByDescending(d => d.Id)
             .Select((d, o) => new CallNativeDto
             {
                 OrderId = o.Id,
@@ -237,7 +237,7 @@ public abstract class DefaultCallApplication : ICallApplication
             .WhereIF(!string.IsNullOrEmpty(dto.StaffNo), d => d.StaffNo == dto.StaffNo)
             .WhereIF(!string.IsNullOrEmpty(dto.GroupId), d => d.GroupId == dto.GroupId)
             .WhereIF(dto.OperateState != null, d => d.OperateState == dto.OperateState)
-            .OrderByDescending(d=>d.Id)
+            .OrderByDescending(d => d.Id)
             .ToFixedListAsync(dto, cancellationToken);
     }
 
@@ -261,45 +261,29 @@ public abstract class DefaultCallApplication : ICallApplication
             catch (Exception e)
             {
                 _logger.LogError($"写入callidRelation失败:{e.Message}");
+                return await GetOrSetCallIdAsync(callNo, cancellationToken);
             }
         }
 
         return callOrder.CallId;
     }
 
-    /// <summary>
-    /// 批量获取callId
-    /// </summary>
-    public virtual async Task<List<(string callNo, string callId)>> GetOrSetCallIdRangeAsync(List<string> callNos,
-        CancellationToken cancellationToken)
+    public async Task<CallidRelation> GetRelationAsync(string callNo, CancellationToken cancellation)
     {
-        var relations = await _callIdRelationRepository.Queryable()
-            .Where(d => callNos.Contains(d.Id))
-            .ToListAsync(cancellationToken);
+        return await _callIdRelationRepository.GetAsync(callNo, cancellation);
+    }
 
-        var rsp = new List<(string callNo, string callId)>();
-        var newRelations = new List<CallidRelation>();
-        foreach (var callNo in callNos)
-        {
-            var relation = relations.FirstOrDefault(d => d.Id == callNo);
-            if (relation is null)
-            {
-                relation = new CallidRelation
-                {
-                    Id = callNo,
-                    CallId = Ulid.NewUlid().ToString(),
-                };
-                newRelations.Add(relation);
-                rsp.Add(new(relation.Id, relation.CallId));
-            }
-            else
-            {
-                rsp.Add(new(relation.Id, relation.CallId));
-            }
-        }
+    public async Task AddRelationAsync(CallidRelation relation, CancellationToken cancellation)
+    {
+        await _callIdRelationRepository.AddAsync(relation, cancellation);
+    }
 
-        await _callIdRelationRepository.AddRangeAsync(newRelations, cancellationToken);
-        return rsp;
+    /// <summary>
+    /// 乐观并发更新映射关系
+    /// </summary>
+    public virtual async Task<int> UpdateRelationOptLockAsync(CallidRelation relation, CancellationToken cancellationToken)
+    {
+        return await _callIdRelationRepository.Updateable(relation).ExecuteCommandWithOptLockAsync();
     }
 
     /// <summary>
@@ -352,7 +336,7 @@ public abstract class DefaultCallApplication : ICallApplication
     /// 查询通话记录
     /// </summary>
     public virtual Task<List<TrCallRecord>> QueryTianrunCallsAsync(
-        string? phone = null, 
+        string? phone = null,
         ECallDirection? direction = null,
         DateTime? callStartTimeStart = null,
         DateTime? callStartTimeEnd = null,

+ 7 - 2
src/Hotline.Application/CallCenter/ICallApplication.cs

@@ -77,10 +77,14 @@ namespace Hotline.Application.CallCenter
         /// </summary>
         Task<string> GetOrSetCallIdAsync(string callNo, CancellationToken cancellationToken);
 
+        Task<CallidRelation> GetRelationAsync(string callNo, CancellationToken cancellation);
+
+        Task AddRelationAsync(CallidRelation relation, CancellationToken cancellation);
+
         /// <summary>
-        /// 批量获取callId
+        /// 乐观并发更新映射关系
         /// </summary>
-        Task<List<(string callNo, string callId)>> GetOrSetCallIdRangeAsync(List<string> callNos, CancellationToken cancellationToken);
+        Task<int> UpdateRelationOptLockAsync(CallidRelation relation, CancellationToken cancellationToken);
 
         /// <summary>
         /// 查询通话记录
@@ -123,5 +127,6 @@ namespace Hotline.Application.CallCenter
         /// </summary>
         /// <returns></returns>
         List<Kv> GetTelOperationOptions();
+
     }
 }

+ 2 - 1
src/Hotline.Application/Handlers/FlowEngine/CancelHandler.cs

@@ -35,7 +35,8 @@ public class CancelHandler : INotificationHandler<CancelWorkflowNotify>
                 var order = await _orderDomainService.GetOrderAsync(workflow.ExternalId, cancellationToken: cancellationToken);
                 order.CheckIfFiled();
                 order.Cancel();
-                _mapper.Map(workflow, order);
+                order.UpdateHandlingStatus(workflow.IsInCountersign);
+				_mapper.Map(workflow, order);
                 await _orderRepository.UpdateAsync(order, cancellationToken);
                 break;
         }

+ 3 - 2
src/Hotline.Application/Handlers/FlowEngine/WorkflowEndHandler.cs

@@ -151,8 +151,9 @@ public class WorkflowEndHandler : INotificationHandler<EndWorkflowNotify>
             case WorkflowModuleConsts.OrderHandle:
                 var order = await _orderDomainService.GetOrderAsync(workflow.ExternalId,
                     withExtension: true, cancellationToken: cancellationToken);
-                //order.CheckIfFiled();
-                _mapper.Map(workflow, order);
+				//order.CheckIfFiled();
+				order.UpdateHandlingStatus(workflow.IsInCountersign);
+				_mapper.Map(workflow, order);
                 var now = DateTime.Now;
                 var handleDuration = order.CenterToOrgTime.HasValue && order.ActualHandleTime.HasValue
                     ? _timeLimitDomainService.CalcWorkTime(order.CenterToOrgTime.Value,

+ 2 - 1
src/Hotline.Application/Handlers/FlowEngine/WorkflowNextHandler.cs

@@ -108,7 +108,8 @@ public class WorkflowNextHandler : INotificationHandler<NextStepNotify>
                 var order = await _orderDomainService.GetOrderAsync(workflow.ExternalId, withHotspot: true,
                     withAcceptor: true, withExtension: true, cancellationToken: cancellationToken);
                 order.CheckIfFiled();
-                _mapper.Map(workflow, order);
+                order.UpdateHandlingStatus(workflow.IsInCountersign);
+				_mapper.Map(workflow, order);
 
                 //var expiredTimeChanged = false;
                 //if (data.FlowDirection.HasValue

+ 2 - 1
src/Hotline.Application/Handlers/FlowEngine/WorkflowPreviousHandler.cs

@@ -74,7 +74,8 @@ namespace Hotline.Application.Handlers.FlowEngine
                     var order = await _orderDomainService.GetOrderAsync(workflow.ExternalId, withHotspot: true, withAcceptor: true,
                         withExtension: true, cancellationToken: cancellationToken);
                     order.CheckIfFiled();
-                    _mapper.Map(workflow, order);
+                    order.UpdateHandlingStatus(workflow.IsInCountersign);
+					_mapper.Map(workflow, order);
                     if (notification.TargetStep.StepType is EStepType.Start)
                     {
                         order.Status = EOrderStatus.BackToUnAccept;

+ 2 - 1
src/Hotline.Application/Handlers/FlowEngine/WorkflowRecallHandler.cs

@@ -60,7 +60,8 @@ public class WorkflowRecallHandler : INotificationHandler<RecallNotify>
             case WorkflowModuleConsts.OrderHandle:
                 var order = await _orderDomainService.GetOrderAsync(workflow.ExternalId, withHotspot: true, withAcceptor: true,
                     withExtension: true, cancellationToken: cancellationToken);
-                _mapper.Map(workflow, order);
+                order.UpdateHandlingStatus(workflow.IsInCountersign);
+				_mapper.Map(workflow, order);
                 if (notification.TargetStep.StepType is EStepType.Start)
                 {
                     //if (!bool.TryParse(

+ 3 - 2
src/Hotline.Application/Handlers/FlowEngine/WorkflowStartHandler.cs

@@ -97,8 +97,9 @@ namespace Hotline.Application.Handlers.FlowEngine
                     var order = await _orderDomainService.GetOrderAsync(workflow.ExternalId, withHotspot: true, withAcceptor: true,
                         withExtension: true, cancellationToken);
                     order.CheckIfFiled();
-                    order.StartManageFlow();
-                    _mapper.Map(workflow, order);
+					//order.StartManageFlow();
+					order.UpdateHandlingStatus(workflow.IsInCountersign);
+					_mapper.Map(workflow, order);
                     await _orderRepository.UpdateAsync(order, cancellationToken);
 
                     var publishCallRecordDto = new PublishCallRecrodDto() { };

+ 30 - 6
src/Hotline.Application/Jobs/XingTangCallsSyncJob.cs

@@ -43,7 +43,8 @@ namespace Hotline.Application.Jobs
         {
             var xingtangCalls = await _db.Queryable<XingtangCall>()
                 .Where(d => (d.IsSync == null || !d.IsSync) && (d.Tries == null || d.Tries <= 50))
-                .Take(50)
+                .OrderBy(d => d.Id)
+                .Take(10)
                 .ToListAsync(context.CancellationToken);
 
             var occupyCalls = new List<XingtangCall>();
@@ -67,13 +68,9 @@ namespace Hotline.Application.Jobs
                     .Where(d => staffNos.Contains(d.StaffNo))
                     .ToListAsync(context.CancellationToken);
 
-                var relations = await _callApplication.GetOrSetCallIdRangeAsync(
-                    calls.Select(d => d.CallNo).ToList(),
-                    context.CancellationToken);
-
                 foreach (var call in calls)
                 {
-                    call.Id = relations.First(d => d.callNo == call.CallNo).callId;
+                    call.Id = await GetCallIdAsync(call.CallNo, context.CancellationToken);
                     var user = users.FirstOrDefault(d => d.StaffNo == call.StaffNo);
                     if (user is not null)
                     {
@@ -99,6 +96,33 @@ namespace Hotline.Application.Jobs
             }
         }
 
+        private async Task<string> GetCallIdAsync(string callNo, CancellationToken cancellation)
+        {
+            var relation = await _callApplication.GetRelationAsync(callNo, cancellation);
+            if (relation is null)
+            {
+                relation = new CallidRelation
+                {
+                    Id = callNo,
+                    CallId = Ulid.NewUlid().ToString(),
+                    IsUsed = true
+                };
+                await _callApplication.AddRelationAsync(relation, cancellation);
+                return relation.CallId;
+            }
+
+            if (relation.IsUsed)
+                return Ulid.NewUlid().ToString();
+
+            relation.IsUsed = true;
+            var rows = await _callApplication.UpdateRelationOptLockAsync(relation, cancellation);
+            if (rows > 0)
+                return relation.CallId;
+
+            //重新取relation 重新判断isUsed
+            return await GetCallIdAsync(callNo, cancellation);
+        }
+
         /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
         public void Dispose()
         {

+ 4 - 4
src/Hotline.Application/Mappers/OrderMapperConfigs.cs

@@ -101,10 +101,10 @@ public class OrderMapperConfigs : IRegister
             .Ignore(d => d.AcceptorId)
             .Ignore(d => d.AcceptorName)
             .Ignore(d => d.AcceptorStaffNo)
-			.AfterMapping((s, d) =>
-            {
-                d.UpdateHandlingStatus(s.IsInCountersign);
-            })
+			//.AfterMapping((s, d) =>
+   //         {
+   //             d.UpdateHandlingStatus(s.IsInCountersign);
+   //         })
             ;
 
         config.ForType<Order, PublishDto>()

+ 32 - 2
src/Hotline.Share/Enums/Order/EOrderStatus.cs

@@ -36,10 +36,40 @@ public enum EOrderStatus
     [Description("办理中")]
     Handling = 100,
 
+	/// <summary>
+	/// 特提信件
+	/// </summary>
+	[Description("特提信件")]
+    Special = 101,
+
+    /// <summary>
+    /// 退回信件
+    /// </summary>
+    [Description("退回信件")]
+    SendBack = 102,
+
     /// <summary>
-    /// 会签中
+    /// 移交信件
     /// </summary>
-    [Description("会签中")]
+    [Description("移交信件")]
+    HandOver = 103,
+
+	/// <summary>
+	/// 特提审批中
+	/// </summary>
+	[Description("特提审批中")]
+	SpecialAudit = 105,
+
+	/// <summary>
+	/// 退回审批中
+	/// </summary>
+	[Description("退回审批中")]
+    SendBackAudit = 105,
+
+	/// <summary>
+	/// 会签中
+	/// </summary>
+	[Description("会签中")]
     Countersigning = 200,
 
     /// <summary>

+ 9 - 1
src/Hotline/CallCenter/Calls/CallidRelation.cs

@@ -14,7 +14,15 @@ namespace Hotline.CallCenter.Calls
     /// Id: CallNo
     /// </summary>
     public class CallidRelation : CreationEntity
-    {   
+    {
         public string CallId { get; set; }
+
+        /// <summary>
+        /// 是否被通话记录取用
+        /// </summary>
+        public bool IsUsed { get; set; }
+
+        [SqlSugar.SugarColumn(IsEnableUpdateVersionValidation = true)]
+        public Guid Ver { get; set; }
     }
 }