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

362 lines
15 KiB

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;
using Org.BouncyCastle.Security;
using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Text.Json;
using WeiCloud.Core.BaseModels;
namespace Alarm.DomainService.DahAlarm
{
public class DahuaGeneralCtlService : IDahuaGeneralCtlService
{
private readonly ILogger<DahuaGeneralCtlService> _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;
/// <summary>
///
/// </summary>
/// <param name="logger"></param>
/// <param name="configuration"></param>
/// <param name="http"></param>
/// <param name="mQTTClient"></param>
public DahuaGeneralCtlService(ILogger<DahuaGeneralCtlService> 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;
}
/// <summary>
/// 获取公钥
/// </summary>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<DaHApiResult<PublicKeyDto>> GetPublicKey()
{
DaHApiResult<PublicKeyDto> 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<DaHApiResult<PublicKeyDto>>(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, "大华平台获取公钥出错");
}
return result;
}
/// <summary>
/// 获取token
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<DaHApiResult<LoginResDto>> GetToken(LoginRequestDto dto)
{
DaHApiResult<LoginResDto> result = new DaHApiResult<LoginResDto>() { Success = true, Code = "0" };
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
{
//1. 获取公钥
DaHApiResult<PublicKeyDto> publicKeyResult = await GetPublicKey();
if (!publicKeyResult.Success)
{
result.Code = "500";
result.Msg = publicKeyResult.Msg;
_logger.LogWarning("获取大华公钥失败:{Msg}", publicKeyResult.Msg);
}
//2. 鉴权
dto.PublicKey = publicKeyResult.Data.PublicKey;
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<DaHApiResult<LoginResDto>>();
if (tokenInfo == null || !result.Success || result.Code != "0")
{
result.Success = false;
result.Code = "1004";
result.Msg = "获取大华登录令牌失败";
_logger.LogWarning("获取大华登录令牌失败,返回结果:{Result}", result);
}
//固定的拼接方式
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;
}
/// <summary>
/// 新增报警事件订阅
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<DaHApiResult<object>> AddSubscribeEvent(string accessToken)
{
var result = new DaHApiResult<object> { Success = true, Code = "0", Data = new object() };
try
{
var baseHost = _configuration["DahuaEvent:Host"];
if (string.IsNullOrWhiteSpace(baseHost))
{
return new DaHApiResult<object>
{
Success = false,
Code = "2001",
Msg = "配置 DahuaEvent:Host 为空"
};
}
var url = $"https://{baseHost}/evo-apigw/evo-event/1.0.0/subscribe/mqinfo";
var req = new SubscribeReqDto
{
Param = new SubscribeParam
{
Monitors = new List<MonitorEntry>
{
new()
{
Monitor = _configuration["DahuaEvent:Callback"]!,
Events =
[
new()
{
Category = "alarm",
SubscribeAll = 1,
DomainSubscribe = 2,
EventType = 1
}
]
}
},
Subsystem = new SubsystemInfo { SubsystemType = 0, Name = _configuration["DahuaEvent:SubsystemName"]!, Magic = _configuration["DahuaEvent:SubsystemMagic"]! }
}
};
// —— 发起请求 ——
using var request = new HttpRequestMessage(HttpMethod.Post, url)
{
Content = JsonContent.Create(req, options: new JsonSerializerOptions
{
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull,
PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase
})
};
// 大华网关要求:Authorization 放完整值(Bearer xxx)
request.Headers.TryAddWithoutValidation("Authorization", accessToken);
request.Headers.Accept.ParseAdd("application/json");
using var resp = await _http.SendAsync(request);
var body = await resp.Content.ReadFromJsonAsync<DaHApiResult<object>>();
if (!resp.IsSuccessStatusCode || body is null)
{
return new DaHApiResult<object>
{
Success = false,
Code = "2002",
Msg = $"HTTP {resp.StatusCode} 或响应为空"
};
}
if (!(body.Success && string.Equals(body.Code, "0", StringComparison.OrdinalIgnoreCase)))
{
return new DaHApiResult<object>
{
Success = false,
Code = body.Code ?? "2003",
Msg = body.Msg ?? "订阅失败"
};
}
result.Data = body;
return result;
}
catch (Exception ex)
{
_logger.LogError(ex, "大华事件订阅异常");
return new DaHApiResult<object>
{
Success = false,
Code = "2005",
Msg = "事件订阅异常"
};
}
}
/// <summary>
/// 报警事件订阅回调处理
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<ApiResult<bool>> HandleAsync(EventEnvelopeDto dto)
{
ApiResult<bool> result = new() { Code = 200, Msg = "接口调用成功", Data = true };
try
{
if (dto is null)
{
result.Code = 500;
result.Msg = "请求参数不能为空";
result.Data = false;
_logger.LogWarning("大华报警事件订阅回调处理失败,参数不能为空");
return result;
}
if (dto.Info is not null)
{
//这是大华的残卫报警类型
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);
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "大华报警事件订阅回调处理异常");
result.Code = 500;
result.Msg = "大华报警事件订阅回调处理异常";
result.Data = false;
}
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);
}
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("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent></RSAKeyValue>",
Convert.ToBase64String(publicKeyParam.Modulus.ToByteArrayUnsigned()),
Convert.ToBase64String(publicKeyParam.Exponent.ToByteArrayUnsigned()));
Console.WriteLine(xmlpublicKey);
return xmlpublicKey;
}
#endregion RES加密
}
}