田爽 1 年之前
父節點
當前提交
94589a232c
共有 32 個文件被更改,包括 782 次插入329 次删除
  1. 77 2
      src/Hotline.Api/Controllers/Bi/BiCallController.cs
  2. 81 39
      src/Hotline.Api/Controllers/Bi/BiOrderController.cs
  3. 81 31
      src/Hotline.Api/Controllers/IPPbxController.cs
  4. 93 3
      src/Hotline.Api/Controllers/OrderController.cs
  5. 7 7
      src/Hotline.Api/Controllers/PbxController.cs
  6. 31 3
      src/Hotline.Api/Controllers/TestController.cs
  7. 1 1
      src/Hotline.Api/Controllers/UserController.cs
  8. 7 2
      src/Hotline.Application/CallCenter/Calls/TrApplication.cs
  9. 40 2
      src/Hotline.Application/FlowEngine/WorkflowApplication.cs
  10. 2 1
      src/Hotline.Application/Handlers/FlowEngine/WorkflowStartHandler.cs
  11. 1 16
      src/Hotline.Application/Mappers/CallMapperConfigs.cs
  12. 6 6
      src/Hotline.Application/Orders/OrderApplication.cs
  13. 1 21
      src/Hotline.Repository.SqlSugar/CallCenter/TelRepository.cs
  14. 22 0
      src/Hotline.Repository.SqlSugar/CallCenter/TelRestRepository.cs
  15. 46 0
      src/Hotline.Share/Dtos/CallCenter/BiSeatCallsDto.cs
  16. 6 0
      src/Hotline.Share/Dtos/Order/PublishedDto.cs
  17. 18 1
      src/Hotline.Share/Dtos/Order/QueryOrderDto.cs
  18. 13 0
      src/Hotline.Share/Dtos/TrCallCenter/TrTelDao.cs
  19. 0 15
      src/Hotline.Share/Enums/Order/EProcessType.cs
  20. 23 0
      src/Hotline.Share/Requests/PagedKeywordRequest.cs
  21. 6 0
      src/Hotline/CallCenter/Calls/TrCallRecord.cs
  22. 1 16
      src/Hotline/CallCenter/Tels/ITelRepository.cs
  23. 16 0
      src/Hotline/CallCenter/Tels/ITelRestRepository.cs
  24. 1 1
      src/Hotline/CallCenter/Tels/TelRest.cs
  25. 6 6
      src/Hotline/FlowEngine/Workflows/StepBasicEntity.cs
  26. 26 4
      src/Hotline/FlowEngine/Workflows/Workflow.cs
  27. 30 16
      src/Hotline/FlowEngine/Workflows/WorkflowCountersign.cs
  28. 116 50
      src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs
  29. 11 1
      src/Hotline/Orders/Order.cs
  30. 12 11
      src/Hotline/README.md
  31. 1 9
      src/Hotline/Users/IUserDomainService.cs
  32. 0 65
      src/Hotline/Users/UserDomainService.cs

+ 77 - 2
src/Hotline.Api/Controllers/Bi/BiCallController.cs

@@ -1,12 +1,20 @@
-using Hotline.CallCenter.Calls;
+using Hotline.Caching.Interfaces;
+using Hotline.CallCenter.Calls;
+using Hotline.CallCenter.Tels;
+using Hotline.Orders;
+using Hotline.Repository.SqlSugar.Extensions;
+using Hotline.Settings;
+using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.CallCenter;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Enums.User;
 using Hotline.Share.Requests;
 using Hotline.Users;
+using MapsterMapper;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
 using SqlSugar;
+using System.Data;
 using XF.Domain.Repository;
 
 namespace Hotline.Api.Controllers.Bi;
@@ -18,13 +26,21 @@ public class BiCallController : BaseController
 {
     private readonly IRepository<TrCallRecord> _trCallRecordRepository;
     private readonly IRepository<User> _userRepository;
+    private readonly IRepository<TelRest> _telRestRepository;
+    private readonly IMapper _mapper;
+   
+
 
     public BiCallController(
         IRepository<TrCallRecord> trCallRecordRepository,
-        IRepository<User> userRepository)
+        IRepository<User> userRepository,
+        IRepository<TelRest> telRestRepository,
+        IMapper mapper)
     {
         _trCallRecordRepository = trCallRecordRepository;
         _userRepository = userRepository;
+        _telRestRepository = telRestRepository;
+        _mapper = mapper;
     }
 
     [HttpGet("calls")]
@@ -111,4 +127,63 @@ public class BiCallController : BaseController
               .MergeTable()
               .ToListAsync(HttpContext.RequestAborted);
     }
+
+    /// <summary>
+    /// 小休统计
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("rests")]
+    [AllowAnonymous]
+    public async Task<IReadOnlyList<BiSeatRestDto>> QuerySeatRest([FromQuery]QuerySeatRestRequest dto)
+    {
+        return await _telRestRepository.Queryable()
+            .WhereIF(!string.IsNullOrEmpty(dto.UserName), x => x.UserName.Contains(dto.UserName))
+            .WhereIF(!string.IsNullOrEmpty(dto.StaffNo), x => x.StaffNo.Contains(dto.StaffNo))
+            .WhereIF(dto.StartTime.HasValue,x => x.CreationTime >= dto.StartTime.Value)
+            .WhereIF(dto.EndTime.HasValue,x => x.CreationTime <= dto.EndTime.Value)
+            .GroupBy(x=> new { x.UserId ,x.StaffNo,x.UserName})
+            .Select(x => new BiSeatRestDto
+            { 
+                 UserId = x.UserId,
+                 StaffNo  = x.StaffNo,
+                 UserName = x.UserName,
+                 RestCount = SqlFunc.AggregateCount(x.Id),
+                 RestDuration = SqlFunc.AggregateSum(x.RestDuration/60)
+            })
+            .OrderByIF(dto.SortRule ==0,a=> a.RestDuration,OrderByType.Asc)
+            .OrderByIF(dto.SortRule ==1,a=> a.RestDuration,OrderByType.Desc)
+            .MergeTable()
+            .ToListAsync(HttpContext.RequestAborted);
+    }
+
+    /// <summary>
+    /// 坐席转接统计
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("seatswitch")]
+    [AllowAnonymous]
+    public async Task<PagedDto<BiSeatSwitchDto>> QuerySeatSwitch([FromQuery]QuerySeatSwitchRequest dto)
+    {
+        var (total,items) = await _trCallRecordRepository.Queryable()
+            .Where(x => !string.IsNullOrEmpty(x.AgentTransferNumber))
+            .WhereIF(!string.IsNullOrEmpty(dto.UserName), x => x.UserName.Contains(dto.UserName))
+            .WhereIF(!string.IsNullOrEmpty(dto.CDPN), x => x.CDPN.Contains(dto.CDPN))
+            .WhereIF(dto.StartTime.HasValue,x=>x.CreatedTime >= dto.StartTime.Value)
+            .WhereIF(dto.EndTime.HasValue,x=>x.CreatedTime <= dto.EndTime.Value)
+            .Select(x=> new BiSeatSwitchDto { 
+                UserId = x.UserId,
+                CPN = x.CPN,
+                CDPN = x.CDPN,
+                CreatedTime = x.CreatedTime,
+                TelNo = x.AgentTransferNumber,
+                UserName = x.UserName,
+            })
+            .OrderByDescending(x => x.CreatedTime)
+            .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+
+        return new PagedDto<BiSeatSwitchDto>(total, _mapper.Map<IReadOnlyList<BiSeatSwitchDto>>(items));
+    }
+
 }

+ 81 - 39
src/Hotline.Api/Controllers/Bi/BiOrderController.cs

@@ -12,6 +12,10 @@ using SqlSugar;
 using Hotline.Share.Requests;
 using Hotline.Users;
 using XF.Domain.Repository;
+using Hotline.Caching.Services;
+using Hotline.Share.Dtos.CallCenter;
+using Microsoft.AspNetCore.Authorization;
+using Hotline.Caching.Interfaces;
 
 namespace Hotline.Api.Controllers.Bi
 {
@@ -19,19 +23,21 @@ namespace Hotline.Api.Controllers.Bi
     {
         private readonly IOrderRepository _orderRepository;
         private readonly IRepository<Hotspot> _hotspotTypeRepository;
-        private readonly IRepository<OrderDelay> _orderDelayRepository;
+        private readonly ISystemDicDataCacheManager _sysDicDataCacheManager;
+        private readonly IRepository<OrderVisitDetail> _orderVisitDetailRepository;
 
-		public BiOrderController(
+        public BiOrderController(
             IOrderRepository orderRepository,
             IRepository<Hotspot> hotspotTypeRepository,
-            IRepository<OrderDelay> orderDelayRepository
-			)
+            ISystemDicDataCacheManager sysDicDataCacheManager,
+            IRepository<OrderVisitDetail> orderVisitDetailRepository
+            )
         {
             _orderRepository = orderRepository;
             _hotspotTypeRepository = hotspotTypeRepository;
-            _orderDelayRepository = orderDelayRepository;
-
-		}
+            _sysDicDataCacheManager = sysDicDataCacheManager;
+            _orderVisitDetailRepository = orderVisitDetailRepository;
+        }
 
         /// <summary>
         /// 部门数据统计
@@ -148,42 +154,78 @@ namespace Hotline.Api.Controllers.Bi
             return new PagedDto<HotspotDataLsitVo>(total, items);
 		}
 
+
+        /// <summary>
+        /// 部门不满意统计
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpGet("visit-nosatisfied")]
+        [AllowAnonymous]
+        public async Task<object> QueryVisitNoSatisfied([FromQuery] QueryVisitNoSatiisfiedRequest dto)
+        {
+            var dissatisfiedReason = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.DissatisfiedReason);
+
+            List<dynamic>? list = new List<dynamic>();
+            foreach (var item in dissatisfiedReason)
+            {
+                var table = _orderVisitDetailRepository.Queryable()
+                .Includes(x => x.OrderVisit)
+                .Where(x => x.VisitTarget == Share.Enums.Order.EVisitTarget.Org)
+                .Where(x => x.OrgNoSatisfiedReason != null)
+                .Where(x => !string.IsNullOrEmpty(x.VisitOrgName))
+                .WhereIF(!string.IsNullOrEmpty(dto.OrgName), x => x.VisitOrgName.Contains(dto.OrgName))
+                .WhereIF(dto.StartTime.HasValue, x => x.OrderVisit.VisitTime >= dto.StartTime.Value)
+                .WhereIF(dto.EndTime.HasValue, x => x.OrderVisit.VisitTime <= dto.EndTime.Value)
+                .GroupBy(x => new { x.VisitOrgName, x.VisitOrgCode })
+                .Select(x => new BiVisitNoSatisfiedDto
+                {
+                    Count = SqlFunc.AggregateSum(SqlFunc.IIF(SqlFunc.JsonListObjectAny(x.OrgNoSatisfiedReason, "Key", item.DicDataValue), 1, 0)),
+                    Key = item.DicDataValue,
+                    OrgName = x.VisitOrgName
+                })
+                .OrderByDescending(x => x.Count)
+                .ToPivotList(x => x.Key, x => x.OrgName, x => x.Sum(x => x.Count));
+                list.AddRange(table);
+            }
+            return new { DicReason = dissatisfiedReason, Data = list };
+
+        }
         /// <summary>
         /// 工单延期统计
         /// </summary>
         /// <param name="dto"></param>
         /// <returns></returns>
-		[HttpGet("candelay_data_list")]
-		public async Task<PagedDto<OrderBiCanDelayListVo>> CanDelayDataList([FromQuery] ReportPagedRequest dto)
-		{
-			var query = _orderDelayRepository.Queryable(false, false)
-				.LeftJoin<SystemOrganize>((x, o) => x.ApplyOrgCode == o.Id)
-				.WhereIF(dto.StartTime.HasValue, (x, o) => x.CreationTime >= dto.StartTime)
-				.WhereIF(dto.EndTime.HasValue, (x, o) => x.CreationTime <= dto.EndTime)
-				.WhereIF(!string.IsNullOrEmpty(dto.Keyword), (x, o) => o.Name.Contains(dto.Keyword!))
-				.GroupBy((x, o) => new { x.ApplyOrgCode, o.Name })
-				.Select((x, o) => new OrderBiCanDelayListVo
-				{
-					OrgName = o.Name,
-					OrgId = x.ApplyOrgCode,
-					Agreed = SqlFunc.AggregateSum(SqlFunc.IIF(x.DelayState == EDelayState.Pass, 1, 0)),
-					NotAgreed = SqlFunc.AggregateSum(SqlFunc.IIF(x.DelayState == EDelayState.NoPass, 1, 0)),
-					Approval = SqlFunc.AggregateSum(SqlFunc.IIF(x.DelayState == EDelayState.Examining, 1, 0)),
-				}).MergeTable();
-			switch (dto.SortField)
-			{
-				case "agreed":
-					query = dto.SortRule == 0 ? query.OrderBy(x => x.Agreed) : query.OrderByDescending(x => x.Agreed);
-					break;
-				case "notAgreed":
-					query = dto.SortRule == 0 ? query.OrderBy(x => x.NotAgreed) : query.OrderByDescending(x => x.NotAgreed);
-					break;
-				case "approval":
-					query = dto.SortRule == 0 ? query.OrderBy(x => x.Approval) : query.OrderByDescending(x => x.Approval);
-					break;;
-			}
-			var (total, items) = await query.ToPagedListAsync(dto, HttpContext.RequestAborted);
-			return new PagedDto<OrderBiCanDelayListVo>(total, items);
+        [HttpGet("candelay_data_list")]
+        public async Task<PagedDto<OrderBiCanDelayListVo>> CanDelayDataList([FromQuery] ReportPagedRequest dto)
+        {
+	        var query = _orderDelayRepository.Queryable(false, false)
+		        .LeftJoin<SystemOrganize>((x, o) => x.ApplyOrgCode == o.Id)
+		        .WhereIF(dto.StartTime.HasValue, (x, o) => x.CreationTime >= dto.StartTime)
+		        .WhereIF(dto.EndTime.HasValue, (x, o) => x.CreationTime <= dto.EndTime)
+		        .WhereIF(!string.IsNullOrEmpty(dto.Keyword), (x, o) => o.Name.Contains(dto.Keyword!))
+		        .GroupBy((x, o) => new { x.ApplyOrgCode, o.Name })
+		        .Select((x, o) => new OrderBiCanDelayListVo
+		        {
+			        OrgName = o.Name,
+			        OrgId = x.ApplyOrgCode,
+			        Agreed = SqlFunc.AggregateSum(SqlFunc.IIF(x.DelayState == EDelayState.Pass, 1, 0)),
+			        NotAgreed = SqlFunc.AggregateSum(SqlFunc.IIF(x.DelayState == EDelayState.NoPass, 1, 0)),
+			        Approval = SqlFunc.AggregateSum(SqlFunc.IIF(x.DelayState == EDelayState.Examining, 1, 0)),
+		        }).MergeTable();
+	        switch (dto.SortField)
+	        {
+		        case "agreed":
+			        query = dto.SortRule == 0 ? query.OrderBy(x => x.Agreed) : query.OrderByDescending(x => x.Agreed);
+			        break;
+		        case "notAgreed":
+			        query = dto.SortRule == 0 ? query.OrderBy(x => x.NotAgreed) : query.OrderByDescending(x => x.NotAgreed);
+			        break;
+		        case "approval":
+			        query = dto.SortRule == 0 ? query.OrderBy(x => x.Approval) : query.OrderByDescending(x => x.Approval);
+			        break; ;
+	        }
+	        var (total, items) = await query.ToPagedListAsync(dto, HttpContext.RequestAborted);
+	        return new PagedDto<OrderBiCanDelayListVo>(total, items);
 		}
-	}
 }

+ 81 - 31
src/Hotline.Api/Controllers/IPPbxController.cs

@@ -2,6 +2,7 @@
 using Hotline.Application.CallCenter.Calls;
 using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
+using Hotline.CallCenter.Tels;
 using Hotline.Orders;
 using Hotline.Permissions;
 using Hotline.Repository.SqlSugar.Extensions;
@@ -18,13 +19,14 @@ using Tr.Sdk;
 using Tr.Sdk.Blacklist;
 using Tr.Sdk.Tels;
 using XF.Domain.Authentications;
+using XF.Domain.Exceptions;
 using XF.Domain.Filters;
 using XF.Domain.Repository;
 using XF.Utility.EnumExtensions;
 
 namespace Hotline.Api.Controllers
 {
-    public class IPPbxController:BaseController
+    public class IPPbxController : BaseController
     {
         private readonly ITrClient _trClient;
         private readonly IMapper _mapper;
@@ -39,8 +41,11 @@ namespace Hotline.Api.Controllers
         private readonly IRepository<OrderVisit> _orderVisitRepository;
         private readonly IUserCacheManager _userCacheManager;
         private readonly ICapPublisher _capPublisher;
+        private readonly ITelRestRepository _telRestRepository;
+        private readonly IRepository<User> _userRepository;
 
-        public IPPbxController(ITrClient trClient,IMapper mapper,IUserDomainService userDomainService,ISessionContext sessionContext,IRepository<TrCallRecord> trCallRecordRepository,ITrApplication trApplication,IRepository<TrCallEvaluate> trCallRecord,ISystemDicDataCacheManager systemDicDataCacheManager,ILogger<IPPbxController> logger,IOrderRepository orderRepository,IRepository<OrderVisit> orderVisitRepository, IUserCacheManager userCacheManager,ICapPublisher capPublisher)
+
+        public IPPbxController(ITrClient trClient, IMapper mapper, IUserDomainService userDomainService, ISessionContext sessionContext, IRepository<TrCallRecord> trCallRecordRepository, ITrApplication trApplication, IRepository<TrCallEvaluate> trCallRecord, ISystemDicDataCacheManager systemDicDataCacheManager, ILogger<IPPbxController> logger, IOrderRepository orderRepository, IRepository<OrderVisit> orderVisitRepository, IUserCacheManager userCacheManager, ICapPublisher capPublisher, ITelRestRepository telRestRepository, IRepository<User> userRepository)
         {
             _trClient = trClient;
             _mapper = mapper;
@@ -55,6 +60,8 @@ namespace Hotline.Api.Controllers
             _orderVisitRepository = orderVisitRepository;
             _userCacheManager = userCacheManager;
             _capPublisher = capPublisher;
+            _telRestRepository = telRestRepository;
+            _userRepository = userRepository;
         }
 
         #region 添添呼
@@ -76,7 +83,7 @@ namespace Hotline.Api.Controllers
         /// </summary>
         /// <returns></returns>
         [HttpGet("query-telstate")]
-        public async Task<IReadOnlyList<TrTelStateDto>> TrQueryTelState([FromQuery]string? state)
+        public async Task<IReadOnlyList<TrTelStateDto>> TrQueryTelState([FromQuery] string? state)
         {
             var tels = await _trClient.QueryTelStateAsync(new QueryTelStateRequest() { State = state }, HttpContext.RequestAborted);
             return _mapper.Map<IReadOnlyList<TrTelStateDto>>(tels.AgentList);
@@ -94,7 +101,7 @@ namespace Hotline.Api.Controllers
         /// <returns></returns>
         [Permission(EPermission.AddBlackList)]
         [HttpPost("add-blacklist")]
-        public async Task AddBlacklist([FromBody]TrAddBlacklistDto dto)
+        public async Task AddBlacklist([FromBody] TrAddBlacklistDto dto)
         {
             await _trClient.AddBlacklistAsync(new AddBlacklistRequest() { Phone = dto.Phone, SpecialFlag = dto.SpecialFlag }, HttpContext.RequestAborted);
         }
@@ -106,7 +113,7 @@ namespace Hotline.Api.Controllers
         /// <returns></returns>
         [Permission(EPermission.RemoveBlacklist)]
         [HttpPost("remove-blacklist")]
-        public async Task DelBlacklist([FromBody]TrDelBlacklistDto dto)
+        public async Task DelBlacklist([FromBody] TrDelBlacklistDto dto)
         {
             await _trClient.DelBlacklistAsync(new DelBlacklistRequest() { Phone = dto.Phone, SpecialFlag = dto.SpecialFlag }, HttpContext.RequestAborted);
         }
@@ -117,7 +124,7 @@ namespace Hotline.Api.Controllers
         /// <returns></returns>
         [Permission(EPermission.QueryPagedBlack)]
         [HttpGet("query-blacklist")]
-        public async Task<IReadOnlyList<TrQueryBlacklistResponseDto>> QueryBlacklist([FromQuery]TrQueryBlacklistDto dto)
+        public async Task<IReadOnlyList<TrQueryBlacklistResponseDto>> QueryBlacklist([FromQuery] TrQueryBlacklistDto dto)
         {
             var blacklist = await _trClient.QueryBlacklistAsync(new QueryBlacklistRequest() { Phone = dto.Phone, SpecialFlag = dto.SpecialFlag }, HttpContext.RequestAborted);
             return _mapper.Map<IReadOnlyList<TrQueryBlacklistResponseDto>>(blacklist);
@@ -167,7 +174,43 @@ namespace Hotline.Api.Controllers
             return rsp;
         }
 
+        /// <summary>
+        /// 小休
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost("rest")]
+        public async Task Rest([FromBody] TrRestDto dto)
+        {
+            var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
+            if (work == null)
+                throw UserFriendlyException.SameMessage("分机未签入,不能休息");
 
+            var isResting = await _telRestRepository.IsRestingAsync(work.TelNo, HttpContext.RequestAborted);
+            if (isResting)
+                throw UserFriendlyException.SameMessage("当前坐席正在休息");
+
+            var user = await _userRepository.GetAsync(work.UserId, HttpContext.RequestAborted);
+            var telRest = new TelRest(work.TelNo, work.TelNo, work.UserId, work.UserName, dto.Reason, false, user.StaffNo);
+            await _telRestRepository.AddAsync(telRest, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 取消小休
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost("unrest")]
+        public async Task UnRest()
+        {
+            var work = _userCacheManager.GetWorkByUser(_sessionContext.RequiredUserId);
+            if (work is null)
+                throw UserFriendlyException.SameMessage("分机未签入,不能操作");
+
+            var telRest = await _telRestRepository.GetAsync(x => x.TelNo == work.TelNo && !x.EndTime.HasValue, HttpContext.RequestAborted);
+            if (telRest is null)
+                throw UserFriendlyException.SameMessage("未查询到分机休息信息");
+            telRest.EndRest();
+            await _telRestRepository.UpdateAsync(telRest, HttpContext.RequestAborted);
+        }
 
         #endregion
 
@@ -179,7 +222,7 @@ namespace Hotline.Api.Controllers
         /// <returns></returns>
         [AllowAnonymous]
         [HttpPost("receivecallrecord")]
-        public async Task<OpenResponse> ReceiveCallRecord([FromBody]ReceiveCallRecordDto dto)
+        public async Task<OpenResponse> ReceiveCallRecord([FromBody] ReceiveCallRecordDto dto)
         {
             var model = _mapper.Map<TrCallRecord>(dto);
             model.Duration = 0;
@@ -196,10 +239,10 @@ namespace Hotline.Api.Controllers
                 model.Duration = Convert.ToInt32(tsend.Subtract(tsbegin).TotalSeconds);
             }
             //计算振铃时长
-            if (model.BeginRingTime !=null)
+            if (model.BeginRingTime != null)
             {
                 TimeSpan tsbegin = new TimeSpan(model.BeginRingTime.Value.Ticks);
-                if (model.EndRingTimg!=null)
+                if (model.EndRingTimg != null)
                 {
                     TimeSpan tsend = new TimeSpan(model.EndRingTimg.Value.Ticks);
                     model.RingTimes = Convert.ToInt32(tsend.Subtract(tsbegin).TotalSeconds);
@@ -211,7 +254,7 @@ namespace Hotline.Api.Controllers
                 }
             }
             //计算队列时长
-            if (model.BeginQueueTime!=null && model.EndQueueTime!=null)
+            if (model.BeginQueueTime != null && model.EndQueueTime != null)
             {
                 TimeSpan tsend = new TimeSpan(model.EndQueueTime.Value.Ticks);
                 TimeSpan tsbegin = new TimeSpan(model.BeginQueueTime.Value.Ticks);
@@ -229,7 +272,7 @@ namespace Hotline.Api.Controllers
 
             //获取关联 工单或是回访
             var order = await _orderRepository.GetAsync(x => x.CallId == model.CallAccept, HttpContext.RequestAborted);
-            if (order!=null)
+            if (order != null)
             {
                 model.CallOrderType = ECallOrderType.Order;
                 model.ExternalId = order.Id;
@@ -237,7 +280,7 @@ namespace Hotline.Api.Controllers
             else
             {
                 var orderVisit = await _orderVisitRepository.GetAsync(x => x.CallId == model.CallAccept, HttpContext.RequestAborted);
-                if (orderVisit!=null)
+                if (orderVisit != null)
                 {
                     model.CallOrderType = ECallOrderType.Visit;
                     model.ExternalId = orderVisit.Id;
@@ -246,7 +289,11 @@ namespace Hotline.Api.Controllers
 
             //获取用户
             var work = await _userCacheManager.GetWorkByTelNoLast(model.TelNo);
-            if (work!=null)
+            if (!string.IsNullOrEmpty(model.AgentTransferNumber))
+            {
+                work = await _userCacheManager.GetWorkByTelNoLast(model.AgentTransferNumber);
+            }
+            if (work != null)
             {
                 model.UserId = work.UserId;
                 model.UserName = work.UserName;
@@ -257,15 +304,18 @@ namespace Hotline.Api.Controllers
             if (model.CallDirection == ECallDirection.In)
             {
                 var publishCallRecordDto = new PublishCallRecrodDto() { };
-                if (order!=null)
+                if (order != null)
                 {
                     var orderDto = _mapper.Map<OrderDto>(order);
                     publishCallRecordDto.Order = orderDto;
                 }
                 var trCallDto = _mapper.Map<TrCallDto>(model);
                 publishCallRecordDto.TrCallRecordDto = trCallDto;
-                //推省上
-                await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineCallBye, publishCallRecordDto);
+                if (string.IsNullOrEmpty(model.AgentTransferNumber))
+                {
+                    //推省上
+                    await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineCallBye, publishCallRecordDto);
+                }
             }
 
             return OpenResponse.Ok("success");
@@ -278,7 +328,7 @@ namespace Hotline.Api.Controllers
         /// <returns></returns>
         [AllowAnonymous]
         [HttpPost("receivecallevaluate")]
-        public async Task<OpenResponse> ReceiveCallEvaluate([FromBody]ReceiveCallEvaluateDto dto)
+        public async Task<OpenResponse> ReceiveCallEvaluate([FromBody] ReceiveCallEvaluateDto dto)
         {
             var model = _mapper.Map<TrCallEvaluate>(dto);
             await _trCallEvaluate.AddAsync(model, HttpContext.RequestAborted);
@@ -294,14 +344,14 @@ namespace Hotline.Api.Controllers
         /// <param name="dto"></param>
         /// <returns></returns>
         [HttpGet("calls/call-list")]
-        public async Task<PagedDto<TrCallDto>> GetCallList([FromQuery]GetCallListDto dto)
+        public async Task<PagedDto<TrCallDto>> GetCallList([FromQuery] GetCallListDto dto)
         {
             var (total, items) = await _trCallRecordRepository.Queryable()
                 .WhereIF(!string.IsNullOrEmpty(dto.CPN), x => x.CPN.Contains(dto.CPN))
                 .WhereIF(!string.IsNullOrEmpty(dto.CDPN), x => x.CDPN.Contains(dto.CDPN))
                 .WhereIF(dto.CallDirection != null, x => x.CallDirection == dto.CallDirection)
                 .WhereIF(dto.OnState != null, x => x.OnState == dto.OnState)
-                .WhereIF(!string.IsNullOrEmpty(dto.Gateway),x=>x.Gateway.Contains(dto.Gateway))
+                .WhereIF(!string.IsNullOrEmpty(dto.Gateway), x => x.Gateway.Contains(dto.Gateway))
                 .WhereIF(dto.StartTime.HasValue, x => x.CreatedTime >= dto.StartTime)
                 .WhereIF(dto.EndTime.HasValue, x => x.CreatedTime <= dto.EndTime)
                 .OrderByDescending(x => x.CreatedTime)
@@ -334,13 +384,13 @@ namespace Hotline.Api.Controllers
         /// <param name="dto"></param>
         /// <returns></returns>
         [HttpGet("canlink-order")]
-        public async Task<PagedDto<OrderDto>> CanLinkCallRecordOrder([FromQuery]CanLinkCallRecordOrderDto dto)
+        public async Task<PagedDto<OrderDto>> CanLinkCallRecordOrder([FromQuery] CanLinkCallRecordOrderDto dto)
         {
-            var (total,items) = await _orderRepository.Queryable()
-                .Where(x=> string.IsNullOrEmpty(x.CallId) && x.Source == Share.Enums.Order.ESource.Hotline)
-                .WhereIF(!string.IsNullOrEmpty(dto.Keyword),x=>x.No.Contains(dto.Keyword))
-                .OrderByDescending(x=>x.CreationTime)
-                .ToPagedListAsync(dto.PageIndex,dto.PageSize,HttpContext.RequestAborted);
+            var (total, items) = await _orderRepository.Queryable()
+                .Where(x => string.IsNullOrEmpty(x.CallId) && x.Source == Share.Enums.Order.ESource.Hotline)
+                .WhereIF(!string.IsNullOrEmpty(dto.Keyword), x => x.No.Contains(dto.Keyword))
+                .OrderByDescending(x => x.CreationTime)
+                .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
 
             return new PagedDto<OrderDto>(total, _mapper.Map<IReadOnlyList<OrderDto>>(items));
         }
@@ -351,10 +401,10 @@ namespace Hotline.Api.Controllers
         /// <param name="dto"></param>
         /// <returns></returns>
         [HttpGet("canlink-ordervisit")]
-        public async Task<PagedDto<OrderVisitDto>> CanLinkCallRecordOrderVisit([FromQuery]CanLinkCallRecordOrderVisitDto dto)
+        public async Task<PagedDto<OrderVisitDto>> CanLinkCallRecordOrderVisit([FromQuery] CanLinkCallRecordOrderVisitDto dto)
         {
             var (total, items) = await _orderVisitRepository.Queryable()
-                .Includes(x=>x.Order)
+                .Includes(x => x.Order)
                 .Where(x => string.IsNullOrEmpty(x.CallId) && x.VisitType == Share.Enums.Order.EVisitType.ArtificialVisit)
                 .WhereIF(!string.IsNullOrEmpty(dto.Keyword), x => x.No.Contains(dto.Keyword))
                 .OrderByDescending(x => x.CreationTime)
@@ -368,7 +418,7 @@ namespace Hotline.Api.Controllers
         /// <param name="dto"></param>
         /// <returns></returns>
         [HttpPost("link-callrecord")]
-        public async Task LinkCallRecord([FromBody]LinkCallRecordDto dto)
+        public async Task LinkCallRecord([FromBody] LinkCallRecordDto dto)
         {
             var trRecord = await _trCallRecordRepository.GetAsync(x => x.CallAccept == dto.CallId, HttpContext.RequestAborted);
             if (dto.IsOrder)
@@ -376,19 +426,19 @@ namespace Hotline.Api.Controllers
                 //工单
                 var order = await _orderRepository.GetAsync(x => x.Id == dto.Id, HttpContext.RequestAborted);
                 order.CallId = dto.CallId;
-                await _orderRepository.UpdateAsync(order,HttpContext.RequestAborted);
+                await _orderRepository.UpdateAsync(order, HttpContext.RequestAborted);
                 trRecord.CallOrderType = ECallOrderType.Order;
                 trRecord.ExternalId = order.Id;
 
                 //推省上
-                await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineCallConnectWithOrder,new PublishCallRecrodDto() { Order = _mapper.Map<OrderDto>(order), TrCallRecordDto = _mapper.Map<TrCallDto>(trRecord) } );
+                await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineCallConnectWithOrder, new PublishCallRecrodDto() { Order = _mapper.Map<OrderDto>(order), TrCallRecordDto = _mapper.Map<TrCallDto>(trRecord) });
             }
             else
             {
                 //回访
                 var visit = await _orderVisitRepository.GetAsync(x => x.Id == dto.Id, HttpContext.RequestAborted);
                 visit.CallId = dto.CallId;
-                await _orderVisitRepository.UpdateAsync(visit,HttpContext.RequestAborted);
+                await _orderVisitRepository.UpdateAsync(visit, HttpContext.RequestAborted);
                 trRecord.CallOrderType = ECallOrderType.Visit;
                 trRecord.ExternalId = visit.Id;
             }

+ 93 - 3
src/Hotline.Api/Controllers/OrderController.cs

@@ -212,6 +212,10 @@ public class OrderController : BaseController
                 d => d.Acceptor.Name.Contains(dto.PubMan!) || d.Acceptor.StaffNo.Contains(dto.PubMan!))
             .WhereIF(dto.PubRange == EPublicState.Pub, d => d.OrderPublish.PublishState)
             .WhereIF(dto.PubRange == EPublicState.NoPub, d => !d.OrderPublish.PublishState)
+            .WhereIF(dto.FiledType!=null && dto.FiledType == FiledType.CenterFiled,d=>d.ProcessType == EProcessType.Zhiban)
+            .WhereIF(dto.FiledType!=null && dto.FiledType == FiledType.OrgFiled,d=>d.ProcessType == EProcessType.Jiaoban)
+            .WhereIF(dto.IsCountersign!=null && dto.IsCountersign == true,d=>d.CounterSignType != null)
+            .WhereIF(dto.IsCountersign!=null && dto.IsCountersign == false,d=>d.CounterSignType == null)
             .WhereIF(dto.AcceptTypes.Any(), d => dto.AcceptTypes.Contains(d.AcceptType))
             .WhereIF(dto.HotspotIds.Any(), d => dto.HotspotIds.Contains(d.HotspotId))
             .WhereIF(dto.CreationTimeStart.HasValue, d => d.CreationTime >= dto.CreationTimeStart)
@@ -224,6 +228,95 @@ public class OrderController : BaseController
         return new PagedDto<PublishDto>(total, _mapper.Map<IReadOnlyList<PublishDto>>(items));
     }
 
+    /// <summary>
+    /// 批量发布工单
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpPost("batch-publish")]
+    public async Task BatchPublishOrder([FromBody] BatchPublishOrderDto dto)
+    {
+        foreach (var item in dto.Ids)
+        {
+            var order = await _orderRepository.GetAsync(item, HttpContext.RequestAborted);
+            if (order != null)
+            {
+                try
+                {
+                    OrderPublish orderPublish = new OrderPublish();
+                    orderPublish.OrderId = order.Id;
+                    orderPublish.No = order.No;
+                    orderPublish.PublishState = false;//当前写死为false
+                    orderPublish.ArrangeTitle = order.Title;
+                    orderPublish.ArrangeContent = order.Content;
+                    orderPublish.ArrangeOpinion = order.ActualOpinion;
+                    orderPublish.ProPublishState = false;
+                    orderPublish.FeedBackPhone = order.Contact;
+                    orderPublish.CreatorName = _sessionContext.UserName;
+                    await _orderPublishRepository.AddAsync(orderPublish);
+                    order.Publish(orderPublish.PublishState);
+                    await _orderRepository.UpdateAsync(order);
+                    //推省上
+                    var publishPublishOrder = _mapper.Map<PublishPublishOrderDto>(orderPublish);
+                    publishPublishOrder.Order = _mapper.Map<OrderDto>(order);
+                    await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderPublishOrder, publishPublishOrder);
+
+                    var orderVisit = new OrderVisit();
+                    orderVisit.No = order.No;
+                    orderVisit.OrderId = order.Id;
+                    orderVisit.VisitState = EVisitState.WaitForVisit;
+                    orderVisit.PublishTime = DateTime.Now;
+                    orderVisit.IsCanHandle = true;
+
+                    if (order is { ProcessType: EProcessType.Zhiban, CounterSignType: null })
+                    {
+                        orderVisit.VisitState = EVisitState.Visited;
+                    }
+
+                    if (order.CounterSignType != ECounterSignType.Center)
+                    {
+                        orderVisit.IsCanAiVisit = true;
+                    }
+
+                    string visitId = await _orderVisitRepository.AddAsync(orderVisit);
+
+                    //新增回访信息
+                    var visitedDetail = new List<OrderVisitDetail>();
+
+                    var seatDetail = new OrderVisitDetail();
+                    seatDetail.VisitId = visitId;
+                    seatDetail.VisitTarget = EVisitTarget.Seat;
+
+
+                    var orgDetail = new OrderVisitDetail();
+                    orgDetail.VisitId = visitId;
+                    orgDetail.VisitOrgCode = order.ActualHandleOrgCode;
+                    orgDetail.VisitOrgName = order.ActualHandleOrgName;
+                    orgDetail.VisitTarget = EVisitTarget.Org;
+                    if (order is { ProcessType: EProcessType.Zhiban, CounterSignType: null })
+                    {
+                        var satisfy = new Kv() { Key = "4", Value = "满意" };
+                        orgDetail.OrgProcessingResults = satisfy;
+                        orgDetail.OrgHandledAttitude = satisfy;
+                    }
+                    visitedDetail.Add(orgDetail);
+
+                    if (order is { ProcessType: EProcessType.Zhiban, CounterSignType: null })
+                    {
+                        seatDetail.VoiceEvaluate = EVoiceEvaluate.Satisfied;
+                        seatDetail.SeatEvaluate = ESeatEvaluate.Satisfied;
+                        order.Visited("4", "满意");
+                        order.Status = EOrderStatus.Visited;
+                        await _orderRepository.UpdateAsync(order, HttpContext.RequestAborted);
+                    }
+                    visitedDetail.Add(seatDetail);
+                    await _orderVisitedDetailRepository.AddRangeAsync(visitedDetail, HttpContext.RequestAborted);
+                }
+                catch {}
+            }
+        }
+    }
+
     /// <summary>
     /// 发布工单
     /// </summary>
@@ -255,14 +348,11 @@ public class OrderController : BaseController
         order.Publish(orderPublish.PublishState);
         await _orderRepository.UpdateAsync(order);
 
-
         //推省上
         var publishPublishOrder = _mapper.Map<PublishPublishOrderDto>(orderPublish);
         publishPublishOrder.Order = _mapper.Map<OrderDto>(order);
         await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderPublishOrder, publishPublishOrder);
 
-
-
         var orderVisit = new OrderVisit();
         orderVisit.No = order.No;
         orderVisit.OrderId = order.Id;

+ 7 - 7
src/Hotline.Api/Controllers/PbxController.cs

@@ -267,12 +267,12 @@ namespace Hotline.Api.Controllers
 
             if (isCanApply)
             {
-                var isApply = await _telRepository.IsApplyingAsync(work.TelNo, HttpContext.RequestAborted);
+                var isApply = await _telRestRepository.IsApplyingAsync(work.TelNo, HttpContext.RequestAborted);
                 if (isApply)
                     throw new UserFriendlyException("分机休息申请正在审核阶段");
             }
 
-            var isResting = await _telRepository.IsRestingAsync(work.TelNo, HttpContext.RequestAborted);
+            var isResting = await _telRestRepository.IsRestingAsync(work.TelNo, HttpContext.RequestAborted);
             if (isResting)
                 throw new UserFriendlyException("当前坐席正在休息");
 
@@ -309,11 +309,11 @@ namespace Hotline.Api.Controllers
             if (tel.Data[0].Sigin == 0)
                 throw UserFriendlyException.SameMessage("分机未签入,不能休息");
 
-            var isApply = await _telRepository.IsApplyingAsync(tel.Data[0].TelNo, HttpContext.RequestAborted);
+            var isApply = await _telRestRepository.IsApplyingAsync(tel.Data[0].TelNo, HttpContext.RequestAborted);
             if (isApply)
                 throw UserFriendlyException.SameMessage("分机休息申请正在审核阶段");
 
-            var isResting = await _telRepository.IsRestingAsync(tel.Data[0].TelNo, HttpContext.RequestAborted);
+            var isResting = await _telRestRepository.IsRestingAsync(tel.Data[0].TelNo, HttpContext.RequestAborted);
             if (isResting)
                 throw new UserFriendlyException("当前坐席正在休息");
 
@@ -481,7 +481,7 @@ namespace Hotline.Api.Controllers
             if (telState != ETelStatus.Ready)
                 throw UserFriendlyException.SameMessage("被叫分机不在线或正在通话中");
 
-            bool isRest = await _telRepository.IsRestingAsync(dto.TelNo, HttpContext.RequestAborted);
+            bool isRest = await _telRestRepository.IsRestingAsync(dto.TelNo, HttpContext.RequestAborted);
             if (isRest)
                 throw new UserFriendlyException("被叫分机正在休息不能转接");
 
@@ -537,7 +537,7 @@ namespace Hotline.Api.Controllers
             if (totelState != ETelStatus.Ready)
                 throw UserFriendlyException.SameMessage("被叫分机不在线或正在通话中");
 
-            bool isRest = await _telRepository.IsRestingAsync(dto.TelNo, HttpContext.RequestAborted);
+            bool isRest = await _telRestRepository.IsRestingAsync(dto.TelNo, HttpContext.RequestAborted);
             if (isRest)
                 throw new UserFriendlyException("被叫分机正在休息不能转接");
 
@@ -625,7 +625,7 @@ namespace Hotline.Api.Controllers
             if (totelState != ETelStatus.Ready)
                 throw UserFriendlyException.SameMessage("被叫分机不在线或正在通话中");
 
-            bool isRest = await _telRepository.IsRestingAsync(dto.TelNo, HttpContext.RequestAborted);
+            bool isRest = await _telRestRepository.IsRestingAsync(dto.TelNo, HttpContext.RequestAborted);
             if (isRest)
                 throw new UserFriendlyException("被叫分机正在休息不能转接");
 

+ 31 - 3
src/Hotline.Api/Controllers/TestController.cs

@@ -1,4 +1,5 @@
 using System.Collections.Concurrent;
+using System.Data;
 using DotNetCore.CAP;
 using Hotline.CallCenter.BlackLists;
 using Hotline.CallCenter.Devices;
@@ -129,10 +130,37 @@ public class TestController : BaseController
         //await _queue.EnqueueConcurrentAsync("tq1", order, HttpContext.RequestAborted);
 
         //var i = await _queue.DequeueAsync<Order>("tq1", HttpContext.RequestAborted);
-        
-        var today = DateTime.Today.ToString();
+
+        //var today = DateTime.Today.ToString();
+
+        DataTable dt1 = new DataTable();
+        // 添加列并插入数据
+        dt1.Columns.Add("id", typeof(int));
+        dt1.Columns.Add("name", typeof(string));
+        dt1.Rows.Add(1, "张三");
+        dt1.Rows.Add(2, "李四");
+        DataTable dt2 = new DataTable();
+        // 添加列并插入数据
+        dt2.Columns.Add("id", typeof(int));
+        dt2.Columns.Add("age", typeof(int));
+        dt2.Rows.Add(1, 18);
+        dt2.Rows.Add(2, 20);
+        // 将 dt2 合并到 dt1 中
+
+        var a = from row1 in dt1.AsEnumerable()
+                join row2 in dt2.AsEnumerable() on row1["id"] equals row2["id"]
+                select new
+                {
+                    id = row1["id"],
+                    name = row1["name"],
+                    age = row2["age"]
+                };
+                
+
+        dt1.Merge(dt2);
+
         //await _capPublisher.PublishAsync(EventNames.HotlineOrderFlowStarted, "123", cancellationToken: HttpContext.RequestAborted);
-        return OpenResponse.Ok(today);
+        return OpenResponse.Ok("");
     }
 
     [HttpGet("time")]

+ 1 - 1
src/Hotline.Api/Controllers/UserController.cs

@@ -292,7 +292,7 @@ public class UserController : BaseController
         if (isOnDuty)
         {
             var work = _userCacheManager.GetWorkByUser(userId);
-            isResting = await _telRepository.IsRestingAsync(work.TelNo, HttpContext.RequestAborted);
+            isResting = await _telRestRepository.IsRestingAsync(work.TelNo, HttpContext.RequestAborted);
             telNo = work.TelNo;
         }
 

+ 7 - 2
src/Hotline.Application/CallCenter/Calls/TrApplication.cs

@@ -1,4 +1,5 @@
 using Hotline.Caching.Interfaces;
+using Hotline.CallCenter.Tels;
 using Hotline.Share.Dtos.TrCallCenter;
 using Hotline.Users;
 using Tr.Sdk;
@@ -6,6 +7,7 @@ using XF.Domain.Authentications;
 using XF.Domain.Constants;
 using XF.Domain.Dependency;
 using XF.Domain.Exceptions;
+using XF.Domain.Repository;
 
 namespace Hotline.Application.CallCenter.Calls
 {
@@ -16,13 +18,15 @@ namespace Hotline.Application.CallCenter.Calls
         private readonly IWorkRepository _workRepository;
         private readonly ISystemSettingCacheManager _systemSettingCacheManager;
         private readonly IUserCacheManager _userCacheManager;
-        public TrApplication(ITrClient trClient, ISessionContext sessionContext,IWorkRepository workRepository,ISystemSettingCacheManager systemSettingCacheManager,IUserCacheManager userCacheManager)
+        private readonly ITelRestRepository _telRestRepository;
+        public TrApplication(ITrClient trClient, ISessionContext sessionContext,IWorkRepository workRepository,ISystemSettingCacheManager systemSettingCacheManager,IUserCacheManager userCacheManager, ITelRestRepository telRestRepository)
         {
             _trClient = trClient;
             _sessionContext = sessionContext;
             _workRepository = workRepository;
             _systemSettingCacheManager = systemSettingCacheManager;
             _userCacheManager = userCacheManager;
+            _telRestRepository = telRestRepository;
         }
 
         /// <summary>
@@ -37,6 +41,7 @@ namespace Hotline.Application.CallCenter.Calls
             var work = _userCacheManager.GetWorkByUserNoExp(userId);
             if (work is not null)
             {
+                
                 if (work.TelNo == telNo)
                 {
                     return new TrOnDutyResponseDto() { TelNo = work.TelNo, TelPwd = work.TelPwd, Description = work.Description, QueueId = work.QueueId,StartTime = work.StartTime };
@@ -61,7 +66,7 @@ namespace Hotline.Application.CallCenter.Calls
                 bool IsTelNeedVerify = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.IsTelNeedVerify).SettingValue[0]);
                 if (IsTelNeedVerify)
                 {
-                    return new TrOnDutyResponseDto() { TelNo = telNo, TelPwd = "", Description = telModel[0].Description, QueueId = telModel[0].QueueId,StartTime = work.StartTime };
+                    return new TrOnDutyResponseDto() { TelNo = telNo, TelPwd = "", Description = telModel[0].Description, QueueId = telModel[0].QueueId,StartTime = work.StartTime};
                 }
                 else
                 {

+ 40 - 2
src/Hotline.Application/FlowEngine/WorkflowApplication.cs

@@ -141,7 +141,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, externalId, expiredTimeConfig?.TimeText,
             expiredTimeConfig?.Count, expiredTimeConfig?.TimeType, expiredTimeConfig?.ExpiredTime, cancellationToken);
 
-        var startStep = _workflowDomainService.CreateStartStep(workflow, startStepDefine, dto, 
+        var startStep = _workflowDomainService.CreateStartStep(workflow, startStepDefine, dto,
             new List<Kv> { new(_sessionContext.RequiredUserId, _sessionContext.UserName) });
 
         if (dto.Files.Any())
@@ -527,6 +527,39 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             return dto;
         }
 
+        //if (currentStep.IsInCountersign())
+        //{
+        //    if (currentStep.IsCountersignEndStep)
+        //    {
+        //        //当前待办节点为会签汇总节点时:检查是否为顶级会签汇总节点,t:按配置往下走,f:继续往上汇总,不需要重复往下指派
+        //        if (!currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
+        //        {
+        //            //查找当前节点对应会签开始节点的上级作为下一个cs汇总节点的汇总对象
+        //            var startCountersignStep =
+        //                workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
+        //            if (startCountersignStep is null)
+        //                throw new UserFriendlyException(
+        //                    $"未查询到会签开始节点,workflowId: {workflow.Id}, startStepId: {currentStep.CountersignStartStepId}",
+        //                    "未查询到会签开始节点,数据异常");
+        //            var countersignEndOption = GetCsEndStepByPrev(workflow.Steps, startCountersignStep);
+
+        //            dto.Steps = new List<NextStepOption> { countersignEndOption };
+        //            return dto;
+        //        }
+        //    }
+        //    else
+        //    {
+        //        //汇总节点
+        //        var countersignEndOption = GetCsEndStepByPrev(workflow.Steps, currentStep);
+        //        //按会签策略
+        //        var nextStepOption =
+        //            await GetDynamicStepAsync(currentStep.CountersignPolicy.Value, currentStep.StepType, currentStep.BusinessType,
+        //                cancellationToken);
+        //        dto.Steps = new List<NextStepOption> { nextStepOption, countersignEndOption };
+        //        return dto;
+        //    }
+        //}
+
         if (currentStep.IsInCountersign())
         {
             if (currentStep.IsCountersignEndStep)
@@ -543,7 +576,12 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                             "未查询到会签开始节点,数据异常");
                     var countersignEndOption = GetCsEndStepByPrev(workflow.Steps, startCountersignStep);
 
-                    dto.Steps = new List<NextStepOption> { countersignEndOption };
+                    //按会签策略
+                    var nextStepOption =
+                        await GetDynamicStepAsync(currentStep.CountersignPolicy.Value, currentStep.StepType, currentStep.BusinessType,
+                            cancellationToken);
+
+                    dto.Steps = new List<NextStepOption> { nextStepOption, countersignEndOption };
                     return dto;
                 }
             }

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

@@ -77,10 +77,11 @@ namespace Hotline.Application.Handlers.FlowEngine
                     var publishCallRecordDto = new PublishCallRecrodDto() { };
                     //查询通话记录
                     var callRecord = await _trCallRecordRepository.GetAsync(order.CallId, cancellationToken);
-                    if (callRecord!=null)
+                    if (callRecord != null)
                     {
                         publishCallRecordDto.TrCallRecordDto = _mapper.Map<TrCallDto>(callRecord);
                     }
+
                     publishCallRecordDto.Order = _mapper.Map<OrderDto>(order);
 
                     await _capPublisher.PublishAsync(EventNames.HotlineOrderFlowStarted, publishCallRecordDto,

+ 1 - 16
src/Hotline.Application/Mappers/CallMapperConfigs.cs

@@ -19,31 +19,16 @@ namespace Hotline.Application.Mappers
                 .Map(d => d.RecordingFileName, x => x.recording_file_name)
                 .Map(d => d.CallDirection, x => x.call_direction == "in" ? ECallDirection.In : ECallDirection.Out)
                 .Map(d => d.EndBy, x => x.hangup_side == "caller" ? EEndBy.From : (x.hangup_side == "callee" ? EEndBy.To : EEndBy.None))
+                .Map(d => d.AgentTransferNumber, x => x.agent_transfer_number)
                 .Map(d => d.CallAccept, x => x.call_accept)
                 .Map(d => d.CPN, x => x.caller_id_number)
                 .Map(d => d.CPNName, x => x.caller_id_name)
                 .Map(d=>d.CDPN,x=>x.destination_number)
                 .Map(d => d.RecordingFileUrl, x => x.recording_file_url)
-                //.Map(d => d.CreatedTime, x => x.created_time)
-                //.Map(d=> d.AnsweredTime,x=>x.answered_time)
-                //.IgnoreIf((s, d) => s.answered_time == null, d => d.AnsweredTime)
-                //.Map(d => d.OverTime, x => x.over_time)
                 .Map(d => d.Gateway, x => x.gateway)
                 .Map(d => d.OtherStr, x => x.other_str)
                 .Map(d => d.OtherAccept, x => x.other_accept)
                 .Map(d => d.Status, x => x.status)
-                //.Map(d=>d.BeginIvrTime,x=>x.beginIvrTime)
-                //.Map(d=>d.EndIvrTime,x=>x.endIvrTime)
-                //.Map(d=>d.BeginQueueTime,x=>x.beginQueueTime)
-                //.Map(d=>d.EndQueueTime,x=>x.endQueueTime)
-                //.Map(d=>d.BeginRingTime,x=>x.beginRingTime)
-                //.Map(d=>d.EndRingTimg,x=>x.endRingTime)
-                //.IgnoreIf((s, d) => s.beginIvrTime == null, d => d.BeginIvrTime)
-                //.IgnoreIf((s, d) => s.endIvrTime == null, d => d.EndIvrTime)
-                //.IgnoreIf((s, d) => s.beginQueueTime == null, d => d.BeginQueueTime)
-                //.IgnoreIf((s, d) => s.endQueueTime == null, d => d.EndQueueTime)
-                //.IgnoreIf((s, d) => s.beginRingTime == null, d => d.BeginRingTime)
-                //.IgnoreIf((s, d) => s.endRingTime == null, d => d.EndRingTimg)
                 .Map(d => d.OlaQueue, x => x.ola_queue)
                 .Map(d => d.BatchAccept, x => x.batch_accept)
                 .Map(d => d.IvrDtmf, x => x.ivr_dtmf)

+ 6 - 6
src/Hotline.Application/Orders/OrderApplication.cs

@@ -139,12 +139,12 @@ public class OrderApplication : IOrderApplication, IScopeDependency
 		return new PagedDto<OrderDto>(total, _mapper.Map<IReadOnlyList<OrderDto>>(items));
 	}
 
-	/// <summary>
-	/// 即将超期节点列表
-	/// </summary>
-	/// <param name="dto"></param>
-	/// <param name="cancellationToken"></param>
-	/// <returns></returns>
+	// /// <summary>
+	// /// 即将超期节点列表
+	// /// </summary>
+	// /// <param name="dto"></param>
+	// /// <param name="cancellationToken"></param>
+	// /// <returns></returns>
 	//public async Task<PagedDto<WorkflowOrderDto>> GetAboutToExpireNodeAsync(AboutToExpireListDto dto, CancellationToken cancellationToken)
 	//{
 	//	var setting = _systemSettingCacheManager.GetSetting(SettingConstants.OrderAboutToExpire);

+ 1 - 21
src/Hotline.Repository.SqlSugar/CallCenter/TelRepository.cs

@@ -11,26 +11,6 @@ namespace Hotline.Repository.SqlSugar.CallCenter
         {
         }
 
-        /// <summary>
-        /// 分机是否正在休息
-        /// </summary>
-        /// <param name="telNo"></param>
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        public Task<bool> IsRestingAsync(string telNo, CancellationToken cancellationToken)
-        {
-            return Db.Queryable<TelRest>().AnyAsync(d => d.TelNo == telNo && !d.EndTime.HasValue);
-        }
-
-        /// <summary>
-        /// 分机休息是否在审核阶段
-        /// </summary>
-        /// <param name="telNo"></param>
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        public Task<bool> IsApplyingAsync(string telNo, CancellationToken cancellationToken)
-        {
-            return Db.Queryable<TelRest>().AnyAsync(d => d.TelNo == telNo && !d.EndTime.HasValue && !d.StartTime.HasValue && d.ApplyStatus == Share.Enums.CallCenter.ETelRestApplyStatus.NoAudit);
-        }
+        
     }
 }

+ 22 - 0
src/Hotline.Repository.SqlSugar/CallCenter/TelRestRepository.cs

@@ -10,5 +10,27 @@ namespace Hotline.Repository.SqlSugar.CallCenter
         public TelRestRepository(ISugarUnitOfWork<HotlineDbContext> uow, IDataPermissionFilterBuilder dataPermissionFilterBuilder) : base(uow, dataPermissionFilterBuilder)
         {
         }
+
+        /// <summary>
+        /// 分机是否正在休息
+        /// </summary>
+        /// <param name="telNo"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        public Task<bool> IsRestingAsync(string telNo, CancellationToken cancellationToken)
+        {
+            return Db.Queryable<TelRest>().AnyAsync(d => d.TelNo == telNo && !d.EndTime.HasValue);
+        }
+
+        /// <summary>
+        /// 分机休息是否在审核阶段
+        /// </summary>
+        /// <param name="telNo"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        public Task<bool> IsApplyingAsync(string telNo, CancellationToken cancellationToken)
+        {
+            return Db.Queryable<TelRest>().AnyAsync(d => d.TelNo == telNo && !d.EndTime.HasValue && !d.StartTime.HasValue && d.ApplyStatus == Share.Enums.CallCenter.ETelRestApplyStatus.NoAudit);
+        }
     }
 }

+ 46 - 0
src/Hotline.Share/Dtos/CallCenter/BiSeatCallsDto.cs

@@ -99,4 +99,50 @@ public class BiSeatCallsDto
     /// 工作效率
     /// </summary>
     public double WorkRate => LoginDuration > 0 ? Math.Round(1 - RestDuration / LoginDuration, digits: 4) : 0;
+}
+
+
+public class BiSeatRestDto
+{
+    public string UserId { get; set; }
+    public string? StaffNo { get; set; }
+
+    public string UserName { get; set; }
+
+    public int RestCount { get; set; }
+
+    public double RestDuration { get; set; }
+}
+
+
+public class BiSeatSwitchDto
+{
+    public string? UserId { get; set; }
+
+    public string? UserName { get; set; }
+
+    public string? CPN { get; set; }
+
+    /// <summary>
+    /// 外线号码
+    /// </summary>
+    public string? CDPN { get; set; }
+
+    /// <summary>
+    /// 分机号
+    /// </summary>
+    public string? TelNo { get; set; }
+
+    /// <summary>
+    /// 转接外线时间
+    /// </summary>
+    public DateTime CreatedTime { get; set; }
+}
+
+
+public class BiVisitNoSatisfiedDto
+{
+    public int Count { get; set; }
+    public string Key { get; set; }
+    public string OrgName { get; set; }
 }

+ 6 - 0
src/Hotline.Share/Dtos/Order/PublishedDto.cs

@@ -205,6 +205,12 @@ public class PublishOrderDto
 }
 
 
+public class BatchPublishOrderDto
+{
+    public string[] Ids { get; set; }
+}
+
+
 public class PublishOrderModifyDto
 {
     /// <summary>

+ 18 - 1
src/Hotline.Share/Dtos/Order/QueryOrderDto.cs

@@ -92,6 +92,11 @@ namespace Hotline.Share.Dtos.Order
         /// </summary>
         public EPubState? PubState { get; set; }
 
+        /// <summary>
+        /// 归档方式
+        /// </summary>
+        public FiledType? FiledType { get; set; }
+
         /// <summary>
         /// 工单标题
         /// </summary>
@@ -129,9 +134,21 @@ namespace Hotline.Share.Dtos.Order
         public DateTime? FiledTimeStart { get; set; }
         public DateTime? FiledTimeEnd { get; set; }
 
+        /// <summary>
+        /// 是否会签
+        /// </summary>
+        public bool? IsCountersign { get; set; }
+
     }
 
-    
+    public enum FiledType 
+    {
+        [Description("中心归档")]
+        CenterFiled = 10,
+
+        [Description("部门归档")]
+        OrgFiled = 20,
+    }
 
     public record QueryOrderRedoRecordDto: PagedKeywordRequest
     {

+ 13 - 0
src/Hotline.Share/Dtos/TrCallCenter/TrTelDao.cs

@@ -182,6 +182,14 @@ namespace Hotline.Share.Dtos.TrCallCenter
 
     #endregion
 
+    #region 小休
+
+    public class TrRestDto
+    {
+        public string Reason { get; set; }
+    }
+
+    #endregion
 
     #region 通话记录(对外)
 
@@ -207,6 +215,11 @@ namespace Hotline.Share.Dtos.TrCallCenter
         /// </summary>
         public string hangup_side { get; set; }
 
+        /// <summary>
+        /// 转接分机号
+        /// </summary>
+        public string? agent_transfer_number { get; set; }
+
         /// <summary>
         /// 坐席侧通话流水,无坐席参与则为手机侧通话流水
         /// </summary>

+ 0 - 15
src/Hotline.Share/Enums/Order/EProcessType.cs

@@ -14,19 +14,4 @@ public enum EProcessType
     /// 交办
     /// </summary>
     Jiaoban = 20,
-
-    /// <summary>
-    /// 派单员重派
-    /// </summary>
-    PdyChongpai = 30,
-
-    /// <summary>
-    /// 结果审核重派
-    /// </summary>
-    ShChongpai = 40,
-
-    /// <summary>
-    /// 回访不满意重派	
-    /// </summary>
-    HfChongpai = 50
 }

+ 23 - 0
src/Hotline.Share/Requests/PagedKeywordRequest.cs

@@ -33,4 +33,27 @@ public record ReportPagedRequest : PagedKeywordRequest
 public record HotspotReportPagedRequest : ReportPagedRequest { 
 
 	public string Id { get; set; }
+}
+
+
+public record QuerySeatRestRequest:ReportPagedRequest
+{
+	public string? UserName { get; set; }
+
+	public string? StaffNo { get; set; }
+
+}
+
+public record QuerySeatSwitchRequest:ReportPagedRequest
+{
+	public string? UserName { get; set; }
+
+	public string? CDPN { get; set; }
+
+}
+
+public record QueryVisitNoSatiisfiedRequest:ReportPagedRequest
+{
+	public string? OrgName { get; set; }
+
 }

+ 6 - 0
src/Hotline/CallCenter/Calls/TrCallRecord.cs

@@ -23,6 +23,12 @@ namespace Hotline.CallCenter.Calls
         /// 挂断方
         /// </summary>
         public EEndBy? EndBy { get; set; }
+
+        /// <summary>
+        /// 转接分机号
+        /// </summary>
+        public string? AgentTransferNumber { get; set; }
+
         /// <summary>
         /// 坐席侧通话流水,无坐席参与则为手机侧通话流水
         /// </summary>

+ 1 - 16
src/Hotline/CallCenter/Tels/ITelRepository.cs

@@ -4,21 +4,6 @@ namespace Hotline.CallCenter.Tels
 {
     public interface ITelRepository : IRepository<Tel>
     {
-        /// <summary>
-        /// 分机是否正在休息
-        /// </summary>
-        /// <param name="telNo"></param>
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        Task<bool> IsRestingAsync(string telNo, CancellationToken cancellationToken);
-
-
-        /// <summary>
-        /// 分机休息是否在审核阶段
-        /// </summary>
-        /// <param name="telNo"></param>
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        Task<bool> IsApplyingAsync(string telNo, CancellationToken cancellationToken);
+       
     }
 }

+ 16 - 0
src/Hotline/CallCenter/Tels/ITelRestRepository.cs

@@ -4,5 +4,21 @@ namespace Hotline.CallCenter.Tels
 {
     public interface ITelRestRepository : IRepository<TelRest>
     {
+        /// <summary>
+        /// 分机是否正在休息
+        /// </summary>
+        /// <param name="telNo"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        Task<bool> IsRestingAsync(string telNo, CancellationToken cancellationToken);
+
+
+        /// <summary>
+        /// 分机休息是否在审核阶段
+        /// </summary>
+        /// <param name="telNo"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        Task<bool> IsApplyingAsync(string telNo, CancellationToken cancellationToken);
     }
 }

+ 1 - 1
src/Hotline/CallCenter/Tels/TelRest.cs

@@ -12,7 +12,7 @@ namespace Hotline.CallCenter.Tels
         /// <summary>
         /// 分机id
         /// </summary>
-        public string TelId { get; set; }
+        public string? TelId { get; set; }
 
         /// <summary>
         /// 分机号(冗余)

+ 6 - 6
src/Hotline/FlowEngine/Workflows/StepBasicEntity.cs

@@ -1,11 +1,6 @@
-using Consul;
-using Hotline.File;
-using Hotline.FlowEngine.Definitions;
-using Hotline.Share.Dtos;
+using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.File;
-using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Enums.FlowEngine;
-using Hotline.Share.Enums.Settings;
 using SqlSugar;
 using XF.Domain.Entities;
 using XF.Domain.Repository;
@@ -34,6 +29,11 @@ public abstract class StepBasicEntity : CreationEntity
     /// </summary>
     public string? PrevChosenStepCode { get; set; }
 
+    /// <summary>
+    /// 是否实际办理过该工单
+    /// </summary>
+    public bool IsActualHandled { get; set; }
+
     #region 接办
 
     /// <summary>

+ 26 - 4
src/Hotline/FlowEngine/Workflows/Workflow.cs

@@ -46,6 +46,11 @@ public partial class Workflow : CreationEntity
     /// 到期时间(期满时间)
     /// </summary>
     public DateTime ExpiredTime { get; set; }
+    
+    /// <summary>
+    /// 即将超期时间
+    /// </summary>
+    public DateTime? NearlyExpiredTime { get; set; }
 
     #region 办理时限
 
@@ -322,6 +327,16 @@ public partial class Workflow : CreationEntity
 
     #endregion
 
+    #region 会签实际办理对象
+
+    [SugarColumn(ColumnDataType = "json", IsJson = true)]
+    public List<string>? CsActualHandleOrgIds { get; set; }
+
+    [SugarColumn(ColumnDataType = "json", IsJson = true)]
+    public List<string>? CsActualHandleUserIds { get; set; }
+
+    #endregion
+
     #region 一级部门
 
     /// <summary>
@@ -377,10 +392,6 @@ public partial class Workflow : CreationEntity
 
 }
 
-public partial class WorkflowOrder : Workflow
-{
-    public Order Order { get; set; }
-}
 public partial class Workflow
 {
     [Navigate(NavigateType.OneToOne, nameof(DefinitionId))]
@@ -807,6 +818,17 @@ public partial class Workflow
     /// <returns></returns>
     public WorkflowStep? GetActualStep() => Steps.FirstOrDefault(d => d.Id == ActualHandleStepId);
 
+    public void AddCsActualHandler(string userId, string orgId)
+    {
+        CsActualHandleUserIds ??= new();
+        if (CsActualHandleUserIds.All(d => d != userId))
+            CsActualHandleUserIds.Add(userId);
+
+        CsActualHandleOrgIds ??= new();
+        if (CsActualHandleOrgIds.All(d => d != orgId))
+            CsActualHandleOrgIds.Add(orgId);
+    }
+
     #endregion
 }
 

+ 30 - 16
src/Hotline/FlowEngine/Workflows/WorkflowCountersign.cs

@@ -1,10 +1,8 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using Hotline.Share.Dtos;
 using Hotline.Share.Enums.FlowEngine;
 using SqlSugar;
+using XF.Domain.Entities;
+using XF.Domain.Exceptions;
 using XF.Domain.Repository;
 
 namespace Hotline.FlowEngine.Workflows
@@ -99,7 +97,6 @@ namespace Hotline.FlowEngine.Workflows
         /// </summary>
         public DateTime? EndTime { get; set; }
 
-
         #endregion
 
         /// <summary>
@@ -108,21 +105,22 @@ namespace Hotline.FlowEngine.Workflows
         public string? ParentId { get; set; }
 
         /// <summary>
-        /// 该会签参与成员数量
+        /// 会签类型(非工单办理为null)
         /// </summary>
-        public int Members { get; set; }
+        public ECounterSignType? CounterSignType { get; set; }
 
         /// <summary>
-        /// 会签类型
+        /// 该会签参与成员数量
         /// </summary>
-        public ECounterSignType CounterSignType { get; set; }
+        // public int Members { get; set; }
+        [SugarColumn(ColumnDataType = "json", IsJson = true)]
+        public List<CountersignMember>? Members { get; set; }
+        //todo 发起时记录哪些待办,办理时记录哪些已办
 
         /// <summary>
-        /// 发起会签节点设置的期满时间(冗余)
+        /// 流程指派类型(冗余,非工单办理为null)
         /// </summary>
-        public DateTime StartExpiredTime { get; set; }
-
-
+        public EFlowAssignType? FlowAssignType { get; set; }
 
         #region method
 
@@ -136,7 +134,7 @@ namespace Hotline.FlowEngine.Workflows
         /// </summary>
         public void End(
             string endStepId, string endStepCode, EBusinessType businessType,
-            string userId, string? userName, 
+            string userId, string? userName,
             string orgId, string? orgName,
             string? orgAreaCode, string? orgAreaName)
         {
@@ -152,6 +150,22 @@ namespace Hotline.FlowEngine.Workflows
             EndTime = DateTime.Now;
         }
 
+        public void MemberHandled(string userId, string orgId)
+        {
+            var member = FlowAssignType == EFlowAssignType.Org
+                ? Members.FirstOrDefault(d => d.Key == orgId)
+                : Members.FirstOrDefault(d => d.Key == userId);
+
+            if (member is null)
+                throw new UserFriendlyException($"未找到会签办理对象");
+            member.IsHandled = true;
+        }
+        
         #endregion
     }
-}
+
+    public class CountersignMember : Kv
+    {
+        public bool IsHandled { get; set; }
+    }
+}

+ 116 - 50
src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs

@@ -56,8 +56,10 @@ namespace Hotline.FlowEngine.Workflows
             _fileRepository = fileRepository;
         }
 
-        public async Task<Workflow> CreateWorkflowAsync(WorkflowModule wfModule, string title, string userId, string userCode,
-            string? externalId = null, string? timelimit = null, int? timelimitCount = null, ETimeType? timeType = null, DateTime? expiredTime = null,
+        public async Task<Workflow> CreateWorkflowAsync(WorkflowModule wfModule, string title, string userId,
+            string userCode,
+            string? externalId = null, string? timelimit = null, int? timelimitCount = null, ETimeType? timeType = null,
+            DateTime? expiredTime = null,
             CancellationToken cancellationToken = default)
         {
             var definition = wfModule.Definition;
@@ -93,8 +95,8 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 流程开始
         /// </summary>
-        public async Task StartAsync(Workflow workflow, WorkflowStep startStep, BasicWorkflowDto dto, StepDefine firstStepDefine,
-            FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken)
+        public async Task StartAsync(Workflow workflow, WorkflowStep startStep, BasicWorkflowDto dto,
+            StepDefine firstStepDefine, FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken)
         {
             //1. 创建first节点 (和trace)2.办理开始节点 
 
@@ -106,7 +108,8 @@ namespace Hotline.FlowEngine.Workflows
 
             //办理开始节点
             var counterSignType = GetCounterSignType(startStep.BusinessType);
-            await HandleStepAsync(startStep, workflow, dto, counterSignType, cancellationToken);
+            await HandleStepAsync(startStep, workflow, dto, flowAssignInfo.FlowAssignType, counterSignType,
+                cancellationToken);
             //赋值当前节点的下级办理节点
             if (dto.IsStartCountersign)
                 startStep.CreateCountersignSteps(firstSteps);
@@ -228,7 +231,8 @@ namespace Hotline.FlowEngine.Workflows
             //工单完成以后查看的场景
             if (workflow.Status != EWorkflowStatus.Runnable) return;
 
-            var currentStep = GetUnHandleStep(workflow.Steps, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId);
+            var currentStep = GetUnHandleStep(workflow.Steps, _sessionContext.RequiredOrgId,
+                _sessionContext.RequiredUserId);
             if (currentStep.Status is not EWorkflowStepStatus.WaitForAccept) return;
 
             if (currentStep.Handlers.All(d => d.Key != orgId && d.Key != userId)) return;
@@ -276,13 +280,16 @@ namespace Hotline.FlowEngine.Workflows
 
             if (dto.Files.Any())
                 currentStep.FileJson =
-                    await _fileRepository.AddFileAsync(dto.Files, workflow.ExternalId, currentStep.Id, cancellationToken);
+                    await _fileRepository.AddFileAsync(dto.Files, workflow.ExternalId, currentStep.Id,
+                        cancellationToken);
 
             var counterSignType = GetCounterSignType(currentStep.BusinessType);
 
-            await HandleStepAsync(currentStep, workflow, dto, counterSignType, cancellationToken);
+            await HandleStepAsync(currentStep, workflow, dto, flowAssignInfo.FlowAssignType, counterSignType,
+                cancellationToken);
+
+            currentStep.IsActualHandled = CheckIsActualHandle(workflow, currentStep, nextStepDefine, dto);
 
-            //update realhandle info
             _mapper.Map(dto, workflow);
 
             var updateSteps = new List<WorkflowStep> { currentStep };
@@ -319,20 +326,36 @@ namespace Hotline.FlowEngine.Workflows
                 }
             }
 
-            //操作为回到会签汇总时,更新开始会签节点的会签办理状态
-            if (currentStep.IsInCountersign() && dto.BackToCountersignEnd)
+            if (currentStep.IsInCountersign())
             {
-                if (currentStep.IsCountersignEndStep)
+                //操作为回到会签汇总时,更新开始会签节点的会签办理状态
+                if (dto.BackToCountersignEnd)
                 {
-                    //汇总节点(非顶级)
-                    var csStartStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
-                    if (csStartStep is null)
-                        throw new UserFriendlyException("未查询到会签开始节点");
-                    PrevStepCsHandled(workflow, csStartStep, ref updateSteps);
+                    if (currentStep.IsCountersignEndStep)
+                    {
+                        //汇总节点(非顶级)
+                        var csStartStep =
+                            workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
+                        if (csStartStep is null)
+                            throw new UserFriendlyException("未查询到会签开始节点");
+                        PrevStepCsHandled(workflow, csStartStep, ref updateSteps);
+                    }
+                    else
+                    {
+                        PrevStepCsHandled(workflow, currentStep, ref updateSteps);
+                    }
                 }
                 else
                 {
-                    PrevStepCsHandled(workflow, currentStep, ref updateSteps);
+                    //会签中正常办理节点,更新会签members办理状态
+                    var countersign = workflow.Countersigns.FirstOrDefault(d => d.Id == currentStep.CountersignId);
+                    if (countersign is null)
+                        throw new UserFriendlyException(
+                            $"会签数据异常, workflowId: {currentStep.WorkflowId}, countersignId: {currentStep.CountersignId}",
+                            "会签数据异常");
+                    countersign.MemberHandled(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId);
+                    //update cs
+                    await _workflowCountersignRepository.UpdateAsync(countersign, cancellationToken);
                 }
             }
 
@@ -380,7 +403,8 @@ namespace Hotline.FlowEngine.Workflows
 
             //赋值当前节点的下级办理节点
             if (dto.IsStartCountersign
-                || (currentStep.IsInCountersign() && !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId)))
+                || (currentStep.IsInCountersign() &&
+                    !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId)))
             {
                 currentStep.CreateCountersignSteps(nextSteps);
                 await _workflowStepRepository.UpdateAsync(currentStep, cancellationToken);
@@ -406,6 +430,10 @@ namespace Hotline.FlowEngine.Workflows
             //更新指派信息
             workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlerIds());
 
+            //更新会签实际办理对象信息
+            if (currentStep.IsActualHandled)
+                workflow.AddCsActualHandler(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId);
+
             await _workflowRepository.UpdateAsync(workflow, cancellationToken);
 
             #endregion
@@ -432,7 +460,8 @@ namespace Hotline.FlowEngine.Workflows
             if (workflow.IsInCountersign)
                 throw UserFriendlyException.SameMessage("会签流程不支持退回");
 
-            var currentStep = GetUnHandleStep(workflow.Steps, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId);
+            var currentStep = GetUnHandleStep(workflow.Steps, _sessionContext.RequiredOrgId,
+                _sessionContext.RequiredUserId);
 
             if (currentStep.StepType is EStepType.Start)
                 throw UserFriendlyException.SameMessage("当前流程已退回到开始节点");
@@ -446,13 +475,15 @@ namespace Hotline.FlowEngine.Workflows
             var trace = await PreviousTraceAsync(workflow.Id, dto, currentStep, cancellationToken);
             //保存附件
             if (dto.Files.Any())
-                trace.FileJson = await _fileRepository.AddFileAsync(dto.Files, workflow.ExternalId, trace.Id, cancellationToken);
+                trace.FileJson =
+                    await _fileRepository.AddFileAsync(dto.Files, workflow.ExternalId, trace.Id, cancellationToken);
 
             //复制上一个节点为待接办
             var newPrevStep = await CreatePrevStepAsync(workflow, prevStep, cancellationToken);
 
             //remove workflow.steps
-            await _workflowStepRepository.RemoveRangeAsync(new List<WorkflowStep> { currentStep, prevStep }, cancellationToken);
+            await _workflowStepRepository.RemoveRangeAsync(new List<WorkflowStep> { currentStep, prevStep },
+                cancellationToken);
 
             if (workflow.Status is EWorkflowStatus.Completed)
                 workflow.SetStatusRunnable();
@@ -576,7 +607,8 @@ namespace Hotline.FlowEngine.Workflows
             workflow.ResetHandlers(flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
             await _workflowRepository.UpdateAsync(workflow, cancellationToken);
 
-            await _mediator.Publish(new JumpNotify(workflow, targetStep, dto, flowAssignInfo, isCenterToOrg, isOrgToCenter),
+            await _mediator.Publish(
+                new JumpNotify(workflow, targetStep, dto, flowAssignInfo, isCenterToOrg, isOrgToCenter),
                 cancellationToken);
         }
 
@@ -614,11 +646,13 @@ namespace Hotline.FlowEngine.Workflows
         /// <returns></returns>
         public async Task RejectAsync(Workflow workflow, BasicWorkflowDto dto, CancellationToken cancellationToken)
         {
-            var currentStep = GetUnHandleStep(workflow.Steps, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId);
-            await HandleStepAsync(currentStep, workflow, dto, null, cancellationToken);
+            var currentStep = GetUnHandleStep(workflow.Steps, _sessionContext.RequiredOrgId,
+                _sessionContext.RequiredUserId);
+            await HandleStepAsync(currentStep, workflow, dto, null, null, cancellationToken);
 
             var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine();
-            var endTrace = await EndAsync(workflow, dto, endStepDefine, currentStep, EReviewResult.Failed, cancellationToken);
+            var endTrace = await EndAsync(workflow, dto, endStepDefine, currentStep, EReviewResult.Failed,
+                cancellationToken);
 
             //await _mediator.Publish(new RejectNotify(workflow, dto), cancellationToken);
         }
@@ -659,7 +693,8 @@ namespace Hotline.FlowEngine.Workflows
             if (string.IsNullOrEmpty(stepCode)) throw new ArgumentNullException(nameof(stepCode));
             var stepDefine = workflowDefinition.FindStepDefine(stepCode);
             if (stepDefine == null)
-                throw new UserFriendlyException($"未找到流程中对应的节点,DefineCode: {workflowDefinition.Code}, stepCode: {stepCode}",
+                throw new UserFriendlyException(
+                    $"未找到流程中对应的节点,DefineCode: {workflowDefinition.Code}, stepCode: {stepCode}",
                     "未查询到对应节点");
             return stepDefine;
         }
@@ -674,7 +709,8 @@ namespace Hotline.FlowEngine.Workflows
         /// 查询待回访部门
         /// </summary>
         /// <returns></returns>
-        public async Task<(Kv, IReadOnlyList<Kv>)> GetUnvisitOrgsAsync(string workflowId, CancellationToken cancellationToken)
+        public async Task<(Kv, IReadOnlyList<Kv>)> GetUnvisitOrgsAsync(string workflowId,
+            CancellationToken cancellationToken)
         {
             var workflow = await GetWorkflowAsync(workflowId, withSteps: true, cancellationToken: cancellationToken);
             if (workflow.CounterSignType is not ECounterSignType.Center)
@@ -727,7 +763,8 @@ namespace Hotline.FlowEngine.Workflows
             var workflow = await GetWorkflowAsync(dto.WorkflowId, withDefine: true, withSteps: true,
                 cancellationToken: cancellationToken);
 
-            var currentStep = GetUnHandleStep(workflow.Steps, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId);
+            var currentStep = GetUnHandleStep(workflow.Steps, _sessionContext.RequiredOrgId,
+                _sessionContext.RequiredUserId);
             //var (currentStepBox, currentStep) = GetUnCompleteStep(workflow.Steps, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId);
 
             var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine();
@@ -742,7 +779,8 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 更新期满时间
         /// </summary>
-        public async Task UpdateExpiredTimeAsync(Workflow workflow, DateTime expiredTime, string timelimit, int? timelimiteCount,
+        public async Task UpdateExpiredTimeAsync(Workflow workflow, DateTime expiredTime, string timelimit,
+            int? timelimiteCount,
             ETimeType? timelimitUnit, CancellationToken cancellationToken)
         {
             workflow.ExpiredTime = expiredTime;
@@ -755,7 +793,8 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 新增流程流转记录
         /// </summary>
-        public async Task AddTracesAsync(string workflowId, List<WorkflowTrace> traces, CancellationToken cancellationToken)
+        public async Task AddTracesAsync(string workflowId, List<WorkflowTrace> traces,
+            CancellationToken cancellationToken)
         {
             var workflow = await GetWorkflowAsync(workflowId, cancellationToken: cancellationToken);
             if (workflow is null)
@@ -767,7 +806,8 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 创建开始节点
         /// </summary>
-        public WorkflowStep CreateStartStep(Workflow workflow, StepDefine startStepDefine, BasicWorkflowDto dto, List<Kv> handles)
+        public WorkflowStep CreateStartStep(Workflow workflow, StepDefine startStepDefine, BasicWorkflowDto dto,
+            List<Kv> handles)
         {
             //startstep
             var nextSteps = _mapper.Map<List<StepSimple>>(startStepDefine.NextSteps);
@@ -777,6 +817,7 @@ namespace Hotline.FlowEngine.Workflows
                 if (selectedStep is not null)
                     selectedStep.Selected = true;
             }
+
             var startStep = _mapper.Map<WorkflowStep>(startStepDefine);
             startStep.WorkflowId = workflow.Id;
             startStep.Handlers = handles;
@@ -793,7 +834,8 @@ namespace Hotline.FlowEngine.Workflows
 
         #region private method
 
-        public async Task<WorkflowStep> CreateStartStepAsync(Workflow workflow, StepDefine startStepDefine, BasicWorkflowDto dto,
+        public async Task<WorkflowStep> CreateStartStepAsync(Workflow workflow, StepDefine startStepDefine,
+            BasicWorkflowDto dto,
             List<Kv> handles, EWorkflowTraceStatus traceStatus, CancellationToken cancellationToken)
         {
             var startStep = CreateStartStep(workflow, startStepDefine, dto, handles);
@@ -908,7 +950,8 @@ namespace Hotline.FlowEngine.Workflows
         {
             var countersignId = prevStep.IsStartCountersign ? prevStep.StartCountersignId : prevStep.CountersignId;
 
-            return CreateStepsAsync(workflow, stepDefine, prevStep, dto, flowAssignType, dto.NextHandlers, countersignId,
+            return CreateStepsAsync(workflow, stepDefine, prevStep, dto, flowAssignType, dto.NextHandlers,
+                countersignId,
                 EWorkflowStepStatus.WaitForAccept, prevStep.GetNextStepCountersignPosition(),
                 false, EWorkflowTraceStatus.Normal, cancellationToken);
         }
@@ -957,7 +1000,8 @@ namespace Hotline.FlowEngine.Workflows
             //csEndStep.TimeLimit = GetTimeLimit("");
             csEndStep.StepExpiredTime = expiredTime;
             csEndStep.Handlers = countersignStartStep.Handlers
-                .Where(d => d.Key == countersignStartStep.HandlerId || d.Key == countersignStartStep.HandlerOrgId).ToList();
+                .Where(d => d.Key == countersignStartStep.HandlerId || d.Key == countersignStartStep.HandlerOrgId)
+                .ToList();
 
             csEndStep.Reset();
             csEndStep.ResetParameters();
@@ -1008,7 +1052,7 @@ namespace Hotline.FlowEngine.Workflows
         /// 办理节点
         /// </summary>
         private async Task HandleStepAsync(WorkflowStep step, Workflow workflow, BasicWorkflowDto dto,
-            ECounterSignType? counterSignType, CancellationToken cancellationToken)
+            EFlowAssignType? flowAssignType, ECounterSignType? counterSignType, CancellationToken cancellationToken)
         {
             if (step.Status is EWorkflowStepStatus.Handled)
                 throw UserFriendlyException.SameMessage("当前节点状态已办理");
@@ -1021,7 +1065,7 @@ namespace Hotline.FlowEngine.Workflows
 
             //创建会签数据
             if (dto.IsStartCountersign)
-                await StartCountersignAsync(workflow, step, dto, counterSignType.Value, cancellationToken);
+                await StartCountersignAsync(workflow, step, dto, flowAssignType, counterSignType, cancellationToken);
 
             //办理参数
             _mapper.Map(dto, step);
@@ -1030,6 +1074,23 @@ namespace Hotline.FlowEngine.Workflows
             HandleStep(step, dto.NextStepCode);
         }
 
+        private bool CheckIsActualHandle(Workflow workflow, WorkflowStep step, StepDefine nextStepDefine,
+            BasicWorkflowDto dto)
+        {
+            //1. workflow是否为办理类型 2. 非会签:当前是否为普通节点and下一节点是否为汇总 or endStep 3. 会签:当前操作为汇总还是继续往下办理?thk: 汇总以后但未回到top又往下办理的场景,前面实际办理部门也算作办理部门
+            if (workflow.FlowType is not EFlowType.Handle) return false;
+
+            if (workflow.IsInCountersign)
+            {
+                return dto.BackToCountersignEnd;
+            }
+            else
+            {
+                return step.StepType is EStepType.Normal &&
+                       nextStepDefine.StepType is EStepType.Summary or EStepType.End;
+            }
+        }
+
         /// <summary>
         /// 办理节点(赋值节点的办理对象信息)
         /// </summary>
@@ -1046,10 +1107,10 @@ namespace Hotline.FlowEngine.Workflows
         /// 开始会签(创建会签数据,更新currentStep会签数据)
         /// </summary>
         private async Task StartCountersignAsync(Workflow workflow, WorkflowStep startStep, BasicWorkflowDto dto,
-            ECounterSignType counterSignType, CancellationToken cancellationToken)
+            EFlowAssignType? flowAssignType, ECounterSignType? counterSignType, CancellationToken cancellationToken)
         {
             var countersign = await CreateCountersignAsync(
-                workflow.Id, startStep, dto.NextHandlers.Count,
+                workflow.Id, startStep, dto.NextHandlers, flowAssignType,
                 counterSignType, startStep.StepExpiredTime.Value,
                 startStep.CountersignId, cancellationToken);
             startStep.StartCountersign(countersign.Id);
@@ -1139,9 +1200,8 @@ namespace Hotline.FlowEngine.Workflows
         }
 
         private async Task<WorkflowCountersign> CreateCountersignAsync(
-            string workflowId, WorkflowStep startStep, int memberCount,
-            ECounterSignType counterSignType, DateTime stepExpiredTime,
-            string? parentId = null,
+            string workflowId, WorkflowStep startStep, List<Kv> handlers, EFlowAssignType? flowAssignType,
+            ECounterSignType? counterSignType, DateTime stepExpiredTime, string? parentId = null,
             CancellationToken cancellationToken = default)
         {
             var countersign = new WorkflowCountersign
@@ -1159,9 +1219,9 @@ namespace Hotline.FlowEngine.Workflows
                 StarterOrgAreaName = _sessionContext.OrgAreaName,
 
                 ParentId = parentId,
-                Members = memberCount,
+                Members = _mapper.Map<List<CountersignMember>>(handlers),
+                FlowAssignType = flowAssignType,
                 CounterSignType = counterSignType,
-                StartExpiredTime = stepExpiredTime,
             };
             await _workflowCountersignRepository.AddAsync(countersign, cancellationToken);
             return countersign;
@@ -1209,7 +1269,8 @@ namespace Hotline.FlowEngine.Workflows
             }
         }
 
-        private async Task<WorkflowTrace> PreviousTraceAsync(string workflowId, PreviousWorkflowDto dto, WorkflowStep step,
+        private async Task<WorkflowTrace> PreviousTraceAsync(string workflowId, PreviousWorkflowDto dto,
+            WorkflowStep step,
             CancellationToken cancellationToken)
         {
             var trace = await GetWorkflowTraceAsync(workflowId, step.Id, cancellationToken);
@@ -1247,7 +1308,8 @@ namespace Hotline.FlowEngine.Workflows
         }
 
         private async Task CreateTraceAsync(Workflow workflow, WorkflowStep step,
-            EWorkflowTraceStatus traceStatus = EWorkflowTraceStatus.Normal, CancellationToken cancellationToken = default)
+            EWorkflowTraceStatus traceStatus = EWorkflowTraceStatus.Normal,
+            CancellationToken cancellationToken = default)
         {
             var trace = _mapper.Map<WorkflowTrace>(step);
             trace.Status = traceStatus;
@@ -1317,9 +1379,12 @@ namespace Hotline.FlowEngine.Workflows
                 workflow.SetStatusRunnable();
 
             var targetStepNew = targetIsStartStep
-                ? await CreateStartStepAsync(workflow, targetStepDefine, dto, dto.NextHandlers, traceStatus, cancellationToken)
-                : (await CreateStepsAsync(workflow, targetStepDefine, targetPrevStep, dto, flowAssignInfo.FlowAssignType, dto.NextHandlers,
-                    null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, true, traceStatus, cancellationToken)).First();
+                ? await CreateStartStepAsync(workflow, targetStepDefine, dto, dto.NextHandlers, traceStatus,
+                    cancellationToken)
+                : (await CreateStepsAsync(workflow, targetStepDefine, targetPrevStep, dto,
+                    flowAssignInfo.FlowAssignType, dto.NextHandlers,
+                    null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, true, traceStatus,
+                    cancellationToken)).First();
 
 
             //更新当前办理节点信息
@@ -1415,7 +1480,8 @@ namespace Hotline.FlowEngine.Workflows
             }
 
             return await CreateStepsAsync(workflow, stepDefine, prevStep, dto, flowAssignInfo.FlowAssignType, handlers,
-                null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, true, traceStatus, cancellationToken);
+                null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, true, traceStatus,
+                cancellationToken);
         }
 
         private async Task<List<WorkflowStep>> CreateStepsAsync(

+ 11 - 1
src/Hotline/Orders/Order.cs

@@ -284,7 +284,7 @@ namespace Hotline.Orders
         public DateTime? StartTime { get; set; }
 
         /// <summary>
-        /// 超期时间
+        /// 超期时间(期满时间)
         /// </summary>
         public DateTime? ExpiredTime { get; set; }
 
@@ -329,6 +329,16 @@ namespace Hotline.Orders
 
         public ETimeType? TimeLimitUnit { get; set; }
 
+        #region 会签实际办理对象
+
+        [SugarColumn(ColumnDataType = "json", IsJson = true)]
+        public List<string>? CsActualHandleOrgIds { get; set; }
+
+        [SugarColumn(ColumnDataType = "json", IsJson = true)]
+        public List<string>? CsActualHandleUserIds { get; set; }
+
+        #endregion
+
         #region 实际办理信息(节点,部门,意见)
 
         /// <summary>

+ 12 - 11
src/Hotline/README.md

@@ -1,19 +1,20 @@
 #### 代码编写规范
 
 ##### 1. 参照微软推荐编码规范及约定
-https://docs.microsoft.com/zh-cn/dotnet/csharp/fundamentals/coding-style/identifier-names
-https://docs.microsoft.com/zh-cn/dotnet/csharp/fundamentals/coding-style/coding-conventions
-https://docs.microsoft.com/zh-cn/dotnet/standard/design-guidelines/naming-guidelines
+* https://docs.microsoft.com/zh-cn/dotnet/csharp/fundamentals/coding-style/identifier-names
+* https://docs.microsoft.com/zh-cn/dotnet/csharp/fundamentals/coding-style/coding-conventions
+* https://docs.microsoft.com/zh-cn/dotnet/standard/design-guidelines/naming-guidelines
 
 ##### 2. 该项目扩展规范
 * 枚举:命名以大写E为前缀,在文件列表中可以一目了然
-* 方法命名方式: 
-新增:Addxxx 
-删除:Removexxx 
-修改:Updatexxx 
+* 方法命名方式:
+
+新增:Addxxx  
+删除:Removexxx  
+修改:Updatexxx  
 查询单条数据:Getxxx 
-查询多条数据:Queryxxx 
-查询所有数据:QueryAllxxx 
-异步方法以Async为后缀:GetxxxAsync 
+查询多条数据:Queryxxx  
+查询所有数据:QueryAllxxx  
+异步方法以Async为后缀:GetxxxAsync  
 * WebApi和Web项目的Action都尽量采用异步方法,但命名不加Async后缀(因为此处的调用方不以Action命名来调用,而是以路由规则来访问)
-* 字段以'_'加小写字母为前缀:_orderDomainService
+* 字段以'_'加小写字母为前缀,如:_orderDomainService

+ 1 - 9
src/Hotline/Users/IUserDomainService.cs

@@ -24,15 +24,7 @@ namespace Hotline.Users
         Task<WorkDto?> OffDutyAsync(string userId, CancellationToken cancellationToken);
 
         #region 添添呼
-        /// <summary>
-        /// 上班
-        /// </summary>
-        /// <param name="userId"></param>
-        /// <param name="TelNo"></param>
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        Task<TrOnDutyResponseDto> TrOnDutyAsync(string userId,  string TelNo, CancellationToken cancellationToken);
-
+        
         /// <summary>
         /// 下班
         /// </summary>

+ 0 - 65
src/Hotline/Users/UserDomainService.cs

@@ -119,71 +119,6 @@ namespace Hotline.Users
 
         #region 添添呼
 
-        /// <summary>
-        /// 上班
-        /// </summary>
-        /// <param name="userId"></param>
-        /// <param name="telNo"></param>
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        public async Task<TrOnDutyResponseDto> TrOnDutyAsync(string userId, string telNo, CancellationToken cancellationToken)
-        {
-            var work = _userCacheManager.GetWorkByUserNoExp(userId);
-            if(work is not null)
-            {
-                if(work.TelNo == telNo)
-                {
-                    return new TrOnDutyResponseDto() { TelNo = work.TelNo, TelPwd = work.TelPwd, Description = work.Description };
-                }
-                else
-                {
-                    throw UserFriendlyException.SameMessage("当前用户已签入其他分机");
-                }
-            }
-
-            var telWork = _userCacheManager.GetWorkByTelNoExp(telNo);
-            if (telWork is not null)
-            {
-                throw UserFriendlyException.SameMessage("当前分机已被占用");
-            }
-
-
-            bool IsTelNeedVerify = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.IsTelNeedVerify).SettingValue[0]);
-
-            //var telModel = await _trClient.QueryTelsAsync(new Tr.Sdk.Tels.QueryTelRequest() { TelNo = telNo }, cancellationToken);
-            //if (telModel != null && telModel.Count > 0)
-            //{
-            //    work = new Work(userId, _sessionContext.UserName, telModel[0].Id, telNo, telModel[0].Password, telModel[0].Description);
-            //    await _workRepository.AddAsync(work, cancellationToken);
-            //    if (IsTelNeedVerify)
-            //    {
-            //        return new TrOnDutyResponseDto() { TelNo = telNo, TelPwd = "", Description = telModel[0].Description };
-            //    }
-            //    else
-            //    {
-            //        return new TrOnDutyResponseDto() { TelNo = telNo, TelPwd = telModel[0].Password, Description = telModel[0].Description };
-            //    }
-            //}
-            throw UserFriendlyException.SameMessage("签入异常,未查询到对应分机信息");
-            /*
-             *var work = 缓存.get(userId);
-             * if(work.TelNo == telNo)
-             * t: return work f: throw 
-             *
-             * 获取配置
-             *IsNeedTelNo
-             * IsTelNeedVerify
-             *
-             * user
-             * tel = trClient.get(telNo);
-             * work
-             *
-             * 根据配置 return new();
-             *
-             */
-        }
-
-
         /// <summary>
         /// 下班
         /// </summary>