qinchaoyue 2 maanden geleden
bovenliggende
commit
a763a78ca4
53 gewijzigde bestanden met toevoegingen van 455 en 622 verwijderingen
  1. 1 0
      .gitignore
  2. 0 7
      Hotline.sln
  3. 21 15
      src/Hotline.Api/Controllers/CallController.cs
  4. 0 0
      src/Hotline.Api/Controllers/FilterController/ExportDataController.cs
  5. 25 2
      src/Hotline.Api/Controllers/IPPbxController.cs
  6. 1 1
      src/Hotline.Api/Controllers/Snapshot/SnapshotController.cs
  7. 62 0
      src/Hotline.Api/Filter/StaffNoSessionContextFilter.cs
  8. 0 1
      src/Hotline.Api/Hotline.Api.csproj
  9. 1 0
      src/Hotline.Api/Program.cs
  10. 0 2
      src/Hotline.Api/StartupExtensions.cs
  11. 1 1
      src/Hotline.Api/config/appsettings.Development.json
  12. 33 9
      src/Hotline.Application/CallCenter/DefaultCallApplication.cs
  13. 8 1
      src/Hotline.Application/CallCenter/ICallApplication.cs
  14. 3 3
      src/Hotline.Application/Orders/OrderApplication.cs
  15. 0 63
      src/Hotline.Logger/Filters/ErrorHandlingFilter.cs
  16. 0 22
      src/Hotline.Logger/Hotline.Logger.csproj
  17. 0 59
      src/Hotline.Logger/Models/AccModel.cs
  18. 0 48
      src/Hotline.Logger/Models/BaseLogModel.cs
  19. 0 72
      src/Hotline.Logger/Models/ExceptionModel.cs
  20. 0 188
      src/Hotline.Logger/RequestResponseLoggingMiddleware.cs
  21. 1 7
      src/Hotline.Share/Dtos/CallCenter/NotifyDto.cs
  22. 2 1
      src/Hotline.Share/Dtos/CallCenter/QueryCallsFixedDto.cs
  23. 2 1
      src/Hotline.Share/Dtos/CallCenter/QueryTelOperationsFixedDto.cs
  24. 7 0
      src/Hotline.Share/Dtos/CallCenter/TelDto.cs
  25. 22 2
      src/Hotline.XingTang/CallTelClient.cs
  26. 0 12
      src/Hotline.XingTang/INotifyService.cs
  27. 0 32
      src/Hotline.XingTang/NotifyService.cs
  28. 9 11
      src/Hotline.XingTang/ServiceCollectionExtensions.cs
  29. 57 0
      src/Hotline/Authentications/ChangeSessionProvider.cs
  30. 5 0
      src/Hotline/Authentications/FakeSessionContext.cs
  31. 5 0
      src/Hotline/Authentications/Police110SessionContext.cs
  32. 5 0
      src/Hotline/Authentications/ProvinceSessionContext.cs
  33. 5 0
      src/Hotline/Authentications/YbEnterpriseSessionContext.cs
  34. 5 0
      src/Hotline/Authentications/ZzptSessionContext.cs
  35. 5 0
      src/Hotline/Caching/Interfaces/ISysDicDataCacheManager.cs
  36. 5 0
      src/Hotline/Caching/Interfaces/ISystemSettingCacheManager.cs
  37. 5 0
      src/Hotline/Caching/Services/SysDicDataCacheManager.cs
  38. 4 0
      src/Hotline/Caching/Services/SystemSettingCacheManager.cs
  39. 0 2
      src/Hotline/CallCenter/Tels/CallTelDomain/QueryTelRequest.cs
  40. 1 0
      src/Hotline/CallCenter/Tels/ICallTelClient.cs
  41. 20 0
      src/Hotline/CallCenter/Tels/TelOperation.cs
  42. 16 0
      src/Hotline/SeedData/SystemDicDataSeedData.cs
  43. 5 0
      src/Hotline/Settings/SysDicTypeConsts.cs
  44. 5 0
      src/Tr.Sdk/CallTelClient.cs
  45. 29 37
      src/XF.Domain/Authentications/DefaultSessionContext.cs
  46. 12 0
      src/XF.Domain/Authentications/IChangeSessionProvider.cs
  47. 2 0
      src/XF.Domain/Authentications/ISessionContext.cs
  48. 22 4
      test/Hotline.Tests/Application/DefaultCallApplicationTest.cs
  49. 10 3
      test/Hotline.Tests/Application/OrderSnapshotApplicationTest.cs
  50. 17 15
      test/Hotline.Tests/Controller/TestSessionContext.cs
  51. 9 0
      test/Hotline.Tests/Domain/OrderVisitDomainServiceTest.cs
  52. 6 1
      test/Hotline.Tests/Startup.cs
  53. 1 0
      test/Hotline.Tests/TestBase.cs

+ 1 - 0
.gitignore

@@ -345,3 +345,4 @@ ASALocalRun/
 # BeatPulse healthcheck temp database
 healthchecksdb
 /merge.ps1
+/merge_245_test.ps1

+ 0 - 7
Hotline.sln

@@ -53,8 +53,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XingTang.Sdk", "src\XingTan
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.XingTang", "src\Hotline.XingTang\Hotline.XingTang.csproj", "{9F99C272-5BC2-452C-9D97-BC756AF04669}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.Logger", "src\Hotline.Logger\Hotline.Logger.csproj", "{37784861-ABC0-41F4-87B4-2E08A89A2C42}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.WeChat", "src\Hotline.WeChat\Hotline.WeChat.csproj", "{75215667-65AF-4B7B-85E7-3140239B30CC}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TianQue.Sdk", "src\TianQue.Sdk\TianQue.Sdk.csproj", "{6CF27647-D0E0-4D17-80FB-3EE57864A2B4}"
@@ -147,10 +145,6 @@ Global
 		{9F99C272-5BC2-452C-9D97-BC756AF04669}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{9F99C272-5BC2-452C-9D97-BC756AF04669}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{9F99C272-5BC2-452C-9D97-BC756AF04669}.Release|Any CPU.Build.0 = Release|Any CPU
-		{37784861-ABC0-41F4-87B4-2E08A89A2C42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{37784861-ABC0-41F4-87B4-2E08A89A2C42}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{37784861-ABC0-41F4-87B4-2E08A89A2C42}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{37784861-ABC0-41F4-87B4-2E08A89A2C42}.Release|Any CPU.Build.0 = Release|Any CPU
 		{75215667-65AF-4B7B-85E7-3140239B30CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{75215667-65AF-4B7B-85E7-3140239B30CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{75215667-65AF-4B7B-85E7-3140239B30CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -199,7 +193,6 @@ Global
 		{C3F289D5-C50B-46DB-852C-9543EF9B0355} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{CF2A8B80-FF4E-4291-B383-D735BB629F32} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{9F99C272-5BC2-452C-9D97-BC756AF04669} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
-		{37784861-ABC0-41F4-87B4-2E08A89A2C42} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{75215667-65AF-4B7B-85E7-3140239B30CC} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{6CF27647-D0E0-4D17-80FB-3EE57864A2B4} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{8E4F64EF-314A-45BA-8BB2-46FF5B06F7D5} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}

+ 21 - 15
src/Hotline.Api/Controllers/CallController.cs

@@ -52,18 +52,6 @@ namespace Hotline.Api.Controllers
             _telOperationXthxRepository = telOperationXthxRepository;
         }
 
-        /// <summary>
-        /// 分机状态通知
-        /// </summary>
-        /// <param name="dto"></param>
-        /// <returns></returns>
-        [HttpPost("notify/status")]
-        [LogFilterAlpha("分机状态通知")]
-        [AllowAnonymous]
-        public async Task NotifyStatus([FromBody] NotifyStatusInDto dto)
-        {
-        }
-
         /// <summary>
         /// 查询分机
         /// </summary>
@@ -128,8 +116,17 @@ namespace Hotline.Api.Controllers
         /// <param name="dto"></param>
         /// <returns></returns>
         [HttpGet("calls-fixed")]
-        public Task<IReadOnlyList<CallNativeDto>> QueryCallsFixed([FromQuery] QueryCallsFixedDto dto)
-            => _callApplication.QueryCallsFixedAsync(dto, HttpContext.RequestAborted);
+        public async Task<List<CallNativeDto>> QueryCallsFixed([FromQuery] QueryCallsFixedDto dto)
+            => await _callApplication.QueryCallsFixedAsync(dto, HttpContext.RequestAborted).ToPageListWithoutTotalAsync(dto, HttpContext.RequestAborted);
+
+        /// <summary>
+        /// 查询通话记录(固定数据量)
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpGet("calls-fixed/count")]
+        public async Task<int> QueryCallsFixedCount([FromQuery] QueryCallsFixedDto dto)
+            => await _callApplication.QueryCallsFixedAsync(dto, HttpContext.RequestAborted).CountAsync(HttpContext.RequestAborted);
 
         /// <summary>
         /// 查询通话记录
@@ -165,7 +162,16 @@ namespace Hotline.Api.Controllers
         /// <returns></returns>
         [HttpGet("tel-operations-fixed")]
         public async Task<IReadOnlyList<TelOperation>> QueryTelOperationsAsync([FromQuery] QueryTelOperationsFixedDto dto) =>
-            await _callApplication.QueryTelOperationsAsync(dto).ToFixedListAsync(dto, HttpContext.RequestAborted);
+            await _callApplication.QueryTelOperationsAsync(dto).ToPageListWithoutTotalAsync(dto, HttpContext.RequestAborted);
+
+        /// <summary>
+        /// 查询坐席操作记录(固定数据量)
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpGet("tel-operations-fixed/count")]
+        public async Task<int> QueryTelOperationsCountAsync([FromQuery] QueryTelOperationsFixedDto dto) =>
+            await _callApplication.QueryTelOperationsAsync(dto).CountAsync(HttpContext.RequestAborted);
 
         /// <summary>
         /// 查询坐席操作记录基础数据

+ 0 - 0
src/Hotline.Api/Controllers/ExportData/ExportDataController.cs → src/Hotline.Api/Controllers/FilterController/ExportDataController.cs


+ 25 - 2
src/Hotline.Api/Controllers/IPPbxController.cs

@@ -43,6 +43,8 @@ using Hotline.Tools;
 using Hotline.Api.Filter;
 using XF.Domain.Cache;
 using Hotline.Application.CallCenter;
+using Mapster;
+using Tr.Sdk;
 
 namespace Hotline.Api.Controllers
 {
@@ -75,6 +77,7 @@ namespace Hotline.Api.Controllers
         private readonly ITrCallRecordRepository _callRecordRepository;
         private readonly IIPPbxApplication _iPPbxApplication;
         private readonly ICallTelClient _callTelClient;
+        private readonly IRepository<TelOperation> _telOperationRepository;
 
         public IPPbxController(IMapper mapper, IUserDomainService userDomainService,
             ISessionContext sessionContext, IRepository<TrCallRecord> trCallRecordRepository,
@@ -86,7 +89,7 @@ namespace Hotline.Api.Controllers
             ITelApplication telApplication, IRepository<Quality.Quality> qualiteyRepository,
             IAiQualityService aiQualityService, IRepository<QualityTemplate> qualityTemplate,
             ISystemSettingCacheManager systemSettingCacheManager, IRepository<TelActionRecord> telActionRecordRepository,
-            ISystemMobilAreaApplication systemMobilAreaApplication, IRepository<Work> workRepository, Publisher publisher, ITrCallRecordRepository callRecordRepository, ITypedCache<Work> cacheWork, IIPPbxApplication iPPbxApplication, ICallTelClient callTelClient)
+            ISystemMobilAreaApplication systemMobilAreaApplication, IRepository<Work> workRepository, Publisher publisher, ITrCallRecordRepository callRecordRepository, ITypedCache<Work> cacheWork, IIPPbxApplication iPPbxApplication, ICallTelClient callTelClient, IRepository<TelOperation> telOperationRepository)
         {
             _mapper = mapper;
             _userDomainService = userDomainService;
@@ -115,6 +118,7 @@ namespace Hotline.Api.Controllers
             _cacheWork = cacheWork;
             _iPPbxApplication = iPPbxApplication;
             _callTelClient = callTelClient;
+            _telOperationRepository = telOperationRepository;
         }
 
         #region 添添呼
@@ -457,7 +461,6 @@ namespace Hotline.Api.Controllers
 
         #endregion
 
-
         #region 通话记录(对外)
         /// <summary>
         /// 接收通话记录
@@ -663,6 +666,26 @@ namespace Hotline.Api.Controllers
             return OpenResponse.Ok("success");
         }
 
+        /// <summary>
+        /// 话机状态通知
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [StaffNoSessionContextFilter()]
+        [HttpPost("notify/status")]
+        [LogFilterAlpha("话机状态通知")]
+        public async Task NotifyStatus(NotifyInDto dto)
+        {
+            var userName = _sessionContext.UserName;
+            if (dto.Status == 0) // 签出
+            {
+                await _iPPbxApplication.ResetTelStatus(null, dto.TelNo, CancellationToken.None);
+            }
+            dto.Status = await _callTelClient.GetStatusAsync(dto.Status);
+            await _callApplication.EndActionAsync(dto.Adapt<EndActionInDto>());
+        }
+
         #endregion
 
         #region 通话记录(对内)

+ 1 - 1
src/Hotline.Api/Controllers/Snapshot/SnapshotController.cs

@@ -91,7 +91,7 @@ public class SnapshotController : BaseController
     /// </summary>
     /// <returns></returns>
     [HttpPost("order")]
-    [LogFilter("添加随手拍工单")]
+    [LogFilterAlpha("添加随手拍工单")]
     public async Task<AddSnapshotOrderOutDto> AddOrderAsync([FromBody] AddSnapshotOrderInDto dto)
     {
         var ssp = _systemDicDataCacheManager.SourceChannel.FirstOrDefault(m => m.DicDataName == "随手拍")

+ 62 - 0
src/Hotline.Api/Filter/StaffNoSessionContextFilter.cs

@@ -0,0 +1,62 @@
+using Hotline.Identity.Accounts;
+using Hotline.Settings;
+using Hotline.Share.Dtos.CallCenter;
+using Hotline.Share.Tools;
+using Hotline.Users;
+using IdentityModel;
+using Microsoft.AspNetCore.Mvc.Filters;
+using System.Security.Claims;
+using XF.Domain.Authentications;
+using XF.Domain.Repository;
+using static Hotline.AppDefaults;
+
+namespace Hotline.Api.Filter;
+
+public class StaffNoSessionContextFilter : ActionFilterAttribute
+{
+    private IAccountRepository _accountRepository;
+    private IRepository<User> _userRepository;
+
+    public StaffNoSessionContextFilter()
+    {
+    }
+
+    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
+    {
+        await ReloadUser(context);
+        await next();
+    }
+
+    private async Task ReloadUser(ActionExecutingContext context)
+    {
+        try
+        {
+            _accountRepository = context.HttpContext.RequestServices.GetRequiredService<IAccountRepository>();
+            _userRepository = context.HttpContext.RequestServices.GetRequiredService<IRepository<User>>();
+            var notifyInDto = context.ActionArguments.Values.FirstOrDefault() as NotifyInDto;
+            if (notifyInDto == null) return;
+            var user = _userRepository.Queryable().Where(m => m.StaffNo == notifyInDto.StaffNo).First();
+            if (user == null) return;
+            var account = await _accountRepository.GetExtAsync(m => m.Id == user.Id, m => m.Includes(x => x.Roles));
+
+            List<Claim> userClaims = [
+                new(JwtClaimTypes.Subject, account.Id),
+                new(JwtClaimTypes.PhoneNumber, account.PhoneNo ?? string.Empty),
+                new(ClaimTypes.NameIdentifier, user.Id),
+                new(AppClaimTypes.UserDisplayName, account.Name),
+                new(AppClaimTypes.DepartmentId, user.OrgId ?? string.Empty),
+                new(AppClaimTypes.DepartmentIsCenter, user.Organization?.IsCenter.ToString() ?? string.Empty),
+                new(AppClaimTypes.DepartmentName, user.Organization?.Name ?? string.Empty),
+                new(AppClaimTypes.DepartmentAreaCode, user.Organization?.AreaCode ?? string.Empty),
+                new(AppClaimTypes.DepartmentAreaName, user.Organization?.AreaName ?? string.Empty),
+                new(AppClaimTypes.DepartmentLevel, user.Organization?.Level.ToString() ?? string.Empty),
+                new(AppClaimTypes.AreaId, user.OrgId?.GetHigherOrgId() ?? string.Empty),
+            ];
+            userClaims.AddRange(account.Roles.Select(d => new Claim(JwtClaimTypes.Role, d.Name)));
+            context.HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity(userClaims));
+        }
+        catch
+        {
+        }
+    }
+}

+ 0 - 1
src/Hotline.Api/Hotline.Api.csproj

@@ -28,7 +28,6 @@
 
   <ItemGroup>
     <ProjectReference Include="..\Hotline.Application\Hotline.Application.csproj" />
-    <ProjectReference Include="..\Hotline.Logger\Hotline.Logger.csproj" />
     <ProjectReference Include="..\Hotline.WeChat\Hotline.WeChat.csproj" />
     <ProjectReference Include="..\Hotline.XingTang\Hotline.XingTang.csproj" />
     <ProjectReference Include="..\TianQue.Sdk\TianQue.Sdk.csproj" />

+ 1 - 0
src/Hotline.Api/Program.cs

@@ -1,5 +1,6 @@
 using Hotline.Api;
 using Serilog;
+using XF.Domain.Authentications;
 
 Log.Logger = new LoggerConfiguration()
     .WriteTo.Console()

+ 0 - 2
src/Hotline.Api/StartupExtensions.cs

@@ -29,7 +29,6 @@ using Hotline.Configurations;
 using Hotline.DI;
 using Hotline.Settings.TimeLimitDomain.ExpireTimeSupplier;
 using XF.Domain.Authentications;
-using HotPot.Mvc.Filters;
 using Hotline.WeChat;
 using Hotline.Snapshot.Interfaces;
 using TianQue.Sdk;
@@ -263,7 +262,6 @@ internal static class StartupExtensions
         //app.UseRequestResponseLogging(app.Configuration);
 
         app.UseResponseCompression();
-        app.UseXingTangApiEndpoints();
 
         return app;
     }

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

@@ -1,7 +1,7 @@
 {
   "AllowedHosts": "*",
   "AppConfiguration": {
-    "AppScope": "YiBin",
+    "AppScope": "ZiGong",
     "YiBin": {
       "AreaCode": "511500",
       "CallCenterType": "TianRun", //XunShi、WeiErXin、TianRun、XingTang

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

@@ -1,4 +1,5 @@
-using DotNetCore.CAP;
+using DocumentFormat.OpenXml.Spreadsheet;
+using DotNetCore.CAP;
 using Hotline.Application.Systems;
 using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.BlackLists;
@@ -120,6 +121,24 @@ public abstract class DefaultCallApplication : ICallApplication
             .ToListAsync(cancellationToken);
     }
 
+    /// <summary>
+    /// 记录分机结束动作时间
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    public async Task EndActionAsync(EndActionInDto dto)
+    {
+        var telOperation = await _telOperationRepository.Queryable()
+            .Where(m => m.StaffNo == dto.StaffNo && m.TelNo == dto.TelNo && !m.EndTime.HasValue)
+            .WhereIF(dto.Status != -1, m => m.OperateState != dto.Status)
+            .WhereIF(dto.Status == -1, m => m.OperateStateText == "未知")
+            .OrderByDescending(m => m.CreationTime)
+            .FirstAsync();
+        if (telOperation == null) return;
+        telOperation.EndAction();
+        await _telOperationRepository.UpdateAsync(telOperation);
+    }
+
     /// <summary>
     /// 新增黑名单
     /// </summary>
@@ -236,7 +255,7 @@ public abstract class DefaultCallApplication : ICallApplication
     /// <summary>
     /// 定量查询通话记录
     /// </summary>
-    public virtual async Task<IReadOnlyList<CallNativeDto>> QueryCallsFixedAsync(QueryCallsFixedDto dto, CancellationToken cancellationToken)
+    public virtual ISugarQueryable<CallNativeDto> QueryCallsFixedAsync(QueryCallsFixedDto dto, CancellationToken cancellationToken)
     {
         var query = _callNativeRepository.Queryable(includeDeleted: true)
             .LeftJoin<Order>((d, o) => d.Id == o.CallId)
@@ -265,7 +284,7 @@ public abstract class DefaultCallApplication : ICallApplication
 
         if (dto.Type == 2)
         {
-            var items = await query.Select((d, o, v) => new CallNativeDto
+            return query.Select((d, o, v) => new CallNativeDto
             {
                 OrderId = v.OrderId,
                 OrderNo = v.Order.No,
@@ -273,11 +292,9 @@ public abstract class DefaultCallApplication : ICallApplication
                 CallState = d.CallState,
                 IsVisit = !SqlFunc.IsNullOrEmpty(v.Id),
                 IsOrder = !SqlFunc.IsNullOrEmpty(o.Id),
-            }, true)
-                .ToFixedListAsync(dto, cancellationToken);
-            return items;
+            }, true);
         }
-        return await query.Select((d, o, v) => new CallNativeDto
+        return query.Select((d, o, v) => new CallNativeDto
         {
             OrderId = o.Id,
             OrderNo = o.No,
@@ -285,8 +302,8 @@ public abstract class DefaultCallApplication : ICallApplication
             Title = o.Title,
             IsVisit = !SqlFunc.IsNullOrEmpty(v.Id),
             IsOrder = !SqlFunc.IsNullOrEmpty(o.Id),
-        }, true)
-        .ToFixedListAsync(dto, cancellationToken);
+        }, true);
+
     }
 
     /// <summary>
@@ -548,6 +565,13 @@ public abstract class DefaultCallApplication : ICallApplication
             .Where((o, c) => o.Id == orderId)
             .Select((o, c) => new { o.CallId, c.CallNo })
             .FirstAsync(cancellationToken);
+//#if DEBUG
+//        var order = await _orderRepository.GetAsync(orderId);
+//        var oldName = _sessionContext.UserName;
+//        _sessionContext.ChangeSession("08dcfe32-c260-40b4-839a-aeca1f76244c");
+//        var newName = _sessionContext.UserName;
+
+//#endif
         if (orderCall is null || orderCall.CallNo.IsNullOrEmpty())
         {
             string message = $"延迟更新工单通话, 工单: {orderId} 根据 order.id left join call_native 信息为空; 消息队列无须重试";

+ 8 - 1
src/Hotline.Application/CallCenter/ICallApplication.cs

@@ -13,6 +13,13 @@ namespace Hotline.Application.CallCenter
 {
     public interface ICallApplication
     {
+        /// <summary>
+        /// 记录分机结束动作时间
+        /// </summary>
+        /// <param name="inDto"></param>
+        /// <returns></returns>
+        Task EndActionAsync(EndActionInDto inDto);
+
         /// <summary>
         /// 查询分机
         /// </summary>
@@ -67,7 +74,7 @@ namespace Hotline.Application.CallCenter
         /// <summary>
         /// 定量查询通话记录
         /// </summary>
-        Task<IReadOnlyList<CallNativeDto>> QueryCallsFixedAsync(QueryCallsFixedDto dto, CancellationToken cancellationToken);
+        ISugarQueryable<CallNativeDto> QueryCallsFixedAsync(QueryCallsFixedDto dto, CancellationToken cancellationToken);
 
         /// <summary>
         /// 查询分机操作记录(定量)

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

@@ -3614,8 +3614,8 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             OrgId = startStep.HandlerOrgId,
             OrgName = startStep.HandlerOrgName,
         };
-        var isAutoFillSummaryOpinion = bool.Parse(_systemSettingCacheManager
-            .GetSetting(SettingConstants.IsAutoFillSummaryOpinion).SettingValue[0]);
+        _sessionContext.ChangeSession(startStep.HandlerId);
+        var isAutoFillSummaryOpinion = _systemSettingCacheManager.IsAutoFillSummaryOpinion;
 
         await _workflowDomainService.NextAsync(operater, nextDto, order.ExpiredTime, isAutoFillSummaryOpinion,
             cancellationToken: cancellationToken);
@@ -4977,4 +4977,4 @@ public class OrderApplication : IOrderApplication, IScopeDependency
 
         return query;
     }
-}
+}

+ 0 - 63
src/Hotline.Logger/Filters/ErrorHandlingFilter.cs

@@ -1,63 +0,0 @@
-
-using System;
-using Hotline.Logger.Models;
-using Hotline.Share.Tools;
-using Microsoft.AspNetCore.Mvc.Filters;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.DependencyInjection;
-using Serilog;
-
-namespace HotPot.Mvc.Filters
-{
-    /// <summary>
-    /// 异常拦截处理
-    /// </summary>
-    public class ErrorHandlingFilter : IExceptionFilter
-    {
-        /// <summary>
-        /// 日志记录器
-        /// </summary>
-        private readonly Serilog.ILogger _logger;
-
-        private readonly IConfiguration _configuration;
-
-        public ErrorHandlingFilter(ILogger logger, IConfiguration configuration)
-        {
-            _configuration = configuration;
-            _logger = new LoggerConfiguration()
-                .WriteTo.Logger(configure => configure
-                .MinimumLevel.Debug()
-                .WriteTo.RollingFile(
-                    Path.Combine("logs", "err-log-{Date}.txt"),
-                    retainedFileCountLimit: 7,
-                    outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
-                ))
-                .CreateLogger();
-
-        }
-
-        /// <summary>
-        /// 异常拦截处理
-        /// </summary>
-        /// <param name="context">异常</param>
-        public void OnException(ExceptionContext context)
-        {
-            if (_configuration.GetSection("AccLog").Get<bool>() == false) return;
-            var exceptionModel = new ExceptionModel();
-            try
-            {
-
-                context.HttpContext.Items.TryAdd("ErrorId", exceptionModel.Id);
-                exceptionModel.Context = context.Exception.ToJson();
-                exceptionModel.Method = context.HttpContext.Request.Path;
-                exceptionModel.Message = context.Exception.Message;
-                _logger.Error(exceptionModel.ToJson());
-            }
-            catch
-            {
-                // ignore
-            }
-        }
-    }
-}
-

+ 0 - 22
src/Hotline.Logger/Hotline.Logger.csproj

@@ -1,22 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <TargetFramework>net8.0</TargetFramework>
-    <ImplicitUsings>enable</ImplicitUsings>
-    <Nullable>enable</Nullable>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
-    <PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.2.0" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" />
-    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" />
-    <PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
-    <PackageReference Include="Serilog.Sinks.RollingFile" Version="3.3.0" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <ProjectReference Include="..\Hotline.Share\Hotline.Share.csproj" />
-  </ItemGroup>
-
-</Project>

+ 0 - 59
src/Hotline.Logger/Models/AccModel.cs

@@ -1,59 +0,0 @@
-namespace Hotline.Logger.Models
-{
-    /// <summary>
-    /// 日志类
-    /// </summary>
-    public class AccModel : BaseLogModel
-    {
-        /// <summary>
-        /// 时间间隔
-        /// </summary>
-        /// <returns></returns>
-        public long Interval { get; set; }
-
-        /// <summary>
-        /// 方向 (Response, Request)
-        /// </summary>
-        public string Direction { get; set; } = string.Empty;
-
-        /// <summary>
-        /// 服务地址
-        /// 请求/响应服务的地址
-        /// </summary>
-        public string ServiceUrl { get; set; }
-
-        /// <summary>
-        /// 被调用方法
-        /// 被调用的方法,直接API地址
-        /// </summary>
-        public string Method { get; set; }
-
-        /// <summary>
-        /// 请求入参
-        /// 请求(Request)的请求参数,响应时为空
-        /// </summary>
-        public string InParam { get; set; }
-
-        /// <summary>
-        /// 异常ID
-        /// 调用服务有异常时,异常信息的日志ID,没异常日志时为空
-        /// </summary>
-        public string? ErrorId { get; set; }
-
-        /// <summary>
-        /// 返回值
-        /// 响应结果
-        /// </summary>
-        public string OutResult { get; set; } = string.Empty;
-
-        /// <summary>
-        /// 用户
-        /// </summary>
-        public string User { get; set; }
-
-        /// <summary>
-        /// 用户字段Key
-        /// </summary>
-        public string UserKey = "user_display_name";
-    }
-}

+ 0 - 48
src/Hotline.Logger/Models/BaseLogModel.cs

@@ -1,48 +0,0 @@
-using System.Net;
-
-namespace Hotline.Logger.Models
-{
-    public class BaseLogModel
-    {
-        public BaseLogModel()
-        {
-            Id = Guid.NewGuid().ToString();
-        }
-
-        /// <summary>
-        /// 日志id
-        /// </summary>
-        public string Id { get; set; }
-
-        /// <summary>
-        /// 时间点
-        /// 发起请求时间点
-        /// </summary>
-        public string RequestTime => DateTime.Now.ToString("yyyy年MM月dd日 hh:mm:ss");
-
-        /// <summary>
-        /// 时间戳
-        /// </summary>
-        public long Timestamp =>
-            (long)(DateTime.Now - TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1))).TotalMilliseconds;
-
-        /// <summary>
-        /// 主机名
-        /// 服务所在的机器名
-        /// </summary>
-        public string Host
-        {
-            get
-            {
-                try
-                {
-                    return Dns.GetHostName();
-                }
-                catch (Exception)
-                {
-                    return "获取主机名失败";
-                }
-            }
-        }
-    }
-}

+ 0 - 72
src/Hotline.Logger/Models/ExceptionModel.cs

@@ -1,72 +0,0 @@
-using System.Diagnostics;
-
-namespace Hotline.Logger.Models
-{
-    /// <summary>
-    /// 异常日志
-    /// </summary>
-    public class ExceptionModel : BaseLogModel
-    {
-        /// <summary>
-        /// 进程ID
-        /// 应用程序的进程ID,可为空
-        /// </summary>
-        public string ProcessId
-        {
-            get
-            {
-                try
-                {
-                    return Process.GetCurrentProcess().Id.ToString();
-                }
-                catch
-                {
-                    return "获取进程Id失败";
-                }
-            }
-        }
-
-        /// <summary>
-        /// 线程ID
-        /// 可为空
-        /// </summary>
-        public string ThreadId
-        {
-            get
-            {
-                try
-                {
-                    return Thread.CurrentThread.ManagedThreadId.ToString();
-                }
-                catch
-                {
-                    return "获取线程Id失败";
-                }
-            }
-        }
-
-        /// <summary>
-        /// 错误码
-        /// 程序自定义的错误码,某种错误指定的错误码,没有定义时为为空
-        /// </summary>
-        public string ErrorCode { get; set; } = string.Empty;
-
-        /// <summary>
-        /// 异常信息
-        /// Exception message信息
-        /// </summary>
-        public string Message { get; set; } = string.Empty;
-
-        /// <summary>
-        /// 异常上下文
-        /// 包括了异常堆栈信息,用于分析具体问题
-        /// </summary>
-        public string Context { get; set; } = string.Empty;
-
-        /// <summary>
-        /// 被调用方法
-        /// 被调用的方法,直接API地址
-        /// </summary>
-        public string Method { get; set; }
-    }
-}

+ 0 - 188
src/Hotline.Logger/RequestResponseLoggingMiddleware.cs

@@ -1,188 +0,0 @@
-using Hotline.Logger.Models;
-using Microsoft.Extensions.Configuration;
-using Hotline.Share.Tools;
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Logging;
-using Serilog;
-using System.Text;
-
-namespace Hotline.Logger
-{
-
-    /// <summary>
-    /// Acc 日志记录
-    /// </summary>
-    public class RequestResponseLoggingMiddleware
-    {
-        /// <summary>
-        /// 组件
-        /// </summary>
-        private readonly RequestDelegate _next;
-
-        /// <summary>
-        /// 日志记录器
-        /// </summary>
-        private readonly Serilog.ILogger _logger;
-
-        public RequestResponseLoggingMiddleware(RequestDelegate next)
-        {
-            _next = next;
-            _logger = new LoggerConfiguration()
-                .WriteTo.Logger(configure => configure 
-                .MinimumLevel.Debug()
-                .WriteTo.RollingFile(
-                    Path.Combine("logs", "acc-log-{Date}.txt"),
-                    retainedFileCountLimit: 7,
-                    outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
-                ))
-                .CreateLogger();
-        }
-
-        /// <summary>
-        /// 日志记录器
-        /// </summary>
-        /// <param name="context"> The context.  </param>
-        /// <returns> The <see cref="Task"/>.  </returns>
-        public async Task Invoke(HttpContext context)
-        {
-            var injectedRequestStream = new MemoryStream();
-            try
-            {
-                var requestTime = DateTime.Now;
-                var accModel = new AccModel();
-                context.Items.TryAdd("AccId", accModel.Id);
-                await GetMethod(context, injectedRequestStream, accModel);
-                var originalBodyStream = context.Response.Body;
-                using (var responseBody = new MemoryStream())
-                {
-                    context.Response.Body = responseBody;
-                    await _next(context);
-
-                    accModel = await FormatResponse(context.Response, requestTime, accModel);
-                    if (context.Items.TryGetValue("ErrorId", out var errorId))
-                        accModel.ErrorId = errorId.ToString();
-                    _logger.Information(accModel.ToJson());
-                    await responseBody.CopyToAsync(originalBodyStream);
-                }
-
-            }
-            catch (Exception e)
-            {
-                throw new Exception("Hotline.Logger.RequestResponseLoggingMiddleware.GetMethod", e);
-            }
-            finally
-            {
-                injectedRequestStream.Dispose();
-            }
-        }
-
-        /// <summary>
-        /// 获取 响应日志数据
-        /// </summary>
-        /// <param name="response"> The response.  </param>
-        /// <param name="requestTime"> The request time.  </param>
-        /// <returns> The <see cref="Task"/>.  </returns>
-        private async Task<AccModel> FormatResponse(HttpResponse response, DateTime requestTime, AccModel accModel)
-        {
-            response.Body.Seek(0, SeekOrigin.Begin);
-            var text = await new StreamReader(response.Body).ReadToEndAsync();
-            response.Body.Seek(0, SeekOrigin.Begin);
-            if (response.ContentType.Contains("application/json"))
-            { 
-                accModel.OutResult = text;
-            }
-            accModel.Interval = (long)(DateTime.Now - requestTime).TotalMilliseconds;
-            return accModel;
-        }
-
-        /// <summary>
-        /// 获取请求入参和方法
-        /// </summary>
-        /// <param name="context">
-        /// The context.
-        /// </param>
-        /// <param name="injectedRequestStream">
-        /// The injected Request Stream.
-        /// </param>
-        private async Task GetMethod(HttpContext context, MemoryStream injectedRequestStream, AccModel accModel)
-        {
-            try
-            {
-                if (context.Request.Method.ToUpper() == "GET")
-                {
-                    try
-                    {
-                        accModel.InParam = context.Request.QueryString.ToString();
-                        accModel.Method = context.Request.Path;
-                        accModel.ServiceUrl = context.Request.Host.ToString();
-                    }
-                    catch (Exception e)
-                    {
-                        accModel.InParam = "GetMethod.Get 代码块异常;";
-                        accModel.Method = "GetMethod.Get 代码块异常;";
-                        accModel.ServiceUrl = e.ToString();
-                    }
-                }
-
-                if (context.Request.Method.ToUpper() == "POST" || context.Request.Method.ToUpper() == "PUT" || context.Request.Method.ToUpper() == "DELETE")
-                {
-                    if (context.Request.ContentType?.IndexOf("multipart/form-data", StringComparison.Ordinal) == -1)
-                    {
-                        using var bodyReader = new StreamReader(context.Request.Body);
-                        var bodyAsText = await bodyReader.ReadToEndAsync();
-
-                        var bytesToWrite = Encoding.UTF8.GetBytes(bodyAsText);
-                        injectedRequestStream.Write(bytesToWrite, 0, bytesToWrite.Length);
-                        injectedRequestStream.Seek(0, SeekOrigin.Begin);
-                        context.Request.Body = injectedRequestStream;
-
-                        accModel.InParam = bodyAsText;
-                    }
-                    accModel.Method = context.Request.Path;
-                    accModel.ServiceUrl = context.Request.Host.ToString();
-                }
-
-                try
-                {
-                    accModel.User = context.User.Identities
-                        .FirstOrDefault()?.Claims
-                        .Where(m => m.Type == accModel.UserKey)
-                        .Select(m => m.Value)
-                        .FirstOrDefault() ?? string.Empty;
-                }
-                catch { }
-            }
-            catch (Exception e)
-            {
-                _logger.Error($"Hotline.Logger.RequestResponseLoggingMiddleware.GetMethod: {e}");
-            }
-
-        }
-    }
-
-    /// <summary>
-    /// 注册 acc 日志记录器
-    /// </summary>
-    public static class RequestResponseLoggingMiddlewareExtensions
-    {
-        /// <summary>
-        /// 注册日志记录服务
-        /// </summary>
-        /// <param name="builder">
-        /// The builder.
-        /// </param>
-        /// <returns>
-        /// The <see cref="IApplicationBuilder"/>.
-        /// </returns>
-        public static IApplicationBuilder UseRequestResponseLogging(this IApplicationBuilder builder, IConfiguration configuration)
-        {
-            var isAccLog = configuration.GetSection("AccLog").Get<bool>();
-            if (isAccLog)
-            {
-                return builder.UseMiddleware<RequestResponseLoggingMiddleware>();
-            }
-            return builder;
-        }
-    }
-}

+ 1 - 7
src/Hotline.XingTang/Dto/NotifyDto.cs → src/Hotline.Share/Dtos/CallCenter/NotifyDto.cs

@@ -1,11 +1,5 @@
-using Hotline.Share.Enums.CallCenter;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+namespace Hotline.Share.Dtos.CallCenter;
 
-namespace Hotline.XingTang.Dto;
 public class NotifyInDto
 {
     /// <summary>

+ 2 - 1
src/Hotline.Share/Dtos/CallCenter/QueryCallsFixedDto.cs

@@ -4,11 +4,12 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using Hotline.Share.Enums.CallCenter;
+using Hotline.Share.Requests;
 using Hotline.Share.Tools;
 
 namespace Hotline.Share.Dtos.CallCenter
 {
-    public class QueryCallsFixedDto : QueryFixedDto
+    public record QueryCallsFixedDto : PagedRequest
     {
         public string? OrderNo { get; set; }
 

+ 2 - 1
src/Hotline.Share/Dtos/CallCenter/QueryTelOperationsFixedDto.cs

@@ -1,4 +1,5 @@
 using Hotline.Share.Enums.CallCenter;
+using Hotline.Share.Requests;
 using Hotline.Share.Tools;
 using System;
 using System.Collections.Generic;
@@ -8,7 +9,7 @@ using System.Threading.Tasks;
 
 namespace Hotline.Share.Dtos.CallCenter
 {
-    public class QueryTelOperationsFixedDto : QueryFixedDto
+    public record QueryTelOperationsFixedDto : PagedKeywordRequest
     {
         /// <summary>
         /// 姓名

+ 7 - 0
src/Hotline.Share/Dtos/CallCenter/TelDto.cs

@@ -95,6 +95,13 @@ namespace Hotline.Share.Dtos.CallCenter
     {
     }
 
+    public class EndActionInDto
+    {
+        public string StaffNo { get; set; }
+        public string TelNo { get; set; }
+        public int Status { get; set; }
+    }
+
     public class NotifyStatusInDto
     {
         /// <summary>

+ 22 - 2
src/Hotline.XingTang/CallTelClient.cs

@@ -1,10 +1,18 @@
-using Hotline.CallCenter.Tels;
+using Hotline.Caching.Interfaces;
+using Hotline.CallCenter.Tels;
 using Hotline.CallCenter.Tels.CallTelDomain;
 
 namespace Hotline.XingTang;
 
-internal class CallTelClient : ICallTelClient
+public class CallTelClient : ICallTelClient
 {
+    private readonly ISystemDicDataCacheManager _dicCache;
+
+    public CallTelClient(ISystemDicDataCacheManager dicCache)
+    {
+        _dicCache = dicCache;
+    }
+
     public Task AddBlacklistAsync(AddBlacklistRequest request, CancellationToken token)
     {
         throw new NotImplementedException();
@@ -15,6 +23,18 @@ internal class CallTelClient : ICallTelClient
         throw new NotImplementedException();
     }
 
+    public async Task<int> GetStatusAsync(int status)
+    {
+        var statusMap = _dicCache.XingTangOperationMap;
+        var result = statusMap.FirstOrDefault(m => m.DicDataName == status.ToString());
+        if (result == null) { return -1; }
+        if (int.TryParse(result.DicDataValue, out var value))
+        {
+            return value;
+        }
+        return -1;
+    }
+
     public Task<List<QueryBlacklistResponse>> QueryBlacklistAsync(QueryBlacklistRequest request, CancellationToken token)
     {
         throw new NotImplementedException();

+ 0 - 12
src/Hotline.XingTang/INotifyService.cs

@@ -1,12 +0,0 @@
-using Hotline.XingTang.Dto;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Hotline.XingTang;
-public interface INotifyService
-{
-    Task NotifyStatus(NotifyInDto dto);
-}

+ 0 - 32
src/Hotline.XingTang/NotifyService.cs

@@ -1,32 +0,0 @@
-using Hotline.Application.CallCenter;
-using Hotline.Settings;
-using Hotline.Share.Tools;
-using Hotline.XingTang.Dto;
-using Microsoft.Extensions.Logging;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Hotline.XingTang;
-public class NotifyService : INotifyService
-{
-    private readonly IIPPbxApplication _IPPbxApplication;
-    private readonly ISystemLogRepository _systemLogRepository;
-
-    public NotifyService(IIPPbxApplication iPPbxApplication, ISystemLogRepository systemLogRepository)
-    {
-        _IPPbxApplication = iPPbxApplication;
-        _systemLogRepository = systemLogRepository;
-    }
-
-    public async Task NotifyStatus(NotifyInDto dto)
-    {
-        _systemLogRepository.Add("兴唐分机状态通知", dto);
-        if (dto.Status == 0)
-        {
-            await _IPPbxApplication.ResetTelStatus(null, dto.TelNo, CancellationToken.None);
-        }
-    }
-}

+ 9 - 11
src/Hotline.XingTang/ServiceCollectionExtensions.cs

@@ -1,6 +1,5 @@
 using Fw.Utility.UnifyResponse;
 using Hotline.CallCenter.Tels;
-using Hotline.XingTang.Dto;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.DependencyInjection;
@@ -18,18 +17,17 @@ public static class ServiceCollectionExtensions
     public static IServiceCollection AddXingTangSDK(this IServiceCollection services)
     {
         services.AddScoped<ICallTelClient, CallTelClient>();
-        services.AddScoped<INotifyService, NotifyService>();
         return services;
     }
 
-    public static void UseXingTangApiEndpoints(this WebApplication app)
-    {
+    //public static void UseXingTangApiEndpoints(this WebApplication app)
+    //{
         // 注册 POST 接口
-        app.MapPost("/api/v1/XingTang/notify/status", async (INotifyService service, HttpContext context) =>
-        {
-            var input = await context.Request.ReadFromJsonAsync<NotifyInDto>();
-            await service.NotifyStatus(input!);
-            return Results.Ok(new ApiResponse(0, "ok", ""));
-        });
-    }
+        //app.MapPost("/api/v1/XingTang/notify/status", async (INotifyService service, HttpContext context) =>
+        //{
+        //    var input = await context.Request.ReadFromJsonAsync<NotifyInDto>();
+        //    await service.NotifyStatus(input!);
+        //    return Results.Ok(new ApiResponse(0, "ok", ""));
+        //});
+    //}
 }

+ 57 - 0
src/Hotline/Authentications/ChangeSessionProvider.cs

@@ -0,0 +1,57 @@
+using Hotline.Identity.Accounts;
+using Hotline.Settings;
+using Hotline.Share.Dtos.CallCenter;
+using Hotline.Users;
+using IdentityModel;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Claims;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Authentications;
+using XF.Domain.Dependency;
+using XF.Domain.Repository;
+
+namespace Hotline.Authentications;
+public class ChangeSessionProvider : IChangeSessionProvider, IScopeDependency
+{
+    private readonly IServiceProvider _serviceProvider;
+
+    public ChangeSessionProvider(IServiceProvider serviceProvider)
+    {
+        _serviceProvider = serviceProvider;
+    }
+
+    public HttpContext ChangeSessionByUserId(string id, HttpContext httpContext)
+    {
+        if (httpContext == null) 
+        {
+            httpContext = new DefaultHttpContext();
+        }
+        var userRepository = _serviceProvider.GetService<IRepository<User>>();
+        var accountRepository = _serviceProvider.GetService<IAccountRepository>();
+        var user = userRepository.Queryable().Where(m => m.Id == id).First();
+        if (user == null) return null;
+        var account = accountRepository.GetExtAsync(m => m.Id == user.Id, m => m.Includes(x => x.Roles)).GetAwaiter().GetResult();
+
+        List<Claim> userClaims = [
+            new(JwtClaimTypes.Subject, account.Id),
+                new(JwtClaimTypes.PhoneNumber, account.PhoneNo ?? string.Empty),
+                new(ClaimTypes.NameIdentifier, user.Id),
+                new(AppClaimTypes.UserDisplayName, account.Name),
+                new(AppClaimTypes.DepartmentId, user.OrgId ?? string.Empty),
+                new(AppClaimTypes.DepartmentIsCenter, user.Organization?.IsCenter.ToString() ?? string.Empty),
+                new(AppClaimTypes.DepartmentName, user.Organization?.Name ?? string.Empty),
+                new(AppClaimTypes.DepartmentAreaCode, user.Organization?.AreaCode ?? string.Empty),
+                new(AppClaimTypes.DepartmentAreaName, user.Organization?.AreaName ?? string.Empty),
+                new(AppClaimTypes.DepartmentLevel, user.Organization?.Level.ToString() ?? string.Empty),
+                new(AppClaimTypes.AreaId, user.OrgId?.GetHigherOrgId() ?? string.Empty),
+            ];
+        userClaims.AddRange(account.Roles.Select(d => new Claim(JwtClaimTypes.Role, d.Name)));
+        httpContext.User = new ClaimsPrincipal(new ClaimsIdentity(userClaims));
+        return httpContext;
+    }
+}

+ 5 - 0
src/Hotline/Authentications/FakeSessionContext.cs

@@ -50,5 +50,10 @@ namespace Hotline.Authentications
         /// 例如: 微信的OpenId
         /// </summary>
         public string? OpenId { get; init; }
+
+        public void ChangeSession(string id)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 5 - 0
src/Hotline/Authentications/Police110SessionContext.cs

@@ -62,5 +62,10 @@ namespace Hotline.Authentications
         /// 例如: 微信的OpenId
         /// </summary>
         public string? OpenId { get; init; }
+
+        public void ChangeSession(string id)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 5 - 0
src/Hotline/Authentications/ProvinceSessionContext.cs

@@ -67,5 +67,10 @@ namespace Hotline.Authentications
         /// 例如: 微信的OpenId
         /// </summary>
         public string? OpenId { get; init; }
+
+        public void ChangeSession(string id)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 5 - 0
src/Hotline/Authentications/YbEnterpriseSessionContext.cs

@@ -63,5 +63,10 @@ namespace Hotline.Authentications
         /// 例如: 微信的OpenId
         /// </summary>
         public string? OpenId { get; init; }
+
+        public void ChangeSession(string id)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 5 - 0
src/Hotline/Authentications/ZzptSessionContext.cs

@@ -62,5 +62,10 @@ namespace Hotline.Authentications
         /// 例如: 微信的OpenId
         /// </summary>
         public string? OpenId { get; init; }
+
+        public void ChangeSession(string id)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 5 - 0
src/Hotline/Caching/Interfaces/ISysDicDataCacheManager.cs

@@ -72,5 +72,10 @@ namespace Hotline.Caching.Interfaces
         IReadOnlyCollection<SystemDicDataOutDto> OrderTag { get; }
 
         IReadOnlyCollection<SystemDicDataOutDto> VisitSatisfaction { get; }
+
+        /// <summary>
+        /// 兴唐动作状态映射
+        /// </summary>
+        IReadOnlyCollection<SystemDicDataOutDto> XingTangOperationMap { get; }
     }
 }

+ 5 - 0
src/Hotline/Caching/Interfaces/ISystemSettingCacheManager.cs

@@ -100,5 +100,10 @@ namespace Hotline.Caching.Interfaces
         string TianQuanPostAcceptInfoApi { get; }
 
         bool Snapshot { get; }
+
+        /// <summary>
+        /// 是否开启自动填写办理意见至汇总节点
+        /// </summary>
+        bool IsAutoFillSummaryOpinion { get; }
     }
 }

+ 5 - 0
src/Hotline/Caching/Services/SysDicDataCacheManager.cs

@@ -137,6 +137,11 @@ namespace Hotline.Caching.Services
         /// </summary>
         public IReadOnlyCollection<SystemDicDataOutDto> VisitSatisfaction => GetOrAdd(SysDicTypeConsts.VisitSatisfaction);
 
+        /// <summary>
+        /// 兴唐动作状态映射
+        /// </summary>
+        public IReadOnlyCollection<SystemDicDataOutDto> XingTangOperationMap => GetOrAdd(SysDicTypeConsts.XingTangOperationMap);
+
         public void RemoveSysDicDataCache(string code)
         {
             _cacheSysDicData.Remove(code);

+ 4 - 0
src/Hotline/Caching/Services/SystemSettingCacheManager.cs

@@ -200,5 +200,9 @@ namespace Hotline.Caching.Services
         public bool Snapshot =>
             GetOrDefault("08dd0eca-66b8-4c98-8dec-0c76c29d77e3", SettingConstants.Snapshot, "随手拍功能开关", false, "随手拍功能开关");
 
+        /// <summary>
+        /// 是否开启自动填写办理意见至汇总节点
+        /// </summary>
+        public bool IsAutoFillSummaryOpinion => GetOrDefault(null, SettingConstants.IsAutoFillSummaryOpinion, "是否开启自动填写办理意见至汇总节点", false, "是否开启自动填写办理意见至汇总节点");
     }
 }

+ 0 - 2
src/Hotline/CallCenter/Tels/CallTelDomain/QueryTelRequest.cs

@@ -39,11 +39,9 @@ public class QueryBlacklistResponse
     public string UserId { get; set; }
 
     public string CreationTime { get; set; }
-
     public string Phone { get; set; }
 
     public int SpecialFlag { get; set; }
-
     public int? Priority { get; set; }
 }
 

+ 1 - 0
src/Hotline/CallCenter/Tels/ICallTelClient.cs

@@ -17,4 +17,5 @@ public interface ICallTelClient
     Task DelBlacklistAsync(DelBlacklistRequest request, CancellationToken token);
 
     Task<List<QueryBlacklistResponse>> QueryBlacklistAsync(QueryBlacklistRequest request, CancellationToken token);
+    Task<int> GetStatusAsync(int status);
 }

+ 20 - 0
src/Hotline/CallCenter/Tels/TelOperation.cs

@@ -37,8 +37,28 @@ namespace Hotline.CallCenter.Tels
         public string? OperateStateText { get; set; }
 
         public DateTime? OperateTime { get; set; }
+
+        /// <summary>
+        /// 动作结束时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "动作结束时间")]
+        public DateTime? EndTime { get; set; }
+
+        /// <summary>
+        /// 时长(秒)
+        /// </summary>
+        [SugarColumn(ColumnDescription = "动作时长(秒)")]
+        public double? Duration { get; set; }
+
         public int? Channel { get; set; }
         public string? CompanyId { get; set; }
         public string? Remark { get; set; }
+
+        public void EndAction()
+        {
+            EndTime = DateTime.Now;
+            Duration = (EndTime.Value - OperateTime.Value).TotalSeconds;
+        }
+
     }
 }

+ 16 - 0
src/Hotline/SeedData/SystemDicDataSeedData.cs

@@ -17,6 +17,18 @@ public class SystemDicDataSeedData : ISeedData<SystemDicData>
 
     public IEnumerable<SystemDicData> GetData(string dicTypeCode)
     {
+        if (dicTypeCode == SysDicTypeConsts.XingTangOperationMap)
+        {
+            return [
+                new() { Id = "08dd5248-5553-4c59-880d-3ddd35a32b09", DicDataValue = "16", DicDataName = "0", Sort = 1},
+                new() { Id = "08dd5248-52d9-4330-8e59-111a225006ba", DicDataValue = "0", DicDataName = "100", Sort = 2},
+                new() { Id = "08dd5192-3b06-4705-818d-cbdbf38d3e6b", DicDataValue = "1", DicDataName = "301", Sort = 3},
+                new() { Id = "08dd5191-8539-4436-8f25-1af26a368589", DicDataValue = "2", DicDataName = "202", Sort = 4},
+                new() { Id = "08dd5190-c854-462b-8ee8-ac45ad30b1dc", DicDataValue = "3", DicDataName = "200", Sort = 5},
+                new() { Id = "08dd5190-c185-4ac9-8ef5-e9ab9f8781aa", DicDataValue = "17", DicDataName = "303", Sort = 6},
+                new() { Id = "08dd5190-3fbb-4156-864e-9ebbe46b498e", DicDataValue = "30", DicDataName = "400", Sort = 7},
+                ];
+        }
         if (dicTypeCode == SysDicTypeConsts.SnapshotOrderLabel)
         {
             return [
@@ -192,6 +204,10 @@ public class SystemDicDataSeedData : ISeedData<SystemDicData>
         {
             dicType = ["08dd1f12-0e81-47dc-82d7-559e36a68e0d", "补充奖励类型"];
         }
+        if (dicTypeCode == SysDicTypeConsts.XingTangOperationMap)
+        {
+            dicType = ["08dbefe5-1cc5-4069-8d69-33afd978f68d", "兴唐动作状态映射"];
+        }
 
         return new SystemDicType
         {

+ 5 - 0
src/Hotline/Settings/SysDicTypeConsts.cs

@@ -306,4 +306,9 @@ public class SysDicTypeConsts
     /// 随手拍工单标签
     /// </summary>
     public static string SnapshotOrderLabel = "SnapshotOrderLabel";
+
+    /// <summary>
+    /// 兴唐动作状态映射
+    /// </summary>
+    public static string XingTangOperationMap = "XingTangOperationMap";
 }

+ 5 - 0
src/Tr.Sdk/CallTelClient.cs

@@ -27,6 +27,11 @@ public class CallTelClient : ICallTelClient
         await _trClient.DelBlacklistAsync(request.Adapt<Blacklist.DelBlacklistRequest>(), token);
     }
 
+    public Task<int> GetStatusAsync(int status)
+    {
+        throw new NotImplementedException();
+    }
+
     public async Task<List<QueryBlacklistResponse>> QueryBlacklistAsync(QueryBlacklistRequest request, CancellationToken token)
     {
         var result = await _trClient.QueryBlacklistAsync(request.Adapt<Blacklist.QueryBlacklistRequest>(), token);

+ 29 - 37
src/XF.Domain/Authentications/DefaultSessionContext.cs

@@ -2,6 +2,7 @@
 using System.Security.Claims;
 using IdentityModel;
 using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
 using XF.Domain.Dependency;
 using XF.Domain.Extensions;
 
@@ -9,36 +10,19 @@ namespace XF.Domain.Authentications
 {
     public class DefaultSessionContext : ISessionContext, IScopeDependency
     {
-        public DefaultSessionContext(IHttpContextAccessor httpContextAccessor)
+        private readonly IChangeSessionProvider _changeSessionProvider;
+        private readonly IHttpContextAccessor _contextAccessor;
+        public DefaultSessionContext(IHttpContextAccessor httpContextAccessor, IServiceProvider serviceProvider)
         {
-            var httpContext = httpContextAccessor.HttpContext;
-            if (httpContext is null)
-                //throw new ArgumentNullException(nameof(httpContext));
-                return;
-
-            var user = httpContext.User;
-            //UserId = user.FindFirstValue(JwtClaimTypes.Id);
-            UserId = user.FindFirstValue(ClaimTypes.NameIdentifier);
-            UserName = user.FindFirstValue(AppClaimTypes.UserDisplayName);
-            Phone = user.FindFirstValue(JwtClaimTypes.PhoneNumber);
-            //Roles = user.Claims.Where(d => d.Type == JwtClaimTypes.Role).Select(d => d.Value).ToArray();
-            Roles = user.Claims.Where(d => d.Type == ClaimTypes.Role).Select(d => d.Value).ToArray();
-            OrgId = user.FindFirstValue(AppClaimTypes.DepartmentId);
-            OrgName = user.FindFirstValue(AppClaimTypes.DepartmentName);
-            OrgLevel = user.FindIntValue(AppClaimTypes.DepartmentLevel);
-            OrgAreaCode = user.FindFirstValue(AppClaimTypes.DepartmentAreaCode);
-            OrgAreaName = user.FindFirstValue(AppClaimTypes.DepartmentAreaName);
-            OrgIsCenter = user.FindBoolValue(AppClaimTypes.DepartmentIsCenter);
-            AreaId = user.FindFirstValue(AppClaimTypes.AreaId);
-            ClientId = user.FindFirstValue(JwtClaimTypes.ClientId);
-            StaffNo = user.FindFirstValue(AppClaimTypes.StaffNo);
-            OpenId = user.FindFirstValue(AppClaimTypes.OpenId);
+            _changeSessionProvider = serviceProvider.GetService<IChangeSessionProvider>();
+            _contextAccessor = httpContextAccessor;
+
         }
 
         /// <summary>
         /// Id of current tenant or null for host
         /// </summary>
-        public string? UserId { get; init; }
+        public string? UserId { get => _contextAccessor.HttpContext?.User.FindFirstValue(ClaimTypes.NameIdentifier); init { } }
 
         /// <summary>
         /// Id of current user or throw Exception for guest
@@ -46,43 +30,51 @@ namespace XF.Domain.Authentications
         /// <exception cref="AuthenticationException"></exception>
         public string RequiredUserId => UserId ?? throw new ArgumentNullException();
 
-        public string? UserName { get; init; }
+        public string? UserName { get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.UserDisplayName); init { } }
 
-        public string? Phone { get; init; }
+        public string? Phone { get => _contextAccessor.HttpContext?.User.FindFirstValue(JwtClaimTypes.PhoneNumber); init { } }
 
         /// <summary>
         /// Roles
+        /// 不能使用 JwtClaimTypes.Role 来获取Role
+        /// 在 JwtBearerHandler 处理 JWT 令牌时,它会使用 Microsoft.AspNetCore.Authentication.JwtBearer 自动转换 Claim 名称。
         /// </summary>
-        public string[] Roles { get; init; }
+        // public string[] Roles { get => _contextAccessor.HttpContext?.User.Claims.Where(d => d.Type == JwtClaimTypes.Role).Select(d => d.Value).ToArray(); init { } }
+        public string[] Roles { get => _contextAccessor.HttpContext?.User.Claims.Where(d => d.Type == ClaimTypes.Role).Select(d => d.Value).ToArray(); init { } }
 
-        public string? OrgId { get; init; }
-        public string? OrgName { get; init; }
+        public string? OrgId { get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.DepartmentId); init { } }
+        public string? OrgName { get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.DepartmentName); init { } }
 
-        public int OrgLevel { get; init; }
-        public string? OrgAreaCode { get; init; }
-        public bool OrgIsCenter { get; init; }
+        public int OrgLevel { get => _contextAccessor.HttpContext?.User.FindIntValue(AppClaimTypes.DepartmentLevel) ?? 0; init { } }
+        public string? OrgAreaCode { get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.DepartmentAreaCode); init { } }
+        public bool OrgIsCenter { get => _contextAccessor.HttpContext?.User.FindBoolValue(AppClaimTypes.DepartmentIsCenter)??false; init { } }
 
         /// <summary>
         /// 部门行政区划名称
         /// </summary>
-        public string? OrgAreaName { get; init; }
+        public string? OrgAreaName { get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.DepartmentAreaName); init { } }
 
-        public string? AreaId { get; init; }
+        public string? AreaId { get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.AreaId); init { } }
 
         public string RequiredOrgId => OrgId ?? throw new ArgumentNullException();
         
-        public string? ClientId { get; init; }
+        public string? ClientId { get => _contextAccessor.HttpContext?.User.FindFirstValue(JwtClaimTypes.ClientId); init { } }
 
         /// <summary>
         /// 工号
         /// </summary>
-        public string? StaffNo { get; init; }
+        public string? StaffNo { get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.StaffNo); init { } }
 
         /// <summary>
         /// 第三方平台用户唯一标识
         /// 例如: 微信的OpenId
         /// </summary>
-        public string? OpenId { get; init; }
+        public string? OpenId { get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.OpenId); init { } }
+
+        public void ChangeSession(string id)
+        {
+            _contextAccessor.HttpContext = _changeSessionProvider.ChangeSessionByUserId(id, _contextAccessor.HttpContext);
+        }
     }
 
     public static class ClaimsPrincipalExtensions

+ 12 - 0
src/XF.Domain/Authentications/IChangeSessionProvider.cs

@@ -0,0 +1,12 @@
+using Microsoft.AspNetCore.Http;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace XF.Domain.Authentications;
+public interface IChangeSessionProvider
+{
+    HttpContext ChangeSessionByUserId(string id, HttpContext httpContext);
+}

+ 2 - 0
src/XF.Domain/Authentications/ISessionContext.cs

@@ -51,4 +51,6 @@ public interface ISessionContext
     /// 例如: 微信的OpenId
     /// </summary>
     string? OpenId { get; init; }
+
+    void ChangeSession(string id);
 }

+ 22 - 4
test/Hotline.Tests/Application/DefaultCallApplicationTest.cs

@@ -2,9 +2,11 @@
 using Hotline.Api.Controllers;
 using Hotline.Application.CallCenter;
 using Hotline.CallCenter.Calls;
+using Hotline.CallCenter.Tels;
 using Hotline.Identity.Accounts;
 using Hotline.Identity.Roles;
 using Hotline.Orders;
+using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Settings;
 using Hotline.Share.Dtos.CallCenter;
 using Hotline.Share.Dtos.Order;
@@ -26,14 +28,30 @@ public class DefaultCallApplicationTest : TestBase
     private readonly IRepository<CallNative> _callNativeRepository;
     public readonly IFixture _fixture;
     private readonly IOrderRepository _orderRepository;
+    private readonly ICallTelClient _callTelClient;
 
-    public DefaultCallApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, XingTangCallApplication defaultCallApplication, IOrderVisitRepository orderVisitRepository, IRepository<CallNative> callNativeRepository, IOrderRepository orderRepository, IThirdIdentiyService thirdService, IThirdAccountRepository thirdAccount, ITypedCache<SystemSetting> cacheSettingData) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdService, thirdAccount, cacheSettingData)
+    public DefaultCallApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, XingTangCallApplication defaultCallApplication, IOrderVisitRepository orderVisitRepository, IRepository<CallNative> callNativeRepository, IOrderRepository orderRepository, IThirdIdentiyService thirdService, IThirdAccountRepository thirdAccount, ITypedCache<SystemSetting> cacheSettingData, ICallTelClient callTelClient) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdService, thirdAccount, cacheSettingData)
     {
         _fixture = new Fixture();
         _defaultCallApplication = defaultCallApplication;
         _orderVisitRepository = orderVisitRepository;
         _callNativeRepository = callNativeRepository;
         _orderRepository = orderRepository;
+        _callTelClient = callTelClient;
+    }
+
+    [Theory]
+    [InlineData(16, 0)]
+    [InlineData(0, 100)]
+    [InlineData(1, 301)]
+    [InlineData(2, 202)]
+    [InlineData(3, 200)]
+    [InlineData(17, 303)]
+    [InlineData(30, 400)]
+    public async Task GetStatus_Test(int xingtang, int hotline)
+    { 
+        var result = await _callTelClient.GetStatusAsync(hotline);
+        result.ShouldBe(xingtang);
     }
 
     [Fact]
@@ -43,7 +61,7 @@ public class DefaultCallApplicationTest : TestBase
         inDto.CallStartTimeEnd = DateTime.Now;
         inDto.CallStartTimeStart = "2024/10/01 00:00:00".ObjToDate();
 
-        var items = await _defaultCallApplication.QueryCallsFixedAsync(inDto, new CancellationToken());
+        var items = await _defaultCallApplication.QueryCallsFixedAsync(inDto, new CancellationToken()).ToPageListWithoutTotalAsync(inDto, CancellationToken.None);
         items.ShouldNotBeNull();
     }
 
@@ -55,7 +73,7 @@ public class DefaultCallApplicationTest : TestBase
         inDto.CallStartTimeStart = "2024/11/08 00:00:00".ObjToDate();
         inDto.FromNo = "8205175";
 
-        var items1 = await _defaultCallApplication.QueryCallsFixedAsync(inDto, new CancellationToken());
+        var items1 = await _defaultCallApplication.QueryCallsFixedAsync(inDto, new CancellationToken()).ToPageListWithoutTotalAsync(inDto, CancellationToken.None);
         items1.ShouldNotBeNull();
         foreach (var item in items1.Where(m => m.IsOrder))
         {
@@ -67,7 +85,7 @@ public class DefaultCallApplicationTest : TestBase
         inDto.ToNo = "13990006670";
         inDto.FromNo = null;
         inDto.Type = 2;
-        var items = await _defaultCallApplication.QueryCallsFixedAsync(inDto, new CancellationToken());
+        var items = await _defaultCallApplication.QueryCallsFixedAsync(inDto, new CancellationToken()).ToPageListWithoutTotalAsync(inDto, CancellationToken.None);
         items.ShouldNotBeNull();
         items.Count().ShouldNotBe(0, "通过被叫号码查询失败");
         foreach (var item in items.Where(m => m.IsVisit))

+ 10 - 3
test/Hotline.Tests/Application/OrderSnapshotApplicationTest.cs

@@ -3,6 +3,7 @@ using Hotline.Application.Snapshot;
 using Hotline.Caching.Interfaces;
 using Hotline.Identity.Accounts;
 using Hotline.Identity.Roles;
+using Hotline.Orders;
 using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Repository.SqlSugar.Snapshot;
 using Hotline.Settings;
@@ -39,8 +40,9 @@ public class OrderSnapshotApplicationTest : TestBase
     private readonly IRedPackAuditRepository _redPackAuditRepository;
     private readonly IRedPackRecordRepository _redPackRecordRepository;
     private readonly ISnapshotLabelLogRepository _snapshotLabelLogRepository;
+    private readonly IOrderRepository _orderRepository;
 
-    public OrderSnapshotApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, IThirdIdentiyService thirdIdentiyService, IThirdAccountRepository thirdAccountRepository, OrderServiceMock orderServiceMock, ISystemDicDataCacheManager systemDicDataCacheManager, IOrderSnapshotRepository orderSnapshotRepository, IOrderSnapshotApplication orderSnapshotApplication, ISnapshotApplication snapshotApplication, IIndustryLogRepository industryLogRepository, ICommunityInfoRepository communityInfoRepository, IRedPackAuditRepository redPackAuditRepository, IRedPackRecordRepository redPackRecordRepository, ISnapshotLabelLogRepository snapshotLabelLogRepository, ITypedCache<SystemSetting> cacheSettingData) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdIdentiyService, thirdAccountRepository, cacheSettingData)
+    public OrderSnapshotApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, IThirdIdentiyService thirdIdentiyService, IThirdAccountRepository thirdAccountRepository, OrderServiceMock orderServiceMock, ISystemDicDataCacheManager systemDicDataCacheManager, IOrderSnapshotRepository orderSnapshotRepository, IOrderSnapshotApplication orderSnapshotApplication, ISnapshotApplication snapshotApplication, IIndustryLogRepository industryLogRepository, ICommunityInfoRepository communityInfoRepository, IRedPackAuditRepository redPackAuditRepository, IRedPackRecordRepository redPackRecordRepository, ISnapshotLabelLogRepository snapshotLabelLogRepository, ITypedCache<SystemSetting> cacheSettingData, IOrderRepository orderRepository) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdIdentiyService, thirdAccountRepository, cacheSettingData)
     {
         _orderServiceMock = orderServiceMock;
         _systemDicDataCacheManager = systemDicDataCacheManager;
@@ -52,6 +54,7 @@ public class OrderSnapshotApplicationTest : TestBase
         _redPackAuditRepository = redPackAuditRepository;
         _redPackRecordRepository = redPackRecordRepository;
         _snapshotLabelLogRepository = snapshotLabelLogRepository;
+        _orderRepository = orderRepository;
     }
 
     /// <summary>
@@ -66,10 +69,11 @@ public class OrderSnapshotApplicationTest : TestBase
             .办理到网格员(SetZuoXi)
             .StepHandle(async order =>
             {
-                Thread.Sleep(30 * 1000);
+                Thread.Sleep(20 * 1000);
+                var orderEntity = await _orderRepository.GetAsync(order.Id);
+                orderEntity.ActualHandleStepName.ShouldBe("工单标记");
             }
             ).GetCreateResult();
-        order.Id.ShouldNotBeNull();
     }
 
     /// <summary>
@@ -88,6 +92,7 @@ public class OrderSnapshotApplicationTest : TestBase
     [Fact]
     public async Task SnapshotWorkflow_Guider_Test()
     {
+        SetSettingCache(SettingConstants.OvertimeBack, "4");
         var snapshotLabels = _systemDicDataCacheManager.SnapshotOrderLabel;
         var inputLable = snapshotLabels.Where(m => m.DicDataValue == "bss").ToList();
         var order = _orderServiceMock.CreateSnapshotOrder(SetWeiXin)
@@ -143,6 +148,8 @@ public class OrderSnapshotApplicationTest : TestBase
                 community.ShouldNotBeNull();
                 community.Name.ShouldBe(replyDto.OrgName);
                 community.FullName.ShouldBe(replyDto.OrgFullName);
+                var orderEntity = await _orderRepository.GetAsync(order.Id);
+                orderEntity.ActualHandleStepName.ShouldBe("工单标记");
             })
             .办理到派单员(Set班长)
             .办理到一级部门(SetPaiDanYuan)

+ 17 - 15
test/Hotline.Tests/Controller/DefaultSessionContext.cs → test/Hotline.Tests/Controller/TestSessionContext.cs

@@ -1,28 +1,30 @@
 using System.Security.Authentication;
 using System.Security.Claims;
+using Hotline.Authentications;
 using IdentityModel;
 using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
 using XF.Domain.Authentications;
 using XF.Domain.Dependency;
 
 namespace Hotline.Tests.Controller;
-public class DefaultSessionContext : ISessionContext, IScopeDependency
+public class TestSessionContext : ISessionContext, IScopeDependency
 {
     private readonly IHttpContextAccessor _contextAccessor;
-    public DefaultSessionContext(IHttpContextAccessor httpContextAccessor)
+    private readonly IChangeSessionProvider _changeSessionProvider;
+    public TestSessionContext(IHttpContextAccessor httpContextAccessor, IServiceProvider serviceProvider)
     {
         _contextAccessor = httpContextAccessor;
-        //Roles = user.Claims.Where(d => d.Type == JwtClaimTypes.Role).Select(d => d.Value).ToArray();
+        _changeSessionProvider = serviceProvider.GetService<IChangeSessionProvider>();
+        //if (httpContextAccessor.HttpContext == null) _contextAccessor.HttpContext = new DefaultHttpContext();
     }
-    private HttpContext _content = new DefaultHttpContext();
 
-    private HttpContext GetContext()
+    public void ChangeSession(string id)
     {
-        return _contextAccessor.HttpContext;
+        _contextAccessor.HttpContext = _changeSessionProvider.ChangeSessionByUserId(id, _contextAccessor.HttpContext);
     }
-    public HttpContext? HttpContext { get => GetContext(); set => _content = value; }
 
-    public string? OpenId { get { return _contextAccessor.HttpContext.User.FindFirstValue(AppClaimTypes.OpenId); } init { } }
+    public string? OpenId { get { return _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.OpenId); } init { } }
 
     /// <summary>
     /// Id of current tenant or null for host
@@ -37,7 +39,7 @@ public class DefaultSessionContext : ISessionContext, IScopeDependency
     /// Id of current user or throw Exception for guest
     /// </summary>
     /// <exception cref="AuthenticationException"></exception>
-    public string RequiredUserId => _contextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
+    public string RequiredUserId => _contextAccessor.HttpContext?.User.FindFirstValue(ClaimTypes.NameIdentifier);
     public string? UserName
     {
         get { return _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.UserDisplayName); }
@@ -46,7 +48,7 @@ public class DefaultSessionContext : ISessionContext, IScopeDependency
 
     public string? Phone
     {
-        get { return _contextAccessor.HttpContext.User.FindFirstValue(JwtClaimTypes.PhoneNumber); }
+        get { return _contextAccessor.HttpContext?.User.FindFirstValue(JwtClaimTypes.PhoneNumber); }
         init { }
     }
 
@@ -80,13 +82,13 @@ public class DefaultSessionContext : ISessionContext, IScopeDependency
 
     public string? OrgAreaCode
     {
-        get { return _contextAccessor.HttpContext.User.FindFirstValue(AppClaimTypes.DepartmentAreaCode); }
+        get { return _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.DepartmentAreaCode); }
         init { }
     }
 
     public bool OrgIsCenter
     {
-        get { return _contextAccessor.HttpContext.User.FindBoolValue(AppClaimTypes.DepartmentIsCenter); }
+        get { return _contextAccessor.HttpContext?.User.FindBoolValue(AppClaimTypes.DepartmentIsCenter) ?? false; }
         init { }
     }
 
@@ -95,7 +97,7 @@ public class DefaultSessionContext : ISessionContext, IScopeDependency
     /// </summary>
     public string? OrgAreaName
     {
-        get { return _contextAccessor.HttpContext.User.FindFirstValue(AppClaimTypes.DepartmentAreaName); }
+        get { return _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.DepartmentAreaName); }
         init { }
     }
 
@@ -106,7 +108,7 @@ public class DefaultSessionContext : ISessionContext, IScopeDependency
     }
     public string? ClientId
     {
-        get { return _contextAccessor.HttpContext.User.FindFirstValue(JwtClaimTypes.ClientId); }
+        get { return _contextAccessor.HttpContext?.User.FindFirstValue(JwtClaimTypes.ClientId); }
         init { }
     }
 
@@ -115,7 +117,7 @@ public class DefaultSessionContext : ISessionContext, IScopeDependency
     /// </summary>
     public string? StaffNo
     {
-        get { return _contextAccessor.HttpContext.User.FindFirstValue(AppClaimTypes.StaffNo); }
+        get { return _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.StaffNo); }
         init { }
     }
 }

+ 9 - 0
test/Hotline.Tests/Domain/OrderVisitDomainServiceTest.cs

@@ -285,6 +285,15 @@ public class OrderVisitDomainServiceTest : TestBase
             new () {Name = "默认满意的规则", AppScope = "YiBin", ReplyRegular = "默认满意", VisitContent = "超过48小时自动回访", VisitState = EVisitState.Visited, SeatEvaluate = null,VoiceEvaluate = null,OrgProcessingResults=0, OrgHandledAttitude = 0 , VisitType = EVisitType.SmsVisit, SortOrder = 0},
             new () {Name = "对部门处理结果不满意的规则", AppScope = "YiBin", ReplyRegular = "2", VisitContent = "不满意", VisitState = EVisitState.SMSUnsatisfied, SeatEvaluate = null,VoiceEvaluate = null,OrgProcessingResults=2, OrgHandledAttitude = null , VisitType = null, SortOrder = 2},
             new () {Name = "用户回复的非1和2的规则", AppScope = "YiBin", ReplyRegular = "^(?!.*[12]).*$", VisitContent = "不满意", VisitState = EVisitState.SMSUnsatisfied, SeatEvaluate = null,VoiceEvaluate = null,OrgProcessingResults= null, OrgHandledAttitude = null , VisitType = null, SortOrder = 3, IsReplyToOrgVisitContent = true},
+
+            new () {Name = "非常满意的规则", AppScope = "LuZhou", ReplyRegular = "1", VisitContent = "非常满意", VisitState = EVisitState.Visited, SeatEvaluate = null,VoiceEvaluate = null,OrgProcessingResults=5, OrgHandledAttitude = 0, VisitType = EVisitType.SmsVisit, SortOrder = 1},
+            new () {Name = "满意的规则", AppScope = "LuZhou", ReplyRegular = "2", VisitContent = "满意", VisitState = EVisitState.Visited, SeatEvaluate = null,VoiceEvaluate = null,OrgProcessingResults=4, OrgHandledAttitude = 0 , VisitType = EVisitType.SmsVisit, SortOrder = 2},
+            new () {Name = "一般的规则", AppScope = "LuZhou", ReplyRegular = "3", VisitContent = "一般", VisitState = EVisitState.Visited, SeatEvaluate = null, VoiceEvaluate = null,OrgProcessingResults= 4, OrgHandledAttitude = 0 , VisitType = EVisitType.SmsVisit, SortOrder = 3},
+            new () {Name = "不满意的规则", AppScope = "LuZhou", ReplyRegular = "4", VisitContent = "不满意", VisitState = EVisitState.SMSUnsatisfied, SeatEvaluate = null,VoiceEvaluate = null,OrgProcessingResults=2, OrgHandledAttitude = 0 , VisitType = null, SortOrder = 4},
+            new () {Name = "非常不满意的规则", AppScope = "LuZhou", ReplyRegular = "5", VisitContent = "不满意", VisitState = EVisitState.SMSUnsatisfied, SeatEvaluate = null, VoiceEvaluate = null,OrgProcessingResults=2, OrgHandledAttitude = 0, VisitType = null, SortOrder = 5},
+            new () {Name = "默认满意的规则", AppScope = "LuZhou", ReplyRegular = "默认满意", VisitContent = "超过48小时自动回访", VisitState = EVisitState.Visited, SeatEvaluate = null, VoiceEvaluate = null,OrgProcessingResults=0, OrgHandledAttitude = 0, VisitType = EVisitType.SmsVisit, SortOrder = 0},
+            new () {Name = "用户回答非1,2,3,4,5的匹配规则", AppScope = "LuZhou", ReplyRegular = "^(?!.*[12345]).*$", VisitContent = "默认满意", VisitState = EVisitState.Visited, SeatEvaluate = null, VoiceEvaluate = null,OrgProcessingResults=0, OrgHandledAttitude = 0, VisitType = EVisitType.SmsVisit, SortOrder = 6},
+
             ];
 
         foreach (var item in entities)

+ 6 - 1
test/Hotline.Tests/Startup.cs

@@ -11,6 +11,7 @@ using Hotline.Application.Jobs;
 using Hotline.Application.StatisticalReport.CallReport;
 using Hotline.Authentications;
 using Hotline.CallCenter.Configs;
+using Hotline.CallCenter.Tels;
 using Hotline.Configurations;
 using Hotline.DI;
 using Hotline.EventBus;
@@ -18,6 +19,7 @@ using Hotline.File;
 using Hotline.FlowEngine.Workflows;
 using Hotline.Orders;
 using Hotline.Orders.DatabaseEventHandler;
+using Hotline.Pdf;
 using Hotline.Repository.SqlSugar;
 using Hotline.Repository.SqlSugar.DataPermissions;
 using Hotline.Repository.SqlSugar.Extensions;
@@ -33,6 +35,7 @@ using Mapster;
 using MediatR;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Identity;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
@@ -169,7 +172,7 @@ public class Startup
             services.AddScoped<RedPackController>();
             services.AddScoped<PushMessageController>();
             services.AddHttpContextAccessor();
-            services.AddScoped<ISessionContext, Controller.DefaultSessionContext>();
+            services.AddScoped<ISessionContext, Controller.TestSessionContext>();
             services.AddScoped<ISessionContextProvider, SessionContextProvider>();
             services.AddScoped<ICallApplication, XingTangCallApplication>();
             services.AddScoped<XingTangCallApplication>();
@@ -180,6 +183,8 @@ public class Startup
             services.AddXingTangDb(callCenterConfiguration.XingTang);
             services.AddScoped<IGuiderSystemService, TiqnQueService>();
             services.AddScoped<IWorkflowDomainService, WorkflowDomainService>();
+            services.AddScoped<IPdfManager, QuestPdfManager>();
+            services.AddScoped<ICallTelClient, XingTang.CallTelClient>();
 
             //ServiceLocator.Instance = services.BuildServiceProvider();
         }

+ 1 - 0
test/Hotline.Tests/TestBase.cs

@@ -204,5 +204,6 @@ public class TestBase
         ClaimsIdentity identity = new ClaimsIdentity(userClaims);
         var principal = new ClaimsPrincipal(identity);
         _httpContextAccessor.HttpContext.User = principal;
+        
     }
 }