Merge branch 'DataMaintenance' of http://123.132.248.154:10000/HC_YFZX/Infrastructure into DataMaintenance

DataMaintenance
zhangbin 2025-11-27 09:46:56 +08:00
commit 510890b0e1
5 changed files with 1019 additions and 45 deletions

View File

@ -0,0 +1,668 @@
using System;
using System.Text;
using System.Security.Cryptography;
using Newtonsoft.Json;
using System.Globalization;
using InvalidOperationException = System.InvalidOperationException;
namespace Infrastructure.Helpers
{
/// <summary>
/// 大华平台API访问工具类
/// </summary>
public class DaHuaApi
{
private readonly HttpClient _httpClient;
private readonly string _baseUrl = "https://icc-dev.hibetatest.com:4077";
private readonly string _kccKey;
private readonly string _kccSecret;
private readonly string _kccId;
private static OAuthTokenResponse _currentToken;
private static DateTime _tokenExpiry;
/// <summary>
/// 构造函数KCC认证方式
/// </summary>
/// <param name="baseUrl">API基础URL</param>
/// <param name="kccId">KCC ID</param>
/// <param name="kccKey">KCC Key</param>
/// <param name="kccSecret">KCC Secret</param>
public DaHuaApi(string baseUrl, string kccId, string kccKey, string kccSecret)
{
_baseUrl = baseUrl;
_kccId = kccId;
_kccKey = kccKey;
_kccSecret = kccSecret;
_httpClient = new HttpClient();
}
/// <summary>
/// 构造函数OAuth2认证方式
/// </summary>
/// <param name="baseUrl">API基础URL</param>
public DaHuaApi(string baseUrl)
{
_baseUrl = baseUrl;
_httpClient = new HttpClient();
}
/// <summary>
/// 使用用户名密码方式进行OAuth2认证获取Token
/// </summary>
/// <param name="clientId">客户端ID</param>
/// <param name="clientSecret">客户端密钥</param>
/// <param name="username">用户名</param>
/// <param name="password">密码</param>
/// <returns>认证结果</returns>
public async Task<OAuthTokenResponse> AuthenticateAsync(string clientId, string clientSecret, string username,
string password)
{
// 1. 获取公钥
var publicKeyResponse =
await _httpClient.GetAsync($"{_baseUrl}/evo-apigw/evo-oauth/1.0.0/oauth/public-key");
var publicKeyResult =
JsonConvert.DeserializeObject<PublicKeyResponse>(await publicKeyResponse.Content.ReadAsStringAsync());
if (!publicKeyResult.Success)
{
throw new InvalidOperationException($"获取公钥失败: {publicKeyResult.ErrMsg}");
}
var encryptedPassword = RsaHelper.Encrypt(password, publicKeyResult.Data.PublicKey);
// 3. 获取Token
var tokenRequest = new
{
grant_type = "password",
username = username,
password = encryptedPassword,
client_id = clientId,
client_secret = clientSecret,
public_key = publicKeyResult.Data.PublicKey
};
var json = JsonConvert.SerializeObject(tokenRequest);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var tokenResponse =
await _httpClient.PostAsync($"{_baseUrl}/evo-apigw/evo-oauth/1.0.0/oauth/extend/token", content);
var result = await tokenResponse.Content.ReadAsStringAsync();
// {"success":true,"data":{"access_token":"2:3hRw0P7m3YD7skQwL9qNoPtcg9by378W","refresh_token":"KGnWspOzRdwFcD9D3bKztHc12H41vNjT","magicId":"TjNv14H21cHtzKb3D9DcFwdRzOpsWnGK","scope":"supplier","remainderDays":166,"token_type":"bearer","expires_in":7199,"userId":"2"},"code":"0","errMsg":""}
var tokenResult =
JsonConvert.DeserializeObject<OAuthTokenResponse>(result);
if (tokenResult.success)
{
_currentToken = tokenResult;
_tokenExpiry = DateTime.UtcNow.AddSeconds(tokenResult.data.expires_in - 60); // 提前60秒过期以避免边界问题
}
else
{
throw new InvalidOperationException($"获取Token失败: {tokenResult.errMsg}");
}
return tokenResult;
}
/// <summary>
/// 确保Token有效
/// </summary>
private async Task EnsureValidTokenAsync()
{
if (_currentToken == null || DateTime.UtcNow > _tokenExpiry)
{
await AuthenticateAsync("CompanyName", "42bec152-8f04-476a-9aec-e7d616ff3cb3",
"TEST", "OGR28u6_cc");
}
}
/// <summary>
/// 生成KCC签名
/// </summary>
/// <param name="method">HTTP方法</param>
/// <param name="uri">请求URI</param>
/// <param name="body">请求体</param>
/// <param name="timestamp">时间戳</param>
/// <returns>KCC签名字符串</returns>
private string GenerateKccSignature(string method, string uri, string body, long timestamp)
{
// 构建签名原文
var signString = $"POST\n{uri}\n{body}\n{_kccId}\n{timestamp}";
// 使用HMAC-SHA1算法计算签名
using (var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(_kccSecret)))
{
var hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(signString));
return Convert.ToBase64String(hashBytes);
}
}
/// <summary>
/// 设置请求的认证头部
/// </summary>
/// <param name="uri">请求URI</param>
/// <param name="json">请求体JSON</param>
private async Task SetAuthorizationHeaderAsync(string uri, string json)
{
_httpClient.DefaultRequestHeaders.Clear();
// 如果有KCC配置使用KCC认证
if (!string.IsNullOrEmpty(_kccId) && !string.IsNullOrEmpty(_kccKey) && !string.IsNullOrEmpty(_kccSecret))
{
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
var signature = GenerateKccSignature("POST", uri, json, timestamp);
_httpClient.DefaultRequestHeaders.Add("KCC-ID", _kccId);
_httpClient.DefaultRequestHeaders.Add("KCC-TS", timestamp.ToString());
_httpClient.DefaultRequestHeaders.Add("KCC-SIGN", signature);
}
// 如果有Token使用OAuth2认证
else
{
await EnsureValidTokenAsync();
_httpClient.DefaultRequestHeaders.Add("Authorization",
$"{_currentToken.data.token_type} {_currentToken.data.access_token}");
}
}
/// <summary>
/// 获取设备通道分类信息
/// 对应接口: /evo-api/go/geo-term/i.a.o/device/channel/subsystem/page
/// </summary>
/// <param name="request">请求参数</param>
/// <returns>通道分类信息响应</returns>
public async Task<ChannelCategoryResponse> GetChannelCategoriesAsync(ChannelCategoryRequest request)
{
var url = $"{_baseUrl}/evo-apigw/evo-brm/1.2.0/device/channel/subsystem/page";
var uri = "/evo-apigw/evo-brm/1.2.0/device/channel/subsystem/page";
var json = JsonConvert.SerializeObject(request);
// 设置认证头部
await SetAuthorizationHeaderAsync(uri, json);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(url, content);
var responseString = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
// {"success":true,
// "data":{"pageData":
// [{"id":110,"deviceCode":"1000102","unitType":1,
// "unitSeq":0,"channelSeq":0,"channelCode":"1000102$1$0$0",
// "channelName":"1.1.1.1_1","channelType":"1","cameraType":"2",
// "ownerCode":"001001","gpsX":"","gpsY":"","isOnline":0,
// "stat":1,"capability":"00000000000000000000000000000001","chExt":"{}",
// "isVirtual":0,"createTime":"2025-11-11 15:43:30"},
// {"id":108,"deviceCode":"1000099","unitType":7,"unitSeq":0,"channelSeq":0,"channelCode":"1000099$7$0$0",
// "channelSn":"","channelName":"膨化食品测试设备-1_门禁通道_1",
// "ownerCode":"001010","isOnline":0,"stat":1,
// "chExt":"{\"workStatus\":5,\"psamEnable\":0,\"isUse\":\"0\",
// \"channelBindFace\":\"\",\"channelBindOrg\":\"\",\"cpuCardEnable\":1,
// \"physicalId\":\"\",\"channelCapacity\":5,\"channelUnii\":\"\",\"closeTimeOut\":60,\"icCardEnable\":1,\"collaborativeEnable\":0,\"snapshotUpload\":0,\"enableMode\":0,\"timeoutAlarmEnable\":0,\"manapassword\":\"12345678\",\"collectTypeFlag\":7,\"channelPersonType\":1,\"nfcAntiCopyEnable\":0,\"readerList\":[{\"name\":\"膨化食品测试设备-1_门禁通道_1读卡器1\",\"code\":1,\"openType\":26,\"businessType\":1},{\"name\":\"膨化食品测试设备-1_门禁通道_1读卡器2\",\"code\":2,\"openType\":26,\"businessType\":2}],\"icEncryptEnable\":0,\"delayTime\":3,\"status\":\"5\"}","isVirtual":0,"createTime":"2025-11-06 13:31:30"},{"id":109,"deviceCode":"1000099","unitType":1,"unitSeq":0,"channelSeq":0,"channelCode":"1000099$1$0$0","channelSn":"","channelName":"膨化食品测试设备-1_视频通道_1","channelType":"1","cameraType":"2","ownerCode":"001010","isOnline":0,"stat":1,"capability":"00000000000000000000000000000000","chExt":"{\"keyCode\":\"\"}","isVirtual":0,"createTime":"2025-11-06 13:31:30"},{"id":63,"deviceCode":"1000056","unitType":1,"unitSeq":0,"channelSeq":0,"channelCode":"1000056$1$0$0","channelName":"127.0.0.2_1","channelType":"1","cameraType":"2","ownerCode":"001006","gpsX":"","gpsY":"","isOnline":0,"stat":1,"capability":"00000000000000000000001100000001","chExt":"{\"keyCode\":\"\"}","isVirtual":0,"createTime":"2025-09-25 16:22:56"},{"id":10,"deviceCode":"1000008","unitType":1,"unitSeq":0,"channelSeq":0,"channelCode":"1000008$1$0$0","channelSn":"1007749","channelName":"IPC10001001_1","channelType":"1","cameraType":"2","ownerCode":"001002001","gpsX":"","gpsY":"","isOnline":1,"stat":1,"capability":"00000000000000000000000100000001","chExt":"{\"keyCode\":\"\"}","isVirtual":0,"createTime":"2025-09-09 16:18:44"},{"id":4,"deviceCode":"1000002","unitType":4,"unitSeq":0,"channelSeq":0,"channelCode":"1000002$4$0$0","channelName":"9G0D671YAJB4F8F_1","ownerCode":"001001","gpsX":"","gpsY":"","isOnline":1,"stat":1,"isVirtual":0,"createTime":"2025-09-08 20:36:40"},{"id":3,"deviceCode":"1000002","unitType":3,"unitSeq":0,"channelSeq":1,"channelCode":"1000002$3$0$1","channelName":"9G0D671YAJB4F8F_2","ownerCode":"001001","gpsX":"","gpsY":"","isOnline":1,"stat":1,"chExt":"{\"alarmType\":\"2\",\"alarmLevel\":\"1\"}","isVirtual":0,"createTime":"2025-09-08 20:36:40"},{"id":2,"deviceCode":"1000002","unitType":3,"unitSeq":0,"channelSeq":0,"channelCode":"1000002$3$0$0","channelName":"9G0D671YAJB4F8F_1","ownerCode":"001001","gpsX":"","gpsY":"","isOnline":1,"stat":1,"chExt":"{\"alarmType\":\"6001\",\"alarmLevel\":\"1\"}","isVirtual":0,"createTime":"2025-09-08 20:36:40"},{"id":1,"deviceCode":"1000002","unitType":1,"unitSeq":0,"channelSeq":0,"channelCode":"1000002$1$0$0","channelName":"DH-SD-6C3232-HNY-DB-EFR","channelType":"1","cameraType":"2","ownerCode":"001001","gpsX":"120.519063","gpsY":"30.381539","isOnline":1,"stat":1,"capability":"00000000000000010000000000000001","chExt":"{}","isVirtual":0,"createTime":"2025-09-08 20:36:40"}],"currentPage":1,"totalPage":1,"pageSize":10,"totalRows":9},"code":"0","errMsg":""}
return JsonConvert.DeserializeObject<ChannelCategoryResponse>(responseString);
}
throw new HttpRequestException($"请求失败: {response.StatusCode}, {responseString}");
}
/// <summary>
/// 启动RTSP实时预览
/// 对应接口: /evo-api/go/admin/API/MTS/Video/StartVideo
/// </summary>
/// <param name="request">预览请求参数</param>
/// <returns>RTSP预览响应</returns>
public async Task<RtspPreviewResponse> StartRtspPreviewAsync(RtspPreviewRequest request)
{
var url = $"{_baseUrl}/evo-apigw/admin/API/MTS/Video/StartVideo";
var uri = "/evo-apigw/admin/API/MTS/Video/StartVideo";
var requestData = new Dictionary<string, object>
{
["data"] = request.data
};
var json = JsonConvert.SerializeObject(requestData);
// 设置认证头部
await SetAuthorizationHeaderAsync(uri, json);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(url, content);
var responseString = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
return JsonConvert.DeserializeObject<RtspPreviewResponse>(responseString);
}
throw new HttpRequestException($"请求失败: {response.StatusCode}, {responseString}");
}
}
#region OAuth2认证相关模型
/// <summary>
/// 公钥响应
/// </summary>
public class PublicKeyResponse
{
public bool Success { get; set; }
public string Code { get; set; }
public string ErrMsg { get; set; }
public PublicKeyData Data { get; set; }
}
/// <summary>
/// 公钥数据
/// </summary>
public class PublicKeyData
{
public string PublicKey { get; set; }
}
/// <summary>
/// OAuth2 Token响应
/// </summary>
public class OAuthTokenResponse
{
public bool success { get; set; }
public string code { get; set; }
public string errMsg { get; set; }
public OAuthTokenData data { get; set; }
}
/// <summary>
/// OAuth2 Token数据
/// </summary>
public class OAuthTokenData
{
public string access_token { get; set; }
public string refresh_token { get; set; }
public string magicId { get; set; }
public string scope { get; set; }
public int remainderDays { get; set; }
public string token_type { get; set; }
public long expires_in { get; set; }
public string userId { get; set; }
}
#endregion
#region ChannelCategory相关模型定义
/// <summary>
/// 设备通道分类请求参数
/// </summary>
public class ChannelCategoryRequest
{
/// <summary>
/// 当前页码
/// </summary>
public int pageNum { get; set; } = 1;
/// <summary>
/// 每页大小
/// </summary>
public int pageSize { get; set; } = 10;
/// <summary>
/// 排序字段
/// </summary>
public string sort { get; set; }
/// <summary>
/// 排序方式 ASC/DESC
/// </summary>
public string sortType { get; set; }
/// <summary>
/// 所有者编码
/// </summary>
public string ownerCode { get; set; }
/// <summary>
/// 设备编码列表
/// </summary>
public List<string> deviceCodeList { get; set; }
/// <summary>
/// 通道编码列表
/// </summary>
public List<string> channelCodeList { get; set; }
/// <summary>
/// 设备大类
/// </summary>
public int? deviceCategory { get; set; }
/// <summary>
/// 设备小类
/// </summary>
public int? deviceType { get; set; }
/// <summary>
/// 单元类型列表
/// </summary>
public List<int> unitTypeList { get; set; }
/// <summary>
/// 通道类型列表
/// </summary>
public List<int> channelTypeList { get; set; }
/// <summary>
/// 是否在线
/// </summary>
public int? isOnline { get; set; }
/// <summary>
/// 是否已接入
/// </summary>
public int? access { get; set; }
/// <summary>
/// 是否虚拟
/// </summary>
public int? isVirtual { get; set; }
/// <summary>
/// 设备通道状态
/// </summary>
public int? stat { get; set; }
/// <summary>
/// 是否包含子设备编码标签
/// </summary>
public bool includeSubOwnerCodeFlag { get; set; }
}
/// <summary>
/// 通道分类查询响应
/// </summary>
public class ChannelCategoryResponse
{
public string errMsg { get; set; }
public bool success { get; set; }
/// <summary>
/// 状态码
/// </summary>
public int code { get; set; }
/// <summary>
/// 响应数据
/// </summary>
public ChannelCategoryData data { get; set; }
}
/// <summary>
/// 通道分类响应数据
/// </summary>
public class ChannelCategoryData
{
/// <summary>
/// 当前页
/// </summary>
public int currentPage { get; set; }
/// <summary>
/// 总页数
/// </summary>
public int totalPage { get; set; }
/// <summary>
/// 每页大小
/// </summary>
public int pageSize { get; set; }
/// <summary>
/// 总行数
/// </summary>
public int totalRows { get; set; }
/// <summary>
/// 页面数据
/// </summary>
public List<ChannelInfoVO> pageData { get; set; }
}
/// <summary>
/// 通道信息
/// </summary>
public class ChannelInfoVO
{
/// <summary>
/// ID
/// </summary>
public long id { get; set; }
/// <summary>
/// 设备编码
/// </summary>
public string deviceCode { get; set; }
/// <summary>
/// 单元类型
/// </summary>
public int unitType { get; set; }
/// <summary>
/// 单元序列
/// </summary>
public int unitSeq { get; set; }
/// <summary>
/// 通道序列
/// </summary>
public int channelSeq { get; set; }
/// <summary>
/// 通道编码
/// </summary>
public string channelCode { get; set; }
/// <summary>
/// 通道序号
/// </summary>
public string channelSn { get; set; }
/// <summary>
/// 通道名称
/// </summary>
public string channelName { get; set; }
/// <summary>
/// 通道类型
/// </summary>
public string channelType { get; set; }
/// <summary>
/// 摄像头类型
/// </summary>
public string cameraType { get; set; }
/// <summary>
/// 所有者编码
/// </summary>
public string ownerCode { get; set; }
/// <summary>
/// GPS X坐标
/// </summary>
public string gpsX { get; set; }
/// <summary>
/// GPS Y坐标
/// </summary>
public string gpsY { get; set; }
/// <summary>
/// 光栅图ID
/// </summary>
public long mapId { get; set; }
/// <summary>
/// 域ID
/// </summary>
public long domainId { get; set; }
/// <summary>
/// 描述
/// </summary>
public string memo { get; set; }
/// <summary>
/// 是否在线
/// </summary>
public int isOnline { get; set; }
/// <summary>
/// 状态
/// </summary>
public int stat { get; set; }
/// <summary>
/// 能力集
/// </summary>
public string capability { get; set; }
/// <summary>
/// 是否已接入
/// </summary>
public int access { get; set; }
/// <summary>
/// 通道详情扩展信息Json体
/// </summary>
public string chExt { get; set; }
/// <summary>
/// 是否是虚拟通道
/// </summary>
public string isVirtual { get; set; }
}
#endregion
#region RTSP预览相关模型定义
/// <summary>
/// RTSP预览请求参数
/// </summary>
public class RtspPreviewRequest
{
/// <summary>
/// 数据部分
/// </summary>
public RtspPreviewData data { get; set; }
}
/// <summary>
/// RTSP预览数据部分
/// </summary>
public class RtspPreviewData
{
/// <summary>
/// 视频通道编码
/// </summary>
public string channelId { get; set; }
/// <summary>
/// 数据类型1-视频
/// </summary>
public string dataType { get; set; } = "1";
/// <summary>
/// 码流类型1-主码流2-辅码流3-编码流
/// </summary>
public string streamType { get; set; } = "1";
}
/// <summary>
/// RTSP预览响应
/// </summary>
public class RtspPreviewResponse
{
/// <summary>
/// 状态码
/// </summary>
public int code { get; set; }
/// <summary>
/// 描述信息
/// </summary>
public string errMsg { get; set; }
public bool success { get; set; }
/// <summary>
/// 响应数据
/// </summary>
public RtspPreviewResult data { get; set; }
}
/// <summary>
/// RTSP预览结果数据
/// </summary>
public class RtspPreviewResult
{
/// <summary>
/// 最低帧率
/// </summary>
public string minRate { get; set; }
/// <summary>
/// 协议
/// </summary>
public string protocol { get; set; }
/// <summary>
/// IP地址
/// </summary>
public string ip { get; set; }
/// <summary>
/// 端口号
/// </summary>
public string port { get; set; }
/// <summary>
/// 是否启用STUN协议
/// </summary>
public string stunEnable { get; set; }
/// <summary>
/// STUN协议端口
/// </summary>
public string stunPort { get; set; }
/// <summary>
/// RTSP地址
/// </summary>
public string url { get; set; }
/// <summary>
/// 连接类型
/// </summary>
public string connectType { get; set; }
/// <summary>
/// 会话ID
/// </summary>
public string session { get; set; }
/// <summary>
/// 令牌
/// </summary>
public string token { get; set; }
/// <summary>
/// 轨迹ID
/// </summary>
public string trackId { get; set; }
}
#endregion
}

View File

@ -0,0 +1,190 @@
using System;
using System.Security.Cryptography;
using System.Text;
namespace Infrastructure.Helpers
{
/// <summary>
/// RSA加解密帮助类 (.NET 6 兼容版本)
/// </summary>
public class RsaHelper
{
/// <summary>
/// 生成RSA密钥对
/// </summary>
/// <returns>包含公钥和私钥的元组</returns>
public static (string publicKey, string privateKey) GenerateKeyPair()
{
using (var rsa = RSA.Create())
{
// 设置密钥大小为2048位
rsa.KeySize = 2048;
// 使用PEM格式导出密钥
var publicKey = ExportPublicKey(rsa);
var privateKey = ExportPrivateKey(rsa);
return (publicKey, privateKey);
}
}
/// <summary>
/// 使用公钥加密数据
/// </summary>
/// <param name="plainText">明文</param>
/// <param name="publicKey">公钥PEM格式</param>
/// <returns>Base64编码的密文</returns>
public static string Encrypt(string plainText, string publicKey)
{
using (var rsa = RSA.Create())
{
ImportPublicKey(rsa, publicKey);
var data = Encoding.UTF8.GetBytes(plainText);
var encryptedData = rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1);
return Convert.ToBase64String(encryptedData);
}
}
/// <summary>
/// 使用私钥解密数据
/// </summary>
/// <param name="cipherText">Base64编码的密文</param>
/// <param name="privateKey">私钥PEM格式</param>
/// <returns>解密后的明文</returns>
public static string Decrypt(string cipherText, string privateKey)
{
using (var rsa = RSA.Create())
{
ImportPrivateKey(rsa, privateKey);
var data = Convert.FromBase64String(cipherText);
var decryptedData = rsa.Decrypt(data, RSAEncryptionPadding.Pkcs1);
return Encoding.UTF8.GetString(decryptedData);
}
}
/// <summary>
/// 使用私钥签名数据
/// </summary>
/// <param name="data">要签名的数据</param>
/// <param name="privateKey">私钥PEM格式</param>
/// <returns>Base64编码的签名</returns>
public static string SignData(string data, string privateKey)
{
using (var rsa = RSA.Create())
{
ImportPrivateKey(rsa, privateKey);
var dataBytes = Encoding.UTF8.GetBytes(data);
var signatureBytes = rsa.SignData(dataBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
return Convert.ToBase64String(signatureBytes);
}
}
/// <summary>
/// 使用公钥验证签名
/// </summary>
/// <param name="data">原始数据</param>
/// <param name="signature">Base64编码的签名</param>
/// <param name="publicKey">公钥PEM格式</param>
/// <returns>验证是否成功</returns>
public static bool VerifyData(string data, string signature, string publicKey)
{
using (var rsa = RSA.Create())
{
ImportPublicKey(rsa, publicKey);
var dataBytes = Encoding.UTF8.GetBytes(data);
var signatureBytes = Convert.FromBase64String(signature);
return rsa.VerifyData(dataBytes, signatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
}
/// <summary>
/// 导出公钥为PEM格式
/// </summary>
/// <param name="rsa">RSA实例</param>
/// <returns>PEM格式公钥</returns>
private static string ExportPublicKey(RSA rsa)
{
// 使用SubjectPublicKeyInfo格式导出公钥
var publicKey = rsa.ExportSubjectPublicKeyInfo();
return ExportToPem(publicKey, "PUBLIC KEY");
}
/// <summary>
/// 导出私钥为PEM格式
/// </summary>
/// <param name="rsa">RSA实例</param>
/// <returns>PEM格式私钥</returns>
private static string ExportPrivateKey(RSA rsa)
{
// 使用Pkcs8格式导出私钥
var privateKey = rsa.ExportPkcs8PrivateKey();
return ExportToPem(privateKey, "PRIVATE KEY");
}
/// <summary>
/// 从PEM格式导入公钥
/// </summary>
/// <param name="rsa">RSA实例</param>
/// <param name="publicKey">PEM格式公钥</param>
private static void ImportPublicKey(RSA rsa, string publicKey)
{
var keyData = ImportFromPem(publicKey);
// 使用ImportSubjectPublicKeyInfo导入公钥
rsa.ImportSubjectPublicKeyInfo(keyData, out _);
}
/// <summary>
/// 从PEM格式导入私钥
/// </summary>
/// <param name="rsa">RSA实例</param>
/// <param name="privateKey">PEM格式私钥</param>
private static void ImportPrivateKey(RSA rsa, string privateKey)
{
var keyData = ImportFromPem(privateKey);
// 使用ImportPkcs8PrivateKey导入私钥
rsa.ImportPkcs8PrivateKey(keyData, out _);
}
/// <summary>
/// 将字节数组导出为PEM格式
/// </summary>
/// <param name="keyData">密钥数据</param>
/// <param name="type">密钥类型</param>
/// <returns>PEM格式字符串</returns>
private static string ExportToPem(byte[] keyData, string type)
{
var builder = new StringBuilder();
builder.AppendLine($"-----BEGIN {type}-----");
var base64String = Convert.ToBase64String(keyData);
for (int i = 0; i < base64String.Length; i += 64)
{
builder.AppendLine(base64String.Substring(i, Math.Min(64, base64String.Length - i)));
}
builder.AppendLine($"-----END {type}-----");
return builder.ToString();
}
/// <summary>
/// 从PEM格式导入数据
/// </summary>
/// <param name="pemData">PEM格式数据</param>
/// <returns>密钥字节数组</returns>
private static byte[] ImportFromPem(string pemData)
{
var lines = pemData.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
var base64Data = new StringBuilder();
foreach (var line in lines)
{
var trimmedLine = line.Trim();
if (!trimmedLine.StartsWith("---") && !string.IsNullOrEmpty(trimmedLine))
{
base64Data.Append(trimmedLine);
}
}
return Convert.FromBase64String(base64Data.ToString());
}
}
}

View File

@ -32,6 +32,7 @@ using SqlSugar;
using System.Net.WebSockets;
using System.Security.Cryptography;
using System.Text;
using Infrastructure.Helpers;
using Yitter.IdGenerator;
using static Org.BouncyCastle.Math.EC.ECCurve;
@ -43,7 +44,9 @@ namespace OpenAuth.App.ServiceApp.FireManagement
private IConfiguration _configuration;
IOptions<KikvisionConfig> _options;
IOptions<JPushClientConfig> _jpoptions;
public FireManagementApp(IConfiguration configuration, IOptions<KikvisionConfig> options, IOptions<JPushClientConfig> jpoptions, ISugarUnitOfWork<SugarDbContext> unitWork,
public FireManagementApp(IConfiguration configuration, IOptions<KikvisionConfig> options,
IOptions<JPushClientConfig> jpoptions, ISugarUnitOfWork<SugarDbContext> unitWork,
ISimpleClient<FmFireclueTask> repository, IAuth auth) : base(unitWork, repository, auth)
{
_auth = auth;
@ -128,6 +131,7 @@ namespace OpenAuth.App.ServiceApp.FireManagement
}
}
}
//删除任务
public async Task<Response<bool>> DeleteTask(long id)
{
@ -158,6 +162,7 @@ namespace OpenAuth.App.ServiceApp.FireManagement
}
}
}
//更新任务状态
public async Task<Response<bool>> ReceiveFireClueTask(long taskid, int state, string recipient)
{
@ -178,6 +183,7 @@ namespace OpenAuth.App.ServiceApp.FireManagement
}
}
}
//更新任务图片或者新增线索
public async Task<Response<bool>> ReceiveTaskImage(string info)
{
@ -196,7 +202,6 @@ namespace OpenAuth.App.ServiceApp.FireManagement
else
{
//新增线索信息
}
if (db.Commit())
@ -209,11 +214,13 @@ namespace OpenAuth.App.ServiceApp.FireManagement
}
}
}
class ReciveTaskInfo
{
public long taskid { get; set; }
public string path { get; set; }
}
/// <summary>
/// 查询下发人员
/// </summary>
@ -250,6 +257,7 @@ namespace OpenAuth.App.ServiceApp.FireManagement
return new Response<List<FmFireclueTask>> { Result = tasks };
}
}
/// <summary>
/// 任务详情
/// </summary>
@ -278,7 +286,7 @@ namespace OpenAuth.App.ServiceApp.FireManagement
Audio = a.Audio,
Image = a.Image,
})
.Mapper(a => a.StateName = ((TaskState)a.State).GetDescription())
.Mapper(a => a.StateName = ((TaskState)a.State).GetDescription())
.FirstAsync();
var rep = db.FmFireTaskUser.AsQueryable().Where(r => r.UserId == userid && r.TaskId == id).First();
if (rep != null)
@ -286,6 +294,7 @@ namespace OpenAuth.App.ServiceApp.FireManagement
info.State = rep.State;
info.StateName = ((TaskState)rep.State).GetDescription();
}
return new Response<FireClueTaskResp> { Result = info };
}
}
@ -586,6 +595,7 @@ namespace OpenAuth.App.ServiceApp.FireManagement
}
}
}
/// <summary>
/// 添加火情日志
/// </summary>
@ -596,7 +606,8 @@ namespace OpenAuth.App.ServiceApp.FireManagement
using (var db = base.UnitWork.CreateContext())
{
var userId = _auth.GetCurrentUser().User.Id;
info.CreateId = userId; info.CreateTime = DateTime.Now;
info.CreateId = userId;
info.CreateTime = DateTime.Now;
await db.FmFireclueinfoLog.InsertAsync(info);
if (db.Commit())
{
@ -608,6 +619,7 @@ namespace OpenAuth.App.ServiceApp.FireManagement
}
}
}
//按月统计火情线索
public async Task<Response<List<FireInfoForChart>>> GetFireClueStatistics(int type)
{
@ -753,6 +765,7 @@ namespace OpenAuth.App.ServiceApp.FireManagement
return new Response<List<FmFireclueinfoLog>> { Result = list };
}
}
#endregion
/// <summary>
@ -961,33 +974,36 @@ namespace OpenAuth.App.ServiceApp.FireManagement
}
};
}
/// <summary>
/// 查询人员上报的线索
/// </summary>
/// <param name="userid"></param>
/// <returns></returns>
public async Task<Response<PageInfo<List<FireClueInfoResp>>>> LoadFireClueInfoByUserId(int pageIndex, int state, int pageSize, string userid)
public async Task<Response<PageInfo<List<FireClueInfoResp>>>> LoadFireClueInfoByUserId(int pageIndex, int state,
int pageSize, string userid)
{
using (var db = base.UnitWork.CreateContext())
{
RefAsync<int> total = 0;
var list = await db.FmFireclueInfo.AsQueryable().Where(a => a.CreateId.ToString() == userid).Select(a => new FireClueInfoResp
{
Id = a.Id,
CreateId = a.CreateId.ToString(),
ReportPerson = a.ReportPerson,
Lat = a.Lat,
Lng = a.Lng,
Address = a.Address,
ReportTime = a.ReportTime,
Describe = a.Describe,
SourceType = a.SourceType,
State = a.State,
AreaName = a.AreaName,
ClueNo = a.ClueNo,
}).Mapper(a => a.SourceTypeName = ((SourceType)a.SourceType).GetDescription())
.Mapper(a => a.StateName = ((State)a.State).GetDescription())
.OrderBy(a => a.ReportTime, SqlSugar.OrderByType.Desc).ToPageListAsync(pageIndex, pageSize, total);
var list = await db.FmFireclueInfo.AsQueryable().Where(a => a.CreateId.ToString() == userid).Select(a =>
new FireClueInfoResp
{
Id = a.Id,
CreateId = a.CreateId.ToString(),
ReportPerson = a.ReportPerson,
Lat = a.Lat,
Lng = a.Lng,
Address = a.Address,
ReportTime = a.ReportTime,
Describe = a.Describe,
SourceType = a.SourceType,
State = a.State,
AreaName = a.AreaName,
ClueNo = a.ClueNo,
}).Mapper(a => a.SourceTypeName = ((SourceType)a.SourceType).GetDescription())
.Mapper(a => a.StateName = ((State)a.State).GetDescription())
.OrderBy(a => a.ReportTime, SqlSugar.OrderByType.Desc).ToPageListAsync(pageIndex, pageSize, total);
return new Response<PageInfo<List<FireClueInfoResp>>>
{
Code = 200,
@ -1001,9 +1017,12 @@ namespace OpenAuth.App.ServiceApp.FireManagement
}
}
private static readonly string baseUrl = "http://10.176.126.121:8766/liveBroadCast/bd/api/v1/device/liveBroadCast";
private static readonly string baseUrl =
"http://10.176.126.121:8766/liveBroadCast/bd/api/v1/device/liveBroadCast";
private static readonly string sk = "ba98a152296f13c565d2a6dfd219994f"; // 用户的sk
private static readonly string account = "AK202006291525121"; // 用户的ak
public async Task<string> GetFlvUrlAsync(string deviceCode)
{
// 获取当前的时间戳
@ -1036,8 +1055,10 @@ namespace OpenAuth.App.ServiceApp.FireManagement
requestMessage.Headers.Add("signature", signature);
// 设置 Content-Type 和 Accept 请求头
requestMessage.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
requestMessage.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
requestMessage.Content.Headers.ContentType =
new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
requestMessage.Headers.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
// 发送请求
var response = await client.SendAsync(requestMessage);
@ -1076,12 +1097,15 @@ namespace OpenAuth.App.ServiceApp.FireManagement
return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
}
}
public async Task<string> GetLive(string deviceCode)
{
string flvUrl = await GetFlvUrlAsync(deviceCode);
return flvUrl;
}
#region socket
public async Task SendSocketMessage(WebSocketModel wsModel)
{
try
@ -1091,16 +1115,16 @@ namespace OpenAuth.App.ServiceApp.FireManagement
string ws = _configuration.GetSection("WebSocket").Value;
await webSocket.ConnectAsync(new Uri(ws), CancellationToken.None);
byte[] data = Encoding.UTF8.GetBytes(Newtonsoft.Json.JsonConvert.SerializeObject(wsModel));
await webSocket.SendAsync(new ArraySegment<byte>(data), System.Net.WebSockets.WebSocketMessageType.Text, true, CancellationToken.None);
await webSocket.CloseAsync(System.Net.WebSockets.WebSocketCloseStatus.NormalClosure, "Closing connection", CancellationToken.None);
await webSocket.SendAsync(new ArraySegment<byte>(data),
System.Net.WebSockets.WebSocketMessageType.Text, true, CancellationToken.None);
await webSocket.CloseAsync(System.Net.WebSockets.WebSocketCloseStatus.NormalClosure,
"Closing connection", CancellationToken.None);
//webSocket.Dispose();
}
}
catch (Exception ex)
{
}
}
/// <summary>
@ -1116,7 +1140,8 @@ namespace OpenAuth.App.ServiceApp.FireManagement
Audience audience = new Audience();
if (infoid != 0)
{
var renwu = db.FmFireTaskUser.AsQueryable().Where(r => r.TaskId == infoid).Select(r => r.UserId).ToList();
var renwu = db.FmFireTaskUser.AsQueryable().Where(r => r.TaskId == infoid).Select(r => r.UserId)
.ToList();
var users = db.User.AsQueryable().Where(r => renwu.Contains(r.Id.ToString())).ToList();
if (users.Count > 0)
{
@ -1131,6 +1156,7 @@ namespace OpenAuth.App.ServiceApp.FireManagement
audience.Alias = users.Select(a => a.Account).ToList();
}
}
PushPayload pushPayload = new PushPayload()
{
Platform = new List<string> { "android", "ios" },
@ -1156,9 +1182,11 @@ namespace OpenAuth.App.ServiceApp.FireManagement
return "success";
}
}
#endregion
#region 海康设备
/// <summary>
/// 获取单个设备的视频流
/// </summary>
@ -1168,7 +1196,8 @@ namespace OpenAuth.App.ServiceApp.FireManagement
[AllowAnonymous]
public string GetPreviewURLs(string cameraIndexCode, string protocol)
{
HttpUtillib.SetPlatformInfo(_options.Value.AppKey, _options.Value.SecretKey, _options.Value.Url, _options.Value.Port, true);
HttpUtillib.SetPlatformInfo(_options.Value.AppKey, _options.Value.SecretKey, _options.Value.Url,
_options.Value.Port, true);
var obj = new
{
@ -1186,11 +1215,14 @@ namespace OpenAuth.App.ServiceApp.FireManagement
{
return Encoding.UTF8.GetString(result);
}
return "fail";
}
#endregion
#region 人员类型统计
#region 人员类型统计
/// <summary>
/// 人员类型统计
/// </summary>
@ -1229,16 +1261,19 @@ namespace OpenAuth.App.ServiceApp.FireManagement
Result = res
};
}
#endregion
#region 获取物资相关接口
/// <summary>
/// 获取全部营房信息
/// </summary>
/// <returns></returns>
public dynamic GetAllYingFang()
{
string sql = @"SELECT ""id"",""dwmc"",""dwzd"",""lsdw"",""dwzdjd"",""dwzdwd"",""dzxm"",""lxfs"",""syrs"",""zbqk"",""tbr"", st_astext(""geom"") as geom FROM ""fm_shp_1692148217"" ";
string sql =
@"SELECT ""id"",""dwmc"",""dwzd"",""lsdw"",""dwzdjd"",""dwzdwd"",""dzxm"",""lxfs"",""syrs"",""zbqk"",""tbr"", st_astext(""geom"") as geom FROM ""fm_shp_1692148217"" ";
using (var db = Repository.AsSugarClient())
{
@ -1258,7 +1293,9 @@ namespace OpenAuth.App.ServiceApp.FireManagement
{
string sql = string.Empty;
if (!string.IsNullOrEmpty(wuzi))
sql = "SELECT \"id\", \"type\",\"name\",info,st_astext(geom) as geom FROM \"fm_shp_1686205187\" WHERE info like '%" + wuzi + "%'";
sql =
"SELECT \"id\", \"type\",\"name\",info,st_astext(geom) as geom FROM \"fm_shp_1686205187\" WHERE info like '%" +
wuzi + "%'";
else
sql = "SELECT \"id\", \"type\",\"name\",info,st_astext(geom) as geom FROM \"fm_shp_1686205187\" ";
@ -1266,8 +1303,10 @@ namespace OpenAuth.App.ServiceApp.FireManagement
data.count = dt.Rows.Count;
data.data = dt;
}
return data;
}
#endregion
#region 任务相关
@ -1277,7 +1316,8 @@ namespace OpenAuth.App.ServiceApp.FireManagement
/// </summary>
/// <param name="userid"></param>
/// <returns></returns>
public async Task<Response<PageInfo<List<FireClueTaskResp>>>> LoadFireClueTaskByUserId(string userid, int pageIndex, int pageSize)
public async Task<Response<PageInfo<List<FireClueTaskResp>>>> LoadFireClueTaskByUserId(string userid,
int pageIndex, int pageSize)
{
using (SugarDbContext db = base.UnitWork.CreateContext())
{
@ -1301,14 +1341,15 @@ namespace OpenAuth.App.ServiceApp.FireManagement
Audio = a.Audio,
Image = a.Image,
}).Mapper(c => c.StateName = ((TaskState)c.State).GetDescription())
.OrderBy(a => a.TaskTime, OrderByType.Desc)
.ToPageListAsync(pageIndex, pageSize, totalNumber);
.OrderBy(a => a.TaskTime, OrderByType.Desc)
.ToPageListAsync(pageIndex, pageSize, totalNumber);
return new Response<PageInfo<List<FireClueTaskResp>>>
{
Result = new PageInfo<List<FireClueTaskResp>> { Items = query, Total = totalNumber }
};
}
}
/// <summary>
/// 查询所有任务
/// </summary>
@ -1336,17 +1377,18 @@ namespace OpenAuth.App.ServiceApp.FireManagement
Audio = a.Audio,
TaskTime = a.TaskTime,
RecipientUsers = SqlFunc.Subqueryable<FmFireTaskUser>().Where(r => r.TaskId == a.Id)
.LeftJoin<SysUser>((r, s) => r.UserId == s.Id.ToString())
.SelectStringJoin((a, s) => s.Name, ","),
.LeftJoin<SysUser>((r, s) => r.UserId == s.Id.ToString())
.SelectStringJoin((a, s) => s.Name, ","),
}).Mapper(c => c.StateName = ((TaskState)c.State).GetDescription())
.OrderBy(a => a.TaskTime, OrderByType.Desc)
.ToPageListAsync(pageIndex, pageSize, totalNumber);
.OrderBy(a => a.TaskTime, OrderByType.Desc)
.ToPageListAsync(pageIndex, pageSize, totalNumber);
return new Response<PageInfo<List<FireClueTaskResp>>>
{
Result = new PageInfo<List<FireClueTaskResp>> { Items = query, Total = totalNumber }
};
}
}
/// <summary>
/// 查询火情线索
/// </summary>
@ -1354,7 +1396,8 @@ namespace OpenAuth.App.ServiceApp.FireManagement
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <returns></returns>
public async Task<Response<PageInfo<List<FireClueInfoResp>>>> LoadFireClueInfoByAreaName(string areaname, int pageIndex = 1, int pageSize = 10)
public async Task<Response<PageInfo<List<FireClueInfoResp>>>> LoadFireClueInfoByAreaName(string areaname,
int pageIndex = 1, int pageSize = 10)
{
using (SugarDbContext db = base.UnitWork.CreateContext())
{
@ -1382,15 +1425,16 @@ namespace OpenAuth.App.ServiceApp.FireManagement
FireType = 1
})
.Mapper(a => a.SourceTypeName = ((SourceType)a.SourceType).GetDescription())
.Mapper(a => a.StateName = ((State)a.State).GetDescription())
.OrderBy(a => a.ReportTime, SqlSugar.OrderByType.Desc)
.ToPageListAsync(pageIndex, pageSize, totalNumber);
.Mapper(a => a.StateName = ((State)a.State).GetDescription())
.OrderBy(a => a.ReportTime, SqlSugar.OrderByType.Desc)
.ToPageListAsync(pageIndex, pageSize, totalNumber);
return new Response<PageInfo<List<FireClueInfoResp>>>
{
Result = new PageInfo<List<FireClueInfoResp>> { Items = query, Total = totalNumber }
};
}
}
/// <summary>
/// App获取在线人员实时位置信息
/// </summary>
@ -1424,7 +1468,9 @@ namespace OpenAuth.App.ServiceApp.FireManagement
};
}
}
#endregion
public async Task<Response<dynamic>> ListCameraWithInRange(string lng, string lat, int radius)
{
// '{lng}', '{lat}'
@ -1445,5 +1491,61 @@ ORDER BY
Result = x
};
}
public async Task<Response<dynamic>> ListDaHuaCameraInfo(int pageNum, int pageSize, int isOnline)
{
var daHuaApi = new DaHuaApi("https://icc-dev.hibetatest.com:4077");
var channelCategoryRequest = new ChannelCategoryRequest
{
pageNum = pageNum,
pageSize = pageSize,
isOnline = 1,
unitTypeList = new List<int> { 1 },
};
Console.WriteLine(JsonConvert.SerializeObject(channelCategoryRequest));
var channelCategoryResponse = await daHuaApi.GetChannelCategoriesAsync(channelCategoryRequest);
if (channelCategoryResponse.success)
{
return new Response<dynamic>
{
Result = channelCategoryResponse.data.pageData
};
}
return new Response<dynamic>
{
Code = channelCategoryResponse.code,
Message = channelCategoryResponse.errMsg
};
}
public async Task<Response<dynamic>> GetDaHuaRtsp(string channelCode,string streamType)
{
var daHuaApi = new DaHuaApi("https://icc-dev.hibetatest.com:4077");
var request = new RtspPreviewRequest
{
data = new RtspPreviewData
{
channelId = channelCode,
dataType = "1",
streamType = streamType //主码流
}
};
var y = await daHuaApi.StartRtspPreviewAsync(request);
if (y.success)
{
return new Response<dynamic>
{
Result = y.data
};
}
return new Response<dynamic>
{
Code = y.code,
Message = y.errMsg
};
}
}
}

View File

@ -28,4 +28,16 @@ public class CameraController : ControllerBase
{
return await _app.ListCameraWithInRange(lng, lat, radius);
}
[HttpGet]
public async Task<Response<dynamic>> ListDaHuaCameraInfo(int pageNum = 1, int pageSize = 10, int isOnline = 1)
{
return await _app.ListDaHuaCameraInfo(pageNum, pageSize, isOnline);
}
[HttpGet]
public async Task<Response<dynamic>> GetDaHuaRtsp(string channelCode, string streamType = "1")
{
return await _app.GetDaHuaRtsp(channelCode, streamType);
}
}

View File

@ -25,6 +25,7 @@
<None Remove="log\**" />
<None Remove="wwwroot\**" />
<EmbeddedResource Include="index.html" />
<None Remove="temp-keys\key-4421ef20-c9a7-4085-ab32-780e0497cf9b.xml" />
</ItemGroup>
<ItemGroup>
@ -81,6 +82,7 @@
<ItemGroup>
<Folder Include="Controllers\BaseControllers\Permission\" />
<Folder Include="Controllers\ServiceControllers\DaHuaCamera\" />
</ItemGroup>
</Project>