diff --git a/Waste.Application/Device/DeviceAppService.cs b/Waste.Application/Device/DeviceAppService.cs index 76a6e12..20ae37a 100644 --- a/Waste.Application/Device/DeviceAppService.cs +++ b/Waste.Application/Device/DeviceAppService.cs @@ -14,7 +14,7 @@ namespace Waste.Application /// /// 设备接口 /// - public class DeviceAppService:IDynamicApiController + public class DeviceAppService : IDynamicApiController { private readonly IDeviceService _deviceService; public DeviceAppService(IDeviceService deviceService) @@ -61,5 +61,14 @@ namespace Waste.Application { return await _deviceService.SetStatusAsync(id, status); } + /// + /// 配置设备推送信息 + /// + /// + /// + public async Task SetConfigAsync(DeviceConfigC2SDto input) + { + return await _deviceService.SetConfigAsync(input); + } } } diff --git a/Waste.Application/Device/DeviceService.cs b/Waste.Application/Device/DeviceService.cs index 8d45f97..f9844c2 100644 --- a/Waste.Application/Device/DeviceService.cs +++ b/Waste.Application/Device/DeviceService.cs @@ -1,6 +1,7 @@ using Furion.DependencyInjection; using Furion.DistributedIDGenerator; using Furion.DynamicApiController; +using Mapster; using Microsoft.AspNetCore.Mvc; using Nirvana.Common; using Nirvana.Common.ApiBase; @@ -148,6 +149,20 @@ namespace Waste.Application.Device FacEcode = device.FacEcode }; } + /// + /// 获取设备配置详情 + /// + /// 设备ID + /// + public async Task GetConfigAsync(Guid id) + { + var data= await dbClient.Queryable().FirstAsync(x => x.DeviceId == id); + if(data == null) + { + data= new W_DeviceConfig(); + } + return data; + } /// /// 设备列表 @@ -185,7 +200,7 @@ namespace Waste.Application.Device temquery = temquery.Where(x => SqlFunc.Subqueryable().Where(sql).Any()); } string sorts = string.Format("{0} {1}", param.sort, param.order); - var query = await temquery.OrderBy(x=>x.LastHeartTime,OrderByType.Desc) + var query = await temquery.OrderBy(x => x.LastHeartTime, OrderByType.Desc) .Select(x => new DeviceList { Id = x.Id, @@ -261,6 +276,36 @@ namespace Waste.Application.Device limit = param.limit }; } + /// + /// 配置设备推送信息 + /// + /// + /// + public async Task SetConfigAsync(DeviceConfigC2SDto input) + { + if (!await dbClient.Queryable().AnyAsync(x => x.Id == input.Id)) + { + return new ResultInfo(ResultState.FAIL, "设备未找到"); + } + input.Body = input.Body.ToStr(); + input.Url = input.Url.ToLower(); + if (!input.Id.IsEmpty() && await dbClient.Queryable().AnyAsync(x => x.DeviceId == input.Id)) + { + await dbClient.Updateable().SetColumns(x => new W_DeviceConfig + { + Url = input.Url, + Body = input.Body + }).Where(x => x.DeviceId == input.Id).ExecuteCommandAsync(); + } + else + { + var insertdata = input.Adapt(); + insertdata.DeviceId = input.Id; + insertdata.CreateTime = DateTime.Now; + await dbClient.Insertable(insertdata).ExecuteCommandAsync(); + } + return new ResultInfo(ResultState.SUCCESS, "配置成功"); + } /// /// 设备状态修改 @@ -268,13 +313,14 @@ namespace Waste.Application.Device /// 设备ID /// 设备状态,0-停用,1-正常,2-激活 /// - public async Task SetStatusAsync(Guid id,int status) + public async Task SetStatusAsync(Guid id, int status) { - if(!await dbClient.Queryable().AnyAsync(x=>x.Id == id)) + if (!await dbClient.Queryable().AnyAsync(x => x.Id == id)) { return new ResultInfo(ResultState.FAIL, "设备未找到"); } - await dbClient.Updateable().SetColumns(x => new W_Device { + await dbClient.Updateable().SetColumns(x => new W_Device + { Status = status }).Where(x => x.Id == id).ExecuteCommandAsync(); return new ResultInfo(ResultState.SUCCESS, "设备状态已更新"); diff --git a/Waste.Application/Device/Dtos/DeviceDto.cs b/Waste.Application/Device/Dtos/DeviceDto.cs index 1692cd6..f41b023 100644 --- a/Waste.Application/Device/Dtos/DeviceDto.cs +++ b/Waste.Application/Device/Dtos/DeviceDto.cs @@ -1,7 +1,10 @@ -using System; +using Furion.DataValidation; +using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; using Waste.Domain; @@ -9,7 +12,7 @@ namespace Waste.Application { public class DeviceList : W_Device - { + { /// /// 商户名称 /// @@ -85,7 +88,7 @@ namespace Waste.Application /// /// 设备信息提交 /// - public class DeviceSubmit:W_Device + public class DeviceSubmit : W_Device { /// /// 设备对应的SecretHash @@ -103,7 +106,7 @@ namespace Waste.Application /// /// 设备详情 /// - public class DeviceDetailS2Dto: W_Device + public class DeviceDetailS2Dto : W_Device { /// /// 最近心跳时间 @@ -126,4 +129,44 @@ namespace Waste.Application /// public string version { get; set; } } + /// + /// 设备配置 + /// + public class DeviceConfigC2SDto : IValidatableObject + { + /// + /// 设备ID + /// + public Guid Id { get; set; } + + /// + /// 推送地址,支持http/https + /// + [Required(ErrorMessage = "推送地址不可为空")] + [MaxLength(200, ErrorMessage = "推送地址最多200个字")] + public string Url { get; set; } + + /// + /// 额外推送信息,推送时固定以body参数传递 + /// + [DataValidation(AllowNullValue = true)] + [MaxLength(100, ErrorMessage = "额外推送信息最多100个字")] + public string Body { get; set; } + /// + /// 验证 + /// + /// + /// + public IEnumerable Validate(ValidationContext validationContext) + { + if (!string.IsNullOrEmpty(Body) && (Body.Contains("&") || Body.Contains("?"))) + { + yield return new ValidationResult("额外推送信息不可包含特殊字符&、?", new[] { nameof(Body) }); + } + if(!Regex.IsMatch(Url.ToLower(), @"[http][a-zA-z]+://[^\s]*")) + { + yield return new ValidationResult("推送地址格式不正确", new[] { nameof(Url) }); + } + } + } } diff --git a/Waste.Application/Device/IDeviceService.cs b/Waste.Application/Device/IDeviceService.cs index 9f69ceb..7a30171 100644 --- a/Waste.Application/Device/IDeviceService.cs +++ b/Waste.Application/Device/IDeviceService.cs @@ -51,5 +51,17 @@ namespace Waste.Application /// 设备状态,0-停用,1-正常,2-激活 /// Task SetStatusAsync(Guid id,int status); + /// + /// 配置设备推送信息 + /// + /// + /// + Task SetConfigAsync(DeviceConfigC2SDto input); + /// + /// 获取设备配置详情 + /// + /// 设备ID + /// + Task GetConfigAsync(Guid id); } } diff --git a/Waste.Application/SubscribeInfo/Dtos/SubscribeDto.cs b/Waste.Application/SubscribeInfo/Dtos/SubscribeDto.cs index c7f5878..46cf3f3 100644 --- a/Waste.Application/SubscribeInfo/Dtos/SubscribeDto.cs +++ b/Waste.Application/SubscribeInfo/Dtos/SubscribeDto.cs @@ -82,4 +82,34 @@ namespace Waste.Application.SubscribeInfo /// public string ver { get; set; } } + /// + /// 发送第三方消息 + /// + public class SendThirdMessageSubscriDto + { + /// + /// 垃圾类别 + /// + public string WasteType { get; set; } + /// + /// 重量,单位KG + /// + public string Weight { get; set; } + /// + /// 垃圾桶编号 + /// + public string TrashCode { get; set; } + /// + /// 上报时间 + /// + public long Time { get; set; } + /// + /// 推送地址 + /// + public string Url { get; set; } + /// + /// 额外信息 + /// + public string Body { get; set; } + } } diff --git a/Waste.Application/SubscribeInfo/ISubscribeService.cs b/Waste.Application/SubscribeInfo/ISubscribeService.cs index e7b7648..8f82dcf 100644 --- a/Waste.Application/SubscribeInfo/ISubscribeService.cs +++ b/Waste.Application/SubscribeInfo/ISubscribeService.cs @@ -55,5 +55,11 @@ namespace Waste.Application.SubscribeInfo /// /// void Test(nMyPackage myPackage); + /// + /// 第三方推送设备消息 + /// + /// + /// + Task SeedThirdMessageAsync(SendThirdMessageSubscriDto data); } } diff --git a/Waste.Application/SubscribeInfo/SubscribeService.cs b/Waste.Application/SubscribeInfo/SubscribeService.cs index 77c96fa..0859c34 100644 --- a/Waste.Application/SubscribeInfo/SubscribeService.cs +++ b/Waste.Application/SubscribeInfo/SubscribeService.cs @@ -2,7 +2,9 @@ using Furion.DependencyInjection; using Furion.DistributedIDGenerator; using Furion.Logging.Extensions; +using Furion.RemoteRequest.Extensions; using Newtonsoft.Json; +using Nirvana.Common; using SqlSugar; using System; using System.Collections.Generic; @@ -250,7 +252,37 @@ namespace Waste.Application.SubscribeInfo public void Test(nMyPackage myPackage) { var msg = JsonConvert.SerializeObject(myPackage); - _loggerService.AddLogger(msg,1); + _loggerService.AddLogger(msg, 1); + } + /// + /// 第三方推送设备消息 + /// + /// + /// + [CapSubscribe("third.service.sendmessage")] + public async Task SeedThirdMessageAsync(SendThirdMessageSubscriDto data) + { + string errmsg = string.Empty; + var response = await data.Url + .SetBody(data, "application/json", Encoding.UTF8) + .OnException((res, errors) => + { + errmsg = errors; + }).PostAsync(); + if (errmsg != string.Empty) + { + _loggerService.AddLogger($"第三方设备消息发送失败,内容:{data.ToJson()},返回:{errmsg}", 1); + } + var returnstr = await response.Content.ReadAsStringAsync(); + if (!response.IsSuccessStatusCode) + { + _loggerService.AddLogger($"第三方设备消息发送失败,内容:{data.ToJson()},返回:{returnstr}", 1); + } + if(returnstr.ToLower() != "success") + { + _loggerService.AddLogger($"第三方设备消息发送失败,内容:{data.ToJson()},返回:{returnstr}", 1); + } + _loggerService.AddLogger($"第三方设备消息发送成功,内容:{data.ToJson()},返回:{returnstr}", 3); } } } diff --git a/Waste.Application/ThirdApiInfo/Dtos/ThirdApiInfoDto.cs b/Waste.Application/ThirdApiInfo/Dtos/ThirdApiInfoDto.cs index f91acf0..d6ed379 100644 --- a/Waste.Application/ThirdApiInfo/Dtos/ThirdApiInfoDto.cs +++ b/Waste.Application/ThirdApiInfo/Dtos/ThirdApiInfoDto.cs @@ -253,6 +253,31 @@ namespace Waste.Application.ThirdApiInfo /// 结果集 /// public byte[] databyte { get; set; } - + } + /// + /// 消息发送 + /// + public class SendMessageS2SDto + { + /// + /// 设备ID + /// + public Guid DeviceId { get; set; } + /// + /// 垃圾类别 + /// + public string WasteType { get; set; } + /// + /// 重量,单位KG + /// + public string Weight { get; set; } + /// + /// 垃圾桶编号 + /// + public string TrashCode { get; set; } + /// + /// 上报时间 + /// + public DateTime Time { get; set; } } } diff --git a/Waste.Application/ThirdApiInfo/Message/IMessageService.cs b/Waste.Application/ThirdApiInfo/Message/IMessageService.cs new file mode 100644 index 0000000..ccbb5f0 --- /dev/null +++ b/Waste.Application/ThirdApiInfo/Message/IMessageService.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Waste.Application.ThirdApiInfo.Message +{ + /// + /// 设备消息推送给第三方处理 + /// + public interface IMessageService + { + /// + /// 消息发送 + /// + /// + /// + Task SeedMessageAsync(SendMessageS2SDto input); + } +} diff --git a/Waste.Application/ThirdApiInfo/Message/MessageService.cs b/Waste.Application/ThirdApiInfo/Message/MessageService.cs new file mode 100644 index 0000000..f1cf68c --- /dev/null +++ b/Waste.Application/ThirdApiInfo/Message/MessageService.cs @@ -0,0 +1,58 @@ +using DotNetCore.CAP; +using Furion.DependencyInjection; +using Nirvana.Common; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Waste.Application.SubscribeInfo; +using Waste.Domain; + +namespace Waste.Application.ThirdApiInfo.Message +{ + /// + /// 设备消息推送给第三方处理 + /// + public class MessageService : IMessageService, ITransient + { + private readonly ISqlSugarRepository repository; + private readonly ICapPublisher _capBus; + private readonly SqlSugarClient dbClient; + public MessageService(ISqlSugarRepository sqlSugarRepository, ICapPublisher capBus) + { + repository = sqlSugarRepository; + dbClient = repository.Context; + _capBus = capBus; + } + /// + /// 消息发送 + /// + /// + /// + public async Task SeedMessageAsync(SendMessageS2SDto input) + { + if (!await dbClient.Queryable().AnyAsync(x => x.DeviceId == input.DeviceId)) + { + return; + } + var config = await dbClient.Queryable().Where(x => x.DeviceId == input.DeviceId).Select(x => new W_DeviceConfig + { + Body = x.Body, + Url = x.Url + }).FirstAsync(); + + var time = input.Time.GetTimeStamp(); + await _capBus.PublishAsync("third.service.sendmessage", new SendThirdMessageSubscriDto + { + WasteType = input.WasteType, + Body = config.Body, + Time = time, + TrashCode = input.TrashCode, + Url = config.Url, + Weight = input.Weight + }); + } + } +} diff --git a/Waste.Application/Waste.Application.xml b/Waste.Application/Waste.Application.xml index eae2504..ee0fa60 100644 --- a/Waste.Application/Waste.Application.xml +++ b/Waste.Application/Waste.Application.xml @@ -582,6 +582,13 @@ 设备状态,0-停用,1-正常,2-激活 + + + 配置设备推送信息 + + + + 设备管理 @@ -608,6 +615,13 @@ + + + 获取设备配置详情 + + 设备ID + + 设备列表 @@ -615,6 +629,13 @@ + + + 配置设备推送信息 + + + + 设备状态修改 @@ -765,6 +786,33 @@ 使用的版本号 + + + 设备配置 + + + + + 设备ID + + + + + 推送地址,支持http/https + + + + + 额外推送信息,推送时固定以body参数传递 + + + + + 验证 + + + + 设备管理 @@ -813,6 +861,20 @@ 设备状态,0-停用,1-正常,2-激活 + + + 配置设备推送信息 + + + + + + + 获取设备配置详情 + + 设备ID + + 定时任务 @@ -2017,6 +2079,41 @@ 版本号 + + + 发送第三方消息 + + + + + 垃圾类别 + + + + + 重量,单位KG + + + + + 垃圾桶编号 + + + + + 上报时间 + + + + + 推送地址 + + + + + 额外信息 + + CAP订阅相关接口 @@ -2071,6 +2168,13 @@ + + + 第三方推送设备消息 + + + + CAP订阅相关接口 @@ -2125,6 +2229,13 @@ + + + 第三方推送设备消息 + + + + 地址列表 @@ -2676,6 +2787,36 @@ 结果集 + + + 消息发送 + + + + + 设备ID + + + + + 垃圾类别 + + + + + 重量,单位KG + + + + + 垃圾桶编号 + + + + + 上报时间 + + 设备对接接口 @@ -2723,6 +2864,30 @@ + + + 设备消息推送给第三方处理 + + + + + 消息发送 + + + + + + + 设备消息推送给第三方处理 + + + + + 消息发送 + + + + 开放数据 diff --git a/Waste.CreateDB/Program.cs b/Waste.CreateDB/Program.cs index 5206480..5efe513 100644 --- a/Waste.CreateDB/Program.cs +++ b/Waste.CreateDB/Program.cs @@ -10,8 +10,7 @@ namespace Waste.CreateDB Console.WriteLine("开始创建表!"); var context = new CreateTable(); context.Create(false, 50, - typeof(W_DeviceData), - typeof(W_DeviceResult) + typeof(W_DeviceConfig) ); } } diff --git a/Waste.Domain/DataModel/W_DeviceConfig.cs b/Waste.Domain/DataModel/W_DeviceConfig.cs new file mode 100644 index 0000000..edcc4c0 --- /dev/null +++ b/Waste.Domain/DataModel/W_DeviceConfig.cs @@ -0,0 +1,40 @@ +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Waste.Domain +{ + /// + /// 设备第三方配置信息 + /// + [SugarTable("W_DeviceConfig", TableDescription = "设备第三方配置信息", IsDisabledUpdateAll = false, IsDisabledDelete = true)] + + public class W_DeviceConfig + { + /// + /// 设备ID + /// + [SugarColumn(IsPrimaryKey = true)] + public Guid DeviceId { get; set; } + + /// + /// 推送地址,支持http/https + /// + [SugarColumn(ColumnDataType = "varchar(200)", ColumnDescription = "推送地址,支持http/https")] + public string Url { get; set; } + + /// + /// 额外推送信息,推送时固定以body参数传递 + /// + [SugarColumn(ColumnDataType = "varchar(100)", ColumnDescription = "额外推送信息,推送时固定以body参数传递")] + public string Body { get; set; } + /// + /// 创建时间 + /// + [SugarColumn(ColumnDescription = "创建时间")] + public DateTime CreateTime { get; set; } + } +} diff --git a/Waste.Web.Core/Startup.cs b/Waste.Web.Core/Startup.cs index e67d65b..7c3ea19 100644 --- a/Waste.Web.Core/Startup.cs +++ b/Waste.Web.Core/Startup.cs @@ -5,16 +5,13 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Nirvana.Common; using Serilog; using System; -using System.Collections.Generic; using System.IO; using System.Text; -using System.Threading.Tasks; using Waste.Core; namespace Waste.Web.Core @@ -31,7 +28,7 @@ namespace Waste.Web.Core //}); services.AddDataProtection().PersistKeysToFileSystem(new DirectoryInfo(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "DataProtection")); services.AddCorsAccessor(); - // services.AddRemoteRequest(); + services.AddRemoteRequest(); services.AddHttpClient(); //添加CAP diff --git a/Waste.Web.Entry/Pages/Device/Config.cshtml b/Waste.Web.Entry/Pages/Device/Config.cshtml new file mode 100644 index 0000000..70a3ec5 --- /dev/null +++ b/Waste.Web.Entry/Pages/Device/Config.cshtml @@ -0,0 +1,44 @@ +@page +@model Waste.Web.Entry.Pages.Device.ConfigModel +@{ + ViewData["Title"] = "设备配置"; +} + + + + + 推送地址 + + + + + + 额外参数 + + + 消息推送时会以body参数名原样传递此参数 + + + + 提交 + + + + +@section Scripts{ + + +} \ No newline at end of file diff --git a/Waste.Web.Entry/Pages/Device/Config.cshtml.cs b/Waste.Web.Entry/Pages/Device/Config.cshtml.cs new file mode 100644 index 0000000..5724021 --- /dev/null +++ b/Waste.Web.Entry/Pages/Device/Config.cshtml.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Nirvana.Common; +using System; +using System.Threading.Tasks; +using Waste.Application; +using Waste.Domain; + +namespace Waste.Web.Entry.Pages.Device +{ + public class ConfigModel : PageModel + { + private readonly IDeviceService _deviceService; + public ConfigModel(IDeviceService deviceService) + { + _deviceService = deviceService; + } + public W_DeviceConfig data = new W_DeviceConfig(); + public async Task OnGet(Guid id) + { + data = await _deviceService.GetConfigAsync(id); + data.DeviceId = id; + } + } +} diff --git a/Waste.Web.Entry/Pages/Device/Index.cshtml b/Waste.Web.Entry/Pages/Device/Index.cshtml index 5f732c3..9ba3953 100644 --- a/Waste.Web.Entry/Pages/Device/Index.cshtml +++ b/Waste.Web.Entry/Pages/Device/Index.cshtml @@ -47,10 +47,11 @@ 编辑 @*详情 *@ {{#if (d.status == 1){ }} - 停用 + 停用 {{# } else if (d.status == 0){ }} - 启用 + 启用 {{#} }} + 配置