You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
502 lines
23 KiB
502 lines
23 KiB
using Common.Shared.Application.DaHua; |
|
using Common.Shared.DomainService; |
|
using Microsoft.Extensions.Configuration; |
|
using Microsoft.Extensions.Logging; |
|
using NPOI.XWPF.UserModel; |
|
using System.Net.Http.Json; |
|
using System.Text.Json; |
|
using Video.DomainService.Dahvision; |
|
|
|
namespace Video.DomainService |
|
{ |
|
public class DahuaGeneralCtlService : IDahuaGeneralCtlService |
|
{ |
|
private readonly ILogger<DahuaGeneralCtlService> _logger; |
|
private readonly IConfiguration _configuration; |
|
private readonly ITokenProviderService _tokenProviderService; |
|
// private readonly HttpClient _http; |
|
|
|
public DahuaGeneralCtlService(ILogger<DahuaGeneralCtlService> logger, IConfiguration configuration, ITokenProviderService tokenProviderService, HttpClient http) |
|
{ |
|
_logger = logger; |
|
_configuration = configuration; |
|
_tokenProviderService = tokenProviderService; |
|
|
|
// _http = http; |
|
} |
|
|
|
/// <summary> |
|
/// 开发测试的时候,忽略证书 |
|
/// </summary> |
|
private static readonly HttpClient _http = new HttpClient(new HttpClientHandler |
|
{ |
|
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator |
|
}); |
|
|
|
/// <summary> |
|
/// hls等录像回放 |
|
/// </summary> |
|
/// <param name="dto"></param> |
|
/// <returns></returns> |
|
/// <exception cref="NotImplementedException"></exception> |
|
public async Task<DaHApiResult<UrlDataDto>> RecordVideoUrl(PlaybackReqDto dto, string? ipaddress) |
|
{ |
|
// 1) 参数校验 + 早退 |
|
if (dto == null || string.IsNullOrWhiteSpace(dto.Data.ChannelId)) |
|
{ |
|
_logger.LogWarning("录像请求失败:通道ID不能为空"); |
|
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1007", Msg = "通道ID不能为空" }; |
|
} |
|
|
|
try |
|
{ |
|
var clientId = _configuration["DahuaAuth:ClientId"]; |
|
var token = await _tokenProviderService.GetTokenAsync(clientId!); |
|
if (!_tokenProviderService.IsTokenValid(token)) |
|
{ |
|
_logger.LogWarning("hls等录像回放:token无效"); |
|
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1009", Msg = "token无效" }; |
|
} |
|
var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/video/stream/record"; |
|
if (ipaddress != null) |
|
{ |
|
url = $"https://{ipaddress}/evo-apigw/admin/API/video/stream/record"; |
|
} |
|
|
|
// 3) 构造请求(把 dto 放进 Body),并用 SendAsync 发送,才能带上头 |
|
using var req = new HttpRequestMessage(HttpMethod.Post, url) |
|
{ |
|
Content = JsonContent.Create(dto.Data) // 关键:别再丢 body 了 |
|
}; |
|
req.Headers.TryAddWithoutValidation("Authorization", token); |
|
|
|
using var resp = await _http.SendAsync(req); |
|
var body = await resp.Content.ReadAsStringAsync(); |
|
|
|
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( |
|
result.Data.Url, |
|
ipaddress, |
|
_configuration, |
|
"Time" |
|
) + "?token=" + result.Data.Token; |
|
return result; |
|
} |
|
catch (Exception ex) |
|
{ |
|
_logger.LogError(ex, "大华录像请求出错"); |
|
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1008", Msg = "录像请求失败" }; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// 查询普通录像信息列表 |
|
/// </summary> |
|
/// <param name="dto"></param> |
|
/// <returns></returns> |
|
/// <exception cref="NotImplementedException"></exception> |
|
public async Task<DaHApiResult<RecordsResDto>> QueryRecords(PlaybackReqDto dto) |
|
{ |
|
// 0) 参数校验 + 早退(按需增加更多必填校验) |
|
if (dto == null) |
|
{ |
|
_logger.LogWarning("查询录像信息失败:请求参数不能为空"); |
|
return new DaHApiResult<RecordsResDto> { Success = false, Code = "1009", Msg = "请求参数不能为空" }; |
|
} |
|
|
|
var clientId = _configuration["DahuaAuth:ClientId"]; |
|
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"; |
|
|
|
using var req = new HttpRequestMessage(HttpMethod.Post, url) |
|
{ |
|
Content = JsonContent.Create(dto.Data) // 关键:把 dto 放进请求体 |
|
}; |
|
req.Headers.TryAddWithoutValidation("Authorization", token); |
|
|
|
try |
|
{ |
|
using var resp = await _http.SendAsync(req); // 关键:用 SendAsync 发送 req,才能带上头 |
|
var body = await resp.Content.ReadAsStringAsync(); |
|
|
|
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 = "查询录像信息失败" }; |
|
} |
|
|
|
return result; |
|
} |
|
catch (Exception ex) |
|
{ |
|
_logger.LogError(ex, "查询大华录像信息出错"); |
|
return new DaHApiResult<RecordsResDto> { Success = false, Code = "1008", Msg = "查询录像信息失败" }; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// 设备通道分页查询 |
|
/// </summary> |
|
/// <param name="dto"></param> |
|
/// <returns></returns> |
|
/// <exception cref="ArgumentNullException"></exception> |
|
/// <exception cref="InvalidOperationException"></exception> |
|
public async Task<DaHApiResult<PageInfoDto>> GetChannelPageList(ChannelPageReqDto dto) |
|
{ |
|
if (dto == null) |
|
{ |
|
_logger.LogWarning("通道分页查询失败:请求参数不能为空"); |
|
return new DaHApiResult<PageInfoDto> { Success = false, Code = "1009", Msg = "请求参数不能为空" }; |
|
} |
|
|
|
var clientId = _configuration["DahuaAuth:ClientId"]; |
|
var token = await _tokenProviderService.GetTokenAsync(clientId!); |
|
if (!_tokenProviderService.IsTokenValid(token)) |
|
{ |
|
_logger.LogWarning("通道分页查询失败:token无效"); |
|
return new DaHApiResult<PageInfoDto> { Success = false, Code = "1009", Msg = "token无效" }; |
|
} |
|
|
|
var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/evo-brm/1.2.0/device/channel/subsystem/page"; |
|
|
|
using var req = new HttpRequestMessage(HttpMethod.Post, url) |
|
{ |
|
Content = JsonContent.Create(dto) // 关键:把 dto 放进请求体 |
|
}; |
|
req.Headers.TryAddWithoutValidation("Authorization", token); |
|
|
|
try |
|
{ |
|
using var resp = await _http.SendAsync(req); // 关键:用 SendAsync 发送 req |
|
var body = await resp.Content.ReadAsStringAsync(); |
|
|
|
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 = $"通道分页查询失败" }; |
|
} |
|
|
|
return result; |
|
} |
|
catch (Exception ex) |
|
{ |
|
_logger.LogError(ex, "获取大华设备通道分页查询出错"); |
|
return new DaHApiResult<PageInfoDto> { Success = false, Code = "1007", Msg = "获取大华设备通道分页查询失败" }; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// HLS、FLV、RTMP实时预览接口方式 |
|
/// </summary> |
|
/// <param name="dto"></param> |
|
/// <returns></returns> |
|
/// <exception cref="NotImplementedException"></exception> |
|
public async Task<DaHApiResult<UrlDataDto>> RealtimeStreamUrl(StreamReqDto dto, string? ipaddress) |
|
{ |
|
// 1) 参数校验 + 早退 |
|
if (dto == null || dto.Data == null) |
|
{ |
|
_logger.LogWarning("实时流请求失败:请求参数不能为空"); |
|
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1009", Msg = "请求参数不能为空" }; |
|
} |
|
|
|
try |
|
{ |
|
// 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<UrlDataDto> { Success = false, Code = "1009", Msg = "token无效" }; |
|
} |
|
|
|
var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/video/stream/realtime"; |
|
|
|
if (ipaddress != null) |
|
{ |
|
url = $"https://{ipaddress}/evo-apigw/admin/API/video/stream/realtime"; |
|
} |
|
dto.Data.StreamType = "2"; |
|
// 3) 用 HttpRequestMessage 发送,才能带上自定义头 |
|
using var req = new HttpRequestMessage(HttpMethod.Post, url) |
|
{ |
|
Content = JsonContent.Create(dto.Data) // 等价 PostAsJsonAsync,但不会丢头 |
|
}; |
|
req.Headers.TryAddWithoutValidation("Authorization", token); |
|
|
|
using var resp = await _http.SendAsync(req); |
|
var body = await resp.Content.ReadAsStringAsync(); |
|
|
|
if (!resp.IsSuccessStatusCode) |
|
{ |
|
_logger.LogWarning("实时流请求 HTTP 失败: {Status}, Body: {Body}", (int)resp.StatusCode, body); |
|
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1010", 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 = "1010", Msg = "实时流请求失败" }; |
|
} |
|
result.Data!.Url = UrlHostReplacer.ReplaceHost( |
|
result.Data.Url, |
|
ipaddress, |
|
_configuration, |
|
"Real" |
|
) + "?token=" + result.Data.Token; |
|
return result; |
|
} |
|
catch (Exception ex) |
|
{ |
|
_logger.LogError(ex, "大华实时流请求出错"); |
|
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1010", Msg = "实时流请求失败" }; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// 注销登录 |
|
/// </summary> |
|
/// <param name="authorization"></param> |
|
/// <param name="openId"></param> |
|
/// <param name="userClient"></param> |
|
/// <returns></returns> |
|
/// <exception cref="NotImplementedException"></exception> |
|
public async Task<DaHApiResult<object>> Logout(string authorization, string? openId, int? userClient) |
|
{ |
|
var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/evo-oauth/1.0.0/oauth/logout"; |
|
|
|
try |
|
{ |
|
using var req = new HttpRequestMessage(HttpMethod.Get, url); |
|
req.Headers.TryAddWithoutValidation("Authorization", authorization); |
|
if (!string.IsNullOrWhiteSpace(openId)) |
|
{ |
|
req.Headers.Add("openId", openId); |
|
} |
|
if (userClient != null) |
|
{ |
|
req.Headers.Add("User-Client", userClient.ToString()); |
|
} |
|
|
|
using var resp = await _http.SendAsync(req); |
|
resp.EnsureSuccessStatusCode(); |
|
|
|
var result = await resp.Content.ReadFromJsonAsync<DaHApiResult<object>>(); |
|
|
|
if (!result.Success || result.Code != "0") |
|
{ |
|
result.Success = false; |
|
result.Code = "1011"; |
|
result.Msg = "注销登录失败"; |
|
_logger.LogWarning("注销登录失败,返回结果:{Result}", result); |
|
} |
|
return result; |
|
} |
|
catch (Exception ex) |
|
{ |
|
_logger.LogError(ex, "大华注销登录出错"); |
|
return new DaHApiResult<object> |
|
{ |
|
Success = false, |
|
Code = "1011", |
|
Msg = "注销登录失败" |
|
}; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// rtsp录像回放 |
|
/// (播放命令:ffplay -rtsp_transport tcp -i "rtsp://demo.weienergy.cn:15210/dss/monitor/param/cameraid=1000021%24104%26substream=1?token=430")强制走tcp |
|
/// </summary> |
|
/// <param name="dto"></param> |
|
/// <returns></returns> |
|
/// <exception cref="NotImplementedException"></exception> |
|
public async Task<DaHApiResult<UrlDataDto>> RtspPlaybackByTime(RtspPlayBackReqDto dto, string? ipaddress) |
|
{ |
|
// 参数校验 + 早退 |
|
if (dto == null || string.IsNullOrWhiteSpace(dto.Data.ChannelId)) |
|
{ |
|
_logger.LogWarning("录像请求失败:通道ID不能为空"); |
|
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1007", Msg = "通道ID不能为空" }; |
|
} |
|
|
|
// 先用缓存里的 token,不足5分钟过期再刷新(按你之前的口径来) |
|
var clientId = _configuration["DahuaAuth:ClientId"]; |
|
var token = await _tokenProviderService.GetTokenAsync(clientId!); |
|
if (!_tokenProviderService.IsTokenValid(token)) |
|
{ |
|
_logger.LogWarning("rtsp录像回放:token无效"); |
|
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1009", Msg = "token无效" }; |
|
} |
|
dto.Data.StreamType = "2"; |
|
var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/SS/Playback/StartPlaybackByTime"; |
|
if (ipaddress != null) |
|
{ |
|
url = $"https://{ipaddress}/evo-apigw/admin/API/SS/Playback/StartPlaybackByTime"; |
|
} |
|
|
|
using var req = new HttpRequestMessage(HttpMethod.Post, url) |
|
{ |
|
Content = JsonContent.Create(dto) // 关键:把 dto 放进请求体 |
|
}; |
|
req.Headers.TryAddWithoutValidation("Authorization", token); |
|
|
|
try |
|
{ |
|
using var resp = await _http.SendAsync(req); // 关键:用 SendAsync 发送 req |
|
var body = await resp.Content.ReadAsStringAsync(); |
|
|
|
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( |
|
result.Data.Url, |
|
ipaddress, |
|
_configuration, |
|
"Time" |
|
) + "?token=" + result.Data.Token; |
|
return result; |
|
} |
|
catch (Exception ex) |
|
{ |
|
_logger.LogError(ex, "录像请求异常"); |
|
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1008", Msg = "录像请求失败" }; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// rtsp实时预览接口方式 |
|
/// (播放命令:ffplay -rtsp_transport tcp -i "rtsp://demo.weienergy.cn:15210/dss/monitor/param/cameraid=1000021%24104%26substream=1?token=68")强制走tcp |
|
/// </summary> |
|
/// <param name="dto"></param> |
|
/// <returns></returns> |
|
/// <exception cref="NotImplementedException"></exception> |
|
public async Task<DaHApiResult<UrlDataDto>> RtspStartVideoUrl(StreamRtspReqDto dto, string? ipaddress) |
|
{ |
|
if (dto == null || dto.Data == null) |
|
{ |
|
_logger.LogWarning("实时流请求失败:请求参数不能为空"); |
|
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1005", Msg = "请求参数不能为空" }; |
|
} |
|
|
|
var clientId = _configuration["DahuaAuth:ClientId"]; |
|
var token = await _tokenProviderService.GetTokenAsync(clientId!); |
|
_logger.LogWarning($"我的token: {token}"); |
|
if (!_tokenProviderService.IsTokenValid(token)) |
|
{ |
|
_logger.LogWarning("rtsp实时预览接口方式:token无效"); |
|
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1009", Msg = "token无效" }; |
|
} |
|
dto.Data.StreamType = "2"; |
|
var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/admin/API/MTS/Video/StartVideo"; |
|
if (ipaddress != null) |
|
{ |
|
url = $"https://{ipaddress}/evo-apigw/admin/API/MTS/Video/StartVideo"; |
|
} |
|
_logger.LogWarning($"我的url: {url}"); |
|
using var req = new HttpRequestMessage(HttpMethod.Post, url) |
|
{ |
|
Content = JsonContent.Create(dto) // 等价于 PostAsJsonAsync,但和 req 绑定 |
|
}; |
|
req.Headers.TryAddWithoutValidation("Authorization", token); |
|
|
|
try |
|
{ |
|
using var resp = await _http.SendAsync(req); // 关键:用 SendAsync 发 req |
|
var body = await resp.Content.ReadAsStringAsync(); |
|
|
|
if (!resp.IsSuccessStatusCode) |
|
{ |
|
_logger.LogWarning("实时流请求 HTTP 失败: {Status}, Body: {Body}", (int)resp.StatusCode, body); |
|
_logger.LogWarning($"{resp.StatusCode}: {body}"); |
|
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1014", Msg = $"HTTP错误 {(int)resp.StatusCode}" }; |
|
} |
|
_logger.LogWarning($"大华实时流请求返回body{body}"); |
|
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( |
|
result.Data.Url, |
|
ipaddress, |
|
_configuration, |
|
"Real" |
|
) + "?token=" + result.Data.Token; |
|
return result; |
|
} |
|
catch (Exception ex) |
|
{ |
|
_logger.LogError(ex, $"大华实时流请求出错{ex.Message}"); |
|
_logger.LogWarning($"{ex.Message}"); |
|
return new DaHApiResult<UrlDataDto> { Success = false, Code = "1012", Msg = $"实时流请求失败{ex.Message}" }; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// 下载 |
|
/// </summary> |
|
/// <param name="dto"></param> |
|
/// <returns></returns> |
|
/// <exception cref="NotImplementedException"></exception> |
|
public async Task<string> Download(DownloadReqDto dto) |
|
{ |
|
var clientId = _configuration["DahuaAuth:ClientId"]; |
|
var token = await _tokenProviderService.GetTokenAsync(clientId!); |
|
if (!_tokenProviderService.IsTokenValid(token)) |
|
{ |
|
_logger.LogWarning("下载:token无效"); |
|
return "下载地址无效"; |
|
} |
|
|
|
var url = _configuration["DahuaAuth:Host"]; |
|
if (dto.IpAddress != null) |
|
{ |
|
url = dto.IpAddress; |
|
} |
|
return $"https://" + url + $" /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}"; |
|
} |
|
} |
|
} |