智慧建筑第三方功能集成微服务,目的是聚集所有涉及到第三方厂商调用的功能,按照业务功能划分不同微服务
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
22 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";
}
// 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无效" };
}
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无效" };
}
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 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}";
}
}
}