Compare commits

...

2 Commits

Author SHA1 Message Date
ChenYi
ea48f30d4f 新增子设备表,以及完善业务系统新增设备 2026-01-14 16:51:25 +08:00
ChenYi
376ce4a0a5 完善业务系统对接 2026-01-14 11:56:18 +08:00
13 changed files with 297 additions and 74 deletions

View File

@ -4,7 +4,7 @@
"CorsOrigins": "https://*.IoT.com,http://localhost:4200,http://localhost:3100,http://localhost:80,http://10.10.90.3:4200"
},
"ConnectionStrings": {
"Default": "Data Source=192.168.111.174;Port=13306;Database=JiSheIoTProDB;uid=root;pwd=JiShe!aqG#5kGgh&0;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true;TreatTinyAsBoolean=false;SslMode=None;Pooling=true;"
"Default": "Data Source=192.168.111.174;Port=13306;Database=JiSheIoTProDB386;uid=root;pwd=JiShe!aqG#5kGgh&0;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true;TreatTinyAsBoolean=false;SslMode=None;Pooling=true;"
},
"Hangfire": {
"Redis": {
@ -80,7 +80,8 @@
"AesSecurityKey": "RPTEIGCA1KvDEXS1",
"IsAesEncrypted": false,
"DistributedMessage": 2,
"SnowflakeWorkerId": 1
"SnowflakeWorkerId": 1,
"DownloadDeviceFirmwareBasicUrl": "http://121.42.175.177:32580/Aggregation/Device/DownloadFirmware?Id="
},
"Jwt": {
"Audience": "JiShe.IoT",

View File

@ -2,7 +2,8 @@
"Serilog": {
"Using": [
"Serilog.Sinks.Console",
"Serilog.Sinks.File"
"Serilog.Sinks.File",
"Serilog.Sinks.Grafana.Loki"
],
"MinimumLevel": {
"Default": "Warning",
@ -21,10 +22,27 @@
"Name": "Console"
},
{
"Name": "File",
"Name": "GrafanaLoki",
"Args": {
"path": "logs/logs-.txt",
"rollingInterval": "Hour"
"uri": "http://192.168.111.164:3100",
"labels": [
{
"key": "app",
"value": "OneNETAdmin" //
},
{
"key": "environment",
"value": "dev" // dev/staging/prod
},
{
"key": "version",
"value": "1.2.3" //
}
],
"propertiesAsLabels": [
"Level",
"SourceContext"
]
}
}
]

View File

@ -0,0 +1,40 @@
using JiShe.ServicePro.Enums;
using JiShe.ServicePro.OpenAPIModels;
using System.ComponentModel.DataAnnotations;
namespace JiShe.IoT.BusinessSystemAggregation.Dto
{
/// <summary>
/// 业务系统批量创建设备信息
/// </summary>
public class BatchCreateDeviceBusinessSystemInput
{
/// <summary>
/// 设备信息集合
/// </summary>
[Required(ErrorMessage = "设备信息不能为空")]
public List<OpenApiDeviceInfoInput> DeviceInfos { get; set; }
/// <summary>
/// 物联网平台类型
/// </summary>
[Required(ErrorMessage = "物联网平台类型不能为空")]
public IoTPlatformTypeEnum IoTPlatform { get; set; }
/// <summary>
/// 集中器在物联网平台中对应的产品Id
/// </summary>
[Required(ErrorMessage = "产品Id不能为空")]
public string IoTPlatformProductId { get; set; }
/// <summary>
/// 设备类型,用来区分主设备是网关设备还是直连设备
/// </summary>
public DeviceTypeEnum? DeviceType { get; set; }
/// <summary>
/// 设备来源类型
/// </summary>
public DeviceSourceTypeEnum? DeviceSourceType { get; set; }
}
}

View File

@ -1,36 +0,0 @@
using JiShe.ServicePro.Enums;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JiShe.IoT.BusinessSystemAggregation.Dto
{
/// <summary>
/// 批量创建设备信息输入
/// </summary>
public class BatchCreateDeviceInfoInput
{
/// <summary>
/// 设备来源类型,只接收 预付费业务系统推送、能耗业务系统推送
/// </summary>
public DeviceSourceTypeEnum DeviceSourceType { get; set; }
/// <summary>
/// 物联网平台类型
/// </summary>
public IoTPlatformTypeEnum IoTPlatform { get; set; }
/// <summary>
/// 集中器在物联网平台中对应的产品Id
/// </summary>
public string IoTPlatformProductId { get; set; }
/// <summary>
/// 设备地址集合
/// </summary>
public List<string> DeviceAddresses { get; set; }
}
}

View File

@ -0,0 +1,45 @@
using JiShe.ServicePro.Enums;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JiShe.IoT.BusinessSystemAggregation.Dto
{
/// <summary>
/// 单个查询设备信息输入
/// </summary>
public class QueryDeviceDataInfoInput
{
/// <summary>
/// 设备类型
/// </summary>
public DeviceTypeEnum DeviceType { get; set; }
/// <summary>
/// 数据类型
/// </summary>
public string IoTDataType { get; set; }
/// <summary>
/// 开始时间,最终需要转换为纳秒级时间戳
/// </summary>
public DateTime BeginTime { get; set; }
/// <summary>
/// 结束时间,最终需要转换为纳秒级时间戳
/// </summary>
public DateTime EndTime { get; set; }
/// <summary>
/// 网关或者直连设备地址
/// </summary>
public string DeviceAddress { get; set; }
/// <summary>
/// 子设备地址
/// </summary>
public string SubDeviceAddress { get; set; }
}
}

View File

@ -28,7 +28,14 @@ namespace JiShe.IoT.BusinessSystemAggregation
Task<HttpDataResult<List<IoTDBDynamicObject>>> BatchQueryDeviceDataInfoAsync(OpenApiRequest input);
/// <summary>
/// 业务系统批量新增设备数据Msg 字段为 BatchCreateDeviceInfoInput 实体
/// 业务系统查询单个设备数据Msg 字段为 QueryDeviceDataInfoInput 实体
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<HttpDataResult<List<IoTDBDynamicObject>>> QueryDeviceDataInfoAsync(OpenApiRequest input);
/// <summary>
/// 业务系统批量新增设备数据Msg 字段为 BatchCreateDeviceBusinessSystemInput 实体
/// </summary>
/// <param name="input"></param>
/// <returns></returns>

View File

@ -1,4 +1,5 @@
using JiShe.ServicePro.Enums;
using JiShe.ServicePro.OpenAPIModels;
using System.ComponentModel.DataAnnotations;
namespace JiShe.IoT.DeviceAggregation
@ -9,11 +10,16 @@ namespace JiShe.IoT.DeviceAggregation
public class BatchCreateDeviceAggregationInput
{
/// <summary>
/// 表通信地址集合
/// 设备地址不能为空
/// </summary>
[Required(ErrorMessage = "设备地址不能为空")]
public List<string> AddressList { get; set; }
/// <summary>
/// 设备信息
/// </summary>
public List<OpenApiDeviceInfoInput> DeviceInfos { get; set; }
/// <summary>
/// 物联网平台类型
/// </summary>
@ -30,6 +36,11 @@ namespace JiShe.IoT.DeviceAggregation
/// 设备来源类型
/// </summary>
public DeviceSourceTypeEnum? DeviceSourceTypeEnum { get; set; }
public DeviceSourceTypeEnum? DeviceSourceType { get; set; }
/// <summary>
/// 设备类型
/// </summary>
public DeviceTypeEnum? DeviceType { get; set; }
}
}

View File

@ -1,4 +1,5 @@
using JiShe.ServicePro.Enums;
using JiShe.ServicePro.OpenAPIModels;
using System.ComponentModel.DataAnnotations;
namespace JiShe.IoT.DeviceAggregation.Dto
@ -14,6 +15,11 @@ namespace JiShe.IoT.DeviceAggregation.Dto
[Required(ErrorMessage = "设备地址不能为空")]
public string DeviceAddress { get; set; }
/// <summary>
/// 设备信息
/// </summary>
public OpenApiDeviceInfoInput DeviceInfos { get; set; }
/// <summary>
/// 物联网平台类型
/// </summary>
@ -30,7 +36,13 @@ namespace JiShe.IoT.DeviceAggregation.Dto
/// 设备来源类型
/// </summary>
public DeviceSourceTypeEnum? DeviceSourceTypeEnum { get; set; }
public DeviceSourceTypeEnum? DeviceSourceType { get; set; }
/// <summary>
/// 设备类型,与业务系统无关
/// </summary>
public DeviceTypeEnum? DeviceType { get; set; }
}
}

View File

@ -1,4 +1,5 @@
using JiShe.IoT.DeviceAggregation.Dto;
using JiShe.IoT.BusinessSystemAggregation.Dto;
using JiShe.IoT.DeviceAggregation.Dto;
using JiShe.ServicePro;
using JiShe.ServicePro.Core;
using JiShe.ServicePro.DeviceManagement.DeviceInfos;
@ -103,6 +104,6 @@ namespace JiShe.IoT.DeviceAggregation
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<bool> BatchCreateDeviceBusinessSystemAsync(BatchCreateDeviceAggregationInput input);
Task<bool> BatchCreateDeviceBusinessSystemAsync(BatchCreateDeviceBusinessSystemInput input);
}
}

View File

@ -213,9 +213,94 @@ namespace JiShe.IoT.BusinessSystemAggregation
}
}
/// <summary>
/// 业务系统查询单个设备数据Msg 字段为 QueryDeviceDataInfoInput 实体
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[AllowAnonymous]
public async Task<HttpDataResult<List<IoTDBDynamicObject>>> QueryDeviceDataInfoAsync(OpenApiRequest input)
{
try
{
var handleResult = HandleOpenApiRequest<QueryDeviceDataInfoInput>(input, serverOptions);
if (handleResult.Success == false)
{
return HttpDataResultExtensions.Failed<List<IoTDBDynamicObject>>(null, handleResult.Message, handleResult.LocationCode);
}
var messageBody = handleResult.Data;
if (string.IsNullOrWhiteSpace(messageBody.DeviceAddress))
{
return HttpDataResultExtensions.Failed<List<IoTDBDynamicObject>>(null, "设备地址不能为空", -103);
}
// Lua脚本
string luaScript = @"
local hashKey = KEYS[1]
local fieldKeys = ARGV
return redis.call('HMGET', hashKey, unpack(fieldKeys))";
//执行脚本
var result = await FreeRedisProvider.Instance.EvalAsync
(
luaScript,
new[] { RedisConst.CacheAllDeviceInfoHashKey },
new List<string>() { messageBody.DeviceAddress }.ToArray()
);
List<DeviceCacheInfos> deviceCacheInfos = new List<DeviceCacheInfos>();
// 处理返回结果
if (result is object[] values)
{
foreach (var value in values)
{
var tempFocusInfo = ServiceProJsonSerializer.Deserialize<DeviceCacheInfos>(value as string);
deviceCacheInfos.Add(tempFocusInfo);
}
}
List<IoTDBDynamicObject> queryResult = new List<IoTDBDynamicObject>();
var deviceCacheInfo = deviceCacheInfos.FirstOrDefault(x => x.DeviceAddress == messageBody.DeviceAddress);
if (deviceCacheInfo == null)
{
_logger.LogError($"{nameof(BatchQueryDeviceDataInfoAsync)} 业务系统单个查询设备数据,设备地址:{messageBody.DeviceAddress}未找到设备地址缓存信息,消息体为:{input.Serialize()}");
}
var pageResult = await treeModelService.OpenRequestDeviceDataInfoPageAsync(new DeviceTreeModelDataInfoInput()
{
DeviceAddress = messageBody.DeviceAddress,
DeviceType = messageBody.DeviceType,
IoTDataType = messageBody.IoTDataType,
IsNeedPaging = false,
StartCreationTime = messageBody.BeginTime,
EndCreationTime = messageBody.EndTime,
SubDeviceAddress = messageBody.SubDeviceAddress,
});
//todo 根据业务系统时间间隔要求进行过滤
if (pageResult.Items != null && pageResult.Items.Count > 0)
{
queryResult.AddRange(pageResult.Items);
}
return HttpDataResultExtensions.Success(queryResult, "查询成功");
}
catch (Exception ex)
{
return HttpDataResultExtensions.Failed<List<IoTDBDynamicObject>>(null, $"查询设备数据失败,发送异常:{ex.Message}", -106);
}
}
/// <summary>
/// 业务系统批量新增设备数据Msg 字段为 BatchCreateDeviceInfoInput 实体
/// 业务系统批量新增设备数据Msg 字段为 BatchCreateDeviceBusinessSystemInput 实体
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
@ -224,25 +309,20 @@ namespace JiShe.IoT.BusinessSystemAggregation
{
try
{
var handleResult = HandleOpenApiRequest<BatchCreateDeviceInfoInput>(input, serverOptions);
var handleResult = HandleOpenApiRequest<BatchCreateDeviceBusinessSystemInput>(input, serverOptions);
if (handleResult.Success == false)
{
return HttpDataResultExtensions.Failed(handleResult.Message, handleResult.LocationCode);
}
var messageBody = handleResult.Data;
if (messageBody.DeviceAddresses == null || messageBody.DeviceAddresses.Count <= 0)
if (messageBody.DeviceInfos == null || messageBody.DeviceInfos.Count <= 0)
{
return HttpDataResultExtensions.Failed("设备地址不能为空", -101);
}
var createResult = await deviceAggregationService.BatchCreateDeviceBusinessSystemAsync(new BatchCreateDeviceAggregationInput()
{
DeviceSourceTypeEnum = messageBody.DeviceSourceType,
IoTPlatform = messageBody.IoTPlatform,
IoTPlatformProductId = messageBody.IoTPlatformProductId,
AddressList = messageBody.DeviceAddresses
});
// 批量新增设备
var createResult = await deviceAggregationService.BatchCreateDeviceBusinessSystemAsync(messageBody);
if (createResult == false)
{

View File

@ -1,4 +1,5 @@
using JiShe.IoT.DeviceAggregation.Dto;
using JiShe.IoT.BusinessSystemAggregation.Dto;
using JiShe.IoT.DeviceAggregation.Dto;
using JiShe.ServicePro;
using JiShe.ServicePro.ApacheIoTDB.Provider.Options;
using JiShe.ServicePro.Core;
@ -58,7 +59,7 @@ namespace JiShe.IoT.DeviceAggregation
{
try
{
input.DeviceSourceTypeEnum = ServicePro.Enums.DeviceSourceTypeEnum.AdminSystem;
input.DeviceSourceType = ServicePro.Enums.DeviceSourceTypeEnum.AdminSystem;
if (input.IoTPlatform == ServicePro.Enums.IoTPlatformTypeEnum.CTWing)
{
return await CTWingDeviceCreateAsync(input);
@ -99,7 +100,7 @@ namespace JiShe.IoT.DeviceAggregation
throw new UserFriendlyException($"批量创建设备失败设备信息不能超过100个。");
}
input.DeviceSourceTypeEnum = ServicePro.Enums.DeviceSourceTypeEnum.AdminSystem;
input.DeviceSourceType = ServicePro.Enums.DeviceSourceTypeEnum.AdminSystem;
if (input.IoTPlatform == ServicePro.Enums.IoTPlatformTypeEnum.CTWing)
{
@ -128,7 +129,7 @@ namespace JiShe.IoT.DeviceAggregation
{
try
{
input.DeviceSourceTypeEnum = DeviceSourceTypeEnum.Workshop;
input.DeviceSourceType = DeviceSourceTypeEnum.Workshop;
if (input.IoTPlatform == ServicePro.Enums.IoTPlatformTypeEnum.CTWing)
{
@ -168,7 +169,7 @@ namespace JiShe.IoT.DeviceAggregation
throw new UserFriendlyException($"批量创建设备失败设备信息不能超过100个。");
}
input.DeviceSourceTypeEnum = DeviceSourceTypeEnum.Workshop;
input.DeviceSourceType = DeviceSourceTypeEnum.Workshop;
if (input.IoTPlatform == ServicePro.Enums.IoTPlatformTypeEnum.CTWing)
{
@ -651,32 +652,38 @@ namespace JiShe.IoT.DeviceAggregation
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<bool> BatchCreateDeviceBusinessSystemAsync(BatchCreateDeviceAggregationInput input)
public async Task<bool> BatchCreateDeviceBusinessSystemAsync(BatchCreateDeviceBusinessSystemInput input)
{
try
{
if (input.AddressList == null || input.AddressList.Count <= 0)
if (input.DeviceInfos == null || input.DeviceInfos.Count <= 0)
{
throw new UserFriendlyException($"业务系统批量创建设备信息,设备信息不能为空。");
}
if (input.AddressList.Count > 100)
if (input.DeviceInfos.Count > 100)
{
throw new UserFriendlyException($"业务系统批量创建设备信息设备信息不能超过100个。");
}
if (input.DeviceSourceTypeEnum != DeviceSourceTypeEnum.Prepay && input.DeviceSourceTypeEnum != DeviceSourceTypeEnum.Energy)
if (input.DeviceSourceType != DeviceSourceTypeEnum.Prepay && input.DeviceSourceType != DeviceSourceTypeEnum.Energy)
{
throw new UserFriendlyException($"业务系统批量创建设备信息,设备来源异常。");
}
if (input.IoTPlatform == ServicePro.Enums.IoTPlatformTypeEnum.CTWing)
{
return await CTWingDeviceBatchCreateAsync(input);
var batchCreateDeviceInput = input.Adapt<BatchCreateDeviceAggregationInput>();
batchCreateDeviceInput.AddressList = input.DeviceInfos.Select(f => f.DeviceAddress.Trim()).ToList();
return await CTWingDeviceBatchCreateAsync(batchCreateDeviceInput);
}
else if (input.IoTPlatform == ServicePro.Enums.IoTPlatformTypeEnum.OneNET)
{
return await OneNETDeviceBatchCreateAsync(input);
var batchCreateDeviceInput = input.Adapt<BatchCreateDeviceAggregationInput>();
batchCreateDeviceInput.AddressList = input.DeviceInfos.Select(f => f.DeviceAddress.Trim()).ToList();
return await OneNETDeviceBatchCreateAsync(batchCreateDeviceInput);
}
throw new UserFriendlyException($"不支持的物联网平台");
@ -708,7 +715,7 @@ namespace JiShe.IoT.DeviceAggregation
throw new UserFriendlyException($"OneNET创建设备失败未找到对应的产品配置信息。");
}
if (input.DeviceSourceTypeEnum == DeviceSourceTypeEnum.Workshop && !productInfo.IsEnabled) //车间生产推送,必须是已经启用的产品才可以
if (input.DeviceSourceType == DeviceSourceTypeEnum.Workshop && !productInfo.IsEnabled) //车间生产推送,必须是已经启用的产品才可以
{
throw new UserFriendlyException($"车间生产推送OneNET创建设备失败产品未启用。");
}
@ -719,6 +726,10 @@ namespace JiShe.IoT.DeviceAggregation
createDeviceInput.PlatformPassword = productInfo.ProductAccesskey;
createDeviceInput.IoTPlatformProductName = productInfo.ProductName;
createDeviceInput.AccountPhoneNumber = productInfo.AccountPhoneNumber;
if (input.DeviceType.HasValue)
{
createDeviceInput.DeviceType = input.DeviceType.Value;
}
var insertResult = await deviceAppService.CreateAsync(createDeviceInput);
if (insertResult == null)
@ -742,6 +753,7 @@ namespace JiShe.IoT.DeviceAggregation
return false;
}
//更新OneNET平台推送结果
await DeviceUpdateHandler(insertResult, pushResult, pushResult.Data.SecurityKey);
return true;
@ -764,7 +776,7 @@ namespace JiShe.IoT.DeviceAggregation
{
var productInfo = await FreeSqlDbContext.Instance.Select<OneNETProductInfos>()
.Where(e => e.IoTPlatformProductId == input.IoTPlatformProductId)//此处不需要过滤产品状态,方便测试产品配置信息是否准确,避免跟车间生产搞混
.WhereIf(input.DeviceSourceTypeEnum == DeviceSourceTypeEnum.Workshop, e => e.IsEnabled == true)
.WhereIf(input.DeviceSourceType == DeviceSourceTypeEnum.Workshop, e => e.IsEnabled == true)
.FirstAsync();
if (productInfo == null)
@ -778,8 +790,27 @@ namespace JiShe.IoT.DeviceAggregation
AddressList = input.AddressList,
DeviceInputs = new List<CreateDeviceInput>()
};
//检查网关或者直连设备信息是否已经存在
var checkDevicesInfos = await deviceAppService.FindByDeviceAddressAsync(new FindByDeviceAddressInput()
{
AddressList = input.AddressList,
IoTPlatform = input.IoTPlatform,
IoTPlatformProductId = input.IoTPlatformProductId,
});
foreach (var item in input.AddressList)
{
if (checkDevicesInfos != null)
{
if (checkDevicesInfos.Any(e => e.DeviceAddress == item))
{
logger.LogError($"{nameof(OneNETDeviceBatchCreateAsync)} 平台{input.IoTPlatform} 产品 {input.IoTPlatformProductId} 下设备信息已存在:{item}");
continue;
}
}
CreateDeviceInput createDeviceInput = input.Adapt<CreateDeviceInput>();
createDeviceInput.DeviceName = item;
createDeviceInput.DeviceAddress = item;
@ -788,7 +819,18 @@ namespace JiShe.IoT.DeviceAggregation
createDeviceInput.PlatformPassword = productInfo.ProductAccesskey;
createDeviceInput.IoTPlatformProductName = productInfo.ProductName;
createDeviceInput.AccountPhoneNumber = productInfo.AccountPhoneNumber;
createDeviceInput.DeviceSourceTypeEnum = input.DeviceSourceTypeEnum.Value;
createDeviceInput.DeviceSourceType = input.DeviceSourceType.Value;
createDeviceInput.DeviceType = input.DeviceType.Value;
if (input.DeviceSourceType.HasValue)
{
createDeviceInput.DeviceSourceType = input.DeviceSourceType.Value;
}
if (input.DeviceType.HasValue)
{
createDeviceInput.DeviceType = input.DeviceType.Value;
}
batchCreateDeviceInput.DeviceInputs.Add(createDeviceInput);
}
@ -819,6 +861,7 @@ namespace JiShe.IoT.DeviceAggregation
return false;
}
//更新OneNET平台推送结果
foreach (var item in insertResult)
{
var successEntity = pushResult.Data.Successlist.Where(d => d.DeviceName == item.IoTPlatformDeviceOpenInfo).FirstOrDefault();

View File

@ -1,6 +1,6 @@
{
"ConnectionStrings": {
"Default": "Data Source=192.168.111.174;Port=13306;Database=JiSheIoTProDB;uid=root;pwd=JiShe!aqG#5kGgh&0;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true;TreatTinyAsBoolean=false;SslMode=None;Pooling=true;"
"Default": "Data Source=192.168.111.174;Port=13306;Database=JiSheIoTProDB386;uid=root;pwd=JiShe!aqG#5kGgh&0;charset=utf8mb4;Allow User Variables=true;AllowLoadLocalInfile=true;TreatTinyAsBoolean=false;SslMode=None;Pooling=true;"
},
"IoTDBOptions": {
"UserName": "root",

View File

@ -69,6 +69,7 @@ namespace JiShe.IoT.EntityFrameworkCore
// 设备管理
public DbSet<DeviceManagementInfo> DeviceManagementInfo { get; set; }
public DbSet<SubDeviceManagementInfo> SubDeviceManagementInfo { get; set; }
public DbSet<DeviceThingModelManagement> DeviceThingModelManagement { get; set; }
public DbSet<DeviceThingModelPropertyInfo> DeviceThingModelDetailInfo { get; set; }
public DbSet<DeviceThingModelCommandInfo> DeviceThingModelCommandInfo { get; set; }