Browse Source

Merge branch 'master' of http://110.188.24.182:10023/Fengwo/hotline

田爽 1 năm trước cách đây
mục cha
commit
287129690f

+ 1 - 8
Hotline.sln

@@ -41,9 +41,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wex.Sdk", "src\Wex.Sdk\Wex.
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.Wex", "src\Hotline.Wex\Hotline.Wex.csproj", "{40B6FBEC-0524-430C-8B28-22229681D0F8}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tr.Sdk", "src\Tr.Sdk\Tr.Sdk.csproj", "{BB901B26-EA97-4D6F-8CAC-14DB321E1733}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hotline.Tr", "src\Hotline.Tr\Hotline.Tr.csproj", "{42A37A81-9B4A-4A05-8EBC-90D1E580E1DD}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tr.Sdk", "src\Tr.Sdk\Tr.Sdk.csproj", "{BB901B26-EA97-4D6F-8CAC-14DB321E1733}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -107,10 +105,6 @@ Global
 		{BB901B26-EA97-4D6F-8CAC-14DB321E1733}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{BB901B26-EA97-4D6F-8CAC-14DB321E1733}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{BB901B26-EA97-4D6F-8CAC-14DB321E1733}.Release|Any CPU.Build.0 = Release|Any CPU
-		{42A37A81-9B4A-4A05-8EBC-90D1E580E1DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{42A37A81-9B4A-4A05-8EBC-90D1E580E1DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{42A37A81-9B4A-4A05-8EBC-90D1E580E1DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{42A37A81-9B4A-4A05-8EBC-90D1E580E1DD}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -134,7 +128,6 @@ Global
 		{F2E2E925-39FF-41BB-B199-34E50A3AC1FB} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{40B6FBEC-0524-430C-8B28-22229681D0F8} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{BB901B26-EA97-4D6F-8CAC-14DB321E1733} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
-		{42A37A81-9B4A-4A05-8EBC-90D1E580E1DD} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {4B8EA790-BD13-4422-8D63-D6DBB77B823F}

+ 9 - 4
src/Hotline.Api/Controllers/TestController.cs

@@ -13,11 +13,11 @@ using Hotline.Users;
 using MediatR;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.SignalR;
 using Microsoft.Extensions.Options;
 using NewRock.Sdk;
 using NewRock.Sdk.Security;
 using SqlSugar;
+using Tr.Sdk;
 using Wex.Sdk;
 using XC.RSAUtil;
 using XF.Domain.Authentications;
@@ -57,6 +57,7 @@ public class TestController : BaseController
 
     private readonly ITimeLimitDomainService _timeLimitDomainService;
     private readonly IWfModuleDomainService _wfModuleDomainService;
+    private readonly ITrClient _trClient;
 
 
     //private readonly ITypedCache<List<User>> _cache;
@@ -85,7 +86,9 @@ public class TestController : BaseController
         IRepository<OrderUrge> orderUrgeRepository,
         IRepositoryTextSearch<OrderTs> repositoryts,
         ITimeLimitDomainService timeLimitDomainService,
-        IWfModuleDomainService wfModuleDomainService)
+        IWfModuleDomainService wfModuleDomainService,
+        ITrClient trClient
+        )
     {
         _logger = logger;
         _authorizeGenerator = authorizeGenerator;
@@ -106,6 +109,7 @@ public class TestController : BaseController
         _repositoryts = repositoryts;
         _timeLimitDomainService = timeLimitDomainService;
         _wfModuleDomainService = wfModuleDomainService;
+        _trClient = trClient;
     }
 
     [HttpGet("time")]
@@ -128,11 +132,12 @@ public class TestController : BaseController
         //int a = _timeLimitDomainService.CalcWorkTime(DateTime.Now, DateTime.Parse("2023-09-11 16:21:00"));
         //int m = _timeLimitDomainService.CalcWorkTime(DateTime.Parse("2023-09-19 12:00:00"), DateTime.Parse("2023-09-20 18:00:00"), false);
         //var r = _timeLimitDomainService.CalcEndTime(DateTime.Parse("2023-09-19 18:00:00"), "35", false);
-        await _wfModuleDomainService.PersistenceModulesAsync(HttpContext.RequestAborted);
-        return OpenResponse.Ok(DateTime.Now.ToString("F"));
+        //await _wfModuleDomainService.PersistenceModulesAsync(HttpContext.RequestAborted);
 
         //var rsp = await _daprClient.InvokeMethodAsync<ApiResponse<string>>(HttpMethod.Get, "identity", "api/v1/Test/time", HttpContext.RequestAborted);
         //var rsp1 = await _daprClient.InvokeMethodAsync<int, ApiResponse<string>>(HttpMethod.Post, "identity", "api/v1/Test/time1", 222, HttpContext.RequestAborted);
+        
+        return OpenResponse.Ok(DateTime.Now.ToString("F"));
     }
 
     [HttpGet("pgsql")]

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

@@ -1,4 +1,5 @@
 using System.Text.Json.Serialization;
+using System.Xml.Linq;
 using Hotline.Api;
 using Newtonsoft.Json;
 using RestSharp;
@@ -11,51 +12,6 @@ Log.Logger = new LoggerConfiguration()
 
 Log.Information("Hotline service is Starting up");
 
-//var a = "http://internal.ttf-cti.com:8080";
-////var r = await a
-////    .AppendPathSegment("api/login")
-////    .SetQueryParams(new { username = "yscs", password = "123456" })
-////    .GetAsync();
-////var h = r.Headers.FirstOrDefault(d => d.Name == "X-Auth-Token");
-
-//var p = new Pa { Phone = "13312341234", SpecialFlag = 4 };
-//var njp = Newtonsoft.Json.JsonConvert.SerializeObject(p);
-//var jp = System.Text.Json.JsonSerializer.Serialize(p);
-
-//FlurlHttp.GlobalSettings.JsonSerializer = new TrUrlEncodedSerializer();
-
-//var r1 = await a
-//    .AppendPathSegment("api/special_phone/insert")
-//    //.WithHeader(h.Name, h.Value)
-//    .WithHeader("X-Auth-Token", "8352d42165d7cf6821e53e926d66adba54c309e6")
-//    //.PostUrlEncodedAsync(new { phone = "13312341234", special_flag = 4 });
-//    //.PostUrlEncodedAsync(p);
-//    //.ConfigureRequest(s =>
-//    //{
-//    //    s.JsonSerializer = new Tr.Sdk.TrUrlEncodedSerializer();
-//    //})
-//    .BeforeCall(d =>
-//    {
-//        var b = d.RequestBody;
-//        var c = d.Request;
-//    })
-//    .PostUrlEncodedAsync(p);
-
-//var r2 = await r1;
-
-//var options = new RestClientOptions("http://internal.ttf-cti.com:8080");
-
-//var client = new RestClient(options);
-//client.AddDefaultHeader("X-Auth-Token", "8352d42165d7cf6821e53e926d66adba54c309e6");
-
-//var reqToken = new RestRequest("api/login")
-//    .AddObject(new { username = "yscs", password = "123456" });
-
-//var r1 = await client.GetAsync(reqToken);
-//var h = r1.Headers.FirstOrDefault(d => d.Name == "X-Auth-Token");
-//7a906970b1ce06a503807e75762da323782f4a29
-
-
 try
 {
     var builder = WebApplication.CreateBuilder(args);
@@ -96,14 +52,3 @@ finally
     Log.Information("Shut down complete");
     Log.CloseAndFlush();
 }
-
-class Pa
-{
-    [JsonProperty(PropertyName = "phone")]
-    [JsonPropertyName("phone")]
-    public string Phone { get; set; }
-
-    [JsonProperty(PropertyName = "special_flag")]
-    [JsonPropertyName("special_flag")]
-    public int SpecialFlag { get; set; }
-}

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

@@ -12,10 +12,9 @@ using Hotline.Wex;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Identity;
 using Serilog;
-using System.Text.Json;
 using Fw.Utility.Client;
 using Mapster;
-using MapsterMapper;
+using Tr.Sdk;
 using XF.Domain.Dependency;
 using XF.Domain.Filters;
 using XF.Domain.Options;
@@ -89,6 +88,10 @@ internal static class StartupExtensions
         //wex
         services.AddWex(callCenterConfiguration.Wex.Address);
 
+        //tr
+        var trConfig = configuration.GetRequiredSection("Tr").Get<TrConfiguration>();
+        services.AddTrSdk(trConfig.Address, trConfig.Username, trConfig.Password);
+
         //sqlsugar
         services.AddSqlSugar(configuration);
         services.AddWexDb(configuration);

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

@@ -13,23 +13,18 @@
       "Address": "http://222.212.82.225:8083",
       "Username": "admin",
       "Password": "Wex@12345"
-    },
-    "Tr": {
-      "Address": "http://internal.ttf-cti.com:8080",
-      "Username": "yscs",
-      "Password": "123456"
     }
   },
   "ConnectionStrings": {
     "Hotline": "PORT=5432;DATABASE=hotline;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;",
-    "Redis": "110.188.24.182:50179,password=fengwo22@@",
+    "Redis": "110.188.24.182:50179",
     "MongoDB": "mongodb://192.168.100.121:27017",
     "Wex": "server=222.212.82.225;Port=4509;Database=fs_kft;Uid=root;Pwd=Wex@12345;"
   },
   "Cache": {
     "Host": "110.188.24.182",
     "Port": 50179,
-    "Password": "fengwo22@@",
+    //"Password": "fengwo22@@",
     "Database": 1
   },
   "Swagger": true,
@@ -93,6 +88,11 @@
     "ServerAddresses": [ "http://110.188.24.28:8848" ],
     "Namespace": "17503980-9b0d-4d3e-8e35-c842c41fb888", //debug
     "ServiceName": "hotline"
+  },
+  "Tr": {
+    "Address": "http://internal.ttf-cti.com:8080",
+    "Username": "yscs",
+    "Password": "123456"
   }
 
 }

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

@@ -13,11 +13,6 @@
       "Address": "http://222.212.82.225:8083",
       "Username": "admin",
       "Password": "Wex@12345"
-    },
-    "Tr": {
-      "Address": "http://internal.ttf-cti.com:8080",
-      "Username": "yscs",
-      "Password": "123456"
     }
   },
   "ConnectionStrings": {
@@ -93,5 +88,10 @@
     "ServerAddresses": [ "http://110.188.24.28:8848" ],
     "Namespace": "f5017bc5-af0a-4f85-8e38-6718accc8f36", //dev
     "ServiceName": "hotline"
+  },
+  "Tr": {
+    "Address": "http://internal.ttf-cti.com:8080",
+    "Username": "yscs",
+    "Password": "123456"
   }
 }

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

@@ -444,6 +444,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 //当前待办节点为会签汇总节点时:检查是否为顶级会签发起节点(csstate==none),t:按配置往下走,f:继续往上汇总,不需要重复往下指派
                 if (currentStep.CountersignStartStepId != workflow.TopCountersignStepId)
                 {
+                    countersignEndOption.InputRealHandler = true;
                     dto.Steps = new List<NextStepOption> { countersignEndOption };
                     return dto;
                 }
@@ -605,6 +606,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             {
                 Key = stepDefine.Code,
                 Value = stepDefine.Name,
+                InputRealHandler = stepDefine.StepType == EStepType.Summary,
                 Items = handlers
             });
         }

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

@@ -14,9 +14,9 @@
     <ProjectReference Include="..\Hotline.Application.Contracts\Hotline.Application.Contracts.csproj" />
     <ProjectReference Include="..\Hotline.NewRock\Hotline.NewRock.csproj" />
     <ProjectReference Include="..\Hotline.Repository.SqlSugar\Hotline.Repository.SqlSugar.csproj" />
-    <ProjectReference Include="..\Hotline.Tr\Hotline.Tr.csproj" />
     <ProjectReference Include="..\Hotline.Wex\Hotline.Wex.csproj" />
     <ProjectReference Include="..\Hotline\Hotline.csproj" />
+    <ProjectReference Include="..\Tr.Sdk\Tr.Sdk.csproj" />
     <ProjectReference Include="..\XF.EasyCaching\XF.EasyCaching.csproj" />
   </ItemGroup>
 

+ 5 - 0
src/Hotline.Share/Dtos/FlowEngine/NextStepsDto.cs

@@ -23,6 +23,11 @@ public class NextStepOption : Kv
     /// </summary>
     public bool BackToCountersignEnd { get; set; }
 
+    /// <summary>
+    /// 是否需要填写真实办理人信息
+    /// </summary>
+    public bool InputRealHandler { get; set; }
+
     public IReadOnlyList<Kv> Items { get; set; }
 }
 

+ 0 - 7
src/Hotline.Tr/Class1.cs

@@ -1,7 +0,0 @@
-namespace Hotline.Tr
-{
-    public class Class1
-    {
-
-    }
-}

+ 0 - 14
src/Hotline.Tr/Hotline.Tr.csproj

@@ -1,14 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <TargetFramework>net7.0</TargetFramework>
-    <ImplicitUsings>enable</ImplicitUsings>
-    <Nullable>enable</Nullable>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <ProjectReference Include="..\Hotline\Hotline.csproj" />
-    <ProjectReference Include="..\Tr.Sdk\Tr.Sdk.csproj" />
-  </ItemGroup>
-
-</Project>

+ 0 - 2
src/Hotline/CallCenter/Devices/CallCenterConfiguration.cs

@@ -7,6 +7,4 @@ public class CallCenterConfiguration
     public DeviceConfigs DeviceConfigs { get; set; }
 
     public WexConfiguration Wex { get; set; }
-
-    public TrConfiguration Tr { get; set; }
 }

+ 15 - 0
src/NewRock.Sdk/XsConfiguration.cs

@@ -0,0 +1,15 @@
+namespace NewRock.Sdk
+{
+    public class XsConfiguration
+    {
+        public string Address { get; set; }
+        public bool Authorize { get; set; }
+        public string ReceiveKey { get; set; }
+        public string SendKey { get; set; }
+
+        /// <summary>
+        /// 认证过期时间(秒)
+        /// </summary>
+        public int Expired { get; set; }
+    }
+}

+ 23 - 0
src/Tr.Sdk/Blacklist/AddBlacklistRequest.cs

@@ -0,0 +1,23 @@
+using RestSharp;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml.Linq;
+
+namespace Tr.Sdk.Blacklist
+{
+    public class AddBlacklistRequest : TrRequest
+    {
+        [RequestProperty(Name = "phone")]
+        public string Phone { get; set; }
+
+        [RequestProperty(Name = "special_flag")]
+        public int SpecialFlag { get; set; }
+
+        public override string Path() => "api/special_phone/insert";
+
+        public override Method HttpMethod() => Method.Post;
+    }
+}

+ 0 - 6
src/Tr.Sdk/Defaults.cs

@@ -1,6 +0,0 @@
-namespace Tr.Sdk;
-
-internal class Defaults
-{
-    internal static readonly string ServiceName = "Tr";
-}

+ 3 - 32
src/Tr.Sdk/ITrClient.cs

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
 
 namespace Tr.Sdk
 {
-    public interface ITrClient
+    public partial interface ITrClient
     {
         /// <summary>
         /// 执行操作呼叫中心请求
@@ -17,36 +17,7 @@ namespace Tr.Sdk
         /// <param name="request"></param>
         /// <param name="cancellationToken"></param>
         /// <returns></returns>
-        Task<TResponse?> ExecuteAsync<TRequest, TResponse>(
-            TRequest request,
-            CancellationToken cancellationToken) where TRequest : ITrRequest;
-
-        Task<string> GetTokenAsync(TokenRequest request, CancellationToken cancellationToken);
-    }
-
-    public class TokenRequest : ITrRequest
-    {
-        public TokenRequest()
-        {
-            Url = "login/token";
-            Method = HttpMethod.Get;
-        }
-
-        public string Url { get; init; }
-
-        public HttpMethod Method { get; init; }
-
-        public string Username { get; set; }
-
-        public string Password { get; set; }
-    }
-
-    public interface ITrRequest
-    {
-        [JsonIgnore]
-        string Url { get; }
-
-        [JsonIgnore]
-        HttpMethod Method { get; }
+        Task<TResponse> ExecuteAsync<TRequest, TResponse>(TRequest request, CancellationToken cancellationToken)
+            where TRequest : TrRequest;
     }
 }

+ 15 - 0
src/Tr.Sdk/Tels/ITrClient.Tel.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Tr.Sdk.Tels;
+
+namespace Tr.Sdk
+{
+    public partial interface ITrClient
+    {
+        Task<List<QueryTelResponse>> QueryTelsAsync(QueryTelRequest request, CancellationToken cancellationToken) =>
+            ExecuteAsync<QueryTelRequest, List<QueryTelResponse>>(request, cancellationToken);
+    }
+}

+ 20 - 0
src/Tr.Sdk/Tels/QueryTelRequest.cs

@@ -0,0 +1,20 @@
+using System.Text.Json.Serialization;
+using RestSharp;
+
+namespace Tr.Sdk.Tels
+{
+    public class QueryTelRequest : TrRequest
+    {
+        public override string Path() => "api/ola/extensions/detail";
+
+
+        public override Method HttpMethod() => Method.Get;
+    }
+
+    public class QueryTelResponse
+    {
+        [JsonPropertyName("uuid")]
+        public string Id { get; set; }
+        public string Name { get; set; }
+    }
+}

+ 55 - 0
src/Tr.Sdk/TrAuthenticator.cs

@@ -0,0 +1,55 @@
+using RestSharp;
+using RestSharp.Authenticators;
+
+namespace Tr.Sdk;
+
+public class TrAuthenticator : AuthenticatorBase
+{
+    private readonly string _tokenKey = "X-Auth-Token";
+    private readonly string _baseUrl;
+    private readonly string _apiKey;
+    private readonly string _apiSecret;
+
+    public TrAuthenticator(string baseUrl, string apiKey, string apiSecret) : base("")
+    {
+        _baseUrl = baseUrl;
+        _apiKey = apiKey;
+        _apiSecret = apiSecret;
+    }
+
+    protected override async ValueTask<Parameter> GetAuthenticationParameter(string accessToken)
+    {
+        if (string.IsNullOrEmpty(Token))
+        {
+            Token = await GetTokenAsync(default);
+        }
+        return new HeaderParameter(_tokenKey, Token);
+    }
+
+    public async Task<string> GetTokenAsync(CancellationToken cancellationToken)
+    {
+        var options = new RestClientOptions(_baseUrl);
+        using var client = new RestClient(options);
+        var request = new RestRequest("api/login")
+            .AddObject(new { username = _apiKey, password = _apiSecret });
+        try
+        {
+            var response = await client.GetAsync(request, cancellationToken);
+            if (!response.IsSuccessful)
+                throw new HttpRequestException(
+                    $"呼叫中心服务授权请求失败,HttpCode: {response.StatusCode}, Error: {response.ErrorMessage}");
+            var token = response.Headers?.FirstOrDefault(d => d.Name == _tokenKey);
+            if (token is null)
+                throw new HttpRequestException("呼叫中心服务授权失败");
+            if (token?.Value is null)
+                throw new HttpRequestException("呼叫中心token无效");
+
+            return token.Value.ToString();
+        }
+        catch (Exception e)
+        {
+            throw new HttpRequestException($"呼叫中心授权请求失败,Error: {e.Message}");
+        }
+
+    }
+}

+ 47 - 69
src/Tr.Sdk/TrClient.cs

@@ -1,75 +1,53 @@
-//using System.Net.Http.Json;
-//using System.Text.Json;
+using System.Net.Http.Json;
+using System.Text.Json;
+using RestSharp;
 
-//namespace Tr.Sdk;
+namespace Tr.Sdk;
 
-//public partial class TrClient : ITrClient
-//{
-//    private readonly IHttpClientFactory _httpClientFactory;
+public class TrClient : ITrClient, IDisposable
+{
+    private readonly RestClient _client;
 
-//    public TrClient(IHttpClientFactory httpClientFactory)
-//    {
-//        _httpClientFactory = httpClientFactory;
-//    }
+    public TrClient(string baseUrl, string apiKey, string apiSecret)
+    {
+        var options = new RestClientOptions(baseUrl)
+        {
+            Authenticator = new TrAuthenticator(baseUrl, apiKey, apiSecret)
+        };
+        _client = new RestClient(options);
+    }
 
-//    /// <summary>
-//    /// 执行操作呼叫中心请求
-//    /// </summary>
-//    /// <typeparam name="TRequest"></typeparam>
-//    /// <typeparam name="TResponse"></typeparam>
-//    /// <param name="request"></param>
-//    /// <param name="cancellationToken"></param>
-//    /// <returns></returns>
-//    public async Task<TResponse?> ExecuteAsync<TRequest, TResponse>(TRequest request, CancellationToken cancellationToken) where TRequest : ITrRequest
-//    {
-//        //var httpClient = _httpClientFactory.CreateClient(Defaults.ServiceName);
-//        //var token = await _tokenManager.GetTokenAsync(cancellationToken);
-//        //httpClient.DefaultRequestHeaders.Add("X-Auth-Token", token);
-//        //try
-//        //{
-//        //    if (request.Method == HttpMethod.Get)
-//        //    {
-//        //        return await httpClient.GetFromJsonAsync<TResponse>(request.Url, cancellationToken);
-//        //    }
-//        //    else if (request.Method == HttpMethod.Post)
-//        //    {
-//        //        using var content = new FormUrlEncodedContent
-//        //        await httpClient.PostAsync(request.Url, new FormUrlEncodedContent(), cancellationToken);
-//        //    }
-//        //    else
-//        //    {
-//        //        throw new ArgumentOutOfRangeException();
-//        //    }
+    /// <summary>
+    /// 执行操作呼叫中心请求
+    /// </summary>
+    /// <typeparam name="TRequest"></typeparam>
+    /// <typeparam name="TResponse"></typeparam>
+    /// <param name="request"></param>
+    /// <param name="cancellationToken"></param>
+    /// <returns></returns>
+    public async Task<TResponse> ExecuteAsync<TRequest, TResponse>(TRequest request, CancellationToken cancellationToken)
+        where TRequest : TrRequest
+    {
+        var req = new RestRequest(request.Path(), request.HttpMethod())
+            .AddObject(request);
+        try
+        {
+            var response = await _client.ExecuteAsync<TResponse>(req, cancellationToken);
+            if (!response?.IsSuccessful ?? false)
+                throw new HttpRequestException($"请求呼叫中心服务失败, HttpCode: {response.StatusCode}, Error: {response.ErrorMessage}");
 
-//        //    var rsp = await httpClient.PostAsJsonAsync(request.Url, request, cancellationToken: cancellationToken);
-//        //    if (!rsp.IsSuccessStatusCode)
-//        //        throw new HttpRequestException("请求呼叫中心服务失败");
-//        //    var response = await rsp.Content.ReadFromJsonAsync<TResponse>(new JsonSerializerOptions
-//        //    {
-//        //        PropertyNamingPolicy = JsonNamingPolicy.CamelCase
-//        //    }, cancellationToken);
-//        //    if (response?.Code == 401)
-//        //    {
-//        //        await _tokenManager.RefreshTokenAsync(cancellationToken);
-//        //        response = await rsp.Content.ReadFromJsonAsync<TResponse>(new JsonSerializerOptions
-//        //        {
-//        //            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
-//        //        }, cancellationToken);
-//        //    }
-//        //    if (response?.Code != 200)
-//        //        throw new HttpRequestException(response?.Msg);
-//        //    return response;
-//        //}
-//        //catch (Exception)
-//        //{
-//        //    throw;
-//        //}
+            return response.Data;
+        }
+        catch (Exception e)
+        {
+            throw new HttpRequestException($"呼叫中心请求失败,Error: {e.Message}");
+        }
+    }
 
-//        throw new NotImplementedException();
-//    }
-
-//    public async Task<string> GetTokenAsync(TokenRequest request, CancellationToken cancellationToken)
-//    {
-//        throw new NotImplementedException();
-//    }
-//}
+    /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
+    public void Dispose()
+    {
+        _client?.Dispose();
+        GC.SuppressFinalize(this);
+    }
+}

+ 1 - 1
src/Hotline/CallCenter/Devices/TrConfiguration.cs → src/Tr.Sdk/TrConfiguration.cs

@@ -1,4 +1,4 @@
-namespace Hotline.CallCenter.Devices;
+namespace Tr.Sdk;
 
 public class TrConfiguration
 {

+ 9 - 0
src/Tr.Sdk/TrRequest.cs

@@ -0,0 +1,9 @@
+using RestSharp;
+
+namespace Tr.Sdk;
+
+public abstract class TrRequest
+{
+    public abstract string Path();
+    public abstract Method HttpMethod();
+}

+ 18 - 0
src/Tr.Sdk/TrSdkStartupExtensions.cs

@@ -0,0 +1,18 @@
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tr.Sdk
+{
+    public static class TrSdkStartupExtensions
+    {
+        public static IServiceCollection AddTrSdk(this IServiceCollection services, string baseUrl, string apiKey, string apiSecret)
+        {
+            services.AddSingleton<ITrClient, TrClient>(_ => new TrClient(baseUrl, apiKey, apiSecret));
+            return services;
+        }
+    }
+}

+ 8 - 0
src/Wex.Sdk/WexConfiguration.cs

@@ -0,0 +1,8 @@
+namespace Wex.Sdk;
+
+public class WexConfiguration
+{
+    public string Address { get; set; }
+    public string Username { get; set; }
+    public string Password { get; set; }
+}