From 65513b6235c37d5c36519bf34982ca2c4c5f49fd Mon Sep 17 00:00:00 2001 From: LiuXin Date: Wed, 13 Aug 2025 17:20:35 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=8A=A5=E8=AD=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Alarm.Application.csproj | 0 .../RequestDto/SubscribeEventReqDto.cs | 1 - .../ResponeDto/EventEnvelopeDto.cs | 0 .../Alarm.DomainService.csproj | 5 +-- .../DahAlarm/DahuaGeneralCtlService.cs | 34 ++++++++++++++++++- .../DahAlarm/IDahuaGeneralCtlService.cs | 0 .../AlarmService.API/AlarmService.API.csproj | 4 ++- .../AlarmService/AlarmService.API/Program.cs | 9 +++++ .../AlarmService.API/appsettings.json | 13 ++++++- .../Common.Shared.DomainService.csproj | 1 - .../MqttClient/IMqttClientService.cs | 3 +- .../MqttClient/MQTTClient.cs | 15 ++++++++ .../MqttClient/MqttClientService.cs | 5 ++- WeiCloud.Fusion/WeiCloud.Fusion.sln | 28 +++++++-------- 14 files changed, 92 insertions(+), 26 deletions(-) rename WeiCloud.Fusion/{ => AlarmService}/Alarm.Application/Alarm.Application.csproj (100%) rename WeiCloud.Fusion/{ => AlarmService}/Alarm.Application/RequestDto/SubscribeEventReqDto.cs (99%) rename WeiCloud.Fusion/{ => AlarmService}/Alarm.Application/ResponeDto/EventEnvelopeDto.cs (100%) rename WeiCloud.Fusion/{ => AlarmService}/Alarm.DomainService/Alarm.DomainService.csproj (64%) rename WeiCloud.Fusion/{ => AlarmService}/Alarm.DomainService/DahAlarm/DahuaGeneralCtlService.cs (87%) rename WeiCloud.Fusion/{ => AlarmService}/Alarm.DomainService/DahAlarm/IDahuaGeneralCtlService.cs (100%) diff --git a/WeiCloud.Fusion/Alarm.Application/Alarm.Application.csproj b/WeiCloud.Fusion/AlarmService/Alarm.Application/Alarm.Application.csproj similarity index 100% rename from WeiCloud.Fusion/Alarm.Application/Alarm.Application.csproj rename to WeiCloud.Fusion/AlarmService/Alarm.Application/Alarm.Application.csproj diff --git a/WeiCloud.Fusion/Alarm.Application/RequestDto/SubscribeEventReqDto.cs b/WeiCloud.Fusion/AlarmService/Alarm.Application/RequestDto/SubscribeEventReqDto.cs similarity index 99% rename from WeiCloud.Fusion/Alarm.Application/RequestDto/SubscribeEventReqDto.cs rename to WeiCloud.Fusion/AlarmService/Alarm.Application/RequestDto/SubscribeEventReqDto.cs index caf5969..21a454e 100644 --- a/WeiCloud.Fusion/Alarm.Application/RequestDto/SubscribeEventReqDto.cs +++ b/WeiCloud.Fusion/AlarmService/Alarm.Application/RequestDto/SubscribeEventReqDto.cs @@ -7,7 +7,6 @@ namespace Alarm.Application.RequestDto [JsonPropertyName("param")] public required SubscribeParam Param { get; init; } } - public sealed class SubscribeParam { [JsonPropertyName("monitors")] diff --git a/WeiCloud.Fusion/Alarm.Application/ResponeDto/EventEnvelopeDto.cs b/WeiCloud.Fusion/AlarmService/Alarm.Application/ResponeDto/EventEnvelopeDto.cs similarity index 100% rename from WeiCloud.Fusion/Alarm.Application/ResponeDto/EventEnvelopeDto.cs rename to WeiCloud.Fusion/AlarmService/Alarm.Application/ResponeDto/EventEnvelopeDto.cs diff --git a/WeiCloud.Fusion/Alarm.DomainService/Alarm.DomainService.csproj b/WeiCloud.Fusion/AlarmService/Alarm.DomainService/Alarm.DomainService.csproj similarity index 64% rename from WeiCloud.Fusion/Alarm.DomainService/Alarm.DomainService.csproj rename to WeiCloud.Fusion/AlarmService/Alarm.DomainService/Alarm.DomainService.csproj index 1df926c..7adf5ab 100644 --- a/WeiCloud.Fusion/Alarm.DomainService/Alarm.DomainService.csproj +++ b/WeiCloud.Fusion/AlarmService/Alarm.DomainService/Alarm.DomainService.csproj @@ -13,9 +13,10 @@ + + + - - diff --git a/WeiCloud.Fusion/Alarm.DomainService/DahAlarm/DahuaGeneralCtlService.cs b/WeiCloud.Fusion/AlarmService/Alarm.DomainService/DahAlarm/DahuaGeneralCtlService.cs similarity index 87% rename from WeiCloud.Fusion/Alarm.DomainService/DahAlarm/DahuaGeneralCtlService.cs rename to WeiCloud.Fusion/AlarmService/Alarm.DomainService/DahAlarm/DahuaGeneralCtlService.cs index c95d75b..db1ff2f 100644 --- a/WeiCloud.Fusion/Alarm.DomainService/DahAlarm/DahuaGeneralCtlService.cs +++ b/WeiCloud.Fusion/AlarmService/Alarm.DomainService/DahAlarm/DahuaGeneralCtlService.cs @@ -1,6 +1,7 @@ using Alarm.Application.RequestDto; using Alarm.Application.ResponeDto; using Common.Shared.Application.DaHua; +using Common.Shared.DomainService.MqttClient; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Org.BouncyCastle.Crypto.Parameters; @@ -17,12 +18,37 @@ namespace Alarm.DomainService.DahAlarm private readonly ILogger _logger; private readonly IConfiguration _configuration; private readonly HttpClient _http; + private readonly MQTTClient _mqttClient; + private readonly IMqttClientService _mqttClientService; + private string mqttHostIp; + private int mqttHostPort; + private int mqttTimeout; + private string mqttUsername; + private string mqttPassword; + private string mqttClientId; + private string topicName; - public DahuaGeneralCtlService(ILogger logger, IConfiguration configuration, HttpClient http) + /// + /// + /// + /// + /// + /// + /// + public DahuaGeneralCtlService(ILogger logger, IConfiguration configuration, HttpClient http, MQTTClient mQTTClient, IMqttClientService mqttClientService) { _logger = logger; _configuration = configuration; _http = http; + _mqttClient = mQTTClient; + mqttHostIp = _configuration["SubscribeMQTT:HostIP"]!;//IP地址 + mqttHostPort = _configuration["SubscribeMQTT:HostPort"].ToInt();//端口号 + mqttTimeout = _configuration["SubscribeMQTT:Timeout"].ToInt();//超时时间 + mqttUsername = _configuration["SubscribeMQTT:UserName"]!;//用户名 + mqttPassword = _configuration["SubscribeMQTT:Password"]!;//密码 + mqttClientId = _configuration["SubscribeMQTT:ClientId"]!; + topicName = _configuration["SubscribeMQTT:TopicName"]!; + _mqttClientService = mqttClientService; } /// @@ -258,6 +284,12 @@ namespace Alarm.DomainService.DahAlarm //这是大华的残卫报警类型 if (dto.Info.AlarmType == 4321) { + //拼接物联平台标准的mqtt消息格式 + var payload = "[{\"taglabel\":\"" + dto.Info.DeviceCode + " + \".alart.\" + " + dto.Info.DeviceName + "\",\"value\":\"" + dto.Info.AlarmStat + "\",\"time\":\"" + DateTimeOffset.UtcNow.ToUnixTimeSeconds() + "\"}]"; + + await _mqttClient.EnsureConnectedAsync(mqttHostIp, mqttHostPort, mqttUsername, mqttPassword, topicName, mqttClientId); + + await _mqttClientService.PublishAsync("/zrh/sun/alarm", payload); } } } diff --git a/WeiCloud.Fusion/Alarm.DomainService/DahAlarm/IDahuaGeneralCtlService.cs b/WeiCloud.Fusion/AlarmService/Alarm.DomainService/DahAlarm/IDahuaGeneralCtlService.cs similarity index 100% rename from WeiCloud.Fusion/Alarm.DomainService/DahAlarm/IDahuaGeneralCtlService.cs rename to WeiCloud.Fusion/AlarmService/Alarm.DomainService/DahAlarm/IDahuaGeneralCtlService.cs diff --git a/WeiCloud.Fusion/AlarmService/AlarmService.API/AlarmService.API.csproj b/WeiCloud.Fusion/AlarmService/AlarmService.API/AlarmService.API.csproj index 446c877..3e9bdc0 100644 --- a/WeiCloud.Fusion/AlarmService/AlarmService.API/AlarmService.API.csproj +++ b/WeiCloud.Fusion/AlarmService/AlarmService.API/AlarmService.API.csproj @@ -7,9 +7,11 @@ - + + + diff --git a/WeiCloud.Fusion/AlarmService/AlarmService.API/Program.cs b/WeiCloud.Fusion/AlarmService/AlarmService.API/Program.cs index 5e4b3a3..f1bc05b 100644 --- a/WeiCloud.Fusion/AlarmService/AlarmService.API/Program.cs +++ b/WeiCloud.Fusion/AlarmService/AlarmService.API/Program.cs @@ -1,6 +1,7 @@ using AlarmService.API.Infrastructure; using Autofac; using Autofac.Extensions.DependencyInjection; +using Common.Shared.DomainService.MqttClient; using Microsoft.OpenApi.Models; using NLog; using NLog.Extensions.Logging; @@ -86,6 +87,14 @@ namespace AlarmService.API options.Limits.MaxRequestBodySize = 200 * 1024 * 1024; // Ĭ 200MB }); + #region mqttclient + + builder.Services.AddSingleton(); + + builder.Services.AddSingleton(); + + #endregion mqttclient + var app = builder.Build(); if (app.Environment.IsDevelopment()) diff --git a/WeiCloud.Fusion/AlarmService/AlarmService.API/appsettings.json b/WeiCloud.Fusion/AlarmService/AlarmService.API/appsettings.json index 10f68b8..df0f96a 100644 --- a/WeiCloud.Fusion/AlarmService/AlarmService.API/appsettings.json +++ b/WeiCloud.Fusion/AlarmService/AlarmService.API/appsettings.json @@ -5,5 +5,16 @@ "Microsoft.AspNetCore": "Warning" } }, + "SubscribeMQTT": { + "TopicName": "datasource_points_AXYJPT_v4", + "ProjectId": 530522108656160, + "HostIP": "v4.weienergy.cn", + "HostPort": 18883, + "Timeout": 5000, + "UserName": "test", + "Password": "test123", + "ClientId": "datasource_points_AXYJPT_v4", + "ApiUrl": "http://v4.weienergy.cn/datastream" + }, "AllowedHosts": "*" -} +} \ No newline at end of file diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/Common.Shared.DomainService.csproj b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/Common.Shared.DomainService.csproj index 3530a2b..bad3a67 100644 --- a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/Common.Shared.DomainService.csproj +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/Common.Shared.DomainService.csproj @@ -9,7 +9,6 @@ - diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/IMqttClientService.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/IMqttClientService.cs index 136e26a..cf53d32 100644 --- a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/IMqttClientService.cs +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/IMqttClientService.cs @@ -1,5 +1,4 @@ -using MQTTnet; -using MQTTnet.Client; +using MQTTnet.Client; namespace Common.Shared.DomainService.MqttClient { diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/MQTTClient.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/MQTTClient.cs index 64addd6..b072841 100644 --- a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/MQTTClient.cs +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/MQTTClient.cs @@ -10,12 +10,27 @@ namespace Common.Shared.DomainService.MqttClient { private readonly ILogger _logger; internal IMqttClient mqttClient; + public bool IsConnected => mqttClient != null && mqttClient.IsConnected; public MQTTClient(ILogger logger) { _logger = logger; } + /// + /// 如果未连接,则按参数调用 Init;成功后返回当前连接状态。 + /// + public async Task EnsureConnectedAsync( + string serverIp, int port, string authUser, string authPwd, + string topicNameStrs = "", string clientId = "") + { + if (mqttClient == null || !mqttClient.IsConnected) + { + await Init(serverIp, port, authUser, authPwd, topicNameStrs, clientId); + } + return mqttClient != null && mqttClient.IsConnected; + } + private async Task Subscribe(string topicNameStrs) { if (!string.IsNullOrWhiteSpace(topicNameStrs)) diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/MqttClientService.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/MqttClientService.cs index 912ffb1..0dcae36 100644 --- a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/MqttClientService.cs +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/MqttClientService.cs @@ -1,9 +1,8 @@ -using Common.Shared.DomainService.MqttClient; -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using MQTTnet; using MQTTnet.Client; -namespace WeiCloud.SafetyFirePro.Services +namespace Common.Shared.DomainService.MqttClient { public class MqttClientService : IMqttClientService { diff --git a/WeiCloud.Fusion/WeiCloud.Fusion.sln b/WeiCloud.Fusion/WeiCloud.Fusion.sln index b0e6721..93850b8 100644 --- a/WeiCloud.Fusion/WeiCloud.Fusion.sln +++ b/WeiCloud.Fusion/WeiCloud.Fusion.sln @@ -47,16 +47,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AlarmService", "AlarmServic EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlarmService.API", "AlarmService\AlarmService.API\AlarmService.API.csproj", "{2677EAF0-9F7F-4969-B8B1-3006F35EB93E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Alarm.DomainService", "Alarm.DomainService\Alarm.DomainService.csproj", "{3ED553C4-3A63-4613-B979-472FDA5EA346}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common.SharedService", "Common.SharedService", "{80F3B34B-C334-44D2-A861-31FD403AD57D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Alarm.Application", "Alarm.Application\Alarm.Application.csproj", "{89367194-A636-41B9-81F0-283DCB84C296}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common.Shared.Application", "Common.SharedService\Common.Shared.Application\Common.Shared.Application.csproj", "{9A5FBAFF-EBE8-3156-5547-FB3ED1DEB545}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common.Shared.DomainService", "Common.SharedService\Common.Shared.DomainService\Common.Shared.DomainService.csproj", "{C2757FC0-54A9-BBD3-2E23-55F2F3912BA4}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Alarm.DomainService", "AlarmService\Alarm.DomainService\Alarm.DomainService.csproj", "{B6DDF83D-591E-38B6-2902-1624BE8AE9B9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Alarm.Application", "AlarmService\Alarm.Application\Alarm.Application.csproj", "{4B2C6EBE-E719-9F40-ADE6-C82DA632E554}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -123,14 +123,6 @@ Global {2677EAF0-9F7F-4969-B8B1-3006F35EB93E}.Debug|Any CPU.Build.0 = Debug|Any CPU {2677EAF0-9F7F-4969-B8B1-3006F35EB93E}.Release|Any CPU.ActiveCfg = Release|Any CPU {2677EAF0-9F7F-4969-B8B1-3006F35EB93E}.Release|Any CPU.Build.0 = Release|Any CPU - {3ED553C4-3A63-4613-B979-472FDA5EA346}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3ED553C4-3A63-4613-B979-472FDA5EA346}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3ED553C4-3A63-4613-B979-472FDA5EA346}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3ED553C4-3A63-4613-B979-472FDA5EA346}.Release|Any CPU.Build.0 = Release|Any CPU - {89367194-A636-41B9-81F0-283DCB84C296}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {89367194-A636-41B9-81F0-283DCB84C296}.Debug|Any CPU.Build.0 = Debug|Any CPU - {89367194-A636-41B9-81F0-283DCB84C296}.Release|Any CPU.ActiveCfg = Release|Any CPU - {89367194-A636-41B9-81F0-283DCB84C296}.Release|Any CPU.Build.0 = Release|Any CPU {9A5FBAFF-EBE8-3156-5547-FB3ED1DEB545}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9A5FBAFF-EBE8-3156-5547-FB3ED1DEB545}.Debug|Any CPU.Build.0 = Debug|Any CPU {9A5FBAFF-EBE8-3156-5547-FB3ED1DEB545}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -139,6 +131,14 @@ Global {C2757FC0-54A9-BBD3-2E23-55F2F3912BA4}.Debug|Any CPU.Build.0 = Debug|Any CPU {C2757FC0-54A9-BBD3-2E23-55F2F3912BA4}.Release|Any CPU.ActiveCfg = Release|Any CPU {C2757FC0-54A9-BBD3-2E23-55F2F3912BA4}.Release|Any CPU.Build.0 = Release|Any CPU + {B6DDF83D-591E-38B6-2902-1624BE8AE9B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B6DDF83D-591E-38B6-2902-1624BE8AE9B9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6DDF83D-591E-38B6-2902-1624BE8AE9B9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B6DDF83D-591E-38B6-2902-1624BE8AE9B9}.Release|Any CPU.Build.0 = Release|Any CPU + {4B2C6EBE-E719-9F40-ADE6-C82DA632E554}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4B2C6EBE-E719-9F40-ADE6-C82DA632E554}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4B2C6EBE-E719-9F40-ADE6-C82DA632E554}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4B2C6EBE-E719-9F40-ADE6-C82DA632E554}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -159,10 +159,10 @@ Global {40B0D902-553C-C52F-71A2-56FB176FCCBD} = {44DAA396-C724-480A-A2BC-9A33D29E8FEA} {9F2BD2C5-6496-419D-B87A-4F481E963C4D} = {19A25984-FFA8-49BE-A710-6F269A406C61} {2677EAF0-9F7F-4969-B8B1-3006F35EB93E} = {18791734-CA81-482D-964A-CA6D0F308B8E} - {3ED553C4-3A63-4613-B979-472FDA5EA346} = {18791734-CA81-482D-964A-CA6D0F308B8E} - {89367194-A636-41B9-81F0-283DCB84C296} = {18791734-CA81-482D-964A-CA6D0F308B8E} {9A5FBAFF-EBE8-3156-5547-FB3ED1DEB545} = {80F3B34B-C334-44D2-A861-31FD403AD57D} {C2757FC0-54A9-BBD3-2E23-55F2F3912BA4} = {80F3B34B-C334-44D2-A861-31FD403AD57D} + {B6DDF83D-591E-38B6-2902-1624BE8AE9B9} = {18791734-CA81-482D-964A-CA6D0F308B8E} + {4B2C6EBE-E719-9F40-ADE6-C82DA632E554} = {18791734-CA81-482D-964A-CA6D0F308B8E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {379A56DA-D3F0-4E7E-8FF7-DA8E20015BF3} -- 2.36.2 From 85992efb2af1ab569cf4dc0b90cd42f64bfebb4a Mon Sep 17 00:00:00 2001 From: LiuXin Date: Wed, 13 Aug 2025 17:29:32 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AlarmService/AlarmService.API/AlarmService.API.csproj | 1 + WeiCloud.Fusion/VideoService/Video.API/Video.API.csproj | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/WeiCloud.Fusion/AlarmService/AlarmService.API/AlarmService.API.csproj b/WeiCloud.Fusion/AlarmService/AlarmService.API/AlarmService.API.csproj index 3e9bdc0..c867cbb 100644 --- a/WeiCloud.Fusion/AlarmService/AlarmService.API/AlarmService.API.csproj +++ b/WeiCloud.Fusion/AlarmService/AlarmService.API/AlarmService.API.csproj @@ -4,6 +4,7 @@ net8.0 enable enable + True diff --git a/WeiCloud.Fusion/VideoService/Video.API/Video.API.csproj b/WeiCloud.Fusion/VideoService/Video.API/Video.API.csproj index e36b91d..77b0250 100644 --- a/WeiCloud.Fusion/VideoService/Video.API/Video.API.csproj +++ b/WeiCloud.Fusion/VideoService/Video.API/Video.API.csproj @@ -9,7 +9,6 @@ - -- 2.36.2 From a1d097b29aa64dd97276688c752cbb8adae6116c Mon Sep 17 00:00:00 2001 From: LiuXin Date: Wed, 13 Aug 2025 21:17:42 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E8=B0=83=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DaHua/ResponeDto/DahuaVideoResDto.cs | 18 ++++-- .../DaHua/VideoManageController.cs | 13 +++- .../VideoService/Video.API/appsettings.json | 10 +-- .../RequestDto/DahuaVideoQueryDto.cs | 15 ++--- .../ResponeDto/DahuaVideoResDto.cs | 63 +++++++++++++++++++ .../Dahvision/DahuaGeneralCtlService.cs | 35 ++++++----- .../Dahvision/IRootVideoPlaybackService.cs | 2 +- .../Dahvision/RootVideoPlaybackService.cs | 7 ++- 8 files changed, 124 insertions(+), 39 deletions(-) diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/DaHua/ResponeDto/DahuaVideoResDto.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/DaHua/ResponeDto/DahuaVideoResDto.cs index 594fd95..b2bc4ea 100644 --- a/WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/DaHua/ResponeDto/DahuaVideoResDto.cs +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/DaHua/ResponeDto/DahuaVideoResDto.cs @@ -35,13 +35,13 @@ namespace Common.Shared.Application.DaHua public string ClientId { get; set; } /// - /// 授权范围,固定为 ["*"] + /// 授权范围 /// [JsonPropertyName("scope")] - public string[] Scope { get; set; } + public string Scope { get; set; } // 改为string类型 /// - /// access_token 有效期(秒),默认 2 小时(7200 秒) + /// access_token 有效期(秒) /// [JsonPropertyName("expires_in")] public long ExpiresIn { get; set; } @@ -50,19 +50,25 @@ namespace Common.Shared.Application.DaHua /// 鉴权 Token /// [JsonPropertyName("access_token")] - public string AccessToken { get; set; } + public string? AccessToken { get; set; } /// - /// 刷新 Token(有效期 1 天) + /// 刷新 Token /// [JsonPropertyName("refresh_token")] public string RefreshToken { get; set; } /// - /// Token 类型,固定为 "bearer" + /// Token 类型 /// [JsonPropertyName("token_type")] public string TokenType { get; set; } = "bearer"; + + /// + /// 剩余天数(新增字段) + /// + [JsonPropertyName("remainderDays")] + public int RemainderDays { get; set; } } /// diff --git a/WeiCloud.Fusion/VideoService/Video.API/Controllers/DaHua/VideoManageController.cs b/WeiCloud.Fusion/VideoService/Video.API/Controllers/DaHua/VideoManageController.cs index 382937b..cebc054 100644 --- a/WeiCloud.Fusion/VideoService/Video.API/Controllers/DaHua/VideoManageController.cs +++ b/WeiCloud.Fusion/VideoService/Video.API/Controllers/DaHua/VideoManageController.cs @@ -6,6 +6,9 @@ using WeiCloud.Core.BaseModels; namespace Video.API.Controllers.DaHua { + /// + /// 大华视频 + /// [Route("api/[controller]/[action]")] [ApiController] public class VideoManageController : ControllerBase @@ -14,6 +17,12 @@ namespace Video.API.Controllers.DaHua private readonly IRootVideoPlaybackService _rootVideoPlaybackService; private readonly IConfiguration _configuration; + /// + /// 构造 + /// + /// + /// + /// public VideoManageController(ILogger logger, IRootVideoPlaybackService rootVideoPlaybackService, IConfiguration configuration) { _logger = logger; @@ -29,9 +38,9 @@ namespace Video.API.Controllers.DaHua /// /// [HttpPost("token/dh")] - public async Task> GetDaHToken(LoginRequestDto dto) + public async Task> GetDaHToken() { - return await _rootVideoPlaybackService.GetDaHToken(dto); + return await _rootVideoPlaybackService.GetDaHToken(); } /// diff --git a/WeiCloud.Fusion/VideoService/Video.API/appsettings.json b/WeiCloud.Fusion/VideoService/Video.API/appsettings.json index 22333bd..1ab7d76 100644 --- a/WeiCloud.Fusion/VideoService/Video.API/appsettings.json +++ b/WeiCloud.Fusion/VideoService/Video.API/appsettings.json @@ -25,10 +25,10 @@ "VideoOpen": "1", //0表示部署视频对接,1表示不对接 //大华摄像头的配置 "DahuaAuth": { - "Host": "v4.weienergy.cn", - "ClientId": "test", - "ClientSecret": "", - "Username": "", - "Password": "" + "Host": "demo.weienergy.cn:15214", + "ClientId": "taiyanggong", + "ClientSecret": "6d6c78f8-3d4c-4e76-ab6b-827942a7b725", + "Username": "system", + "Password": "Admin123" } } \ No newline at end of file diff --git a/WeiCloud.Fusion/VideoService/Video.Application/RequestDto/DahuaVideoQueryDto.cs b/WeiCloud.Fusion/VideoService/Video.Application/RequestDto/DahuaVideoQueryDto.cs index f272dad..524565c 100644 --- a/WeiCloud.Fusion/VideoService/Video.Application/RequestDto/DahuaVideoQueryDto.cs +++ b/WeiCloud.Fusion/VideoService/Video.Application/RequestDto/DahuaVideoQueryDto.cs @@ -52,11 +52,11 @@ namespace Video.Application [JsonPropertyName("streamType")] public string StreamType { get; set; } - /// - /// 输出类型,如 "hls"、"rtmp" 等,如果RTSP的回放,不加此字段 - /// - [JsonPropertyName("type")] - public string? Type { get; set; } = "hls"; + ///// + ///// 输出类型,如 "hls"、"rtmp" 等,如果RTSP的回放,不加此字段 + ///// + //[JsonPropertyName("type")] + //public string? Type { get; set; } = ""; /// /// 录像类型:1-定时录像,2-移动侦测,3-报警录像等(字符串形式) @@ -81,11 +81,6 @@ namespace Video.Application /// [JsonPropertyName("recordSource")] public string RecordSource { get; set; } - - /// - /// 鉴权的token - /// - public string? Token { get; set; } } /// diff --git a/WeiCloud.Fusion/VideoService/Video.Application/ResponeDto/DahuaVideoResDto.cs b/WeiCloud.Fusion/VideoService/Video.Application/ResponeDto/DahuaVideoResDto.cs index fc82b08..98bfdb3 100644 --- a/WeiCloud.Fusion/VideoService/Video.Application/ResponeDto/DahuaVideoResDto.cs +++ b/WeiCloud.Fusion/VideoService/Video.Application/ResponeDto/DahuaVideoResDto.cs @@ -305,7 +305,70 @@ namespace Video.Application /// public class UrlDataDto { + /// + /// 最小速率 + /// + [JsonPropertyName("minRate")] + public object MinRate { get; set; } // 用object类型兼容null和可能的数值类型 + + /// + /// 协议类型 + /// + [JsonPropertyName("protocol")] + public string Protocol { get; set; } // 可为null + + /// + /// IP地址 + /// + [JsonPropertyName("ip")] + public string Ip { get; set; } // 可为null + + /// + /// 端口号 + /// + [JsonPropertyName("port")] + public object Port { get; set; } // 用object类型兼容null和可能的数值类型 + + /// + /// STUN启用状态 + /// + [JsonPropertyName("stunEnable")] + public bool? StunEnable { get; set; } // 可空布尔类型 + + /// + /// STUN端口 + /// + [JsonPropertyName("stunPort")] + public object StunPort { get; set; } // 用object类型兼容null和可能的数值类型 + + /// + /// RTSP地址 + /// [JsonPropertyName("url")] public string Url { get; set; } + + /// + /// 连接类型 + /// + [JsonPropertyName("connectType")] + public string ConnectType { get; set; } // 可为null + + /// + /// 会话标识 + /// + [JsonPropertyName("session")] + public string Session { get; set; } + + /// + /// 令牌 + /// + [JsonPropertyName("token")] + public string Token { get; set; } + + /// + /// 轨道标识 + /// + [JsonPropertyName("trackId")] + public string TrackId { get; set; } // 可为null } } \ No newline at end of file diff --git a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs index 993c9ce..9342094 100644 --- a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs +++ b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs @@ -14,15 +14,24 @@ namespace Video.DomainService { private readonly ILogger _logger; private readonly IConfiguration _configuration; - private readonly HttpClient _http; + // private readonly HttpClient _http; - public DahuaGeneralCtlService(ILogger logger, IConfiguration configuration, HttpClient http) + public DahuaGeneralCtlService(ILogger logger, IConfiguration configuration) { _logger = logger; _configuration = configuration; - _http = http; + + //_http = http; } + /// + /// 开发测试的时候,忽略证书 + /// + private static readonly HttpClient _http = new HttpClient(new HttpClientHandler + { + ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator + }); + /// /// 获取公钥 /// @@ -58,6 +67,9 @@ namespace Video.DomainService catch (Exception ex) { _logger.LogWarning(ex, "大华平台获取公钥出错"); + result.Success = false; + result.Code = "1001"; + result.Msg = "获取大华公钥失败"; } return result; } @@ -69,7 +81,7 @@ namespace Video.DomainService /// public async Task> GetToken(LoginRequestDto dto) { - DaHApiResult result = new() { Success = true, Code = "0" }; + DaHApiResult result = new() { Success = true, Code = "0", Data = new LoginResDto { } }; if (dto is null) { result.Success = false; @@ -104,8 +116,9 @@ namespace Video.DomainService result.Msg = "获取大华登录令牌失败"; _logger.LogWarning("获取大华登录令牌失败,返回结果:{Result}", result); } + result = tokenInfo!; //固定的拼接方式 - result.Data.AccessToken = string.Concat(tokenInfo!.Data.TokenType, " ", tokenInfo.Data.AccessToken); + result.Data.AccessToken = string.Concat(tokenInfo?.Data.TokenType, " ", tokenInfo?.Data.AccessToken); } catch (Exception ex) { @@ -136,9 +149,7 @@ namespace Video.DomainService { // 2) Token:优先入参,其次缓存/获取(建议返回完整的 "Bearer xxx") var clientId = _configuration["DahuaAuth:ClientId"]; - var token = string.IsNullOrWhiteSpace(dto.Token) - ? await GetCachedOrFetchTokenAsync(clientId) - : dto.Token; + var token = "12"; /*string.IsNullOrWhiteSpace(dto.Token) ? await GetCachedOrFetchTokenAsync(clientId) : dto.Token;*/ var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/video/stream/record"; @@ -190,9 +201,7 @@ namespace Video.DomainService } var clientId = _configuration["DahuaAuth:ClientId"]; - var token = string.IsNullOrWhiteSpace(dto.Token) - ? await GetCachedOrFetchTokenAsync(clientId) // 建议用这个轻量封装;返回完整 "Bearer xxx" - : dto.Token; + var token = "11"; /*string.IsNullOrWhiteSpace(dto.Token) ? await GetCachedOrFetchTokenAsync(clientId) : dto.Token;*/ var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/SS/Record/QueryRecords"; @@ -487,9 +496,7 @@ namespace Video.DomainService // 先用缓存里的 token,不足5分钟过期再刷新(按你之前的口径来) var clientId = _configuration["DahuaAuth:ClientId"]; - var token = string.IsNullOrWhiteSpace(dto.Token) - ? await GetCachedOrFetchTokenAsync(clientId) - : dto.Token; + var token = await GetCachedOrFetchTokenAsync(clientId);// string.IsNullOrWhiteSpace(dto.Token) ? await GetCachedOrFetchTokenAsync(clientId) : dto.Token; var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/video/stream/record"; diff --git a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/IRootVideoPlaybackService.cs b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/IRootVideoPlaybackService.cs index 2e1193c..867d5b6 100644 --- a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/IRootVideoPlaybackService.cs +++ b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/IRootVideoPlaybackService.cs @@ -18,7 +18,7 @@ namespace Video.DomainService /// /// /// - Task> GetDaHToken(LoginRequestDto dto); + Task> GetDaHToken(); /// /// 大华的实时视频 diff --git a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/RootVideoPlaybackService.cs b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/RootVideoPlaybackService.cs index c37f47f..5ee4aaa 100644 --- a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/RootVideoPlaybackService.cs +++ b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/RootVideoPlaybackService.cs @@ -55,7 +55,7 @@ namespace Video.DomainService /// /// /// - public async Task> GetDaHToken(LoginRequestDto dto) + public async Task> GetDaHToken() { ApiResult result = new() { Code = 200, Msg = "接口调用成功" }; //1. 获取公钥 @@ -66,8 +66,13 @@ namespace Video.DomainService result.Msg = publicKeyResult.Msg; _logger.LogWarning("获取大华公钥失败:{Msg}", publicKeyResult.Msg); } + LoginRequestDto dto = new(); //2. 鉴权 dto.PublicKey = publicKeyResult.Data.PublicKey; + dto.ClientId = _cfg["DahuaAuth:ClientId"]!; + dto.ClientSecret = _cfg["DahuaAuth:ClientSecret"]!; + dto.Password = _cfg["DahuaAuth:Password"]!; + dto.Username = _cfg["DahuaAuth:Username"]!; DaHApiResult loginResult = await _dahuaGeneralCtlService.GetToken(dto); if (!loginResult.Success) -- 2.36.2 From 21a29230bb4b5fe77eb0b4c21cfd8d11edbc9659 Mon Sep 17 00:00:00 2001 From: LiuXin Date: Thu, 14 Aug 2025 11:42:12 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E9=87=8D=E6=9E=84=E5=A4=A7=E5=8D=8E?= =?UTF-8?q?=E7=9A=84token=E8=8E=B7=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Manage.AppHost.AppHost/AppHost.cs | 3 + .../Manage.AppHost.AppHost.csproj | 1 + .../Common.Shared.API.csproj | 13 + .../Common.Shared.API/Common.Shared.API.http | 6 + .../Common.Shared.API/Program.cs | 27 ++ .../Properties/launchSettings.json | 41 ++ .../Common.Shared.API/WeatherForecast.cs | 13 + .../Common.Shared.API/appsettings.json | 17 + ...VideoQueryDto.cs => DahuaTokenQueryDto.cs} | 30 ++ ...ahuaVideoResDto.cs => DahuaTokenResDto.cs} | 0 .../DaHTokenService/ITokenProviderService.cs | 13 + .../DaHTokenService/TokenCacheService.cs | 35 ++ .../DaHTokenService/TokenProviderService.cs | 350 +++++++++++++++++ .../DaHua/VideoManageController.cs | 13 +- .../HostService/DahuaTokenRefreshJob.cs | 50 --- .../VideoService/Video.API/Program.cs | 27 +- .../VideoService/Video.API/Video.API.csproj | 4 + .../VideoService/Video.API/appsettings.json | 5 +- .../RequestDto/DahuaVideoQueryDto.cs | 61 +-- .../Dahvision/DahuaGeneralCtlService.cs | 367 +----------------- .../Dahvision/IDahuaGeneralCtlService.cs | 30 +- .../Dahvision/IRootVideoPlaybackService.cs | 9 +- .../Dahvision/RootVideoPlaybackService.cs | 46 +-- .../Dahvision/TokenCacheService.cs | 19 - .../Video.DomainService.csproj | 1 + WeiCloud.Fusion/WeiCloud.Fusion.sln | 7 + 26 files changed, 618 insertions(+), 570 deletions(-) create mode 100644 WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Common.Shared.API.csproj create mode 100644 WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Common.Shared.API.http create mode 100644 WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Program.cs create mode 100644 WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Properties/launchSettings.json create mode 100644 WeiCloud.Fusion/Common.SharedService/Common.Shared.API/WeatherForecast.cs create mode 100644 WeiCloud.Fusion/Common.SharedService/Common.Shared.API/appsettings.json rename WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/DaHua/RequestDto/{DahuaVideoQueryDto.cs => DahuaTokenQueryDto.cs} (66%) rename WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/DaHua/ResponeDto/{DahuaVideoResDto.cs => DahuaTokenResDto.cs} (100%) create mode 100644 WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/ITokenProviderService.cs create mode 100644 WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/TokenCacheService.cs create mode 100644 WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/TokenProviderService.cs delete mode 100644 WeiCloud.Fusion/VideoService/Video.API/HostService/DahuaTokenRefreshJob.cs delete mode 100644 WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/TokenCacheService.cs diff --git a/WeiCloud.Fusion/AspireApp/Manage.AppHost.AppHost/AppHost.cs b/WeiCloud.Fusion/AspireApp/Manage.AppHost.AppHost/AppHost.cs index 7128e49..d3e2148 100644 --- a/WeiCloud.Fusion/AspireApp/Manage.AppHost.AppHost/AppHost.cs +++ b/WeiCloud.Fusion/AspireApp/Manage.AppHost.AppHost/AppHost.cs @@ -6,9 +6,12 @@ var apiService = builder.AddProject("apiserv var videoapi = builder.AddProject("videoapi"); var alarmapi = builder.AddProject("alarmapi"); +var sharedapi = builder.AddProject("sharedapi"); + builder.AddProject("webfrontend") .WithReference(videoapi) .WithReference(alarmapi) + .WithReference(sharedapi) .WithExternalHttpEndpoints() .WithHttpHealthCheck("/health") .WithReference(apiService) diff --git a/WeiCloud.Fusion/AspireApp/Manage.AppHost.AppHost/Manage.AppHost.AppHost.csproj b/WeiCloud.Fusion/AspireApp/Manage.AppHost.AppHost/Manage.AppHost.AppHost.csproj index ed94359..716dcc0 100644 --- a/WeiCloud.Fusion/AspireApp/Manage.AppHost.AppHost/Manage.AppHost.AppHost.csproj +++ b/WeiCloud.Fusion/AspireApp/Manage.AppHost.AppHost/Manage.AppHost.AppHost.csproj @@ -12,6 +12,7 @@ + diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Common.Shared.API.csproj b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Common.Shared.API.csproj new file mode 100644 index 0000000..cb23834 --- /dev/null +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Common.Shared.API.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Common.Shared.API.http b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Common.Shared.API.http new file mode 100644 index 0000000..24bd5d3 --- /dev/null +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Common.Shared.API.http @@ -0,0 +1,6 @@ +@Common.Shared.API_HostAddress = http://localhost:5258 + +GET {{Common.Shared.API_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Program.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Program.cs new file mode 100644 index 0000000..de3db9d --- /dev/null +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Program.cs @@ -0,0 +1,27 @@ +namespace Common.Shared.API +{ + public class Program + { + public static void Main(string[] args) + { + var builder = WebApplication.CreateBuilder(args); + + // Add services to the container. + + builder.Services.AddControllers(); + + var app = builder.Build(); + + // Configure the HTTP request pipeline. + + app.UseHttpsRedirection(); + + app.UseAuthorization(); + + + app.MapControllers(); + + app.Run(); + } + } +} diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Properties/launchSettings.json b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Properties/launchSettings.json new file mode 100644 index 0000000..85ae56e --- /dev/null +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:25714", + "sslPort": 44346 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "weatherforecast", + "applicationUrl": "http://localhost:5258", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "weatherforecast", + "applicationUrl": "https://localhost:7150;http://localhost:5258", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "weatherforecast", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/WeatherForecast.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/WeatherForecast.cs new file mode 100644 index 0000000..2af2eee --- /dev/null +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/WeatherForecast.cs @@ -0,0 +1,13 @@ +namespace Common.Shared.API +{ + public class WeatherForecast + { + public DateOnly Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string? Summary { get; set; } + } +} diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/appsettings.json b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/appsettings.json new file mode 100644 index 0000000..cc0cc39 --- /dev/null +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/appsettings.json @@ -0,0 +1,17 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + //大华摄像头的配置 + "DahuaAuth": { + "Host": "demo.weienergy.cn:15214", + "ClientId": "taiyanggong", + "ClientSecret": "6d6c78f8-3d4c-4e76-ab6b-827942a7b725", + "Username": "system", + "Password": "Admin123" + }, + "AllowedHosts": "*" +} \ No newline at end of file diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/DaHua/RequestDto/DahuaVideoQueryDto.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/DaHua/RequestDto/DahuaTokenQueryDto.cs similarity index 66% rename from WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/DaHua/RequestDto/DahuaVideoQueryDto.cs rename to WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/DaHua/RequestDto/DahuaTokenQueryDto.cs index b372dd6..53d3676 100644 --- a/WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/DaHua/RequestDto/DahuaVideoQueryDto.cs +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/DaHua/RequestDto/DahuaTokenQueryDto.cs @@ -59,4 +59,34 @@ namespace Common.Shared.Application.DaHua [JsonPropertyName("verifyCodeFlag")] public int VerifyCodeFlag { get; set; } = 0; } + + /// + /// 刷新 access_token 的请求参数模型 + /// + public class RefreshTokenReqDto + { + /// + /// 认证类型,固定值:refresh_token + /// + [JsonPropertyName("grant_type")] + public string GrantType { get; set; } = "refresh_token"; // 默认值,通常固定 + + /// + /// 客户端ID(与认证接口中一致) + /// + [JsonPropertyName("client_id")] + public string ClientId { get; set; } + + /// + /// 客户端密钥(与认证接口中一致) + /// + [JsonPropertyName("client_secret")] + public string ClientSecret { get; set; } + + /// + /// 刷新令牌(refresh_token),用于获取新的 access_token + /// + [JsonPropertyName("refresh_token")] + public string RefreshToken { get; set; } + } } \ No newline at end of file diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/DaHua/ResponeDto/DahuaVideoResDto.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/DaHua/ResponeDto/DahuaTokenResDto.cs similarity index 100% rename from WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/DaHua/ResponeDto/DahuaVideoResDto.cs rename to WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/DaHua/ResponeDto/DahuaTokenResDto.cs diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/ITokenProviderService.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/ITokenProviderService.cs new file mode 100644 index 0000000..7c35a70 --- /dev/null +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/ITokenProviderService.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Common.Shared.DomainService.DaHToken +{ + public interface ITokenProviderService + { + Task GetTokenAsync(string clientId); + } +} \ No newline at end of file diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/TokenCacheService.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/TokenCacheService.cs new file mode 100644 index 0000000..1725135 --- /dev/null +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/TokenCacheService.cs @@ -0,0 +1,35 @@ +using System.Collections.Concurrent; + +namespace Common.Shared.DomainService +{ + public static class TokenCache + { + public static readonly ConcurrentDictionary TokenMap = new(); + } + + public static class TokenLockProvider + { + private static readonly ConcurrentDictionary LockMap = new(); + + public static SemaphoreSlim GetLock(string key) + { + return LockMap.GetOrAdd(key, _ => new SemaphoreSlim(1, 1)); + } + } + + /// + /// 用于token缓存的条目类 + /// + public class TokenEntry + { + /// + /// token:这是token_type + 空格 + access_token这样格式的 + /// + public string? AccessToken { get; set; } + + /// + /// 添加时间 + /// + public DateTimeOffset ExpireAt { get; set; } + } +} \ No newline at end of file diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/TokenProviderService.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/TokenProviderService.cs new file mode 100644 index 0000000..adcdf37 --- /dev/null +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/TokenProviderService.cs @@ -0,0 +1,350 @@ +using Common.Shared.Application.DaHua; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using System.Net.Http.Json; +using System.Security.Cryptography; +using System.Text.Json; +using static System.Net.WebRequestMethods; + +namespace Common.Shared.DomainService.DaHToken +{ + /// + /// 获取大华icc平台的token服务 + /// + public class TokenProviderService : ITokenProviderService + { + private readonly IConfiguration _configuration; + private readonly ILogger _logger; + + public TokenProviderService(IConfiguration configuration, ILogger logger) + { + _configuration = configuration; + _logger = logger; + } + + /// + /// 开发测试的时候,忽略证书 + /// + private static readonly HttpClient _http = new HttpClient(new HttpClientHandler + { + ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator + }); + + public async Task GetTokenAsync(string clientId) + { + if (TokenCache.TokenMap.TryGetValue(clientId, out var tokenEntry) + && tokenEntry.ExpireAt > DateTimeOffset.UtcNow.AddMinutes(5)) + { + return tokenEntry.AccessToken!; + } + + var tokenLock = TokenLockProvider.GetLock(clientId); + await tokenLock.WaitAsync(); + try + { + // 加锁后再次检查,防止重复刷新 + if (TokenCache.TokenMap.TryGetValue(clientId, out tokenEntry) + && tokenEntry.ExpireAt > DateTimeOffset.UtcNow.AddMinutes(5)) + { + return tokenEntry.AccessToken!; + } + + var refreshed = await TryRefreshOrLoginAsync(clientId, tokenEntry); + return refreshed.AccessToken!; + } + finally + { + tokenLock.Release(); + } + } + + private async Task TryRefreshOrLoginAsync(string clientId, TokenEntry? current) + { + try + { + TokenEntry refreshed; + + if (current?.AccessToken is { } refreshToken) + { + var dto = new RefreshTokenReqDto + { + ClientId = clientId, + ClientSecret = _configuration["DahuaAuth:ClientSecret"]!, + GrantType = "refresh_token", + //刷新要求去掉 Bearer 前缀 + RefreshToken = refreshToken.Replace("Bearer ", string.Empty) + }; + + var result = await this.RefreshToken(dto); + + if (result?.Data != null) + { + refreshed = new TokenEntry + { + AccessToken = string.Concat(result.Data.TokenType, " ", result.Data.AccessToken), + + ExpireAt = DateTimeOffset.UtcNow.AddSeconds(result.Data.ExpiresIn) + }; + _logger.LogInformation("Refresh 成功: {ClientId}", clientId); + } + else + { + _logger.LogWarning("RefreshToken 失败,尝试重新登录"); + refreshed = await GetDaHToken(); + } + } + else + { + refreshed = await GetDaHToken(); + } + + // 更新缓存 + TokenCache.TokenMap[clientId] = refreshed; + return refreshed; + } + catch (Exception ex) + { + _logger.LogError(ex, "获取 token 异常:{ClientId}", clientId); + return new TokenEntry + { + AccessToken = string.Empty, + ExpireAt = DateTimeOffset.UtcNow.AddMinutes(1) + }; + } + } + + private async Task GetDaHToken() + { + //1. 获取公钥 + DaHApiResult publicKeyResult = await GetPublicKey(); + + LoginRequestDto dto = new(); + //2. 鉴权 + dto.PublicKey = publicKeyResult.Data.PublicKey; + dto.ClientId = _configuration["DahuaAuth:ClientId"]!; + dto.ClientSecret = _configuration["DahuaAuth:ClientSecret"]!; + dto.Password = _configuration["DahuaAuth:Password"]!; + dto.Username = _configuration["DahuaAuth:Username"]!; + + DaHApiResult loginResult = await GetToken(dto); + + TokenEntry refreshed = new() + { + AccessToken = string.Concat(loginResult!.Data.TokenType, " ", loginResult.Data.AccessToken), + ExpireAt = DateTimeOffset.UtcNow.AddSeconds(120) + }; + return refreshed; + } + + /// + /// 刷新token,2个小时过期的 + /// + /// + /// + /// + /// + /// + private async Task> RefreshToken(RefreshTokenReqDto dto) + { + DaHApiResult result = new DaHApiResult() { Success = true, Code = "0" }; + + if (string.IsNullOrWhiteSpace(dto.RefreshToken)) + { + result.Success = false; + result.Code = "1005"; + result.Msg = "刷新令牌不能为空"; + _logger.LogWarning("刷新大华令牌失败,刷新令牌不能为空"); + return result; + } + + try + { + var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/evo-oauth/1.0.0/oauth/extend/refresh/token"; + + using var resp = await _http.PostAsJsonAsync(url, dto); + resp.EnsureSuccessStatusCode(); + + result = await resp.Content.ReadFromJsonAsync>(); + + if (!result.Success || result.Code != "0") + { + result.Success = false; + result.Code = "1006"; + result.Msg = "刷新大华令牌失败"; + _logger.LogWarning("刷新大华令牌失败,返回结果:{Result}", result); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "刷新大华令牌出错"); + result.Success = false; + result.Code = "1006"; + result.Msg = "刷新大华令牌失败"; + } + return result; + } + + /// + /// 获取公钥 + /// + /// + /// + private async Task> GetPublicKey() + { + DaHApiResult result = new() { Success = true, Code = "0", Data = new PublicKeyDto() { PublicKey = "" } }; + try + { + var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/evo-oauth/1.0.0/oauth/public-key"; + + using var resp = await _http.GetAsync(url); + resp.EnsureSuccessStatusCode(); + var json = await resp.Content.ReadAsStringAsync(); + var envelope = JsonSerializer.Deserialize>(json, new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }); + + if (envelope?.Data?.PublicKey is null or { Length: 0 }) + { + _logger.LogWarning("获取大华公钥失败,返回结果:{Result}", json); + result.Success = false; + result.Code = "1001"; + result.Msg = "获取大华公钥失败"; + + return result; + } + + result.Data.PublicKey = envelope.Data.PublicKey; + } + catch (Exception ex) + { + _logger.LogWarning(ex, "大华平台获取公钥出错"); + result.Success = false; + result.Code = "1001"; + result.Msg = "获取大华公钥失败"; + } + return result; + } + + /// + /// 获取token + /// + /// + /// + private async Task> GetToken(LoginRequestDto dto) + { + DaHApiResult result = new() { Success = true, Code = "0", Data = new LoginResDto { } }; + if (dto is null) + { + result.Success = false; + + result.Code = "1002"; + result.Msg = "请求参数不能为空"; + _logger.LogWarning("获取大华登录令牌失败,参数不能为空"); + return result; + } + if (string.IsNullOrWhiteSpace(dto.Password)) + { + result.Success = false; + result.Code = "1003"; + result.Msg = "密码不能为空"; + _logger.LogWarning("获取大华登录令牌失败,密码不能为空"); + return result; + } + try + { + var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/evo-oauth/1.0.0/oauth/extend/token"; + //必须加密 + dto.Password = EncryptByPublicKey(dto.Password, dto.PublicKey!); + using var resp = await _http.PostAsJsonAsync(url, dto); + resp.EnsureSuccessStatusCode(); + + var tokenInfo = await resp.Content.ReadFromJsonAsync>(); + + if (tokenInfo == null || !result.Success || result.Code != "0") + { + result.Success = false; + result.Code = "1004"; + result.Msg = "获取大华登录令牌失败"; + _logger.LogWarning("获取大华登录令牌失败,返回结果:{Result}", result); + } + result = tokenInfo!; + //固定的拼接方式 + result.Data.AccessToken = string.Concat(tokenInfo?.Data.TokenType, " ", tokenInfo?.Data.AccessToken); + + TokenEntry refreshed = new TokenEntry + { + AccessToken = string.Concat(result!.Data.TokenType, " ", result.Data.AccessToken), + + ExpireAt = DateTimeOffset.UtcNow.AddSeconds(result.Data.ExpiresIn) + }; + } + catch (Exception ex) + { + _logger.LogError(ex, "获取大华登录令牌出错"); + result.Success = false; + result.Code = "1004"; + result.Msg = "获取大华登录令牌失败"; + } + return result; + } + + #region RES加密 + + private static String EncryptByPublicKey(String context, String publicKey) + { + RSACryptoServiceProvider rsa = new(); + + rsa.ImportParameters(FromXmlStringExtensions(ConvertToXmlPublicJavaKey(publicKey))); + byte[] byteText = System.Text.Encoding.UTF8.GetBytes(context); + byte[] byteEntry = rsa.Encrypt(byteText, false); + return Convert.ToBase64String(byteEntry); + } + + private static RSAParameters FromXmlStringExtensions(string xmlString) + { + RSAParameters parameters = new RSAParameters(); + + System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument(); + xmlDoc.LoadXml(xmlString); + + if (xmlDoc.DocumentElement!.Name.Equals("RSAKeyValue")) + { + foreach (System.Xml.XmlNode node in xmlDoc.DocumentElement.ChildNodes) + { + switch (node.Name) + { + case "Modulus": parameters.Modulus = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; + case "Exponent": parameters.Exponent = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; + case "P": parameters.P = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; + case "Q": parameters.Q = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; + case "DP": parameters.DP = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; + case "DQ": parameters.DQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; + case "InverseQ": parameters.InverseQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; + case "D": parameters.D = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; + } + } + } + else + { + throw new Exception("Invalid XML RSA key."); + } + + return parameters; + } + + private static string ConvertToXmlPublicJavaKey(string publicJavaKey) + { + RsaKeyParameters publicKeyParam = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(publicJavaKey)); + string xmlpublicKey = string.Format("{0}{1}", + Convert.ToBase64String(publicKeyParam.Modulus.ToByteArrayUnsigned()), + Convert.ToBase64String(publicKeyParam.Exponent.ToByteArrayUnsigned())); + Console.WriteLine(xmlpublicKey); + return xmlpublicKey; + } + + #endregion RES加密 + } +} \ No newline at end of file diff --git a/WeiCloud.Fusion/VideoService/Video.API/Controllers/DaHua/VideoManageController.cs b/WeiCloud.Fusion/VideoService/Video.API/Controllers/DaHua/VideoManageController.cs index cebc054..a160f72 100644 --- a/WeiCloud.Fusion/VideoService/Video.API/Controllers/DaHua/VideoManageController.cs +++ b/WeiCloud.Fusion/VideoService/Video.API/Controllers/DaHua/VideoManageController.cs @@ -32,17 +32,6 @@ namespace Video.API.Controllers.DaHua #region 大华视频处理 - /// - /// 大华视频的登录获取Token - /// - /// - /// - [HttpPost("token/dh")] - public async Task> GetDaHToken() - { - return await _rootVideoPlaybackService.GetDaHToken(); - } - /// /// 大华视频回放 /// @@ -60,7 +49,7 @@ namespace Video.API.Controllers.DaHua /// /// [HttpPost("rtspplayback/dh")] - public async Task> RtspPlaybackByTime(PlaybackReqDto dto) + public async Task> RtspPlaybackByTime(RtspPlaybackReqDto dto) { return await _rootVideoPlaybackService.RtspPlaybackByTime(dto); } diff --git a/WeiCloud.Fusion/VideoService/Video.API/HostService/DahuaTokenRefreshJob.cs b/WeiCloud.Fusion/VideoService/Video.API/HostService/DahuaTokenRefreshJob.cs deleted file mode 100644 index 1fd6bd0..0000000 --- a/WeiCloud.Fusion/VideoService/Video.API/HostService/DahuaTokenRefreshJob.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Quartz; -using Video.DomainService; - -namespace Video.API.HostService -{ - /// - /// 大华的token的刷新任务 - /// - public class DahuaTokenRefreshJob : IJob - { - private readonly ILogger _logger; - private readonly IDahuaGeneralCtlService _dahuaGeneralCtlService; - private readonly IConfiguration _configuration; - - public DahuaTokenRefreshJob(IDahuaGeneralCtlService dahuaGeneralCtlService, ILogger logger, IConfiguration configuration) - { - _dahuaGeneralCtlService = dahuaGeneralCtlService; - _logger = logger; - _configuration = configuration; - } - - public async Task Execute(IJobExecutionContext context) - { - if (_configuration["VideoOpen"] == "0") - { - if (TokenCache.TokenMap.Keys.Count > 0) - { - foreach (var clientId in TokenCache.TokenMap.Keys) - { - try - { - var token = await _dahuaGeneralCtlService.GetAccessTokenAsync(clientId); - _logger.LogInformation("刷新 token 成功:{ClientId} -> {Len}", clientId, token.Length); - } - catch (Exception ex) - { - _logger.LogWarning(ex, "刷新 token 失败:{ClientId}", clientId); - Console.WriteLine(ex.Message); - } - } - } - else - { - var token = await _dahuaGeneralCtlService.GetAccessTokenAsync(_configuration["DahuaAuth:ClientId"]); - _logger.LogInformation("刷新 token 成功:{ClientId} -> {Len}", _configuration["DahuaAuth:ClientId"], token.Length); - } - } - } - } -} \ No newline at end of file diff --git a/WeiCloud.Fusion/VideoService/Video.API/Program.cs b/WeiCloud.Fusion/VideoService/Video.API/Program.cs index dfb300b..2eafd59 100644 --- a/WeiCloud.Fusion/VideoService/Video.API/Program.cs +++ b/WeiCloud.Fusion/VideoService/Video.API/Program.cs @@ -1,5 +1,6 @@ using Autofac; using Autofac.Extensions.DependencyInjection; +using Common.Shared.DomainService.DaHToken; using Microsoft.OpenApi.Models; using NLog; using NLog.Extensions.Logging; @@ -7,7 +8,6 @@ using NLog.Web; using Quartz; using Quartz.Simpl; using System.Reflection; -using Video.API.HostService; using Video.API.Infrastructure; using Video.Application; @@ -113,31 +113,18 @@ namespace Video.API #endregion CAPע - #region ʱ - - builder.Services.AddQuartz(q => - { - q.UseJobFactory(); - - // DahuaToken ˢ - var jobKey2 = new JobKey("DahuaTokenRefresh"); - q.AddJob(opts => opts.WithIdentity(jobKey2)); - q.AddTrigger(opts => opts - .ForJob(jobKey2) - .WithIdentity("DahuaTokenRefresh-trigger") - .WithSimpleSchedule(x => x.WithInterval(TimeSpan.FromMinutes(120)).RepeatForever())); - }); - - builder.Services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true); - - #endregion ʱ - // ȫĬС builder.WebHost.ConfigureKestrel(options => { options.Limits.MaxRequestBodySize = 200 * 1024 * 1024; // Ĭ 200MB }); + #region עtoken + + builder.Services.AddSingleton(); + + #endregion עtoken + var app = builder.Build(); if (app.Environment.IsDevelopment()) diff --git a/WeiCloud.Fusion/VideoService/Video.API/Video.API.csproj b/WeiCloud.Fusion/VideoService/Video.API/Video.API.csproj index 77b0250..8eaeee2 100644 --- a/WeiCloud.Fusion/VideoService/Video.API/Video.API.csproj +++ b/WeiCloud.Fusion/VideoService/Video.API/Video.API.csproj @@ -28,4 +28,8 @@ + + + + diff --git a/WeiCloud.Fusion/VideoService/Video.API/appsettings.json b/WeiCloud.Fusion/VideoService/Video.API/appsettings.json index 1ab7d76..9e1987e 100644 --- a/WeiCloud.Fusion/VideoService/Video.API/appsettings.json +++ b/WeiCloud.Fusion/VideoService/Video.API/appsettings.json @@ -26,9 +26,6 @@ //大华摄像头的配置 "DahuaAuth": { "Host": "demo.weienergy.cn:15214", - "ClientId": "taiyanggong", - "ClientSecret": "6d6c78f8-3d4c-4e76-ab6b-827942a7b725", - "Username": "system", - "Password": "Admin123" + "ClientId": "taiyanggong" } } \ No newline at end of file diff --git a/WeiCloud.Fusion/VideoService/Video.Application/RequestDto/DahuaVideoQueryDto.cs b/WeiCloud.Fusion/VideoService/Video.Application/RequestDto/DahuaVideoQueryDto.cs index 524565c..934e2a9 100644 --- a/WeiCloud.Fusion/VideoService/Video.Application/RequestDto/DahuaVideoQueryDto.cs +++ b/WeiCloud.Fusion/VideoService/Video.Application/RequestDto/DahuaVideoQueryDto.cs @@ -6,39 +6,57 @@ using System.Text.Json.Serialization; namespace Video.Application { /// - /// 刷新 access_token 的请求参数模型 + /// hls、rtmp回放请求的数据部分 /// - public class RefreshTokenReqDto + public class PlaybackReqDto { /// - /// 认证类型,固定值:refresh_token + /// 通道ID(格式如:1000018$1$0$0) /// - [JsonPropertyName("grant_type")] - public string GrantType { get; set; } = "refresh_token"; // 默认值,通常固定 + [JsonPropertyName("channelId")] + public string ChannelId { get; set; } + + /// + /// 流类型:1-主码流,2-子码流(通常为字符串或数字) + /// + [JsonPropertyName("streamType")] + public string StreamType { get; set; } /// - /// 客户端ID(与认证接口中一致) + /// 输出类型,如 "hls"、"rtmp" 等,如果RTSP的回放,不加此字段 /// - [JsonPropertyName("client_id")] - public string ClientId { get; set; } + [JsonPropertyName("type")] + public string? Type { get; set; } = "hls"; /// - /// 客户端密钥(与认证接口中一致) + /// 录像类型:1-定时录像,2-移动侦测,3-报警录像等(字符串形式) /// - [JsonPropertyName("client_secret")] - public string ClientSecret { get; set; } + [JsonPropertyName("recordType")] + public string RecordType { get; set; } /// - /// 刷新令牌(refresh_token),用于获取新的 access_token + /// 回放开始时间,格式:"yyyy-M-d HH:mm:ss" /// - [JsonPropertyName("refresh_token")] - public string RefreshToken { get; set; } + [JsonPropertyName("beginTime")] + public string BeginTime { get; set; } + + /// + /// 回放结束时间,格式:"yyyy-M-d HH:mm:ss" + /// + [JsonPropertyName("endTime")] + public string EndTime { get; set; } + + /// + /// 录像来源:1-设备,2-平台,3-云端等 + /// + [JsonPropertyName("recordSource")] + public string RecordSource { get; set; } } /// - /// 回放请求的数据部分 + /// hls、rtmp回放请求的数据部分 /// - public class PlaybackReqDto + public class RtspPlaybackReqDto { /// /// 通道ID(格式如:1000018$1$0$0) @@ -52,12 +70,6 @@ namespace Video.Application [JsonPropertyName("streamType")] public string StreamType { get; set; } - ///// - ///// 输出类型,如 "hls"、"rtmp" 等,如果RTSP的回放,不加此字段 - ///// - //[JsonPropertyName("type")] - //public string? Type { get; set; } = ""; - /// /// 录像类型:1-定时录像,2-移动侦测,3-报警录像等(字符串形式) /// @@ -224,11 +236,6 @@ namespace Video.Application /// [JsonPropertyName("dataType")] public string? DataType { get; set; } - - /// - /// 请求头认证 - /// - public string? Token { get; set; } } /// diff --git a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs index 9342094..547fcbe 100644 --- a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs +++ b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs @@ -1,4 +1,5 @@ using Common.Shared.Application.DaHua; +using Common.Shared.DomainService.DaHToken; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Org.BouncyCastle.Crypto.Parameters; @@ -14,12 +15,14 @@ namespace Video.DomainService { private readonly ILogger _logger; private readonly IConfiguration _configuration; + private readonly ITokenProviderService _tokenProviderService; // private readonly HttpClient _http; - public DahuaGeneralCtlService(ILogger logger, IConfiguration configuration) + public DahuaGeneralCtlService(ILogger logger, IConfiguration configuration, ITokenProviderService tokenProviderService) { _logger = logger; _configuration = configuration; + _tokenProviderService = tokenProviderService; //_http = http; } @@ -32,104 +35,6 @@ namespace Video.DomainService ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator }); - /// - /// 获取公钥 - /// - /// - /// - public async Task> GetPublicKey() - { - DaHApiResult result = new() { Success = true, Code = "0", Data = new PublicKeyDto() { PublicKey = "" } }; - try - { - var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/evo-oauth/1.0.0/oauth/public-key"; - - using var resp = await _http.GetAsync(url); - resp.EnsureSuccessStatusCode(); - var json = await resp.Content.ReadAsStringAsync(); - var envelope = JsonSerializer.Deserialize>(json, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); - - if (envelope?.Data?.PublicKey is null or { Length: 0 }) - { - _logger.LogWarning("获取大华公钥失败,返回结果:{Result}", json); - result.Success = false; - result.Code = "1001"; - result.Msg = "获取大华公钥失败"; - - return result; - } - - result.Data.PublicKey = envelope.Data.PublicKey; - } - catch (Exception ex) - { - _logger.LogWarning(ex, "大华平台获取公钥出错"); - result.Success = false; - result.Code = "1001"; - result.Msg = "获取大华公钥失败"; - } - return result; - } - - /// - /// 获取token - /// - /// - /// - public async Task> GetToken(LoginRequestDto dto) - { - DaHApiResult result = new() { Success = true, Code = "0", Data = new LoginResDto { } }; - if (dto is null) - { - result.Success = false; - - result.Code = "1002"; - result.Msg = "请求参数不能为空"; - _logger.LogWarning("获取大华登录令牌失败,参数不能为空"); - return result; - } - if (string.IsNullOrWhiteSpace(dto.Password)) - { - result.Success = false; - result.Code = "1003"; - result.Msg = "密码不能为空"; - _logger.LogWarning("获取大华登录令牌失败,密码不能为空"); - return result; - } - try - { - var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/evo-oauth/1.0.0/oauth/extend/token"; - //必须加密 - dto.Password = EncryptByPublicKey(dto.Password, dto.PublicKey!); - using var resp = await _http.PostAsJsonAsync(url, dto); - resp.EnsureSuccessStatusCode(); - - var tokenInfo = await resp.Content.ReadFromJsonAsync>(); - - if (tokenInfo == null || !result.Success || result.Code != "0") - { - result.Success = false; - result.Code = "1004"; - result.Msg = "获取大华登录令牌失败"; - _logger.LogWarning("获取大华登录令牌失败,返回结果:{Result}", result); - } - result = tokenInfo!; - //固定的拼接方式 - result.Data.AccessToken = string.Concat(tokenInfo?.Data.TokenType, " ", tokenInfo?.Data.AccessToken); - } - catch (Exception ex) - { - _logger.LogError(ex, "获取大华登录令牌出错"); - result.Success = false; - result.Code = "1004"; - result.Msg = "获取大华登录令牌失败"; - } - return result; - } - /// /// 录像回放 /// @@ -149,7 +54,7 @@ namespace Video.DomainService { // 2) Token:优先入参,其次缓存/获取(建议返回完整的 "Bearer xxx") var clientId = _configuration["DahuaAuth:ClientId"]; - var token = "12"; /*string.IsNullOrWhiteSpace(dto.Token) ? await GetCachedOrFetchTokenAsync(clientId) : dto.Token;*/ + var token = await _tokenProviderService.GetTokenAsync(clientId!); var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/video/stream/record"; @@ -201,7 +106,7 @@ namespace Video.DomainService } var clientId = _configuration["DahuaAuth:ClientId"]; - var token = "11"; /*string.IsNullOrWhiteSpace(dto.Token) ? await GetCachedOrFetchTokenAsync(clientId) : dto.Token;*/ + var token = await _tokenProviderService.GetTokenAsync(clientId!); var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/SS/Record/QueryRecords"; @@ -238,54 +143,6 @@ namespace Video.DomainService } } - /// - /// 刷新token,2个小时过期的 - /// - /// - /// - /// - /// - /// - public async Task> RefreshToken(RefreshTokenReqDto dto) - { - DaHApiResult result = new DaHApiResult() { Success = true, Code = "0" }; - - if (string.IsNullOrWhiteSpace(dto.RefreshToken)) - { - result.Success = false; - result.Code = "1005"; - result.Msg = "刷新令牌不能为空"; - _logger.LogWarning("刷新大华令牌失败,刷新令牌不能为空"); - return result; - } - - try - { - var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/evo-oauth/1.0.0/oauth/extend/refresh/token"; - - using var resp = await _http.PostAsJsonAsync(url, dto); - resp.EnsureSuccessStatusCode(); - - result = await resp.Content.ReadFromJsonAsync>(); - - if (!result.Success || result.Code != "0") - { - result.Success = false; - result.Code = "1006"; - result.Msg = "刷新大华令牌失败"; - _logger.LogWarning("刷新大华令牌失败,返回结果:{Result}", result); - } - } - catch (Exception ex) - { - _logger.LogError(ex, "刷新大华令牌出错"); - result.Success = false; - result.Code = "1006"; - result.Msg = "刷新大华令牌失败"; - } - return result; - } - /// /// 设备通道分页查询 /// @@ -302,9 +159,7 @@ namespace Video.DomainService } var clientId = _configuration["DahuaAuth:ClientId"]; - var token = string.IsNullOrWhiteSpace(dto.Token) - ? await GetCachedOrFetchTokenAsync(clientId) // 建议统一用这个轻量封装 - : dto.Token; // 约定这里是完整 "Bearer xxx" + var token = await _tokenProviderService.GetTokenAsync(clientId!); var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/evo-brm/1.2.0/device/channel/subsystem/page"; @@ -360,9 +215,7 @@ namespace Video.DomainService { // 2) Token:优先用入参;否则走缓存/获取(建议返回已带前缀的 "Bearer xxx") var clientId = _configuration["DahuaAuth:ClientId"]; - var token = string.IsNullOrWhiteSpace(dto.Data.Token) - ? await GetCachedOrFetchTokenAsync(clientId) // 见下方简版实现 - : dto.Data.Token; + var token = await _tokenProviderService.GetTokenAsync(clientId!); var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/video/stream/realtime"; @@ -449,43 +302,13 @@ namespace Video.DomainService } } - /// - /// 获取AccessToken - /// - /// - /// - /// - /// - public async Task GetAccessTokenAsync(string? clientId, CancellationToken ct = default) - { - try - { - TokenEntry refreshed = new(); - if (clientId == null || clientId == "") - { - clientId = _configuration["DahuaAuth:ClientId"]; - } - - if (!TokenCache.TokenMap.TryGetValue(clientId!, out var entry) || TokenIsExpiring(entry)) - { - refreshed = await TryRefreshOrLoginAsync(clientId!, entry); - } - return refreshed.AccessToken; - } - catch (Exception ex) - { - _logger.LogError(ex, "获取大华AccessToken出错"); - return string.Empty; // 返回空字符串表示获取失败 - } - } - /// /// rtsp录像回放 /// /// /// /// - public async Task> RtspPlaybackByTime(PlaybackReqDto dto) + public async Task> RtspPlaybackByTime(RtspPlaybackReqDto dto) { // 参数校验 + 早退 if (dto == null || string.IsNullOrWhiteSpace(dto.ChannelId)) @@ -496,7 +319,7 @@ namespace Video.DomainService // 先用缓存里的 token,不足5分钟过期再刷新(按你之前的口径来) var clientId = _configuration["DahuaAuth:ClientId"]; - var token = await GetCachedOrFetchTokenAsync(clientId);// string.IsNullOrWhiteSpace(dto.Token) ? await GetCachedOrFetchTokenAsync(clientId) : dto.Token; + var token = await _tokenProviderService.GetTokenAsync(clientId!); var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/video/stream/record"; @@ -548,9 +371,7 @@ namespace Video.DomainService } var clientId = _configuration["DahuaAuth:ClientId"]; - var token = string.IsNullOrWhiteSpace(dto.Data.Token) - ? await GetCachedOrFetchTokenAsync(clientId) - : dto.Data.Token; + var token = await _tokenProviderService.GetTokenAsync(clientId!); var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/MTS/Video/StartVideo"; @@ -586,171 +407,5 @@ namespace Video.DomainService return new DaHApiResult { Success = false, Code = "1010", Msg = "实时流请求失败" }; } } - - #region GetToken的辅助方法 - - private async Task GetCachedOrFetchTokenAsync(string clientId) - { - if (TokenCache.TokenMap.TryGetValue(clientId, out var e) && e != null && e.ExpireAt > DateTimeOffset.UtcNow.AddMinutes(5)) - return e.AccessToken; // 缓存可用 - - var refreshed = await GetAccessTokenAsync(clientId); - return refreshed; - } - - private static bool TokenIsExpiring(TokenEntry entry) - { - return entry.ExpireAt <= DateTimeOffset.UtcNow.AddMinutes(5); - } - - private async Task TryRefreshOrLoginAsync(string clientId, TokenEntry? current) - { - try - { - TokenEntry refreshed; - - if (current != null) - { - RefreshTokenReqDto refreshTokenReqDto = new() - { - ClientId = clientId, - RefreshToken = current.RefreshToken, - ClientSecret = _configuration["DahuaAuth:ClientSecret"], - GrantType = "refresh_token" - }; - //刷新token - var result = await this.RefreshToken(refreshTokenReqDto); - refreshed = new TokenEntry - { - AccessToken = result.Data.AccessToken, - RefreshToken = result.Data.RefreshToken, - ExpireAt = DateTimeOffset.UtcNow.AddSeconds(result.Data.ExpiresIn) - }; - _logger.LogWarning("Refresh 成功: {ClientId}", clientId); - } - else - { - var publicKeyResult = await this.GetPublicKey(); - if (publicKeyResult != null && publicKeyResult.Data != null) - { - _logger.LogWarning("获取公钥成功: {PublicKey}", publicKeyResult.Data.PublicKey); - - LoginRequestDto loginRequestDto = new() - { - ClientId = clientId, - ClientSecret = _configuration["DahuaAuth:ClientSecret"], - Username = _configuration["DahuaAuth:Username"], - Password = _configuration["DahuaAuth:Password"], - PublicKey = publicKeyResult.Data.PublicKey, - GrantType = "password", - VerifyCodeFlag = 0 // 默认不开启动态验证码 - }; - - var result = await this.GetToken(loginRequestDto); - if (result.Data != null) - { - refreshed = new TokenEntry - { - AccessToken = string.Concat(result!.Data.TokenType, " ", result.Data.AccessToken), - RefreshToken = result.Data.RefreshToken, - ExpireAt = DateTimeOffset.UtcNow.AddSeconds(result.Data.ExpiresIn) - }; - _logger.LogWarning("Login 成功: {ClientId}", clientId); - } - else - { - _logger.LogWarning("获取公钥失败"); - return new TokenEntry - { - AccessToken = string.Empty, - RefreshToken = string.Empty, - ExpireAt = DateTimeOffset.UtcNow.AddMinutes(5) // 设置一个短期的过期时间,避免后续调用失败 - }; - } - } - else - { - _logger.LogWarning("获取公钥失败"); - return new TokenEntry - { - AccessToken = string.Empty, - RefreshToken = string.Empty, - ExpireAt = DateTimeOffset.UtcNow.AddMinutes(5) // 设置一个短期的过期时间,避免后续调用失败 - }; - } - } - - TokenCache.TokenMap[clientId] = refreshed; - return refreshed; - } - catch (Exception ex) - { - _logger.LogError(ex, "获取 token 失败:{ClientId}", clientId); - return new TokenEntry - { - AccessToken = string.Empty, - RefreshToken = string.Empty, - ExpireAt = DateTimeOffset.UtcNow.AddMinutes(5) // 设置一个短期的过期时间,避免后续调用失败 - }; - } - } - - #endregion GetToken的辅助方法 - - #region RES加密 - - private static String EncryptByPublicKey(String context, String publicKey) - { - RSACryptoServiceProvider rsa = new(); - - rsa.ImportParameters(FromXmlStringExtensions(ConvertToXmlPublicJavaKey(publicKey))); - byte[] byteText = System.Text.Encoding.UTF8.GetBytes(context); - byte[] byteEntry = rsa.Encrypt(byteText, false); - return Convert.ToBase64String(byteEntry); - } - - public static RSAParameters FromXmlStringExtensions(string xmlString) - { - RSAParameters parameters = new RSAParameters(); - - System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument(); - xmlDoc.LoadXml(xmlString); - - if (xmlDoc.DocumentElement!.Name.Equals("RSAKeyValue")) - { - foreach (System.Xml.XmlNode node in xmlDoc.DocumentElement.ChildNodes) - { - switch (node.Name) - { - case "Modulus": parameters.Modulus = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - case "Exponent": parameters.Exponent = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - case "P": parameters.P = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - case "Q": parameters.Q = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - case "DP": parameters.DP = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - case "DQ": parameters.DQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - case "InverseQ": parameters.InverseQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - case "D": parameters.D = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - } - } - } - else - { - throw new Exception("Invalid XML RSA key."); - } - - return parameters; - } - - public static string ConvertToXmlPublicJavaKey(string publicJavaKey) - { - RsaKeyParameters publicKeyParam = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(publicJavaKey)); - string xmlpublicKey = string.Format("{0}{1}", - Convert.ToBase64String(publicKeyParam.Modulus.ToByteArrayUnsigned()), - Convert.ToBase64String(publicKeyParam.Exponent.ToByteArrayUnsigned())); - Console.WriteLine(xmlpublicKey); - return xmlpublicKey; - } - - #endregion RES加密 } } \ No newline at end of file diff --git a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/IDahuaGeneralCtlService.cs b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/IDahuaGeneralCtlService.cs index 9d48e1a..2ecd11b 100644 --- a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/IDahuaGeneralCtlService.cs +++ b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/IDahuaGeneralCtlService.cs @@ -8,26 +8,6 @@ namespace Video.DomainService /// public interface IDahuaGeneralCtlService { - /// - /// 获取公钥 - /// - /// - Task> GetPublicKey(); - - /// - /// 鉴权 - /// - /// PublicKey - /// - Task> GetToken(LoginRequestDto dto); - - /// - /// 刷新token - /// - /// - /// - Task> RefreshToken(RefreshTokenReqDto dto); - /// /// 查询普通录像信息列表(后续可能用于hls的拼接) /// @@ -47,7 +27,7 @@ namespace Video.DomainService /// /// /// - Task> RtspPlaybackByTime(PlaybackReqDto dto); + Task> RtspPlaybackByTime(RtspPlaybackReqDto dto); /// /// 设备通道分页查询,需要用于HlsRecordVideo @@ -76,13 +56,5 @@ namespace Video.DomainService /// 没有返回值 /// Task> Logout(string authorization, string? openId, int? userClient); - - /// - /// 根据 clientId 获取当前可用 token(自动处理过期) - /// - /// 如果不传就从appsetting中得到 - /// - /// - Task GetAccessTokenAsync(string? clientId, CancellationToken ct = default); } } \ No newline at end of file diff --git a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/IRootVideoPlaybackService.cs b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/IRootVideoPlaybackService.cs index 867d5b6..cb32584 100644 --- a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/IRootVideoPlaybackService.cs +++ b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/IRootVideoPlaybackService.cs @@ -13,13 +13,6 @@ namespace Video.DomainService /// Task> GetDaHRecordVideoUrl(PlaybackReqDto dto); - /// - /// 大华的token获取 - /// - /// - /// - Task> GetDaHToken(); - /// /// 大华的实时视频 /// @@ -39,7 +32,7 @@ namespace Video.DomainService /// /// /// - Task> RtspPlaybackByTime(PlaybackReqDto dto); + Task> RtspPlaybackByTime(RtspPlaybackReqDto dto); /// /// 大华设备通道分页查询 diff --git a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/RootVideoPlaybackService.cs b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/RootVideoPlaybackService.cs index 5ee4aaa..9277e3c 100644 --- a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/RootVideoPlaybackService.cs +++ b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/RootVideoPlaybackService.cs @@ -50,50 +50,6 @@ namespace Video.DomainService return result; } - /// - /// 大华的鉴权 - /// - /// - /// - public async Task> GetDaHToken() - { - ApiResult result = new() { Code = 200, Msg = "接口调用成功" }; - //1. 获取公钥 - DaHApiResult publicKeyResult = await _dahuaGeneralCtlService.GetPublicKey(); - if (!publicKeyResult.Success) - { - result.Code = 500; - result.Msg = publicKeyResult.Msg; - _logger.LogWarning("获取大华公钥失败:{Msg}", publicKeyResult.Msg); - } - LoginRequestDto dto = new(); - //2. 鉴权 - dto.PublicKey = publicKeyResult.Data.PublicKey; - dto.ClientId = _cfg["DahuaAuth:ClientId"]!; - dto.ClientSecret = _cfg["DahuaAuth:ClientSecret"]!; - dto.Password = _cfg["DahuaAuth:Password"]!; - dto.Username = _cfg["DahuaAuth:Username"]!; - - DaHApiResult loginResult = await _dahuaGeneralCtlService.GetToken(dto); - if (!loginResult.Success) - { - result.Code = 500; - result.Msg = loginResult.Msg; - _logger.LogWarning("大华鉴权失败:{Msg}", loginResult.Msg); - return result; - } - //大华的规则 - result.Data = loginResult.Data.AccessToken; - TokenEntry refreshed = new TokenEntry - { - AccessToken = result.Data, - RefreshToken = result.Data, - ExpireAt = DateTimeOffset.UtcNow.AddSeconds(120) - }; - TokenCache.TokenMap[dto.ClientId] = refreshed; - return result; - } - /// /// 大华实时 /// @@ -184,7 +140,7 @@ namespace Video.DomainService /// /// /// - public async Task> RtspPlaybackByTime(PlaybackReqDto dto) + public async Task> RtspPlaybackByTime(RtspPlaybackReqDto dto) { ApiResult result = new ApiResult() { Code = 200, Msg = "接口调用成功" }; diff --git a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/TokenCacheService.cs b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/TokenCacheService.cs deleted file mode 100644 index 80801fc..0000000 --- a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/TokenCacheService.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.Concurrent; - -namespace Video.DomainService -{ - public static class TokenCache - { - public static readonly ConcurrentDictionary TokenMap = new(); - } - - /// - /// 用于token缓存的条目类 - /// - public class TokenEntry - { - public string AccessToken { get; set; } - public string RefreshToken { get; set; } - public DateTimeOffset ExpireAt { get; set; } - } -} \ No newline at end of file diff --git a/WeiCloud.Fusion/VideoService/Video.DomainService/Video.DomainService.csproj b/WeiCloud.Fusion/VideoService/Video.DomainService/Video.DomainService.csproj index 9866c24..7b3b365 100644 --- a/WeiCloud.Fusion/VideoService/Video.DomainService/Video.DomainService.csproj +++ b/WeiCloud.Fusion/VideoService/Video.DomainService/Video.DomainService.csproj @@ -8,6 +8,7 @@ + diff --git a/WeiCloud.Fusion/WeiCloud.Fusion.sln b/WeiCloud.Fusion/WeiCloud.Fusion.sln index 93850b8..16a8e93 100644 --- a/WeiCloud.Fusion/WeiCloud.Fusion.sln +++ b/WeiCloud.Fusion/WeiCloud.Fusion.sln @@ -57,6 +57,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Alarm.DomainService", "Alar EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Alarm.Application", "AlarmService\Alarm.Application\Alarm.Application.csproj", "{4B2C6EBE-E719-9F40-ADE6-C82DA632E554}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common.Shared.API", "Common.SharedService\Common.Shared.API\Common.Shared.API.csproj", "{1ACFAAE8-C86D-4582-B0B4-542B74970737}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -139,6 +141,10 @@ Global {4B2C6EBE-E719-9F40-ADE6-C82DA632E554}.Debug|Any CPU.Build.0 = Debug|Any CPU {4B2C6EBE-E719-9F40-ADE6-C82DA632E554}.Release|Any CPU.ActiveCfg = Release|Any CPU {4B2C6EBE-E719-9F40-ADE6-C82DA632E554}.Release|Any CPU.Build.0 = Release|Any CPU + {1ACFAAE8-C86D-4582-B0B4-542B74970737}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1ACFAAE8-C86D-4582-B0B4-542B74970737}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1ACFAAE8-C86D-4582-B0B4-542B74970737}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1ACFAAE8-C86D-4582-B0B4-542B74970737}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -163,6 +169,7 @@ Global {C2757FC0-54A9-BBD3-2E23-55F2F3912BA4} = {80F3B34B-C334-44D2-A861-31FD403AD57D} {B6DDF83D-591E-38B6-2902-1624BE8AE9B9} = {18791734-CA81-482D-964A-CA6D0F308B8E} {4B2C6EBE-E719-9F40-ADE6-C82DA632E554} = {18791734-CA81-482D-964A-CA6D0F308B8E} + {1ACFAAE8-C86D-4582-B0B4-542B74970737} = {80F3B34B-C334-44D2-A861-31FD403AD57D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {379A56DA-D3F0-4E7E-8FF7-DA8E20015BF3} -- 2.36.2 From 1a572fbb369f22ea5746a6a1893a11c4c8edece0 Mon Sep 17 00:00:00 2001 From: LiuXin Date: Thu, 14 Aug 2025 19:12:55 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E5=AE=8C=E5=96=84=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E6=92=AD=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DahAlarm/DahuaGeneralCtlService.cs | 5 +- .../AlarmService/AlarmService.API/Program.cs | 8 +- .../AlarmService.API/appsettings.json | 11 +- .../Common.Shared.API.csproj | 16 +++ .../Infrastructure/AutoMapperProfile.cs | 15 +++ .../Infrastructure/ConfigureAutofac.cs | 31 +++++ .../Common.Shared.API/NLog.config | 57 +++++++++ .../Common.Shared.API/Program.cs | 116 ++++++++++++++++-- .../Common.Shared.API/Startup.cs | 19 +++ .../DaHua/DaHApiResult.cs | 71 ++++++++++- .../DaHTokenService/ITokenProviderService.cs | 2 +- .../DaHTokenService/TokenProviderService.cs | 29 +++-- .../MqttClient/IMqttClientService.cs | 2 +- .../MqttClient/MQTTClient.cs | 2 +- .../MqttClient/MqttClientListService.cs | 2 +- .../MqttClient/MqttClientService.cs | 2 +- .../DaHua/VideoManageController.cs | 2 +- .../VideoService/Video.API/Program.cs | 3 +- .../VideoService/Video.API/Video.API.csproj | 4 - .../VideoService/Video.API/appsettings.json | 7 +- .../RequestDto/DahuaVideoQueryDto.cs | 44 +++++-- .../ResponeDto/DahuaVideoResDto.cs | 22 ++++ .../Dahvision/DahuaGeneralCtlService.cs | 11 +- .../Dahvision/IDahuaGeneralCtlService.cs | 2 +- .../Dahvision/IRootVideoPlaybackService.cs | 2 +- .../Dahvision/RootVideoPlaybackService.cs | 2 +- 26 files changed, 425 insertions(+), 62 deletions(-) create mode 100644 WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Infrastructure/AutoMapperProfile.cs create mode 100644 WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Infrastructure/ConfigureAutofac.cs create mode 100644 WeiCloud.Fusion/Common.SharedService/Common.Shared.API/NLog.config create mode 100644 WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Startup.cs diff --git a/WeiCloud.Fusion/AlarmService/Alarm.DomainService/DahAlarm/DahuaGeneralCtlService.cs b/WeiCloud.Fusion/AlarmService/Alarm.DomainService/DahAlarm/DahuaGeneralCtlService.cs index db1ff2f..3f893c4 100644 --- a/WeiCloud.Fusion/AlarmService/Alarm.DomainService/DahAlarm/DahuaGeneralCtlService.cs +++ b/WeiCloud.Fusion/AlarmService/Alarm.DomainService/DahAlarm/DahuaGeneralCtlService.cs @@ -1,11 +1,12 @@ using Alarm.Application.RequestDto; using Alarm.Application.ResponeDto; using Common.Shared.Application.DaHua; -using Common.Shared.DomainService.MqttClient; +using Common.Shared.DomainService; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; +using System; using System.Net.Http.Json; using System.Security.Cryptography; using System.Text.Json; @@ -234,7 +235,7 @@ namespace Alarm.DomainService.DahAlarm }; } - if (!(body.Success && string.Equals(body.Code, "0", StringComparison.OrdinalIgnoreCase))) + if (!(body.Success && string.Equals((string?)body.Code, "0", StringComparison.OrdinalIgnoreCase))) { return new DaHApiResult { diff --git a/WeiCloud.Fusion/AlarmService/AlarmService.API/Program.cs b/WeiCloud.Fusion/AlarmService/AlarmService.API/Program.cs index f1bc05b..357545a 100644 --- a/WeiCloud.Fusion/AlarmService/AlarmService.API/Program.cs +++ b/WeiCloud.Fusion/AlarmService/AlarmService.API/Program.cs @@ -1,7 +1,7 @@ using AlarmService.API.Infrastructure; using Autofac; using Autofac.Extensions.DependencyInjection; -using Common.Shared.DomainService.MqttClient; +using Common.Shared.DomainService; using Microsoft.OpenApi.Models; using NLog; using NLog.Extensions.Logging; @@ -43,6 +43,12 @@ namespace AlarmService.API #endregion Cors + #region עtoken + + builder.Services.AddSingleton(); + + #endregion עtoken + #region SwaggerUI builder.Services.AddEndpointsApiExplorer(); diff --git a/WeiCloud.Fusion/AlarmService/AlarmService.API/appsettings.json b/WeiCloud.Fusion/AlarmService/AlarmService.API/appsettings.json index df0f96a..355c787 100644 --- a/WeiCloud.Fusion/AlarmService/AlarmService.API/appsettings.json +++ b/WeiCloud.Fusion/AlarmService/AlarmService.API/appsettings.json @@ -16,5 +16,14 @@ "ClientId": "datasource_points_AXYJPT_v4", "ApiUrl": "http://v4.weienergy.cn/datastream" }, - "AllowedHosts": "*" + "AllowedHosts": "*", + //大华摄像头的配置 + "DahuaAuth": { + "Host": "demo.weienergy.cn:15214", + "ClientId": "taiyanggong", + + "ClientSecret": "6d6c78f8-3d4c-4e76-ab6b-827942a7b725", + "Username": "system", + "Password": "Admin123" + } } \ No newline at end of file diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Common.Shared.API.csproj b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Common.Shared.API.csproj index cb23834..844cff7 100644 --- a/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Common.Shared.API.csproj +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Common.Shared.API.csproj @@ -4,10 +4,26 @@ net8.0 enable enable + True + + + + + + + + + + + + + + + diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Infrastructure/AutoMapperProfile.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Infrastructure/AutoMapperProfile.cs new file mode 100644 index 0000000..075247f --- /dev/null +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Infrastructure/AutoMapperProfile.cs @@ -0,0 +1,15 @@ +namespace Video.API.Infrastructure +{ + /// + /// AutoMapper + /// + public class AutoMapperProfile + { + /// + /// 캯 + /// + public AutoMapperProfile() + { + } + } +} \ No newline at end of file diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Infrastructure/ConfigureAutofac.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Infrastructure/ConfigureAutofac.cs new file mode 100644 index 0000000..e027a5e --- /dev/null +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Infrastructure/ConfigureAutofac.cs @@ -0,0 +1,31 @@ +using Autofac; +using System.Reflection; + +namespace Common.Shared.API.Infrastructure +{ + /// + /// autofac + /// + public class ConfigureAutofac : Autofac.Module + { + /// + /// + /// + /// + protected override void Load(ContainerBuilder builder) + { + //Assembly assemblysServices1 = Assembly.Load("WeiCloud.Core"); + //builder.RegisterAssemblyTypes(assemblysServices1).Where(t => t.Namespace != "" && t.Namespace != null && t.Name.EndsWith("Service") && t.Namespace.StartsWith("WeiCloud.Core")) + // .AsImplementedInterfaces() + // .InstancePerLifetimeScope().PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies); + + var assemblysServices = Assembly.Load("Common.Shared.DomainService"); + builder.RegisterAssemblyTypes(assemblysServices) + .Where(x => x.Name.EndsWith("Service")) + .AsImplementedInterfaces() + .InstancePerLifetimeScope().PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies); + + // builder.RegisterType(typeof(GrainFactory)).PropertiesAutowired().InstancePerLifetimeScope(); + } + } +} \ No newline at end of file diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/NLog.config b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/NLog.config new file mode 100644 index 0000000..55c5da9 --- /dev/null +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/NLog.config @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Program.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Program.cs index de3db9d..c2abe8d 100644 --- a/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Program.cs +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Program.cs @@ -1,27 +1,123 @@ +using Autofac; +using Autofac.Extensions.DependencyInjection; +using Common.Shared.API.Infrastructure; +using Microsoft.OpenApi.Models; +using NLog; +using NLog.Extensions.Logging; +using NLog.Web; +using System.Reflection; + namespace Common.Shared.API { public class Program { public static void Main(string[] args) { - var builder = WebApplication.CreateBuilder(args); + var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger(); + logger.Debug("init main"); + try + { + var builder = WebApplication.CreateBuilder(args); + + builder.Services.AddHttpContextAccessor(); + builder.Services.AddHttpClient(); + + builder.Services.AddControllers(); + builder.Services.AddSingleton(builder.Configuration); + + #region Cors + + builder.Services.AddCors(options => + { + options.AddPolicy("_myAllowSpecificOrigins", + builder => + { + builder.AllowAnyOrigin() //ԴʱAPIã + .AllowAnyHeader() + .AllowAnyMethod() + .AllowCredentials() + .SetIsOriginAllowed((h) => true);//ΪSignalrӵ + }); + }); + + #endregion Cors + + #region SwaggerUI + + builder.Services.AddEndpointsApiExplorer(); + builder.Services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1.0", new OpenApiInfo + { + Version = "v1.0", + Title = "WeiCloud.IoT",// + Description = "һ廯ƽ̨", + Contact = new OpenApiContact + { + Name = "hi7t", + Email = "", + Url = null + } + }); + //c.OperationFilter(); + var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; + var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); + c.IncludeXmlComments(xmlPath, true); + c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First()); + }); + + #endregion SwaggerUI + + builder.Services.AddLogging(m => { m.AddNLog(); }); - // Add services to the container. + #region Autofac - builder.Services.AddControllers(); + builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()) + .ConfigureContainer(builder => + { + builder.RegisterModule(new ConfigureAutofac()); + }); - var app = builder.Build(); + #endregion Autofac - // Configure the HTTP request pipeline. + // ȫĬС + builder.WebHost.ConfigureKestrel(options => + { + options.Limits.MaxRequestBodySize = 200 * 1024 * 1024; // Ĭ 200MB + }); - app.UseHttpsRedirection(); + var app = builder.Build(); - app.UseAuthorization(); + if (app.Environment.IsDevelopment()) + { + app.UseSwagger(); + app.UseSwaggerUI(c => + { + c.SwaggerEndpoint("/swagger/v1.0/swagger.json", "WeiCloud.IoT-v1.0"); + }); + } + app.UseHttpsRedirection(); - app.MapControllers(); + app.UseAuthorization(); - app.Run(); + app.MapControllers(); + // Startup ʵ + var startup = new Startup(builder.Configuration); + startup.Configure(app, app.Environment, builder.Configuration); + app.Run(); + } + catch (Exception exception) + { + // NLog: catch setup errors + logger.Error(exception, "Stopped program because of exception"); + throw; + } + finally + { + // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux) + NLog.LogManager.Shutdown(); + } } } -} +} \ No newline at end of file diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Startup.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Startup.cs new file mode 100644 index 0000000..1b32cec --- /dev/null +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.API/Startup.cs @@ -0,0 +1,19 @@ +using WeiCloud.Utils.Common; + +namespace Common.Shared.API +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IConfiguration configuration) + { + ServiceLocator.Instance = app.ApplicationServices; + } + } +} \ No newline at end of file diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/DaHua/DaHApiResult.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/DaHua/DaHApiResult.cs index 43ed80f..0dff463 100644 --- a/WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/DaHua/DaHApiResult.cs +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/DaHua/DaHApiResult.cs @@ -1,4 +1,6 @@ -using System.Text.Json.Serialization; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Globalization; namespace Common.Shared.Application.DaHua { @@ -9,10 +11,73 @@ namespace Common.Shared.Application.DaHua public class DaHApiResult { [JsonPropertyName("code")] - public string Code { get; set; } + [JsonConverter(typeof(FlexibleStringConverter))] + public string? Code { get; set; } + // errMsg 和 desc 都可能出现,做一个统一的“Message”来使用 [JsonPropertyName("errMsg")] - public string Msg { get; set; } + public string? Msg { get; set; } + + [JsonPropertyName("desc")] + public string? Desc { get; set; } + + [JsonIgnore] + public string? Message => !string.IsNullOrWhiteSpace(Msg) ? Msg : Desc; + + [JsonPropertyName("data")] + public T? Data { get; set; } + + [JsonPropertyName("success")] + public bool Success { get; set; } + } + + public sealed class FlexibleStringConverter : JsonConverter + { + public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.String: + return reader.GetString(); + + case JsonTokenType.Number: + // 先尽量按整数,再按 decimal,最后兜底 double + if (reader.TryGetInt64(out long i)) + return i.ToString(CultureInfo.InvariantCulture); + if (reader.TryGetDecimal(out decimal m)) + return m.ToString(CultureInfo.InvariantCulture); + + double d = reader.GetDouble(); + return d.ToString(CultureInfo.InvariantCulture); + + case JsonTokenType.True: + return "true"; + + case JsonTokenType.False: + return "false"; + + case JsonTokenType.Null: + return null; + + default: + throw new JsonException($"Unsupported token for string: {reader.TokenType}"); + } + } + + public override void Write(Utf8JsonWriter writer, string? value, JsonSerializerOptions options) + { + if (value is null) { writer.WriteNullValue(); return; } + writer.WriteStringValue(value); + } + } + + public class DaHApiDescResult + { + [JsonPropertyName("code")] + public int Code { get; set; } // 修改为 int 类型 + + [JsonPropertyName("desc")] // 修改为 "desc" 而不是 "errMsg" + public string Desc { get; set; } [JsonPropertyName("data")] public T Data { get; set; } diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/ITokenProviderService.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/ITokenProviderService.cs index 7c35a70..3540a21 100644 --- a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/ITokenProviderService.cs +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/ITokenProviderService.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Common.Shared.DomainService.DaHToken +namespace Common.Shared.DomainService { public interface ITokenProviderService { diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/TokenProviderService.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/TokenProviderService.cs index adcdf37..7c75d56 100644 --- a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/TokenProviderService.cs +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/TokenProviderService.cs @@ -6,9 +6,8 @@ using Org.BouncyCastle.Security; using System.Net.Http.Json; using System.Security.Cryptography; using System.Text.Json; -using static System.Net.WebRequestMethods; -namespace Common.Shared.DomainService.DaHToken +namespace Common.Shared.DomainService { /// /// 获取大华icc平台的token服务 @@ -27,7 +26,7 @@ namespace Common.Shared.DomainService.DaHToken /// /// 开发测试的时候,忽略证书 /// - private static readonly HttpClient _http = new HttpClient(new HttpClientHandler + private static readonly HttpClient _http = new(new HttpClientHandler { ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator }); @@ -77,9 +76,9 @@ namespace Common.Shared.DomainService.DaHToken RefreshToken = refreshToken.Replace("Bearer ", string.Empty) }; - var result = await this.RefreshToken(dto); + var result = await RefreshToken(dto); - if (result?.Data != null) + if (result?.Data != null && result.Data.AccessToken != "" && result.Data.AccessToken != null) { refreshed = new TokenEntry { @@ -132,7 +131,7 @@ namespace Common.Shared.DomainService.DaHToken TokenEntry refreshed = new() { - AccessToken = string.Concat(loginResult!.Data.TokenType, " ", loginResult.Data.AccessToken), + AccessToken = loginResult.Data.AccessToken, ExpireAt = DateTimeOffset.UtcNow.AddSeconds(120) }; return refreshed; @@ -293,7 +292,7 @@ namespace Common.Shared.DomainService.DaHToken #region RES加密 - private static String EncryptByPublicKey(String context, String publicKey) + private static string EncryptByPublicKey(string context, string publicKey) { RSACryptoServiceProvider rsa = new(); @@ -316,14 +315,14 @@ namespace Common.Shared.DomainService.DaHToken { switch (node.Name) { - case "Modulus": parameters.Modulus = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - case "Exponent": parameters.Exponent = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - case "P": parameters.P = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - case "Q": parameters.Q = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - case "DP": parameters.DP = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - case "DQ": parameters.DQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - case "InverseQ": parameters.InverseQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; - case "D": parameters.D = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; + case "Modulus": parameters.Modulus = string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText); break; + case "Exponent": parameters.Exponent = string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText); break; + case "P": parameters.P = string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText); break; + case "Q": parameters.Q = string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText); break; + case "DP": parameters.DP = string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText); break; + case "DQ": parameters.DQ = string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText); break; + case "InverseQ": parameters.InverseQ = string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText); break; + case "D": parameters.D = string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText); break; } } } diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/IMqttClientService.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/IMqttClientService.cs index cf53d32..e12fa0d 100644 --- a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/IMqttClientService.cs +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/IMqttClientService.cs @@ -1,6 +1,6 @@ using MQTTnet.Client; -namespace Common.Shared.DomainService.MqttClient +namespace Common.Shared.DomainService { public interface IMqttClientService { diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/MQTTClient.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/MQTTClient.cs index b072841..6890e0d 100644 --- a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/MQTTClient.cs +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/MQTTClient.cs @@ -4,7 +4,7 @@ using MQTTnet.Client; using MQTTnet.Protocol; using WeiCloud.Core.BaseModels; -namespace Common.Shared.DomainService.MqttClient +namespace Common.Shared.DomainService { public class MQTTClient { diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/MqttClientListService.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/MqttClientListService.cs index a5222f7..dd2379a 100644 --- a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/MqttClientListService.cs +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/MqttClientListService.cs @@ -7,7 +7,7 @@ using System.Collections.Concurrent; using System.Text; using WeiCloud.Utils.Common; -namespace Common.Shared.DomainService.MqttClient +namespace Common.Shared.DomainService { public class MqttClientListService { diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/MqttClientService.cs b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/MqttClientService.cs index 0dcae36..1bde8c0 100644 --- a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/MqttClientService.cs +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/MqttClient/MqttClientService.cs @@ -2,7 +2,7 @@ using MQTTnet; using MQTTnet.Client; -namespace Common.Shared.DomainService.MqttClient +namespace Common.Shared.DomainService { public class MqttClientService : IMqttClientService { diff --git a/WeiCloud.Fusion/VideoService/Video.API/Controllers/DaHua/VideoManageController.cs b/WeiCloud.Fusion/VideoService/Video.API/Controllers/DaHua/VideoManageController.cs index a160f72..a830ca0 100644 --- a/WeiCloud.Fusion/VideoService/Video.API/Controllers/DaHua/VideoManageController.cs +++ b/WeiCloud.Fusion/VideoService/Video.API/Controllers/DaHua/VideoManageController.cs @@ -71,7 +71,7 @@ namespace Video.API.Controllers.DaHua /// /// [HttpPost("rtspstart/dh")] - public async Task> RtspStartVideoUrl(StreamReqDto dto) + public async Task> RtspStartVideoUrl(StreamRtspReqDto dto) { return await _rootVideoPlaybackService.RtspStartVideoUrl(dto); } diff --git a/WeiCloud.Fusion/VideoService/Video.API/Program.cs b/WeiCloud.Fusion/VideoService/Video.API/Program.cs index 2eafd59..e58b492 100644 --- a/WeiCloud.Fusion/VideoService/Video.API/Program.cs +++ b/WeiCloud.Fusion/VideoService/Video.API/Program.cs @@ -1,12 +1,11 @@ using Autofac; using Autofac.Extensions.DependencyInjection; -using Common.Shared.DomainService.DaHToken; +using Common.Shared.DomainService; using Microsoft.OpenApi.Models; using NLog; using NLog.Extensions.Logging; using NLog.Web; using Quartz; -using Quartz.Simpl; using System.Reflection; using Video.API.Infrastructure; using Video.Application; diff --git a/WeiCloud.Fusion/VideoService/Video.API/Video.API.csproj b/WeiCloud.Fusion/VideoService/Video.API/Video.API.csproj index 8eaeee2..77b0250 100644 --- a/WeiCloud.Fusion/VideoService/Video.API/Video.API.csproj +++ b/WeiCloud.Fusion/VideoService/Video.API/Video.API.csproj @@ -28,8 +28,4 @@ - - - - diff --git a/WeiCloud.Fusion/VideoService/Video.API/appsettings.json b/WeiCloud.Fusion/VideoService/Video.API/appsettings.json index 9e1987e..f58cfc9 100644 --- a/WeiCloud.Fusion/VideoService/Video.API/appsettings.json +++ b/WeiCloud.Fusion/VideoService/Video.API/appsettings.json @@ -26,6 +26,11 @@ //大华摄像头的配置 "DahuaAuth": { "Host": "demo.weienergy.cn:15214", - "ClientId": "taiyanggong" + "ClientId": "taiyanggong", + + "ClientSecret": "6d6c78f8-3d4c-4e76-ab6b-827942a7b725", + + "Username": "system", + "Password": "Admin123" } } \ No newline at end of file diff --git a/WeiCloud.Fusion/VideoService/Video.Application/RequestDto/DahuaVideoQueryDto.cs b/WeiCloud.Fusion/VideoService/Video.Application/RequestDto/DahuaVideoQueryDto.cs index 934e2a9..f597276 100644 --- a/WeiCloud.Fusion/VideoService/Video.Application/RequestDto/DahuaVideoQueryDto.cs +++ b/WeiCloud.Fusion/VideoService/Video.Application/RequestDto/DahuaVideoQueryDto.cs @@ -79,8 +79,8 @@ namespace Video.Application /// /// 回放开始时间,格式:"yyyy-M-d HH:mm:ss" /// - [JsonPropertyName("beginTime")] - public string BeginTime { get; set; } + [JsonPropertyName("startTime")] + public string startTime { get; set; } /// /// 回放结束时间,格式:"yyyy-M-d HH:mm:ss" @@ -230,24 +230,54 @@ namespace Video.Application /// [JsonPropertyName("type")] public string? Type { get; set; } + } + /// + /// 实时 流播放请求响应包装类 + /// + public class StreamReqDto + { /// - /// rtsp专用,有datatype没有type,有type没有datatype + /// 请求数据 /// - [JsonPropertyName("dataType")] - public string? DataType { get; set; } + [JsonPropertyName("data")] + public StreamRequestData Data { get; set; } } /// /// 实时 流播放请求响应包装类 /// - public class StreamReqDto + public class StreamRtspReqDto { /// /// 请求数据 /// [JsonPropertyName("data")] - public StreamRequestData Data { get; set; } + public StreamRtspRequestData Data { get; set; } + } + + /// + /// 实时流播放请求数据实体 + /// + public class StreamRtspRequestData + { + /// + /// 通道编码 + /// + [JsonPropertyName("channelId")] + public string ChannelId { get; set; } + + /// + /// 码流类型:1-主码流,2-子码流 + /// + [JsonPropertyName("streamType")] + public string StreamType { get; set; } + + /// + /// rtsp专用,有datatype没有type,有type没有datatype + /// + [JsonPropertyName("dataType")] + public string? DataType { get; set; } } /// diff --git a/WeiCloud.Fusion/VideoService/Video.Application/ResponeDto/DahuaVideoResDto.cs b/WeiCloud.Fusion/VideoService/Video.Application/ResponeDto/DahuaVideoResDto.cs index 98bfdb3..76f9bc5 100644 --- a/WeiCloud.Fusion/VideoService/Video.Application/ResponeDto/DahuaVideoResDto.cs +++ b/WeiCloud.Fusion/VideoService/Video.Application/ResponeDto/DahuaVideoResDto.cs @@ -370,5 +370,27 @@ namespace Video.Application /// [JsonPropertyName("trackId")] public string TrackId { get; set; } // 可为null + + // 添加JSON中存在的新属性 + [JsonPropertyName("urlList")] + public object UrlList { get; set; } // 可为null + + [JsonPropertyName("stream")] + public object Stream { get; set; } // 可为null + + [JsonPropertyName("innerIp")] + public string InnerIp { get; set; } // 新增IP属性 + + [JsonPropertyName("compress")] + public bool? Compress { get; set; } // 压缩标识 + + [JsonPropertyName("reachable")] + public object Reachable { get; set; } // 可为null + + [JsonPropertyName("wssDirect")] + public int? WssDirect { get; set; } // 新增数值属性 + + [JsonPropertyName("netFlag")] + public string NetFlag { get; set; } // 网络标识 } } \ No newline at end of file diff --git a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs index 547fcbe..81d127f 100644 --- a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs +++ b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs @@ -1,11 +1,8 @@ using Common.Shared.Application.DaHua; -using Common.Shared.DomainService.DaHToken; +using Common.Shared.DomainService; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; -using Org.BouncyCastle.Crypto.Parameters; -using Org.BouncyCastle.Security; using System.Net.Http.Json; -using System.Security.Cryptography; using System.Text.Json; using Video.Application; @@ -321,7 +318,7 @@ namespace Video.DomainService var clientId = _configuration["DahuaAuth:ClientId"]; var token = await _tokenProviderService.GetTokenAsync(clientId!); - var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/video/stream/record"; + var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/SS/Playback/StartPlaybackByTime"; using var req = new HttpRequestMessage(HttpMethod.Post, url) { @@ -362,7 +359,7 @@ namespace Video.DomainService /// /// /// - public async Task> RtspStartVideoUrl(StreamReqDto dto) + public async Task> RtspStartVideoUrl(StreamRtspReqDto dto) { if (dto == null || dto.Data == null) { @@ -393,7 +390,7 @@ namespace Video.DomainService } var result = JsonSerializer.Deserialize>(body); - if (result == null || !result.Success || result.Code != "0") + if (result == null || !result.Success || result.Code != "100") { _logger.LogWarning("实时流请求业务失败: {Body}", body); return new DaHApiResult { Success = false, Code = "1010", Msg = "实时流请求失败" }; diff --git a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/IDahuaGeneralCtlService.cs b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/IDahuaGeneralCtlService.cs index 2ecd11b..257ae7a 100644 --- a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/IDahuaGeneralCtlService.cs +++ b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/IDahuaGeneralCtlService.cs @@ -49,7 +49,7 @@ namespace Video.DomainService /// /// /// - Task> RtspStartVideoUrl(StreamReqDto dto); + Task> RtspStartVideoUrl(StreamRtspReqDto dto); /// /// 注销认证信息 diff --git a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/IRootVideoPlaybackService.cs b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/IRootVideoPlaybackService.cs index cb32584..bf18574 100644 --- a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/IRootVideoPlaybackService.cs +++ b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/IRootVideoPlaybackService.cs @@ -25,7 +25,7 @@ namespace Video.DomainService /// /// /// - Task> RtspStartVideoUrl(StreamReqDto dto); + Task> RtspStartVideoUrl(StreamRtspReqDto dto); /// /// rtsp录像回放 diff --git a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/RootVideoPlaybackService.cs b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/RootVideoPlaybackService.cs index 9277e3c..fe78aaa 100644 --- a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/RootVideoPlaybackService.cs +++ b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/RootVideoPlaybackService.cs @@ -121,7 +121,7 @@ namespace Video.DomainService /// /// /// - public async Task> RtspStartVideoUrl(StreamReqDto dto) + public async Task> RtspStartVideoUrl(StreamRtspReqDto dto) { ApiResult result = new ApiResult() { Code = 200, Msg = "接口调用成功" }; var urlReult = await _dahuaGeneralCtlService.RtspStartVideoUrl(dto); -- 2.36.2