修复视频token的性能问题

dev_lx
刘鑫 7 days ago
parent d0b8aa44a7
commit d575e76c9a
  1. 91
      WeiCloud.Fusion/Common.SharedService/Common.Shared.DomainService/DaHTokenService/TokenProviderService.cs
  2. 38
      WeiCloud.Fusion/VideoService/Video.DomainService/Dahvision/DahuaGeneralCtlService.cs

@ -1,8 +1,6 @@
using Common.Shared.Application.DaHua;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
using OfficeOpenXml.FormulaParsing.LexicalAnalysis;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using System.Net.Http.Json;
@ -35,36 +33,29 @@ namespace Common.Shared.DomainService
public async Task<string> GetTokenAsync(string clientId)
{
if (TokenCache.TokenMap.TryGetValue(clientId, out var tokenEntry)
&& tokenEntry.ExpireAt > DateTimeOffset.UtcNow.AddMinutes(1))
if (TokenCache.TokenMap.TryGetValue(clientId, out var tokenEntry))
{
if (!IsTokenValid(tokenEntry.AccessToken!))
{
_logger.LogWarning("从字典里匹配");
// ✅ 快速通道:缓存有效直接返回
if (tokenEntry.ExpireAt > DateTimeOffset.UtcNow.AddMinutes(1) && IsTokenValid(tokenEntry.AccessToken))
return tokenEntry.AccessToken;
return tokenEntry.AccessToken!;
}
// ✅ 异步刷新任务(不阻塞主线程)
_ = Task.Run(() => SafeRefreshTokenAsync(clientId, tokenEntry));
return tokenEntry.AccessToken; // 临时返回旧 token
}
// 缓存中没有,才走锁逻辑
var tokenLock = TokenLockProvider.GetLock(clientId);
_logger.LogWarning("枷锁");
await tokenLock.WaitAsync();
try
{
// 加锁后再次检查,防止重复刷新
if (TokenCache.TokenMap.TryGetValue(clientId, out tokenEntry)
&& tokenEntry.ExpireAt > DateTimeOffset.UtcNow.AddMinutes(5))
{
if (!IsTokenValid(tokenEntry.AccessToken!))
{
_logger.LogWarning("再次匹配");
return tokenEntry.AccessToken!;
}
}
if (TokenCache.TokenMap.TryGetValue(clientId, out tokenEntry) &&
tokenEntry.ExpireAt > DateTimeOffset.UtcNow.AddMinutes(1) &&
IsTokenValid(tokenEntry.AccessToken))
return tokenEntry.AccessToken;
var refreshed = await TryRefreshOrLoginAsync(clientId, tokenEntry);
TokenCache.TokenMap[clientId] = refreshed;
return refreshed.AccessToken;
}
finally
@ -73,6 +64,21 @@ namespace Common.Shared.DomainService
}
}
// 异步后台刷新
private async Task SafeRefreshTokenAsync(string clientId, TokenEntry? current)
{
try
{
var refreshed = await TryRefreshOrLoginAsync(clientId, current);
if (!string.IsNullOrWhiteSpace(refreshed.AccessToken))
TokenCache.TokenMap[clientId] = refreshed;
}
catch (Exception ex)
{
_logger.LogError(ex, "后台刷新 Token 异常");
}
}
private async Task<TokenEntry> TryRefreshOrLoginAsync(string clientId, TokenEntry? current)
{
try
@ -98,13 +104,11 @@ namespace Common.Shared.DomainService
{
AccessToken = string.Concat(result.Data.TokenType, " ", result.Data.AccessToken),
ExpireAt = DateTimeOffset.UtcNow.AddSeconds(result.Data.ExpiresIn)
ExpireAt = DateTimeOffset.UtcNow.AddSeconds(result.Data.ExpiresIn - 180)
};
_logger.LogInformation("Refresh 成功: {ClientId}", clientId);
}
else
{
_logger.LogWarning("RefreshToken 失败,尝试重新登录");
refreshed = await GetDaHToken();
}
}
@ -150,8 +154,7 @@ namespace Common.Shared.DomainService
dto.ClientSecret = _configuration["DahuaAuth:ClientSecret"]!;
dto.Password = _configuration["DahuaAuth:Password"]!;
dto.Username = _configuration["DahuaAuth:Username"]!;
_logger.LogWarning("在GetDaHToken方法中的记录:Dahua Host = {Host}, ClientId = {ClientId}",
_configuration["DahuaAuth:Host"], _configuration["DahuaAuth:ClientId"], _configuration["DahuaAuth:ClientSecret"]);
DaHApiResult<LoginResDto> loginResult = await GetToken(dto);
TokenEntry refreshed = new()
@ -179,7 +182,7 @@ namespace Common.Shared.DomainService
result.Success = false;
result.Code = "1005";
result.Msg = "刷新令牌不能为空";
_logger.LogWarning("刷新大华令牌失败,刷新令牌不能为空");
return result;
}
@ -197,7 +200,6 @@ namespace Common.Shared.DomainService
result.Success = false;
result.Code = "1006";
result.Msg = "刷新大华令牌失败";
_logger.LogWarning("刷新大华令牌失败,返回结果:{Result}", result);
}
}
catch (Exception ex)
@ -221,11 +223,11 @@ namespace Common.Shared.DomainService
try
{
var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/evo-oauth/1.0.0/oauth/public-key";
_logger.LogWarning($"获取的url{url}");
using var resp = await _http.GetAsync(url);
resp.EnsureSuccessStatusCode();
var json = await resp.Content.ReadAsStringAsync();
_logger.LogWarning($"获取密钥的结果json{json}");
var envelope = JsonSerializer.Deserialize<DaHApiResult<PublicKeyDto>>(json, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
@ -233,7 +235,7 @@ namespace Common.Shared.DomainService
if (envelope?.Data?.PublicKey is null or { Length: 0 })
{
_logger.LogWarning("获取大华公钥失败,返回结果:{Result}", json);
_logger.LogError("获取大华公钥失败,返回结果:{Result}", json);
result.Success = false;
result.Code = "1001";
result.Msg = "获取大华公钥失败";
@ -267,7 +269,7 @@ namespace Common.Shared.DomainService
result.Code = "1002";
result.Msg = "请求参数不能为空";
_logger.LogWarning("获取大华登录令牌失败,参数不能为空");
return result;
}
if (string.IsNullOrWhiteSpace(dto.Password))
@ -275,27 +277,25 @@ namespace Common.Shared.DomainService
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";
//必须加密
_logger.LogWarning($"获取token的url{url}");
_logger.LogWarning($"获取token的dto的json{dto.ToJson()}");
dto.Password = EncryptByPublicKey(dto.Password, dto.PublicKey!);
using var resp = await _http.PostAsJsonAsync(url, dto);
resp.EnsureSuccessStatusCode();
_logger.LogWarning("这是正式请求的:Dahua Host = {Host}, ClientId = {ClientId},ClientSecret={ClientSecret}", _configuration["DahuaAuth:Host"], _configuration["DahuaAuth:ClientId"], _configuration["DahuaAuth:ClientSecret"]);
var tokenInfo = await resp.Content.ReadFromJsonAsync<DaHApiResult<LoginResDto>>();
_logger.LogWarning($"获取大华登录令牌返回结果:{tokenInfo.ToJson()}");
if (tokenInfo == null || !result.Success || result.Code != "0")
{
result.Success = false;
result.Code = "1004";
result.Msg = "获取大华登录令牌失败";
_logger.LogWarning($"获取大华登录令牌失败,返回结果:{result}");
}
result = tokenInfo!;
//固定的拼接方式
@ -306,7 +306,7 @@ namespace Common.Shared.DomainService
{
AccessToken = string.Concat(result!.Data.TokenType, " ", result.Data.AccessToken),
ExpireAt = DateTimeOffset.UtcNow.AddSeconds(result.Data.ExpiresIn)
ExpireAt = DateTimeOffset.UtcNow.AddSeconds(result.Data.ExpiresIn - 180)
};
}
else
@ -314,7 +314,6 @@ namespace Common.Shared.DomainService
result.Success = false;
result.Code = "1006";
result.Msg = "没拿到token";
_logger.LogWarning($"获取大华登录令牌失败,返回结果:{result}");
}
}
catch (Exception ex)
@ -332,14 +331,10 @@ namespace Common.Shared.DomainService
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
public bool IsTokenValid(string token)
public bool IsTokenValid(string? token)
{
// 避免 NullReferenceException
if (string.IsNullOrWhiteSpace(token) && token.Length < 10)
return false;
// 统一写法,后续改条件只改这里
return true;
if (string.IsNullOrWhiteSpace(token)) return false;
return token.Length > 10;
}
#region RES加密

@ -45,7 +45,7 @@ namespace Video.DomainService
// 1) 参数校验 + 早退
if (dto == null || string.IsNullOrWhiteSpace(dto.Data.ChannelId))
{
_logger.LogWarning("录像请求失败:通道ID不能为空");
_logger.LogError("录像请求失败:通道ID不能为空");
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1007", Msg = "通道ID不能为空" };
}
@ -114,7 +114,6 @@ namespace Video.DomainService
// 0) 参数校验 + 早退(按需增加更多必填校验)
if (dto == null)
{
_logger.LogWarning("查询录像信息失败:请求参数不能为空");
return new DaHApiResult<RecordsResDto> { Success = false, Code = "1009", Msg = "请求参数不能为空" };
}
@ -122,7 +121,6 @@ namespace Video.DomainService
var token = await _tokenProviderService.GetTokenAsync(clientId!);
if (!_tokenProviderService.IsTokenValid(token))
{
_logger.LogWarning("查询普通录像信息列表:token无效");
return new DaHApiResult<RecordsResDto> { Success = false, Code = "1009", Msg = "token无效" };
}
var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/SS/Record/QueryRecords";
@ -144,14 +142,12 @@ namespace Video.DomainService
if (!resp.IsSuccessStatusCode)
{
_logger.LogWarning("查询录像信息 HTTP 失败: {Status}, Body: {Body}", (int)resp.StatusCode, body);
return new DaHApiResult<RecordsResDto> { Success = false, Code = "1008", Msg = $"HTTP错误 {(int)resp.StatusCode}" };
}
var result = JsonSerializer.Deserialize<DaHApiResult<RecordsResDto>>(body);
if (result == null || !result.Success)
{
_logger.LogWarning("查询录像信息业务失败: {Body}", body);
return new DaHApiResult<RecordsResDto> { Success = false, Code = "1008", Msg = "查询录像信息失败" };
}
@ -175,7 +171,6 @@ namespace Video.DomainService
{
if (dto == null)
{
_logger.LogWarning("通道分页查询失败:请求参数不能为空");
return new DaHApiResult<PageInfoDto> { Success = false, Code = "1009", Msg = "请求参数不能为空" };
}
@ -183,7 +178,6 @@ namespace Video.DomainService
var token = await _tokenProviderService.GetTokenAsync(clientId!);
if (!_tokenProviderService.IsTokenValid(token))
{
_logger.LogWarning("通道分页查询失败:token无效");
return new DaHApiResult<PageInfoDto> { Success = false, Code = "1009", Msg = "token无效" };
}
@ -206,14 +200,12 @@ namespace Video.DomainService
if (!resp.IsSuccessStatusCode)
{
_logger.LogWarning("通道分页查询 HTTP 失败: {Status}, Body: {Body}", (int)resp.StatusCode, body);
return new DaHApiResult<PageInfoDto> { Success = false, Code = "1007", Msg = $"HTTP错误 {(int)resp.StatusCode}" };
}
var result = JsonSerializer.Deserialize<DaHApiResult<PageInfoDto>>(body);
if (result == null || !result.Success)
{
_logger.LogWarning("通道分页查询业务失败: {Body}", body);
return new DaHApiResult<PageInfoDto> { Success = false, Code = "1007", Msg = $"通道分页查询失败" };
}
@ -378,7 +370,6 @@ namespace Video.DomainService
// 参数校验 + 早退
if (dto == null || string.IsNullOrWhiteSpace(dto.Data.ChannelId))
{
_logger.LogWarning("录像请求失败:通道ID不能为空");
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1007", Msg = "通道ID不能为空" };
}
@ -387,7 +378,6 @@ namespace Video.DomainService
var token = await _tokenProviderService.GetTokenAsync(clientId!);
if (!_tokenProviderService.IsTokenValid(token))
{
_logger.LogWarning("rtsp录像回放:token无效");
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1009", Msg = "token无效" };
}
@ -424,7 +414,6 @@ namespace Video.DomainService
var resultRecord = JsonSerializer.Deserialize<DaHApiResult<RecordDataResDto>>(bodys);
if (resultRecord == null || !resultRecord.Success)
{
_logger.LogWarning("查询普通录像信息: {Body}", bodys);
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1008", Msg = "查询普通录像信息" };
}
var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/SS/Playback/StartPlaybackByTime";
@ -450,14 +439,12 @@ namespace Video.DomainService
if (!resp.IsSuccessStatusCode)
{
_logger.LogWarning("录像请求 HTTP 失败: {Status}, Body: {Body}", (int)resp.StatusCode, body);
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1008", Msg = $"HTTP错误 {(int)resp.StatusCode}" };
}
var result = JsonSerializer.Deserialize<DaHApiResult<UrlDataDto>>(body);
if (result == null || !result.Success)
{
_logger.LogWarning("录像请求业务失败: {Body}", body);
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1008", Msg = "录像请求失败" };
}
result.Data!.Url = UrlHostReplacer.ReplaceHost(
@ -487,7 +474,6 @@ namespace Video.DomainService
{
if (dto == null || dto.Data == null)
{
_logger.LogWarning("实时流请求失败:请求参数不能为空");
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1005", Msg = "请求参数不能为空" };
}
@ -496,7 +482,6 @@ namespace Video.DomainService
if (!_tokenProviderService.IsTokenValid(token))
{
_logger.LogWarning("rtsp实时预览接口方式:token无效");
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1009", Msg = "token无效" };
}
@ -522,15 +507,12 @@ namespace Video.DomainService
var body = await reader.ReadToEndAsync();
if (!resp.IsSuccessStatusCode)
{
_logger.LogWarning("实时流请求 HTTP 失败: {Status}, Body: {Body}", (int)resp.StatusCode, body);
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1014", Msg = $"HTTP错误 {(int)resp.StatusCode}" };
}
var result = JsonSerializer.Deserialize<DaHApiResult<UrlDataDto>>(body);
if (result == null || !result.Success)
{
_logger.LogWarning("实时流请求业务反序列化失败: {Body}", body);
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1013", Msg = "实时流请求业务反序列化失败" };
}
result.Data!.Url = UrlHostReplacer.ReplaceHost(
@ -544,7 +526,7 @@ namespace Video.DomainService
catch (Exception ex)
{
_logger.LogError(ex, $"大华实时流请求出错{ex.Message}");
_logger.LogWarning($"{ex.Message}");
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1012", Msg = $"实时流请求失败{ex.Message}" };
}
}
@ -561,7 +543,6 @@ namespace Video.DomainService
var token = await _tokenProviderService.GetTokenAsync(clientId!);
if (!_tokenProviderService.IsTokenValid(token))
{
_logger.LogWarning("下载:token无效");
return "下载地址无效";
}
@ -587,7 +568,6 @@ namespace Video.DomainService
if (!_tokenProviderService.IsTokenValid(token))
{
_logger.LogWarning("抓拍接口方式:token无效");
return new DaHApiResult<SnapshotResDto> { Success = false, Code = "1009", Msg = "token无效" };
}
@ -610,20 +590,16 @@ namespace Video.DomainService
var body = await reader.ReadToEndAsync();
if (!resp.IsSuccessStatusCode)
{
_logger.LogWarning("抓拍 失败: {Status}, Body: {Body}", (int)resp.StatusCode, body);
return new DaHApiResult<SnapshotResDto> { Success = false, Code = "1014", Msg = $"抓拍错误 {(int)resp.StatusCode}" };
}
var outer = JsonSerializer.Deserialize<DaHApiResult<string>>(body);
if (outer == null || !outer.Success || outer.Data == null)
{
_logger.LogWarning("抓拍请求业务失败: {Body}", body);
return new DaHApiResult<SnapshotResDto> { Success = false, Code = "1013", Msg = "抓拍请求失败" };
}
var result = JsonSerializer.Deserialize<SnapResponseVO>(outer.Data);
if (result == null)
{
_logger.LogWarning("抓拍请求业务反序列化失败: {Body}", body);
return new DaHApiResult<SnapshotResDto> { Success = false, Code = "1013", Msg = "抓拍反序列化失败" };
}
@ -654,7 +630,6 @@ namespace Video.DomainService
if (!_tokenProviderService.IsTokenValid(token))
{
_logger.LogWarning("控制云台镜头接口方式:token无效");
return new DaHApiResult<CameraControlResDto> { Success = false, Code = "1009", Msg = "token无效" };
}
@ -676,20 +651,16 @@ namespace Video.DomainService
var body = await reader.ReadToEndAsync();
if (!resp.IsSuccessStatusCode)
{
_logger.LogWarning("控制云台镜头失败: {Status}, Body: {Body}", (int)resp.StatusCode, body);
return new DaHApiResult<CameraControlResDto> { Success = false, Code = "1014", Msg = $"控制云台镜头错误 {(int)resp.StatusCode}" };
}
var outer = JsonSerializer.Deserialize<DaHApiResult<CameraControlResDto>>(body);
if (outer == null || !outer.Success || outer.Data == null)
{
_logger.LogWarning("控制云台镜头请求业务失败: {Body}", body);
return new DaHApiResult<CameraControlResDto> { Success = false, Code = "1013", Msg = "控制云台镜头请求失败" };
}
if (outer.Data == null)
{
_logger.LogWarning("控制云台镜头请求业务反序列化失败: {Body}", body);
return new DaHApiResult<CameraControlResDto> { Success = false, Code = "1013", Msg = "控制云台镜头反序列化失败" };
}
apiResult.Data = outer.Data;
@ -717,7 +688,6 @@ namespace Video.DomainService
if (!_tokenProviderService.IsTokenValid(token))
{
_logger.LogWarning("云台方向控制接口方式:token无效");
return new DaHApiResult<CameraControlResDto> { Success = false, Code = "1009", Msg = "token无效" };
}
@ -739,20 +709,16 @@ namespace Video.DomainService
var body = await reader.ReadToEndAsync();
if (!resp.IsSuccessStatusCode)
{
_logger.LogWarning("云台方向控制失败: {Status}, Body: {Body}", (int)resp.StatusCode, body);
return new DaHApiResult<CameraControlResDto> { Success = false, Code = "1014", Msg = $"云台方向控制错误 {(int)resp.StatusCode}" };
}
var outer = JsonSerializer.Deserialize<DaHApiResult<CameraControlResDto>>(body);
if (outer == null || !outer.Success || outer.Data == null)
{
_logger.LogWarning("云台方向控制请求业务失败: {Body}", body);
return new DaHApiResult<CameraControlResDto> { Success = false, Code = "1013", Msg = "云台方向控制请求失败" };
}
if (outer.Data == null)
{
_logger.LogWarning("云台方向控制请求业务反序列化失败: {Body}", body);
return new DaHApiResult<CameraControlResDto> { Success = false, Code = "1013", Msg = "云台方向控制反序列化失败" };
}
apiResult.Data = outer.Data;

Loading…
Cancel
Save