xf 2 anni fa
parent
commit
22b695a857
73 ha cambiato i file con 940 aggiunte e 151 eliminazioni
  1. 7 7
      Hotline.sln
  2. 5 5
      src/Hotline.Api/Controllers/HomeController.cs
  3. 1 1
      src/Hotline.Api/Controllers/HotSpotController.cs
  4. 1 1
      src/Hotline.Api/Controllers/IdentityController.cs
  5. 6 9
      src/Hotline.Api/Controllers/PbxController.cs
  6. 1 1
      src/Hotline.Api/Controllers/RoleController.cs
  7. 1 1
      src/Hotline.Api/Controllers/SettingController.cs
  8. 1 1
      src/Hotline.Api/Controllers/TelController.cs
  9. 2 2
      src/Hotline.Api/Controllers/UserController.cs
  10. 1 1
      src/Hotline.Api/Hotline.Api.csproj
  11. 1 1
      src/Hotline.Api/Permissions/IPermissionManager.cs
  12. 1 1
      src/Hotline.Api/Realtimes/CallCenterHub.cs
  13. 1 1
      src/Hotline.Api/Realtimes/RealtimeService.cs
  14. 2 14
      src/Hotline.Api/StartupExtensions.cs
  15. 5 1
      src/Hotline.Api/StartupHelper.cs
  16. 1 1
      src/Hotline.Api/appsettings.Development.json
  17. 1 1
      src/Hotline.Api/appsettings.json
  18. 1 1
      src/Hotline.Application/Handlers/CallCenter/BaseHandler.cs
  19. 1 1
      src/Hotline.Application/Handlers/CallCenter/CallState/AlertExtToExtNotificationHandler.cs
  20. 1 1
      src/Hotline.Application/Handlers/CallCenter/CallState/AlertExtToOuterNotificationHandler.cs
  21. 1 1
      src/Hotline.Application/Handlers/CallCenter/CallState/DtmfNotificationHandler.cs
  22. 1 1
      src/Hotline.Application/Handlers/CallCenter/CallState/RingExtToExtNotificationHandler.cs
  23. 1 1
      src/Hotline.Application/Handlers/CallCenter/CallState/RingExtToOuterNotificationHandler.cs
  24. 1 1
      src/Hotline.Application/Handlers/CallCenter/CallState/RingVisitorToExtNotificationHandler.cs
  25. 3 3
      src/Hotline.Application/Handlers/CallCenter/ExtState/BusyNotificationHandler.cs
  26. 4 4
      src/Hotline.Application/Handlers/CallCenter/ExtState/IdleNotificationHandler.cs
  27. 2 2
      src/Hotline.Application/Handlers/CallCenter/ExtState/OfflineNotificationHandler.cs
  28. 2 2
      src/Hotline.Application/Handlers/CallCenter/ExtState/OnlineNotificationHandler.cs
  29. 1 1
      src/Hotline.Application/Handlers/CallCenter/FlowControl/AnsweredVisitorToExtNotificationHandler.cs
  30. 1 1
      src/Hotline.Application/Handlers/CallCenter/FlowControl/ByeVisitorAndExtNotificationHandler.cs
  31. 1 1
      src/Hotline.Application/Handlers/CallCenter/FlowControl/ByeVisitorAndMenuNotificationHandler.cs
  32. 1 1
      src/Hotline.Application/Handlers/CallCenter/FlowControl/ByeVisitorOffNotificationHandler.cs
  33. 1 1
      src/Hotline.Application/Handlers/CallCenter/FlowControl/EndOfAnnOuterToMenuNotificationHandler.cs
  34. 1 1
      src/Hotline.Application/Handlers/CallCenter/FlowControl/EndOfAnnVisitorToMenuNotificationHandler.cs
  35. 15 12
      src/Hotline.Application/Handlers/CallCenter/FlowControl/IncomingNotificationHandler.cs
  36. 1 1
      src/Hotline.Application/Handlers/CallCenter/FlowControl/InviteNotificationHandler.cs
  37. 1 1
      src/Hotline.Application/Handlers/CallCenter/FlowControl/QueueVisitorToGroupBusyNotificationHandler.cs
  38. 1 1
      src/Hotline.Application/Handlers/CallCenter/System/BootupNotificationHandler.cs
  39. 1 1
      src/Hotline.Application/Handlers/CallCenter/Transient/TransientOuterNotificationHandler.cs
  40. 1 1
      src/Hotline.Application/Hotline.Application.csproj
  41. 1 1
      src/Hotline.Application/Systems/TableAccessLevelInitialService.cs
  42. 1 1
      src/Hotline.CacheManager/CallCacheManager.cs
  43. 1 1
      src/Hotline.CacheManager/DefaultTypedCache.cs
  44. 205 0
      src/Hotline.EasyCaching/DefaultTypedCache.cs
  45. 21 0
      src/Hotline.EasyCaching/Hotline.EasyCaching.csproj
  46. 77 0
      src/Hotline.EasyCaching/StartupExtensions.cs
  47. 1 1
      src/Hotline.NewRock/DeviceManager.cs
  48. 1 1
      src/Hotline.Repository.SqlSugar/DataPermissions/DataPermissionManager.cs
  49. 0 9
      src/Hotline/Caches/IIvrCacheManager.cs
  50. 1 1
      src/Hotline/Caching/Interfaces/ICallCacheManager.cs
  51. 9 0
      src/Hotline/Caching/Interfaces/IIvrCacheManager.cs
  52. 1 1
      src/Hotline/Caching/Interfaces/IRolePermissionsCacheManager.cs
  53. 1 1
      src/Hotline/Caching/Interfaces/ISysDicDataCacheManager.cs
  54. 3 4
      src/Hotline/Caching/Interfaces/ISystemSettingCacheManager.cs
  55. 1 1
      src/Hotline/Caching/Interfaces/ITableAccessLevelCacheManager.cs
  56. 1 1
      src/Hotline/Caching/Interfaces/ITelCacheManager.cs
  57. 1 1
      src/Hotline/Caching/Interfaces/IUserCacheManager.cs
  58. 55 0
      src/Hotline/Caching/Services/BlacklistManager.cs
  59. 66 0
      src/Hotline/Caching/Services/CallCacheManager.cs
  60. 24 0
      src/Hotline/Caching/Services/IvrCacheManager.cs
  61. 41 0
      src/Hotline/Caching/Services/RolePermissionsCacheManager.cs
  62. 28 0
      src/Hotline/Caching/Services/SysDicDataCacheManager.cs
  63. 38 0
      src/Hotline/Caching/Services/SystemSettingCacheManager.cs
  64. 42 0
      src/Hotline/Caching/Services/TableAccessLevelCacheManager.cs
  65. 62 0
      src/Hotline/Caching/Services/TelCacheManager.cs
  66. 113 0
      src/Hotline/Caching/Services/UserCacheManager.cs
  67. 1 1
      src/Hotline/CallCenter/BlackLists/IBlacklistDomainService.cs
  68. 9 9
      src/Hotline/CallCenter/Ivrs/IvrDomainService.cs
  69. 2 2
      src/Hotline/CallCenter/Tels/TelDomainService.cs
  70. 4 4
      src/Hotline/Orders/OrderDomainService.cs
  71. 1 1
      src/Hotline/Users/UserDomainService.cs
  72. 44 20
      src/XF.Domain/Cache/ITypedCache.cs
  73. 2 0
      src/XF.Domain/Dependency/DependencyInjectionExtensions.cs

+ 7 - 7
Hotline.sln

@@ -25,8 +25,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.Application.Contrac
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XF.Domain.Repository", "src\XF.Domain.Repository\XF.Domain.Repository.csproj", "{651A0411-0D52-4B15-9A08-E0692FFE5FA2}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.CacheManager", "src\Hotline.CacheManager\Hotline.CacheManager.csproj", "{7D9CBBE9-9BD9-4397-8BBD-CA2C05BA47D4}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.Repository.SqlSugar", "src\Hotline.Repository.SqlSugar\Hotline.Repository.SqlSugar.csproj", "{AD448062-EE81-4FA9-A39D-AECBF48357CB}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.Api", "src\Hotline.Api\Hotline.Api.csproj", "{EE8D37CB-E40B-4513-A63F-06119BC192E7}"
@@ -37,6 +35,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NewRock.Sdk", "src\NewRock.
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XF.Domain", "src\XF.Domain\XF.Domain.csproj", "{48BA96D1-3EC3-46EA-AB48-EE24DBCB63FC}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.EasyCaching", "src\Hotline.EasyCaching\Hotline.EasyCaching.csproj", "{1838BD32-9E29-4540-B90E-4C335EB69187}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -63,10 +63,6 @@ Global
 		{651A0411-0D52-4B15-9A08-E0692FFE5FA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{651A0411-0D52-4B15-9A08-E0692FFE5FA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{651A0411-0D52-4B15-9A08-E0692FFE5FA2}.Release|Any CPU.Build.0 = Release|Any CPU
-		{7D9CBBE9-9BD9-4397-8BBD-CA2C05BA47D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{7D9CBBE9-9BD9-4397-8BBD-CA2C05BA47D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{7D9CBBE9-9BD9-4397-8BBD-CA2C05BA47D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{7D9CBBE9-9BD9-4397-8BBD-CA2C05BA47D4}.Release|Any CPU.Build.0 = Release|Any CPU
 		{AD448062-EE81-4FA9-A39D-AECBF48357CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{AD448062-EE81-4FA9-A39D-AECBF48357CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{AD448062-EE81-4FA9-A39D-AECBF48357CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -87,6 +83,10 @@ Global
 		{48BA96D1-3EC3-46EA-AB48-EE24DBCB63FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{48BA96D1-3EC3-46EA-AB48-EE24DBCB63FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{48BA96D1-3EC3-46EA-AB48-EE24DBCB63FC}.Release|Any CPU.Build.0 = Release|Any CPU
+		{1838BD32-9E29-4540-B90E-4C335EB69187}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{1838BD32-9E29-4540-B90E-4C335EB69187}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{1838BD32-9E29-4540-B90E-4C335EB69187}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{1838BD32-9E29-4540-B90E-4C335EB69187}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -101,12 +101,12 @@ Global
 		{B77B8550-5947-4521-98E3-8B361211EFF6} = {C43AAC75-963F-41F1-A49C-41D831A55FC5}
 		{4148C990-5F62-4156-8B45-231D8BFB417B} = {C43AAC75-963F-41F1-A49C-41D831A55FC5}
 		{651A0411-0D52-4B15-9A08-E0692FFE5FA2} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
-		{7D9CBBE9-9BD9-4397-8BBD-CA2C05BA47D4} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{AD448062-EE81-4FA9-A39D-AECBF48357CB} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{EE8D37CB-E40B-4513-A63F-06119BC192E7} = {25C73963-4D5E-4654-804A-D2E2D360134B}
 		{03309E0B-FB0B-43E8-99C6-B5372373C0CC} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{4695CC01-5C32-44B2-BC41-3280D20B0584} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{48BA96D1-3EC3-46EA-AB48-EE24DBCB63FC} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
+		{1838BD32-9E29-4540-B90E-4C335EB69187} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {4B8EA790-BD13-4422-8D63-D6DBB77B823F}

+ 5 - 5
src/Hotline.Api/Controllers/HomeController.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.BlackLists;
 using Hotline.CallCenter.Calls;
 using Hotline.CallCenter.Ivrs;
@@ -183,10 +183,10 @@ public class HomeController : BaseController
 
     
 
-    /// <summary>
-    /// 获取当前用户所有按钮
-    /// </summary>
-    /// <returns></returns>
+    ///// <summary>
+    ///// 获取当前用户所有按钮
+    ///// </summary>
+    ///// <returns></returns>
     //[AllowAnonymous]
     //[HttpGet("get-my-auth-button")]
     //public async Task<IReadOnlyList<string>> GetUserAuthorityButtonByToken()

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

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Settings;
 using Hotline.Settings.Hotspots;

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

@@ -1,7 +1,7 @@
 using System.Security.Cryptography;
 using System.Text;
 using Hotline.Application.Identity;
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.Settings;
 using Hotline.Share.Dtos.Identity;
 using Microsoft.AspNetCore.Authorization;

+ 6 - 9
src/Hotline.Api/Controllers/PbxController.cs

@@ -1,14 +1,10 @@
 using Hotline.Application.FlowEngine;
-using Hotline.CacheManager;
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.CallCenter.Devices;
 using Hotline.CallCenter.Ivrs;
-using Hotline.CallCenter.Manage;
 using Hotline.CallCenter.Tels;
-using Hotline.FlowEngine.Definitions;
 using Hotline.Permissions;
-using Hotline.Repository.SqlSugar.CallCenter;
 using Hotline.Settings;
 using Hotline.Share.Dtos.CallCenter;
 using Hotline.Share.Dtos.FlowEngine;
@@ -17,7 +13,6 @@ using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Requests;
 using Hotline.Users;
 using MapsterMapper;
-using Microsoft.AspNetCore.Identity;
 using Microsoft.AspNetCore.Mvc;
 using SqlSugar;
 using XF.Domain.Authentications;
@@ -323,7 +318,7 @@ namespace Hotline.Api.Controllers
             var toWork = _userCacheManager.GetWorkByTel(dto.TelNo);
             if (toWork is null)
                 throw UserFriendlyException.SameMessage("转接分机未进行工作");
-            var tel =await _deviceManager.QueryTelAsync(dto.TelNo,HttpContext.RequestAborted);
+            var tel = await _deviceManager.QueryTelAsync(dto.TelNo, HttpContext.RequestAborted);
             if (tel.TelStatus != ETelStatus.Ready)
                 throw UserFriendlyException.SameMessage("被叫分机不在线或正在通话中");
             await _deviceManager.ExtToExtAsync(work.TelNo, dto.TelNo, HttpContext.RequestAborted);
@@ -780,7 +775,7 @@ namespace Hotline.Api.Controllers
             {
                 throw new UserFriendlyException("未找到当前通话");
             }
-            if (call.CallDirection!= ECallDirection.In)
+            if (call.CallDirection != ECallDirection.In)
             {
                 throw new UserFriendlyException("当前通话不是来电,不能发送评价邀请");
             }
@@ -797,7 +792,9 @@ namespace Hotline.Api.Controllers
             if (correct is null)
                 throw new UserFriendlyException("系统未配置评价,请联系管理员");
             //检查是否有评价录音配置
-            var ivrList = _ivrCacheManager.GetIvrs();
+            var ivrList = await _ivrCacheManager.GetIvrsAsync(HttpContext.RequestAborted);
+            if (!ivrList.Any())
+                throw new UserFriendlyException("未查到任何ivr配置");
             var ivr = ivrList.First(x => x.IvrCategoryId == correct.ReturnValue && x.IsRoot);
 
             _logger.LogInformation("transfer to ivr.no:{ivrNo}", ivr.No);

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

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.Identity.Roles;
 using Hotline.Permissions;
 using Hotline.Repository.SqlSugar;

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

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Manage;
 using Hotline.Permissions;
 using Hotline.Settings;

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

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.CallCenter.Devices;
 using Hotline.Share.Dtos.CallCenter;

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

@@ -1,5 +1,4 @@
-using Hotline.Caches;
-using Hotline.CallCenter.Tels;
+using Hotline.CallCenter.Tels;
 using Hotline.Identity.Accounts;
 using Hotline.Identity.Roles;
 using Hotline.Permissions;
@@ -18,6 +17,7 @@ using Microsoft.Extensions.Options;
 using SqlSugar;
 using XF.Domain.Options;
 using XF.Utility.EnumExtensions;
+using Hotline.Caching.Interfaces;
 
 namespace Hotline.Api.Controllers;
 

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

@@ -5,7 +5,7 @@
     <Nullable>enable</Nullable>
     <ImplicitUsings>enable</ImplicitUsings>
     <GenerateDocumentationFile>True</GenerateDocumentationFile>
-    <NoWarn>$(NoWarn);1591</NoWarn>
+    <NoWarn>$(NoWarn);1591;8618;1803</NoWarn>
   </PropertyGroup>
 
   <ItemGroup>

+ 1 - 1
src/Hotline.Api/Permissions/IPermissionManager.cs

@@ -4,8 +4,8 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
-using Hotline.Caches;
 using XF.Domain.Dependency;
+using Hotline.Caching.Interfaces;
 
 namespace Hotline.Permissions
 {

+ 1 - 1
src/Hotline.Api/Realtimes/CallCenterHub.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.Users;
 using Microsoft.AspNetCore.SignalR;
 using XF.Domain.Authentications;

+ 1 - 1
src/Hotline.Api/Realtimes/RealtimeService.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.Realtimes;
 using Hotline.Share.Dtos.Realtime;
 using Microsoft.AspNetCore.SignalR;

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

@@ -1,32 +1,20 @@
-using System.IdentityModel.Tokens.Jwt;
-using System.Reflection;
-using FluentValidation;
+using FluentValidation;
 using FluentValidation.AspNetCore;
 using Hotline.Api.Realtimes;
 using Hotline.Application;
 using Hotline.Application.Contracts;
-using Hotline.Application.Contracts.Configurations;
-using Hotline.CacheManager;
 using Hotline.CallCenter.Devices;
+using Hotline.EasyCaching;
 using Hotline.Identity.Accounts;
 using Hotline.NewRock;
 using Hotline.Permissions;
-using Hotline.Repository.SqlSugar;
 using Hotline.Repository.SqlSugar.Extensions;
-using Hotline.Settings;
-using Mapster;
-using MapsterMapper;
-using MediatR;
-using Microsoft.AspNetCore.Authentication.JwtBearer;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Identity;
-using Microsoft.IdentityModel.Tokens;
-using Microsoft.OpenApi.Models;
 using Serilog;
 using XF.Domain.Dependency;
 using XF.Domain.Filters;
 using XF.Domain.Options;
-using XF.Domain.Password;
 using XF.Utility.MQ;
 
 namespace Hotline.Api;

+ 5 - 1
src/Hotline.Api/StartupHelper.cs

@@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Authentication.JwtBearer;
 using Microsoft.IdentityModel.Tokens;
 using Microsoft.OpenApi.Models;
 using XF.Domain.Entities;
+using XF.Domain.Exceptions;
 using XF.Domain.Options;
 
 namespace Hotline.Api
@@ -195,7 +196,10 @@ namespace Hotline.Api
         /// <returns></returns>
         public static IServiceCollection RegisterSignalR(this IServiceCollection services, ConfigurationManager configuration)
         {
-            services.AddSignalR().AddStackExchangeRedis(configuration.GetConnectionString("Redis"), options =>
+            var connectionString = configuration.GetConnectionString("Redis");
+            if (string.IsNullOrEmpty(connectionString))
+                throw new UserFriendlyException("未配置signalR的redis连接");
+            services.AddSignalR().AddStackExchangeRedis(connectionString, options =>
             {
                 options.Configuration.ChannelPrefix = "callcenter:signalR:";
             });

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

@@ -55,7 +55,7 @@
   },
   "ConnectionStrings": {
     "Hotline": "PORT=5432;DATABASE=hotline;HOST=192.168.100.121;PASSWORD=fengwo11!!;USER ID=dev;",
-    "Redis": "192.168.100.223:6379",
+    "Redis": "192.168.100.223",
     "MongoDB": "mongodb://192.168.100.121:27017"
   },
   "Swagger": true,

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

@@ -81,7 +81,7 @@
   },
   "ConnectionStrings": {
     "Hotline": " PORT=5432;DATABASE=hotline;HOST=192.168.100.121;PASSWORD=fengwo11!!;USER ID=dev;",
-    "Redis": "192.168.100.223:6379",
+    "Redis": "192.168.100.223",
     "MongoDB": "mongodb://192.168.100.121:27017"
   },
   "Swagger": true,

+ 1 - 1
src/Hotline.Application/Handlers/CallCenter/BaseHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.CallCenter.Devices;
 using Hotline.Share.Dtos.CallCenter;

+ 1 - 1
src/Hotline.Application/Handlers/CallCenter/CallState/AlertExtToExtNotificationHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.Share.Notifications;
 using MediatR;

+ 1 - 1
src/Hotline.Application/Handlers/CallCenter/CallState/AlertExtToOuterNotificationHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Notifications;

+ 1 - 1
src/Hotline.Application/Handlers/CallCenter/CallState/DtmfNotificationHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.CallCenter.Devices;
 using Hotline.CallCenter.Ivrs;

+ 1 - 1
src/Hotline.Application/Handlers/CallCenter/CallState/RingExtToExtNotificationHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.Realtimes;
 using Hotline.Share.Notifications;

+ 1 - 1
src/Hotline.Application/Handlers/CallCenter/CallState/RingExtToOuterNotificationHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Notifications;

+ 1 - 1
src/Hotline.Application/Handlers/CallCenter/CallState/RingVisitorToExtNotificationHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.Realtimes;
 using Hotline.Share.Enums.CallCenter;

+ 3 - 3
src/Hotline.Application/Handlers/CallCenter/ExtState/BusyNotificationHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Tels;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Notifications;
@@ -7,7 +7,7 @@ using XF.Domain.Cache;
 
 namespace Hotline.Application.Handlers.CallCenter.ExtState
 {
-    public class BusyNotificationHandler:INotificationHandler<BusyNotification>
+    public class BusyNotificationHandler : INotificationHandler<BusyNotification>
     {
         private readonly ITelRepository _telRepository;
         private readonly ITelCacheManager _telCacheManager;
@@ -25,7 +25,7 @@ namespace Hotline.Application.Handlers.CallCenter.ExtState
             var telModel = _telCacheManager.GetTel(notification.TelNo);
             telModel.TelStatus = ETelStatus.Active;
             //await _telRepository.UpdateAsync(telModel, cancellationToken);
-            _typedCache.Update(telModel.No,x=>telModel);
+            await _typedCache.SetAsync(telModel.No, telModel, cancellationToken: cancellationToken);
         }
     }
 }

+ 4 - 4
src/Hotline.Application/Handlers/CallCenter/ExtState/IdleNotificationHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Tels;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Notifications;
@@ -7,14 +7,14 @@ using XF.Domain.Cache;
 
 namespace Hotline.Application.Handlers.CallCenter.ExtState
 {
-    public class IdleNotificationHandler:INotificationHandler<IdleNotification>
+    public class IdleNotificationHandler : INotificationHandler<IdleNotification>
     {
         private readonly ITelRepository _telRepository;
         private readonly ITelCacheManager _telCacheManager;
         private readonly ITypedCache<Tel> _typedCache;
         public IdleNotificationHandler(ITelRepository telRepository, ITelCacheManager telCacheManager, ITypedCache<Tel> typedCache)
         {
-            _telRepository=telRepository;
+            _telRepository = telRepository;
             _telCacheManager = telCacheManager;
             _typedCache = typedCache;
         }
@@ -24,7 +24,7 @@ namespace Hotline.Application.Handlers.CallCenter.ExtState
             var telModel = _telCacheManager.GetTel(notification.TelNo);
             telModel.TelStatus = ETelStatus.Ready;
             //await _telRepository.UpdateAsync(telModel, cancellationToken);
-            _typedCache.Update(notification.TelNo, x => telModel);
+            _typedCache.Set(notification.TelNo, telModel);
         }
     }
 }

+ 2 - 2
src/Hotline.Application/Handlers/CallCenter/ExtState/OfflineNotificationHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Tels;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Notifications;
@@ -24,7 +24,7 @@ namespace Hotline.Application.Handlers.CallCenter.ExtState
             var telModel = _telCacheManager.GetTel(notification.TelNo);
             telModel.TelStatus = ETelStatus.Offline;
             await _telRepository.UpdateAsync(telModel, cancellationToken);
-            _typedCache.Update(notification.TelNo, x => telModel);
+            _typedCache.Set(notification.TelNo, telModel);
         }
 
     }

+ 2 - 2
src/Hotline.Application/Handlers/CallCenter/ExtState/OnlineNotificationHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Tels;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Notifications;
@@ -28,7 +28,7 @@ namespace Hotline.Application.Handlers.CallCenter.ExtState
             telModel.TelStatus = ETelStatus.Ready;
             telModel.RegisterIP = notification.RegisterIP;
             await _telRepository.UpdateAsync(telModel,cancellationToken);
-            _typedCache.Update(notification.TelNo, x => telModel);
+            _typedCache.Set(notification.TelNo, telModel);
         }
     }
 }

+ 1 - 1
src/Hotline.Application/Handlers/CallCenter/FlowControl/AnsweredVisitorToExtNotificationHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.Realtimes;
 using Hotline.Share.Enums.CallCenter;

+ 1 - 1
src/Hotline.Application/Handlers/CallCenter/FlowControl/ByeVisitorAndExtNotificationHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.Realtimes;
 using Hotline.Share.Enums.CallCenter;

+ 1 - 1
src/Hotline.Application/Handlers/CallCenter/FlowControl/ByeVisitorAndMenuNotificationHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.Realtimes;
 using Hotline.Share.Dtos.Realtime;

+ 1 - 1
src/Hotline.Application/Handlers/CallCenter/FlowControl/ByeVisitorOffNotificationHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.Realtimes;
 using Hotline.Share.Enums.CallCenter;

+ 1 - 1
src/Hotline.Application/Handlers/CallCenter/FlowControl/EndOfAnnOuterToMenuNotificationHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.CallCenter.Devices;
 using Hotline.CallCenter.Ivrs;

+ 1 - 1
src/Hotline.Application/Handlers/CallCenter/FlowControl/EndOfAnnVisitorToMenuNotificationHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.CallCenter.Devices;
 using Hotline.CallCenter.Ivrs;

+ 15 - 12
src/Hotline.Application/Handlers/CallCenter/FlowControl/IncomingNotificationHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.CallCenter.Devices;
 using Hotline.CallCenter.Ivrs;
@@ -14,6 +14,7 @@ using NewRock.Sdk.Transfer.Queue.Request;
 using Oracle.ManagedDataAccess.Types;
 using XF.Domain.Cache;
 using XF.Domain.Constants;
+using XF.Domain.Exceptions;
 
 namespace Hotline.Application.Handlers.CallCenter.FlowControl
 {
@@ -33,7 +34,7 @@ namespace Hotline.Application.Handlers.CallCenter.FlowControl
         private readonly ICallCacheManager _callCacheManager;
 
         public IncomingNotificationHandler(
-            ICallRepository callRepository, 
+            ICallRepository callRepository,
             ICallDetailRepository callDetailRepository,
             ISystemSettingCacheManager systemSettingCacheManager,
             IIvrCacheManager ivrCacheManager,
@@ -64,7 +65,7 @@ namespace Hotline.Application.Handlers.CallCenter.FlowControl
         {
             var model = await _callRepository.GetAsync(
                 x => x.ConversationId == notification.Visitor.Id && x.Trunk == notification.TrunkId &&
-                     x.FromNo == notification.Visitor.From && x.CreationTime >= DateTime.Now.AddHours(-2),true,x=>x.CreationTime, cancellationToken);
+                     x.FromNo == notification.Visitor.From && x.CreationTime >= DateTime.Now.AddHours(-2), true, x => x.CreationTime, cancellationToken);
             if (model != null)
             {
                 model.CallStatus = ECallStatus.Incoming;
@@ -87,13 +88,15 @@ namespace Hotline.Application.Handlers.CallCenter.FlowControl
                     var correct = GetCorrectIvr(notification.Visitor.To);
 
                     //var ivr = GetCorrectIvr();
-                   
+
 
                     switch (correct.eCorrectIvr)
                     {
                         case ECorrectIvr.Ivr:
-                            
-                            var ivrList = _ivrCacheManager.GetIvrs();
+
+                            var ivrList = await _ivrCacheManager.GetIvrsAsync(cancellationToken);
+                            if (!ivrList.Any())
+                                throw new UserFriendlyException("未查到任何ivr配置");
                             var ivr = ivrList.First(x => x.IvrCategoryId == correct.ReturnValue && x.IsRoot);
 
                             _logger.LogInformation("transfer to ivr.no: {ivrNo}", ivr.No);
@@ -150,12 +153,12 @@ namespace Hotline.Application.Handlers.CallCenter.FlowControl
 
         private CorrectIvr GetCorrectIvr(string to)
         {
-            var worktimeSettings = _worktimeCache.GetOrAdd("worktimesettings", d =>
+            var worktimeSettings = _worktimeCache.GetOrSet("worktimesettings", d =>
             {
-                var settings = _trunkIvrManagerRepository.QueryAsync(x=>x.IsEnable).GetAwaiter().GetResult();
+                var settings = _trunkIvrManagerRepository.QueryAsync(x => x.IsEnable).GetAwaiter().GetResult();
                 return settings;
-            }, ExpireMode.Absolute);
-            var correct = GetCorrectCategory(worktimeSettings.First(x=>x.TrunkId == to));
+            });
+            var correct = GetCorrectCategory(worktimeSettings.First(x => x.TrunkId == to));
             return correct;
         }
 
@@ -165,11 +168,11 @@ namespace Hotline.Application.Handlers.CallCenter.FlowControl
 
             public ECorrectIvr eCorrectIvr { get; set; }
         }
-        
+
 
         private CorrectIvr GetCorrectCategory(TrunkIvrManager settings)
         {
-            if (!settings.WorkDay.Any(x=>x.weekValue == ((int)DateTime.Now.DayOfWeek).ToString()))
+            if (!settings.WorkDay.Any(x => x.weekValue == ((int)DateTime.Now.DayOfWeek).ToString()))
                 return new CorrectIvr() { eCorrectIvr = settings.RestCategory != "" ? ECorrectIvr.Ivr : ECorrectIvr.Group, ReturnValue = settings.RestCategory != "" ? settings.RestCategory : settings.RestToGroup };
 
             var time = TimeOnly.FromDateTime(DateTime.Now);

+ 1 - 1
src/Hotline.Application/Handlers/CallCenter/FlowControl/InviteNotificationHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.BlackLists;
 using Hotline.CallCenter.Calls;
 using Hotline.CallCenter.Devices;

+ 1 - 1
src/Hotline.Application/Handlers/CallCenter/FlowControl/QueueVisitorToGroupBusyNotificationHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.Share.Notifications;
 using MediatR;

+ 1 - 1
src/Hotline.Application/Handlers/CallCenter/System/BootupNotificationHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Devices;
 using Hotline.CallCenter.Ivrs;
 using Hotline.CallCenter.Tels;

+ 1 - 1
src/Hotline.Application/Handlers/CallCenter/Transient/TransientOuterNotificationHandler.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Notifications;

+ 1 - 1
src/Hotline.Application/Hotline.Application.csproj

@@ -15,7 +15,7 @@
 
   <ItemGroup>
     <ProjectReference Include="..\Hotline.Application.Contracts\Hotline.Application.Contracts.csproj" />
-    <ProjectReference Include="..\Hotline.CacheManager\Hotline.CacheManager.csproj" />
+    <ProjectReference Include="..\Hotline.EasyCaching\Hotline.EasyCaching.csproj" />
     <ProjectReference Include="..\Hotline.NewRock\Hotline.NewRock.csproj" />
     <ProjectReference Include="..\Hotline.Repository.SqlSugar\Hotline.Repository.SqlSugar.csproj" />
     <ProjectReference Include="..\Hotline\Hotline.csproj" />

+ 1 - 1
src/Hotline.Application/Systems/TableAccessLevelInitialService.cs

@@ -3,7 +3,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.BlackLists;
 using Hotline.Settings;
 using Microsoft.Extensions.DependencyInjection;

+ 1 - 1
src/Hotline.CacheManager/CallCacheManager.cs

@@ -5,7 +5,7 @@ using XF.Domain.Dependency;
 
 namespace Hotline.CacheManager
 {
-    public class CallCacheManager:ICallCacheManager, IScopeDependency
+    public class CallCacheManager : ICallCacheManager, IScopeDependency
     {
         private readonly ITypedCache<List<Call>> _cacheCall;
         private const string CallKey = "CallQueue";

+ 1 - 1
src/Hotline.CacheManager/DefaultTypedCache.cs

@@ -6,7 +6,7 @@ using XF.Domain.Cache;
 
 namespace Hotline.CacheManager
 {
-    public class DefaultTypedCache<TValue> : ITypedCache<TValue>
+    public class DefaultTypedCache<TValue> //: ITypedCache<TValue>
         where TValue : class
     {
         private readonly ICacheManager<TValue> _cache;

+ 205 - 0
src/Hotline.EasyCaching/DefaultTypedCache.cs

@@ -0,0 +1,205 @@
+using System.Collections;
+using EasyCaching.Core;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using XF.Domain.Cache;
+
+namespace Hotline.EasyCaching
+{
+    public class DefaultTypedCache<TValue> : ITypedCache<TValue>
+    {
+        private readonly IServiceScopeFactory _serviceScopeFactory;
+        private readonly IHybridCachingProvider _hybridCaching;
+        private readonly IRedisCachingProvider _redisCaching;
+        private readonly IEasyCachingProvider _caching;
+        private readonly TimeSpan _expirationDefault = TimeSpan.FromDays(1);
+
+        public DefaultTypedCache(
+            IServiceScopeFactory serviceScopeFactory,
+            IHybridCachingProvider hybridCaching,
+            IRedisCachingProvider redisCaching,
+            IEasyCachingProvider caching
+            )
+        {
+            _serviceScopeFactory = serviceScopeFactory;
+            _hybridCaching = hybridCaching;
+            _redisCaching = redisCaching;
+            _caching = caching;
+        }
+
+        public void Set(string key, TValue value, TimeSpan? expiration = null) =>
+            _hybridCaching.Set(CombinePrefix(key), value, expiration.GetValueOrDefault(_expirationDefault));
+
+        public async Task SetAsync(string key, TValue value, TimeSpan? expiration = null, CancellationToken cancellationToken = default) =>
+            await _hybridCaching.SetAsync(CombinePrefix(key), value, expiration.GetValueOrDefault(_expirationDefault), cancellationToken);
+
+        public TValue? Get(string key) => _hybridCaching.Get<TValue>(CombinePrefix(key)).Value;
+
+        public async Task<TValue> GetAsync(string key, CancellationToken cancellationToken) =>
+            (await _hybridCaching.GetAsync<TValue>(CombinePrefix(key), cancellationToken)).Value;
+
+        public void Remove(string key) => _hybridCaching.Remove(CombinePrefix(key));
+
+        public async Task RemoveAsync(string key, CancellationToken cancellationToken) =>
+            await _hybridCaching.RemoveAsync(CombinePrefix(key), cancellationToken);
+
+        public bool Exists(string key) => _hybridCaching.Exists(CombinePrefix(key));
+
+        public async Task<bool> ExistsAsync(string key, CancellationToken cancellationToken) =>
+            await _hybridCaching.ExistsAsync(CombinePrefix(key), cancellationToken);
+
+        public bool TrySet(string key, TValue value, TimeSpan? expiration = null) =>
+            _hybridCaching.TrySet(CombinePrefix(key), value, expiration.GetValueOrDefault(_expirationDefault));
+
+        public async Task<bool> TrySetAsync(string key, TValue value, TimeSpan? expiration = null, CancellationToken cancellationToken = default) =>
+            await _hybridCaching.TrySetAsync(CombinePrefix(key), value, expiration.GetValueOrDefault(_expirationDefault), cancellationToken);
+
+        public void SetAll(IDictionary<string, TValue> values, TimeSpan? expiration = null)
+        {
+            var newDic = new Dictionary<string, TValue>();
+            foreach (var kvp in values)
+            {
+                newDic.Add(CombinePrefix(kvp.Key), kvp.Value);
+            }
+            _hybridCaching.SetAll(newDic, expiration.GetValueOrDefault(_expirationDefault));
+        }
+
+        public async Task SetAllAsync(IDictionary<string, TValue> values, TimeSpan? expiration = null, CancellationToken cancellationToken = default)
+        {
+            var newDic = new Dictionary<string, TValue>();
+            foreach (var kvp in values)
+            {
+                newDic.Add(CombinePrefix(kvp.Key), kvp.Value);
+            }
+
+            await _hybridCaching.SetAllAsync(newDic, expiration.GetValueOrDefault(_expirationDefault), cancellationToken);
+        }
+
+        public IDictionary<string, TValue> GetByPrefix()
+        {
+            var cacheValueDic = _caching.GetByPrefix<TValue>(CreateRegion());
+            if (cacheValueDic == null) return new Dictionary<string, TValue>();
+            return cacheValueDic.ToDictionary(cacheValue => cacheValue.Key, cacheValue => cacheValue.Value.Value);
+        }
+
+        public async Task<IDictionary<string, TValue>> GetByPrefixAsync(CancellationToken cancellationToken)
+        {
+            var cacheValueDic = await _caching.GetByPrefixAsync<TValue>(CreateRegion(), cancellationToken);
+            if (cacheValueDic == null) return new Dictionary<string, TValue>();
+            return cacheValueDic.ToDictionary(cacheValue => cacheValue.Key, cacheValue => cacheValue.Value.Value);
+        }
+
+        public IReadOnlyList<TValue> GetListByPrefix()
+        {
+            var cacheValueDic = _caching.GetByPrefix<TValue>(CreateRegion());
+            if (cacheValueDic == null) return new List<TValue>();
+            return cacheValueDic.Values.Select(d => d.Value).ToList();
+        }
+
+        public async Task<IReadOnlyList<TValue>> GetListByPrefixAsync(CancellationToken cancellationToken)
+        {
+            var cacheValueDic = await _caching.GetByPrefixAsync<TValue>(CreateRegion(), cancellationToken);
+            if (cacheValueDic == null) return new List<TValue>();
+            return cacheValueDic.Values.Select(d => d.Value).ToList();
+        }
+
+        /// <summary>
+        /// get the cache value, if cache not exists, set the value to cache then return
+        /// </summary>
+        public TValue GetOrSet(string key, TValue value, TimeSpan? expiration = null)
+        {
+            var cacheValue = Get(key);
+            if (cacheValue != null) return cacheValue;
+
+            Set(key, value, expiration);
+            return value;
+
+        }
+
+        public TValue? GetOrSet(string key, Func<string, TValue> valueFactory, TimeSpan? expiration = null)
+        {
+            var cacheValue = Get(key);
+            if (cacheValue != null) return cacheValue;
+
+            var value = valueFactory.Invoke(key);
+            if (value == null) return default;
+            Set(key, value, expiration);
+            return value;
+        }
+
+        public async Task<TValue> GetOrSetAsync(string key, TValue value, TimeSpan? expiration = null, CancellationToken cancellationToken = default)
+        {
+            var cacheValue = await GetAsync(key, cancellationToken);
+            if (cacheValue != null) return cacheValue;
+
+            await SetAsync(key, value, expiration, cancellationToken);
+            return value;
+        }
+
+        public async Task<TValue?> GetOrSetAsync(string key, Func<string, TValue> valueFactory, TimeSpan? expiration = null, CancellationToken cancellationToken = default)
+        {
+            var cacheValue = await GetAsync(key, cancellationToken);
+            if (cacheValue != null) return cacheValue;
+
+            var value = valueFactory.Invoke(key);
+            if (value == null) return default;
+            await SetAsync(key, value, expiration, cancellationToken);
+            return value;
+        }
+
+        public string CombinePrefix(string key) => CreateRegion() + key;
+
+
+        private string CreateRegion()
+        {
+            var valueType = typeof(TValue);
+            //if (valueType.IsGenericType)
+            //{
+            //    throw new NotSupportedException("TCachedValue can not be a Generic Type");
+            //}
+
+            //if (valueType == typeof(string))
+            //{
+            //    throw new NotSupportedException("TCachedValue can not be a string");
+            //}
+
+            if (string.IsNullOrWhiteSpace(valueType.FullName))
+            {
+                throw new NotSupportedException("FullName of type TCachedValue can not be null");
+            }
+
+            using var scope = _serviceScopeFactory.CreateScope();
+            var options = scope.ServiceProvider.GetRequiredService<IOptionsSnapshot<CacheOptions>>();
+            var prefix = options.Value.Prefix;
+
+            //if (valueType.TryGetCustomAttribute<CacheAttribute>(false, out var attr))
+            //{
+            //    if (!string.IsNullOrEmpty(attr.Region))
+            //    {
+            //        return attr.Region;
+            //    }
+            //}
+
+            string region;
+
+            if (valueType.IsGenericType && valueType.GetInterfaces().Any(d => d == typeof(IEnumerable)))
+            {
+                region = "Collection";
+            }
+            else
+            {
+                region = valueType.FullName.Replace('.', ':');
+            }
+
+            if (!string.IsNullOrEmpty(prefix))
+            {
+                prefix += ':';
+                if (!region.StartsWith(prefix))
+                {
+                    region = prefix + region;
+                }
+            }
+            return region;
+        }
+    }
+}

+ 21 - 0
src/Hotline.EasyCaching/Hotline.EasyCaching.csproj

@@ -0,0 +1,21 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="EasyCaching.Bus.Redis" Version="1.9.0" />
+    <PackageReference Include="EasyCaching.HybridCache" Version="1.9.0" />
+    <PackageReference Include="EasyCaching.InMemory" Version="1.9.0" />
+    <PackageReference Include="EasyCaching.Redis" Version="1.9.0" />
+    <PackageReference Include="EasyCaching.Serialization.SystemTextJson" Version="1.9.0" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\Hotline\Hotline.csproj" />
+  </ItemGroup>
+
+</Project>

+ 77 - 0
src/Hotline.EasyCaching/StartupExtensions.cs

@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using System.Threading.Tasks;
+using EasyCaching.Core.Configurations;
+using EasyCaching.Serialization.SystemTextJson.Configurations;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using XF.Domain.Cache;
+
+namespace Hotline.EasyCaching
+{
+    public static class StartupExtensions
+    {
+        public static IServiceCollection AddCache(this IServiceCollection services, Action<CacheOptions> action)
+        {
+            var options = new CacheOptions();
+            action(options);
+
+            services.AddEasyCaching(d =>
+            {
+                d.WithSystemTextJson("xjson");
+
+                // local
+                d.UseInMemory("m1");
+
+                d.UseRedis(config =>
+                {
+                    config.DBConfig.Endpoints.Add(new ServerEndPoint(options.ConnectionString, options.Port));
+                    config.DBConfig.Database = options.Database;
+                    config.SerializerName = "xjson";
+                }, "r1");
+
+                // combine local and distributed
+                d.UseHybrid(config =>
+                    {
+                        config.TopicName = "hotline-topic";
+                        config.EnableLogging = false;
+
+                        // specify the local cache provider name after v0.5.4
+                        config.LocalCacheProviderName = "m1";
+                        // specify the distributed cache provider name after v0.5.4
+                        config.DistributedCacheProviderName = "r1";
+                    }, "h1")
+                    // use redis bus
+                    .WithRedisBus(busConf =>
+                    {
+                        busConf.Endpoints.Add(new ServerEndPoint(options.ConnectionString, options.Port));
+
+                        // do not forget to set the SerializerName for the bus here !!
+                        busConf.SerializerName = "xjson";
+                    });
+            })
+                    .Configure(action)
+                    .AddSingleton(typeof(ITypedCache<>), typeof(DefaultTypedCache<>))
+                ;
+
+            return services;
+        }
+    }
+
+    public class CacheOptions
+    {
+        public string ConnectionString { get; set; }
+
+        public int Port { get; set; }
+
+        public int Database { get; set; } = 1;
+
+        public string Prefix { get; set; }
+
+        public int MaxRdSecond { get; set; }
+    }
+}

+ 1 - 1
src/Hotline.NewRock/DeviceManager.cs

@@ -18,7 +18,7 @@ using XF.Domain.Exceptions;
 using Group = NewRock.Sdk.Control.Request.Group;
 using Hotline.Share.Dtos.CallCenter;
 using Hotline.Share.Enums.CallCenter;
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 
 namespace Hotline.NewRock
 {

+ 1 - 1
src/Hotline.Repository.SqlSugar/DataPermissions/DataPermissionManager.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.SeedData;
 using Hotline.Settings;
 using Hotline.Share.Enums.Settings;

+ 0 - 9
src/Hotline/Caches/IIvrCacheManager.cs

@@ -1,9 +0,0 @@
-using Hotline.CallCenter.Ivrs;
-
-namespace Hotline.Caches
-{
-    public interface IIvrCacheManager
-    {
-        IReadOnlyList<Ivr> GetIvrs();
-    }
-}

+ 1 - 1
src/Hotline/Caches/ICallCacheManager.cs → src/Hotline/Caching/Interfaces/ICallCacheManager.cs

@@ -5,7 +5,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace Hotline.Caches
+namespace Hotline.Caching.Interfaces
 {
     public interface ICallCacheManager
     {

+ 9 - 0
src/Hotline/Caching/Interfaces/IIvrCacheManager.cs

@@ -0,0 +1,9 @@
+using Hotline.CallCenter.Ivrs;
+
+namespace Hotline.Caching.Interfaces
+{
+    public interface IIvrCacheManager
+    {
+        Task<IReadOnlyList<Ivr>> GetIvrsAsync(CancellationToken cancellationToken);
+    }
+}

+ 1 - 1
src/Hotline/Caches/IRolePermissionsCacheManager.cs → src/Hotline/Caching/Interfaces/IRolePermissionsCacheManager.cs

@@ -4,7 +4,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace Hotline.Caches
+namespace Hotline.Caching.Interfaces
 {
     public interface IRolePermissionsCacheManager
     {

+ 1 - 1
src/Hotline/Caches/ISysDicDataCacheManager.cs → src/Hotline/Caching/Interfaces/ISysDicDataCacheManager.cs

@@ -5,7 +5,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace Hotline.Caches
+namespace Hotline.Caching.Interfaces
 {
     public interface ISysDicDataCacheManager
     {

+ 3 - 4
src/Hotline/Caches/ISystemSettingCacheManager.cs → src/Hotline/Caching/Interfaces/ISystemSettingCacheManager.cs

@@ -1,12 +1,11 @@
-
-using Hotline.Settings;
+using Hotline.Settings;
 
-namespace Hotline.Caches
+namespace Hotline.Caching.Interfaces
 {
     public interface ISystemSettingCacheManager
     {
         SystemSetting GetSetting(string code);
 
-        bool DelSystemSetting(string code);
+        void DelSystemSetting(string code);
     }
 }

+ 1 - 1
src/Hotline/Caches/ITableAccessLevelCacheManager.cs → src/Hotline/Caching/Interfaces/ITableAccessLevelCacheManager.cs

@@ -5,7 +5,7 @@ using System.Text;
 using System.Threading.Tasks;
 using Hotline.Share.Enums.Settings;
 
-namespace Hotline.Caches
+namespace Hotline.Caching.Interfaces
 {
     public interface ITableAccessLevelCacheManager
     {

+ 1 - 1
src/Hotline/Caches/ITelCacheManager.cs → src/Hotline/Caching/Interfaces/ITelCacheManager.cs

@@ -1,6 +1,6 @@
 using Hotline.CallCenter.Tels;
 
-namespace Hotline.Caches
+namespace Hotline.Caching.Interfaces
 {
     public interface ITelCacheManager
     {

+ 1 - 1
src/Hotline/Caches/IUserCacheManager.cs → src/Hotline/Caching/Interfaces/IUserCacheManager.cs

@@ -1,6 +1,6 @@
 using Hotline.Users;
 
-namespace Hotline.Caches
+namespace Hotline.Caching.Interfaces
 {
     public interface IUserCacheManager
     {

+ 55 - 0
src/Hotline/Caching/Services/BlacklistManager.cs

@@ -0,0 +1,55 @@
+//using Hotline.CallCenter.BlackLists;
+//using Microsoft.Extensions.DependencyInjection;
+//using Microsoft.Extensions.Hosting;
+
+//namespace Hotline.CacheManager
+//{
+//    public class BlacklistManager : BackgroundService
+//    {
+//        //private readonly ICacheManager<Blacklist> _cacheManager;
+//        private readonly IServiceScopeFactory _serviceScopeFactory;
+
+//        public BlacklistManager(IServiceScopeFactory serviceScopeFactory)
+//        {
+//            _serviceScopeFactory = serviceScopeFactory;
+
+//            //cacheManager.OnAdd += (sender, args) => { Console.WriteLine(args.ToString()); };
+
+//            //cacheManager.OnRemove += (sender, args) => { Console.WriteLine(args.ToString()); };
+
+//            //cacheManager.OnClear += (sender, args) => { Console.WriteLine(args.ToString()); };
+
+//            //cacheManager.OnRemoveByHandle += (sender, args) => { Console.WriteLine(args.ToString()); };
+
+//            //cacheManager.OnPut += (sender, args) => { Console.WriteLine(args.ToString()); };
+
+//            //cacheManager.OnUpdate += (sender, args) => { Console.WriteLine(args.ToString()); };
+
+//        }
+
+//        /// <summary>
+//        /// This method is called when the <see cref="T:Microsoft.Extensions.Hosting.IHostedService" /> starts. The implementation should return a task that represents
+//        /// the lifetime of the long running operation(s) being performed.
+//        /// </summary>
+//        /// <param name="stoppingToken">Triggered when <see cref="M:Microsoft.Extensions.Hosting.IHostedService.StopAsync(System.Threading.CancellationToken)" /> is called.</param>
+//        /// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the long running operations.</returns>
+//        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+//        {
+//            var time = TimeSpan.FromMinutes(5);
+//            await Task.Delay(time, stoppingToken);
+//            while (!stoppingToken.IsCancellationRequested)
+//            {
+//                using var scope = _serviceScopeFactory.CreateScope();
+//                var blacklistRepository = scope.ServiceProvider.GetService<IBlacklistRepository>();
+//                var expiredBlackListItems =
+//                    await blacklistRepository!.QueryAsync(d => d.Expired <= DateTime.Now);
+//                foreach (var blacklistItem in expiredBlackListItems)
+//                {
+//                    await blacklistRepository.RemoveAsync(blacklistItem, true, stoppingToken);
+//                }
+
+//                await Task.Delay(time, stoppingToken);
+//            }
+//        }
+//    }
+//}

+ 66 - 0
src/Hotline/Caching/Services/CallCacheManager.cs

@@ -0,0 +1,66 @@
+using Hotline.Caching.Interfaces;
+using Hotline.CallCenter.Calls;
+using XF.Domain.Cache;
+using XF.Domain.Dependency;
+
+namespace Hotline.Caching.Services
+{
+    public class CallCacheManager : ICallCacheManager, IScopeDependency
+    {
+        private readonly ITypedCache<List<Call>> _cacheCall;
+        private const string CallKey = "CallQueue";
+
+        public CallCacheManager(ITypedCache<List<Call>> cacheCall)
+        {
+            _cacheCall = cacheCall;
+        }
+
+        /// <summary>
+        /// 新增或修改队列
+        /// </summary>
+        /// <param name="list"></param>
+        public void AddCallCache(Call call)
+        {
+            var list = GetCallQueueList();
+            if (list == null)
+            {
+                list = new List<Call>();
+            }
+            list.Add(call);
+            _cacheCall.Set(CallKey, list);
+        }
+
+        /// <summary>
+        /// 获取队列
+        /// </summary>
+        /// <returns></returns>
+        public List<Call>? GetCallQueueList()
+        {
+            var list = _cacheCall.GetOrSet(CallKey, k =>
+            {
+                return new List<Call>();
+            });
+            return list;
+        }
+
+
+        /// <summary>
+        /// 删除队列
+        /// </summary>
+        /// <param name="id"></param>
+        /// <exception cref="NotImplementedException"></exception>
+        public void RemoveCallCache(string id)
+        {
+            var list = _cacheCall.Get(CallKey)?.ToList();
+            if (list != null)
+            {
+                var model = list.FirstOrDefault(x => x.Id == id);
+                if (model != null)
+                {
+                    list.Remove(model);
+                    _cacheCall.Set(CallKey, list);
+                }
+            }
+        }
+    }
+}

+ 24 - 0
src/Hotline/Caching/Services/IvrCacheManager.cs

@@ -0,0 +1,24 @@
+using Hotline.Caching.Interfaces;
+using Hotline.CallCenter.Ivrs;
+using XF.Domain.Cache;
+using XF.Domain.Dependency;
+
+namespace Hotline.Caching.Services;
+
+public class IvrCacheManager : IIvrCacheManager, IScopeDependency
+{
+    private readonly ITypedCache<IReadOnlyList<Ivr>> _ivrCache;
+    private readonly IIvrRepository _ivrRepository;
+
+    public IvrCacheManager(ITypedCache<IReadOnlyList<Ivr>> ivrCache, IIvrRepository ivrRepository)
+    {
+        _ivrCache = ivrCache;
+        _ivrRepository = ivrRepository;
+    }
+
+    public async Task<IReadOnlyList<Ivr>> GetIvrsAsync(CancellationToken cancellationToken)
+    {
+        var ivrs = await _ivrCache.GetOrSetAsync(Ivr.Key, d => _ivrRepository.QueryAsync().GetAwaiter().GetResult(), cancellationToken: cancellationToken);
+        return ivrs ?? new List<Ivr>();
+    }
+}

+ 41 - 0
src/Hotline/Caching/Services/RolePermissionsCacheManager.cs

@@ -0,0 +1,41 @@
+using Hotline.Caching.Interfaces;
+using Hotline.Settings;
+using XF.Domain.Cache;
+using XF.Domain.Dependency;
+
+namespace Hotline.Caching.Services
+{
+    public class RolePermissionsCacheManager : IRolePermissionsCacheManager, IScopeDependency
+    {
+        private const string RolePermissionCacheKey = "RolePermissionCache:";
+        private readonly ITypedCache<IReadOnlyList<string>> _cache;
+        private readonly ISystemAuthorityRepository _systemAuthorityRepository;
+
+        public RolePermissionsCacheManager(ITypedCache<IReadOnlyList<string>> cache, ISystemAuthorityRepository systemAuthorityRepository)
+        {
+            _cache = cache;
+            _systemAuthorityRepository = systemAuthorityRepository;
+        }
+
+        public IReadOnlyList<string> GetPermissions(string role)
+        {
+            return _cache.GetOrSet(GetKey(role), d =>
+                {
+                    //todo 加上全局配置,对未开启模块过滤
+                    var systemAuth = _systemAuthorityRepository.GetAsync(d => d.RoleCode == role).GetAwaiter()
+                        .GetResult();
+                    return systemAuth?.GetPermissions() ?? new();
+                }
+            ) ?? new List<string>();
+        }
+
+        public void RemovePermissions(string role)
+        {
+            _cache.Remove(GetKey(role));
+        }
+
+
+        private string GetKey(string role) =>
+            $"{RolePermissionCacheKey}{role}";
+    }
+}

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

@@ -0,0 +1,28 @@
+using Hotline.Caching.Interfaces;
+using Hotline.Settings;
+using XF.Domain.Cache;
+using XF.Domain.Dependency;
+
+namespace Hotline.Caching.Services
+{
+    public class SysDicDataCacheManager : ISysDicDataCacheManager, IScopeDependency
+    {
+        private readonly ISysDicDataRepository _sysDicDataRepository;
+        private readonly ITypedCache<IReadOnlyList<SysDicData>> _cacheSysDicData;
+
+        public SysDicDataCacheManager(ISysDicDataRepository sysDicDataRepository, ITypedCache<IReadOnlyList<SysDicData>> cacheSysDicData)
+        {
+            _sysDicDataRepository = sysDicDataRepository;
+            _cacheSysDicData = cacheSysDicData;
+        }
+
+        public IReadOnlyList<SysDicData> GetSysDicDataCache(string code)
+        {
+            var sysDicDataList = _cacheSysDicData.GetOrSet(code, k =>
+            {
+                return _sysDicDataRepository.Queryable().Where(x => x.DicTypeCode == code).ToTreeAsync(x => x.Children, it => it.ParentId, "").GetAwaiter().GetResult();
+            });
+            return sysDicDataList;
+        }
+    }
+}

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

@@ -0,0 +1,38 @@
+using Hotline.Caching.Interfaces;
+using Hotline.Settings;
+using XF.Domain.Cache;
+using XF.Domain.Dependency;
+using XF.Domain.Exceptions;
+
+namespace Hotline.Caching.Services
+{
+    public class SystemSettingCacheManager : ISystemSettingCacheManager, IScopeDependency
+    {
+
+        private readonly ITypedCache<SystemSetting> _cacheSystemSetting;
+        private readonly ISystemSettingRepository _systemSettingRepository;
+
+        public SystemSettingCacheManager(ITypedCache<SystemSetting> cacheSystemSetting, ISystemSettingRepository systemSettingRepository)
+        {
+            _cacheSystemSetting = cacheSystemSetting;
+            _systemSettingRepository = systemSettingRepository;
+        }
+
+        public SystemSetting GetSetting(string code)
+        {
+            var setting = _cacheSystemSetting.GetOrSet(code, x =>
+            {
+                var dbsetting = _systemSettingRepository.GetAsync(d => d.Code == code).GetAwaiter().GetResult();
+                if (dbsetting == null)
+                    throw new UserFriendlyException("无效系统设置");
+                return dbsetting;
+            });
+            return setting;
+        }
+
+        public void DelSystemSetting(string code)
+        {
+            _cacheSystemSetting.Remove(code);
+        }
+    }
+}

+ 42 - 0
src/Hotline/Caching/Services/TableAccessLevelCacheManager.cs

@@ -0,0 +1,42 @@
+using Hotline.Caching.Interfaces;
+using Hotline.Settings;
+using XF.Domain.Cache;
+using XF.Domain.Dependency;
+
+namespace Hotline.Caching.Services
+{
+    internal class TableAccessLevelCacheManager : ITableAccessLevelCacheManager, IScopeDependency
+    {
+        private const string TableAccessLevelCacheKey = "TableAccessLevel";
+        private readonly ITypedCache<IReadOnlyList<TableAccessLevelCacheItem>> _cache;
+        private readonly ITableAccessLevelRepository _repository;
+
+        public TableAccessLevelCacheManager(ITypedCache<IReadOnlyList<TableAccessLevelCacheItem>> cache, ITableAccessLevelRepository repository)
+        {
+            _cache = cache;
+            _repository = repository;
+        }
+
+        /// <summary>
+        /// 查询所有
+        /// </summary>
+        /// <returns></returns>
+        public IReadOnlyList<TableAccessLevelCacheItem> QueryAll() => _cache.Get(TableAccessLevelCacheKey);
+
+        /// <summary>
+        /// 重载
+        /// </summary>
+        public void Reload()
+        {
+            var levels = _repository.QueryAsync(d => true).GetAwaiter().GetResult();
+            var items = levels.Select(d => new TableAccessLevelCacheItem
+            {
+                RoleCode = d.RoleCode,
+                TableName = d.TableName,
+                AccessLevel = d.AccessLevel,
+            }).ToList();
+            //_cache.Remove(TableAccessLevelCacheKey);
+            _cache.Set(TableAccessLevelCacheKey, items);
+        }
+    }
+}

+ 62 - 0
src/Hotline/Caching/Services/TelCacheManager.cs

@@ -0,0 +1,62 @@
+using Hotline.Caching.Interfaces;
+using Hotline.CallCenter.Tels;
+using XF.Domain.Cache;
+using XF.Domain.Dependency;
+using XF.Domain.Exceptions;
+
+namespace Hotline.Caching.Services;
+
+public class TelCacheManager : ITelCacheManager, IScopeDependency
+{
+    private readonly ITypedCache<Tel> _cacheTel;
+    private readonly ITypedCache<IReadOnlyList<Tel>> _cacheTelList;
+    private readonly ITypedCache<TelGroup> _cacheTelGroup;
+    private readonly ITelRepository _telRepository;
+    private readonly ITelGroupRepository _telGroupRepository;
+
+    public TelCacheManager(
+        ITypedCache<Tel> cacheTel,
+        ITypedCache<IReadOnlyList<Tel>> cacheTelList,
+        ITypedCache<TelGroup> cacheTelGroup,
+        ITelRepository telRepository,
+        ITelGroupRepository telGroupRepository
+        )
+    {
+        _cacheTel = cacheTel;
+        _cacheTelList = cacheTelList;
+        _cacheTelGroup = cacheTelGroup;
+        _telRepository = telRepository;
+        _telGroupRepository = telGroupRepository;
+    }
+
+    public Tel GetTel(string telNo)
+    {
+        var tel = _cacheTel.GetOrSet(telNo, k =>
+        {
+            var dbTel = _telRepository.GetExtAsync(d => d.No == telNo, d => d.Includes(x => x.Groups))
+                .GetAwaiter().GetResult();
+            if (dbTel == null)
+                throw new UserFriendlyException("无效分机号");
+            return dbTel;
+        });
+        return tel;
+    }
+
+    public TelGroup GetTelGroup(string groupNo)
+    {
+        var group = _cacheTelGroup.GetOrSet(groupNo, k =>
+        {
+            var telGroup = _telGroupRepository.GetExtAsync(d => d.No == groupNo, d => d.Includes(x => x.Tels))
+                .GetAwaiter().GetResult();
+            if (telGroup == null)
+                throw new UserFriendlyException("无效分机组编号");
+            return telGroup;
+        });
+        return group;
+    }
+
+    public IReadOnlyList<Tel> GetTels()
+    {
+        return _cacheTelList.GetOrSet("tellist", d => _telRepository.QueryAsync(x => !x.IsDeleted).GetAwaiter().GetResult()) ?? new List<Tel>();
+    }
+}

+ 113 - 0
src/Hotline/Caching/Services/UserCacheManager.cs

@@ -0,0 +1,113 @@
+using Hotline.Caching.Interfaces;
+using Hotline.Users;
+using XF.Domain.Cache;
+using XF.Domain.Dependency;
+using XF.Domain.Exceptions;
+
+namespace Hotline.Caching.Services;
+
+public class UserCacheManager : IUserCacheManager, IScopeDependency
+{
+    private readonly ITypedCache<Work> _cacheWork;
+    private readonly IWorkRepository _workRepository;
+
+    public UserCacheManager(
+        ITypedCache<Work> cacheWork,
+        IWorkRepository workRepository)
+    {
+        _cacheWork = cacheWork;
+        _workRepository = workRepository;
+    }
+
+    /// <summary>
+    /// 查询用户当前工作记录
+    /// </summary>
+    /// <param name="userId"></param>
+    /// <returns></returns>
+    public Work GetWorkByUser(string userId)
+    {
+        var work = _cacheWork.GetOrSet(Work.GetKey(KeyMode.UserId, userId), k =>
+        {
+            var dbWork = _workRepository.GetCurrentWorkByUserAsync(userId)
+                .GetAwaiter().GetResult();
+            if (dbWork == null)
+                throw new UserFriendlyException("该用户暂无工作记录");
+            return dbWork;
+        });
+        return work;
+    }
+
+    /// <summary>
+    /// 查询分机当前工作记录
+    /// </summary>
+    /// <param name="telNo"></param>
+    /// <returns></returns>
+    /// <exception cref="UserFriendlyException"></exception>
+    public Work GetWorkByTel(string telNo)
+    {
+        var work = _cacheWork.GetOrSet(Work.GetKey(KeyMode.TelNo, telNo), k =>
+        {
+            var dbWork = _workRepository.GetCurrentWorkByTelAsync(telNo)
+                .GetAwaiter().GetResult();
+            if (dbWork == null)
+                throw new UserFriendlyException("该分机暂无工作记录");
+            return dbWork;
+        });
+        return work;
+    }
+
+    /// <summary>
+    /// 查询分机是否处于工作
+    /// </summary>
+    /// <param name="telNo"></param>
+    /// <returns></returns>
+    public async Task<bool> IsWorkingByTelAsync(string telNo, CancellationToken cancellationToken)
+    {
+        var work = _cacheWork.Get(Work.GetKey(KeyMode.TelNo, telNo));
+
+        if (work is null)
+        {
+            var dbWork = await _workRepository.GetCurrentWorkByTelAsync(telNo, cancellationToken);
+            if (dbWork != null)
+            {
+                _cacheWork.Set(Work.GetKey(KeyMode.TelNo, telNo), dbWork);
+                return true;
+            }
+            return false;
+        }
+        return true;
+    }
+
+    /// <summary>
+    /// 查询分机是否处于工作
+    /// </summary>
+    /// <param name="userId"></param>
+    /// <param name="cancellationToken"></param>
+    /// <returns></returns>
+    public async Task<bool> IsWorkingByUserAsync(string userId, CancellationToken cancellationToken)
+    {
+        //return _cacheWork.TryGetOrAdd(Work.GetKey(KeyMode.UserId, userId),
+        //    k => _workRepository.GetCurrentWorkByUserAsync(userId).GetAwaiter().GetResult(),
+        //    out _);
+        var work = _cacheWork.Get(Work.GetKey(KeyMode.UserId, userId));
+        if (work is null)
+        {
+            var dbWork = await _workRepository.GetCurrentWorkByUserAsync(userId, cancellationToken);
+            if (dbWork is null) return false;
+            _cacheWork.Set(Work.GetKey(KeyMode.UserId, userId), dbWork);
+        }
+
+        return true;
+    }
+
+    /// <summary>
+    /// 根据用户更新工作记录
+    /// </summary>
+    /// <param name="work"></param>
+    /// <returns></returns>
+    public void UpdateWorkByUser(Work work)
+    {
+        _cacheWork.Set(Work.GetKey(KeyMode.UserId, work.UserId), work);
+        _cacheWork.Set(Work.GetKey(KeyMode.TelNo, work.TelNo), work);
+    }
+}

+ 1 - 1
src/Hotline/CallCenter/BlackLists/IBlacklistDomainService.cs

@@ -41,7 +41,7 @@ namespace Hotline.CallCenter.BlackLists
         public async Task AddAsync(Blacklist blacklist, CancellationToken cancellationToken = default)
         {
             blacklist.Id = await _blacklistRepository.AddAsync(blacklist, cancellationToken);
-            _blackCache.GetOrAdd(Blacklist.GetKey(blacklist.PhoneNo), blacklist, ExpireMode.Absolute, TimeSpan.FromSeconds(blacklist.Duration));
+            _blackCache.GetOrSet(Blacklist.GetKey(blacklist.PhoneNo), blacklist, TimeSpan.FromSeconds(blacklist.Duration));
         }
 
         /// <summary>

+ 9 - 9
src/Hotline/CallCenter/Ivrs/IvrDomainService.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Devices;
 using Hotline.Settings;
 using Hotline.Share.Dtos.CallCenter;
@@ -56,7 +56,7 @@ public class IvrDomainService : IIvrDomainService, IScopeDependency
     /// <returns></returns>
     public async Task<IvrAnswer?> GetVoiceEndAnswerAsync(string menuId, CancellationToken cancellationToken = default)
     {
-        var ivrs = _ivrCacheManager.GetIvrs();
+        var ivrs = await _ivrCacheManager.GetIvrsAsync(cancellationToken);
         if (!ivrs.Any())
             throw new UserFriendlyException("未查到任何ivr配置");
         var ivr = ivrs.FirstOrDefault(d => d.No == menuId);
@@ -78,7 +78,7 @@ public class IvrDomainService : IIvrDomainService, IScopeDependency
     /// <returns></returns>
     public async Task<IvrAnswer?> GetDtmfAnswerAsync(string menuId, string input, CancellationToken cancellationToken = default)
     {
-        var ivrs = _ivrCacheManager.GetIvrs();
+        var ivrs = await _ivrCacheManager.GetIvrsAsync(cancellationToken);
         if (!ivrs.Any())
             throw new UserFriendlyException("未查到任何ivr配置");
         if (string.IsNullOrEmpty(input))
@@ -290,7 +290,7 @@ public class IvrDomainService : IIvrDomainService, IScopeDependency
 
     #region IVR流程处理
 
-    
+
     public class CorrectIvr
     {
         public string ReturnValue { get; set; }
@@ -298,18 +298,18 @@ public class IvrDomainService : IIvrDomainService, IScopeDependency
         public ECorrectIvr eCorrectIvr { get; set; }
     }
 
-    public CorrectIvr GetCorrectIvr(string to,bool isEvaluate =false )
+    public CorrectIvr GetCorrectIvr(string to, bool isEvaluate = false)
     {
-        var worktimeSettings = _worktimeCache.GetOrAdd("worktimesettings", d =>
+        var worktimeSettings = _worktimeCache.GetOrSet("worktimesettings", d =>
         {
             var settings = _trunkIvrManagerRepository.QueryAsync(x => x.IsEnable).GetAwaiter().GetResult();
             return settings;
-        }, ExpireMode.Absolute,TimeSpan.FromDays(1));
-        var correct = GetCorrectCategory(worktimeSettings.First(x => x.TrunkId == to),isEvaluate);
+        });
+        var correct = GetCorrectCategory(worktimeSettings.First(x => x.TrunkId == to), isEvaluate);
         return correct;
     }
 
-    private CorrectIvr GetCorrectCategory(TrunkIvrManager settings,bool isEvaluate)
+    private CorrectIvr GetCorrectCategory(TrunkIvrManager settings, bool isEvaluate)
     {
         if (isEvaluate)
         {

+ 2 - 2
src/Hotline/CallCenter/Tels/TelDomainService.cs

@@ -1,5 +1,4 @@
-using Hotline.Caches;
-using Hotline.CallCenter.Devices;
+using Hotline.CallCenter.Devices;
 using Hotline.Realtimes;
 using Hotline.Users;
 using System.Reflection;
@@ -8,6 +7,7 @@ using XF.Domain.Constants;
 using XF.Domain.Dependency;
 using XF.Domain.Exceptions;
 using Hotline.Share.Enums.CallCenter;
+using Hotline.Caching.Interfaces;
 
 namespace Hotline.CallCenter.Tels;
 

+ 4 - 4
src/Hotline/Orders/OrderDomainService.cs

@@ -170,17 +170,17 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
     {
         var today = DateTime.Today;
         var cacheKey = $"{OrderNoPrefix}{today:yyyyMMdd}";
-        var cacheOrderNo = _cacheOrderNo.GetOrAdd(cacheKey, f =>
+        var cacheOrderNo = _cacheOrderNo.GetOrSet(cacheKey, f =>
         {
             var todayOrderCount = _orderRepository.Queryable(true)
                 .CountAsync(d => d.CreationTime.Date == today.Date)
                 .GetAwaiter()
                 .GetResult();
             return new CacheOrderNO { TotalCount = todayOrderCount };
-        }, ExpireMode.Absolute, TimeSpan.FromDays(1));
-        cacheOrderNo.TotalCount += 1;
+        });
+        cacheOrderNo!.TotalCount += 1;
         var no = GenerateOrderNo(today, cacheOrderNo.TotalCount);
-        _cacheOrderNo.Update(cacheKey, d => cacheOrderNo);
+        _cacheOrderNo.Set(cacheKey, cacheOrderNo);
         return no;
     }
 

+ 1 - 1
src/Hotline/Users/UserDomainService.cs

@@ -1,4 +1,4 @@
-using Hotline.Caches;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Devices;
 using Hotline.CallCenter.Tels;
 using Hotline.Share.Dtos.CallCenter;

+ 44 - 20
src/XF.Domain/Cache/ITypedCache.cs

@@ -8,40 +8,64 @@ namespace XF.Domain.Cache
 {
     public interface ITypedCache<TValue> //where TValue : class
     {
-        bool Add(string key, TValue value, ExpireMode? expireMode = ExpireMode.None, TimeSpan? timeout = null);
-
-        void Put(string key, TValue value, ExpireMode? expireMode = ExpireMode.None, TimeSpan? timeout = null);
-
-        TValue Update(string key, Func<TValue, TValue> updateValue);
+        /*
+TrySet/TrySetAsync
+Set/SetAsync
+SetAll/SetAllAsync
+Get/GetAsync(with data retriever)
+Get/GetAsync(without data retriever)
+GetByPrefix/GetByPrefixAsync
+GetAll/GetAllAsync
+Remove/RemoveAsync
+RemoveByPrefix/RemoveByPrefixAsync
+RemoveAll/RemoveAllAsync
+Flush/FlushAsync
+GetCount
+GetExpiration/GetExpirationAsync
+         */
+
+        void Set(string key, TValue value, TimeSpan? expiration = null);
+        Task SetAsync(string key, TValue value, TimeSpan? expiration = null, CancellationToken cancellationToken = default);
 
         TValue? Get(string key);
+        Task<TValue?> GetAsync(string key, CancellationToken cancellationToken);
 
-        void Expire(string key, DateTime absoluteExpiration);
+        void Remove(string key);
+        Task RemoveAsync(string key, CancellationToken cancellationToken);
 
-        void Expire(string key, TimeSpan slidingExpiration);
-
-        void Expire(string key, ExpireMode? expireMode = ExpireMode.None, TimeSpan? timeout = null);
+        bool Exists(string key);
+        Task<bool> ExistsAsync(string key, CancellationToken cancellationToken);
 
-        TValue AddOrUpdate(string key, TValue addValue, Func<TValue, TValue> updateValue, ExpireMode? expireMode = ExpireMode.None, TimeSpan? timeout = null);
+        bool TrySet(string key, TValue value, TimeSpan? expiration = null);
+        Task<bool> TrySetAsync(string key, TValue value, TimeSpan? expiration = null, CancellationToken cancellationToken = default);
 
-        TValue AddOrUpdate(string key, TValue addValue, ExpireMode? expireMode = ExpireMode.None, TimeSpan? timeout = null)
-            => AddOrUpdate(key, addValue, _ => addValue, expireMode, timeout);
+        void SetAll(IDictionary<string, TValue> values, TimeSpan? expiration = null);
+        Task SetAllAsync(IDictionary<string, TValue> values, TimeSpan? expiration = null, CancellationToken cancellationToken = default);
 
-        TValue GetOrAdd(string key, TValue value, ExpireMode? expireMode = ExpireMode.None, TimeSpan? timeout = null);
+        IDictionary<string, TValue> GetByPrefix();
+        Task<IDictionary<string, TValue>> GetByPrefixAsync(CancellationToken cancellationToken);
 
-        TValue GetOrAdd(string key, Func<string, TValue> valueFactory, ExpireMode? expireMode = ExpireMode.None, TimeSpan? timeout = null);
+        IReadOnlyList<TValue> GetListByPrefix();
+        Task<IReadOnlyList<TValue>> GetListByPrefixAsync(CancellationToken cancellationToken);
 
-        bool TryGetOrAdd(string key, Func<string, TValue> valueFactory, out TValue? value, ExpireMode? expireMode = ExpireMode.None, TimeSpan? timeout = null);
+        //void RemoveByPrefix(string prefix);
+        //Task RemoveByPrefixAsync(string prefix, CancellationToken cancellationToken);
 
-        bool TryUpdate(string key, Func<TValue, TValue> updateValue, out TValue? value);
+        /// <summary>
+        /// get the cache value, if cache not exists, set the value to cache then return
+        /// </summary>
+        TValue GetOrSet(string key, TValue value, TimeSpan? expiration = null);
+        TValue? GetOrSet(string key, Func<string, TValue> valueFactory, TimeSpan? expiration = null);
+        Task<TValue> GetOrSetAsync(string key, TValue value, TimeSpan? expiration = null, CancellationToken cancellationToken = default);
+        Task<TValue?> GetOrSetAsync(string key, Func<string, TValue> valueFactory, TimeSpan? expiration = null, CancellationToken cancellationToken = default);
 
-        bool Remove(string key);
+        //TValue GetOrAdd(string key, TValue value, ExpireMode? expireMode = ExpireMode.None, TimeSpan? timeout = null);
 
-        bool Exists(string key);
+        //TValue GetOrAdd(string key, Func<string, TValue> valueFactory, ExpireMode? expireMode = ExpireMode.None, TimeSpan? timeout = null);
 
-        string CreateRegion();
+        string CombinePrefix(string key);
     }
-    
+
     public enum ExpireMode
     {
         /// <summary>Defines no expiration.</summary>

+ 2 - 0
src/XF.Domain/Dependency/DependencyInjectionExtensions.cs

@@ -84,6 +84,8 @@ internal class ServiceRegister
 {
     public static void Register(IServiceCollection services, Type type)
     {
+        if (type.Name == "IUserCacheManager")
+            throw new Exception();
         if (type == null) return;
         var serviceType = GetServiceType(type);
         if (HasImplInterface(type, typeof(ISingletonDependency)))