From 3a2c90460f100c3e4ca9a658b135205de641f1f6 Mon Sep 17 00:00:00 2001 From: LiuXin Date: Sun, 17 Aug 2025 17:58:18 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=AE=8C=E5=96=84=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Common.Shared.Application.csproj | 4 +- .../DaHua/VideoManageController.cs | 48 +++++++++---------- .../VideoService/Video.API/appsettings.json | 4 ++ .../Dahvision/DahuaGeneralCtlService.cs | 15 +++--- .../Dahvision/RootVideoPlaybackService.cs | 3 ++ 5 files changed, 42 insertions(+), 32 deletions(-) diff --git a/WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/Common.Shared.Application.csproj b/WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/Common.Shared.Application.csproj index 4e30956..29101b1 100644 --- a/WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/Common.Shared.Application.csproj +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.Application/Common.Shared.Application.csproj @@ -7,11 +7,13 @@ Common.Shared.Application - 1.0.1 + 1.0.2 zrh-lx zrh-lx 包含所有公共使用的 DTO、契约、接口模型等,供微服务之间共享使用 shared;dto;contracts;microservices https://v4.weienergy.cn/(可选) + true + $(NoWarn);1591 diff --git a/WeiCloud.Fusion/VideoService/Video.API/Controllers/DaHua/VideoManageController.cs b/WeiCloud.Fusion/VideoService/Video.API/Controllers/DaHua/VideoManageController.cs index 502f31c..a0e79fe 100644 --- a/WeiCloud.Fusion/VideoService/Video.API/Controllers/DaHua/VideoManageController.cs +++ b/WeiCloud.Fusion/VideoService/Video.API/Controllers/DaHua/VideoManageController.cs @@ -92,7 +92,7 @@ namespace Video.API.Controllers.DaHua /// /// [HttpGet("download/dh")] - public async Task DownloadVideoAsync(PlayDownloadReqDto dto) + public async Task DownloadVideoAsync(PlayDownloadReqDto dto) { var remoteUrl = $"https://{_configuration["DahHost"]}/evo-apigw/evo-httpnode/vod/cam/download.mp4" + @@ -103,28 +103,28 @@ namespace Video.API.Controllers.DaHua $"&videoType={dto.VideoType}" + $"&token={dto.Token}" + $"&recordType={dto.RecordType}"; - - try - { - var handler = new HttpClientHandler - { - ServerCertificateCustomValidationCallback = (_, _, _, _) => true - }; - - using var http = new HttpClient(handler); - var resp = await http.GetAsync(remoteUrl, HttpCompletionOption.ResponseHeadersRead); - - resp.EnsureSuccessStatusCode(); - - var stream = await resp.Content.ReadAsStreamAsync(); - var contentType = "video/mp4"; - - return File(stream, contentType, "video.mp4"); - } - catch (Exception ex) - { - return StatusCode(500, new { success = false, message = "下载失败", error = ex.Message }); - } + return await Task.FromResult(remoteUrl); + //try + //{ + // var handler = new HttpClientHandler + // { + // ServerCertificateCustomValidationCallback = (_, _, _, _) => true + // }; + + // using var http = new HttpClient(handler); + // var resp = await http.GetAsync(remoteUrl, HttpCompletionOption.ResponseHeadersRead); + + // resp.EnsureSuccessStatusCode(); + + // var stream = await resp.Content.ReadAsStreamAsync(); + // var contentType = "video/mp4"; + + // return File(stream, contentType, "video.mp4"); + //} + //catch (Exception ex) + //{ + // return StatusCode(500, new { success = false, message = "下载失败", error = ex.Message }); + //} } /// @@ -146,7 +146,7 @@ namespace Video.API.Controllers.DaHua /// /// [HttpGet("download")] - public async Task Download(DownloadReqDto dto) + public async Task GetDownUrl(DownloadReqDto dto) { return await _rootVideoPlaybackService.Download(dto); } diff --git a/WeiCloud.Fusion/VideoService/Video.API/appsettings.json b/WeiCloud.Fusion/VideoService/Video.API/appsettings.json index f58cfc9..2485a3e 100644 --- a/WeiCloud.Fusion/VideoService/Video.API/appsettings.json +++ b/WeiCloud.Fusion/VideoService/Video.API/appsettings.json @@ -27,6 +27,10 @@ "DahuaAuth": { "Host": "demo.weienergy.cn:15214", "ClientId": "taiyanggong", + "RealRootHost": "192.168.21.18:9100", //RTSP实时播放的icc地址 + "RealReplaceHost": "demo.weienergy.cn:15210", //rtps试试播放的替换地址 + "TimeRootHost": "192.168.21.18:9320", //RTSP历史回放的icc地址 + "TimeReplaceHost": "demo.weienergy.cn:15211", //rtps历史回放的替换地址 "ClientSecret": "6d6c78f8-3d4c-4e76-ab6b-827942a7b725", diff --git a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs index 0e2864a..cfa8167 100644 --- a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs +++ b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs @@ -71,7 +71,7 @@ namespace Video.DomainService } var result = JsonSerializer.Deserialize>(body); - if (result == null || !result.Success || result.Code != "0") + if (result == null || !result.Success) { _logger.LogWarning("录像请求业务失败: {Body}", body); return new DaHApiResult { Success = false, Code = "1008", Msg = "录像请求失败" }; @@ -124,7 +124,7 @@ namespace Video.DomainService } var result = JsonSerializer.Deserialize>(body); - if (result == null || !result.Success || result.Code != "0") + if (result == null || !result.Success) { _logger.LogWarning("查询录像信息业务失败: {Body}", body); return new DaHApiResult { Success = false, Code = "1008", Msg = "查询录像信息失败" }; @@ -177,7 +177,7 @@ namespace Video.DomainService } var result = JsonSerializer.Deserialize>(body); - if (result == null || !result.Success || result.Code != "0") + if (result == null || !result.Success) { _logger.LogWarning("通道分页查询业务失败: {Body}", body); return new DaHApiResult { Success = false, Code = "1007", Msg = $"通道分页查询失败" }; @@ -232,7 +232,7 @@ namespace Video.DomainService } var result = JsonSerializer.Deserialize>(body); - if (result == null || !result.Success || result.Code != "0") + if (result == null || !result.Success) { _logger.LogWarning("实时流请求业务失败: {Body}", body); return new DaHApiResult { Success = false, Code = "1010", Msg = "实时流请求失败" }; @@ -300,6 +300,7 @@ namespace Video.DomainService /// /// rtsp录像回放 + /// (播放命令:ffplay -rtsp_transport tcp -i "rtsp://demo.weienergy.cn:15211/playback/pu/3?token=3")强制走tcp /// /// /// @@ -337,12 +338,12 @@ namespace Video.DomainService } var result = JsonSerializer.Deserialize>(body); - if (result == null || !result.Success || result.Code != "0") + if (result == null || !result.Success) { _logger.LogWarning("录像请求业务失败: {Body}", body); return new DaHApiResult { Success = false, Code = "1008", Msg = "录像请求失败" }; } - result.Data!.Url = result.Data.Url + "?token=" + token; + result.Data!.Url = result.Data.Url.Replace(_configuration["DahuaAuth:TimeRootHost"], _configuration["DahuaAuth:TimeReplaceHost"]) + "?token=" + result.Data.Token; return result; } catch (Exception ex) @@ -395,7 +396,7 @@ namespace Video.DomainService _logger.LogWarning("实时流请求业务失败: {Body}", body); return new DaHApiResult { Success = false, Code = "1010", Msg = "实时流请求失败" }; } - result.Data!.Url = result.Data.Url + "?token=" + result.Data!.Token; + result.Data!.Url = result.Data.Url.Replace(_configuration["DahuaAuth:RealRootHost"], _configuration["DahuaAuth:RealReplaceHost"]) + "?token=" + result.Data!.Token; return result; } catch (Exception ex) diff --git a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/RootVideoPlaybackService.cs b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/RootVideoPlaybackService.cs index ea68a37..b9fd7a8 100644 --- a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/RootVideoPlaybackService.cs +++ b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/RootVideoPlaybackService.cs @@ -5,6 +5,9 @@ using WeiCloud.Core.BaseModels; namespace Video.DomainService { + /// + /// todo:这块后续要做一个动态适配,只要修改配置文件就可以自由选择对接的厂商 + /// public class RootVideoPlayBackService : IRootVideoPlaybackService { private readonly ILogger _logger; From 12e4cf66fe314690743944d6dd7e72f07cbfe396 Mon Sep 17 00:00:00 2001 From: LiuXin Date: Mon, 18 Aug 2025 11:56:59 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BC=98=E5=8C=96token=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DahAlarm/DahuaGeneralCtlService.cs | 18 ++++++++- .../DaHTokenService/ITokenProviderService.cs | 2 + .../DaHTokenService/TokenProviderService.cs | 40 +++++++++++++++---- .../DaHua/VideoManageController.cs | 2 +- .../Dahvision/DahuaGeneralCtlService.cs | 38 +++++++++++++++++- 5 files changed, 88 insertions(+), 12 deletions(-) diff --git a/WeiCloud.Fusion/AlarmService/Alarm.DomainService/DahAlarm/DahuaGeneralCtlService.cs b/WeiCloud.Fusion/AlarmService/Alarm.DomainService/DahAlarm/DahuaGeneralCtlService.cs index 91a6a1f..767bdd7 100644 --- a/WeiCloud.Fusion/AlarmService/Alarm.DomainService/DahAlarm/DahuaGeneralCtlService.cs +++ b/WeiCloud.Fusion/AlarmService/Alarm.DomainService/DahAlarm/DahuaGeneralCtlService.cs @@ -121,6 +121,12 @@ namespace Alarm.DomainService.DahAlarm var clientId = _configuration["DahuaAuth:ClientId"]; var token = await _tokenProviderService.GetTokenAsync(clientId!); + + if (_tokenProviderService.IsTokenValid(token)) + { + _logger.LogWarning("新增报警事件订阅:token无效"); + return new DaHApiResult { Success = false, Code = "1009", Msg = "token无效" }; + } // —— 发起请求 —— using var request = new HttpRequestMessage(HttpMethod.Post, url) { @@ -226,7 +232,11 @@ namespace Alarm.DomainService.DahAlarm { var clientId = _configuration["DahuaAuth:ClientId"]; var token = await _tokenProviderService.GetTokenAsync(clientId!); - + if (_tokenProviderService.IsTokenValid(token)) + { + _logger.LogWarning("取消订阅某个报警事件:token无效"); + return false; + } var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/evo-event/1.0.0/subscribe/mqinfo?name={name}"; try @@ -269,7 +279,11 @@ namespace Alarm.DomainService.DahAlarm { var clientId = _configuration["DahuaAuth:ClientId"]; var token = await _tokenProviderService.GetTokenAsync(clientId!); - + if (_tokenProviderService.IsTokenValid(token)) + { + _logger.LogWarning("获取事件列表:token无效"); + return new DaHApiResult { Success = false, Code = "1009", Msg = "token无效" }; + } var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/evo-event/1.0.0/subscribe/subscribe-list?monitorType=url&category={name}"; try 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 3540a21..0121f69 100644 --- a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/ITokenProviderService.cs +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/ITokenProviderService.cs @@ -9,5 +9,7 @@ namespace Common.Shared.DomainService public interface ITokenProviderService { Task GetTokenAsync(string clientId); + + bool IsTokenValid(string token); } } \ 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 index 7c75d56..f2ee72a 100644 --- a/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/TokenProviderService.cs +++ b/WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/TokenProviderService.cs @@ -51,7 +51,8 @@ namespace Common.Shared.DomainService } var refreshed = await TryRefreshOrLoginAsync(clientId, tokenEntry); - return refreshed.AccessToken!; + + return refreshed.AccessToken; } finally { @@ -98,9 +99,12 @@ namespace Common.Shared.DomainService { refreshed = await GetDaHToken(); } + if (refreshed != null && refreshed.AccessToken != "") + { + // 更新缓存 + TokenCache.TokenMap[clientId] = refreshed; + } - // 更新缓存 - TokenCache.TokenMap[clientId] = refreshed; return refreshed; } catch (Exception ex) @@ -118,10 +122,17 @@ namespace Common.Shared.DomainService { //1. 获取公钥 DaHApiResult publicKeyResult = await GetPublicKey(); - + if (publicKeyResult.Success == false) + { + return new TokenEntry + { + AccessToken = string.Empty, + ExpireAt = DateTimeOffset.UtcNow.AddMinutes(1) + }; + } LoginRequestDto dto = new(); //2. 鉴权 - dto.PublicKey = publicKeyResult.Data.PublicKey; + dto.PublicKey = publicKeyResult.Data!.PublicKey; dto.ClientId = _configuration["DahuaAuth:ClientId"]!; dto.ClientSecret = _configuration["DahuaAuth:ClientSecret"]!; dto.Password = _configuration["DahuaAuth:Password"]!; @@ -131,7 +142,7 @@ namespace Common.Shared.DomainService TokenEntry refreshed = new() { - AccessToken = loginResult.Data.AccessToken, + AccessToken = loginResult.Data!.AccessToken, ExpireAt = DateTimeOffset.UtcNow.AddSeconds(120) }; return refreshed; @@ -271,7 +282,7 @@ namespace Common.Shared.DomainService } result = tokenInfo!; //固定的拼接方式 - result.Data.AccessToken = string.Concat(tokenInfo?.Data.TokenType, " ", tokenInfo?.Data.AccessToken); + result.Data!.AccessToken = string.Concat(tokenInfo?.Data!.TokenType, " ", tokenInfo?.Data!.AccessToken); TokenEntry refreshed = new TokenEntry { @@ -290,6 +301,21 @@ namespace Common.Shared.DomainService return result; } + /// + /// 判断token是否有效 + /// + /// + /// + public bool IsTokenValid(string token) + { + // 避免 NullReferenceException + if (string.IsNullOrWhiteSpace(token)) + return true; + + // 统一写法,后续改条件只改这里 + return token.Length < 10; + } + #region RES加密 private static string EncryptByPublicKey(string context, string publicKey) diff --git a/WeiCloud.Fusion/VideoService/Video.API/Controllers/DaHua/VideoManageController.cs b/WeiCloud.Fusion/VideoService/Video.API/Controllers/DaHua/VideoManageController.cs index a0e79fe..9d3d55a 100644 --- a/WeiCloud.Fusion/VideoService/Video.API/Controllers/DaHua/VideoManageController.cs +++ b/WeiCloud.Fusion/VideoService/Video.API/Controllers/DaHua/VideoManageController.cs @@ -145,7 +145,7 @@ namespace Video.API.Controllers.DaHua /// /// /// - [HttpGet("download")] + [HttpPost("download")] public async Task GetDownUrl(DownloadReqDto dto) { return await _rootVideoPlaybackService.Download(dto); diff --git a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs index cfa8167..482f21a 100644 --- a/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs +++ b/WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs @@ -51,7 +51,11 @@ namespace Video.DomainService // 2) Token:优先入参,其次缓存/获取(建议返回完整的 "Bearer xxx") var clientId = _configuration["DahuaAuth:ClientId"]; var token = await _tokenProviderService.GetTokenAsync(clientId!); - + if (_tokenProviderService.IsTokenValid(token)) + { + _logger.LogWarning("hls等录像回放:token无效"); + return new DaHApiResult { Success = false, Code = "1009", Msg = "token无效" }; + } var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/video/stream/record"; // 3) 构造请求(把 dto 放进 Body),并用 SendAsync 发送,才能带上头 @@ -103,7 +107,11 @@ namespace Video.DomainService var clientId = _configuration["DahuaAuth:ClientId"]; var token = await _tokenProviderService.GetTokenAsync(clientId!); - + if (_tokenProviderService.IsTokenValid(token)) + { + _logger.LogWarning("查询普通录像信息列表:token无效"); + return new DaHApiResult { Success = false, Code = "1009", Msg = "token无效" }; + } var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/SS/Record/QueryRecords"; using var req = new HttpRequestMessage(HttpMethod.Post, url) @@ -156,6 +164,11 @@ namespace Video.DomainService var clientId = _configuration["DahuaAuth:ClientId"]; var token = await _tokenProviderService.GetTokenAsync(clientId!); + if (_tokenProviderService.IsTokenValid(token)) + { + _logger.LogWarning("通道分页查询失败:token无效"); + return new DaHApiResult { Success = false, Code = "1009", Msg = "token无效" }; + } var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/evo-brm/1.2.0/device/channel/subsystem/page"; @@ -212,6 +225,11 @@ namespace Video.DomainService // 2) Token:优先用入参;否则走缓存/获取(建议返回已带前缀的 "Bearer xxx") var clientId = _configuration["DahuaAuth:ClientId"]; var token = await _tokenProviderService.GetTokenAsync(clientId!); + if (_tokenProviderService.IsTokenValid(token)) + { + _logger.LogWarning(" HLS实时流请求失败:token无效"); + return new DaHApiResult { Success = false, Code = "1009", Msg = "token无效" }; + } var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/video/stream/realtime"; @@ -317,6 +335,11 @@ namespace Video.DomainService // 先用缓存里的 token,不足5分钟过期再刷新(按你之前的口径来) var clientId = _configuration["DahuaAuth:ClientId"]; var token = await _tokenProviderService.GetTokenAsync(clientId!); + if (_tokenProviderService.IsTokenValid(token)) + { + _logger.LogWarning("rtsp录像回放:token无效"); + return new DaHApiResult { Success = false, Code = "1009", Msg = "token无效" }; + } var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/SS/Playback/StartPlaybackByTime"; @@ -370,6 +393,11 @@ namespace Video.DomainService var clientId = _configuration["DahuaAuth:ClientId"]; var token = await _tokenProviderService.GetTokenAsync(clientId!); + if (_tokenProviderService.IsTokenValid(token)) + { + _logger.LogWarning("rtsp实时预览接口方式:token无效"); + return new DaHApiResult { Success = false, Code = "1009", Msg = "token无效" }; + } var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/MTS/Video/StartVideo"; @@ -416,6 +444,12 @@ namespace Video.DomainService { var clientId = _configuration["DahuaAuth:ClientId"]; var token = await _tokenProviderService.GetTokenAsync(clientId!); + if (_tokenProviderService.IsTokenValid(token)) + { + _logger.LogWarning("下载:token无效"); + return "下载地址无效"; + } + return _configuration["DahuaAuth:Host"] + $"/evo-apigw/evo-httpnode/vod/cam/download.mp4?vcuid={dto.Vcuid}&subtype={dto.Subtype}&starttime={dto.StartTime}endtime={dto.EndTime}&videoType={dto.VideoType}&token={token}&recordType={dto.RecordType}"; } }