diff --git a/YD_AllHeartRates.Api/FakesAssemblies/YD_AllHeartRates.Commons.1.0.0.0.Fakes.dll b/YD_AllHeartRates.Api/FakesAssemblies/YD_AllHeartRates.Commons.1.0.0.0.Fakes.dll index 77edef3..3610168 100644 Binary files a/YD_AllHeartRates.Api/FakesAssemblies/YD_AllHeartRates.Commons.1.0.0.0.Fakes.dll and b/YD_AllHeartRates.Api/FakesAssemblies/YD_AllHeartRates.Commons.1.0.0.0.Fakes.dll differ diff --git a/YD_AllHeartRates.Api/Mqtt/MqttBackgroundService.cs b/YD_AllHeartRates.Api/Mqtt/MqttBackgroundService.cs index d89d4f8..803938c 100644 --- a/YD_AllHeartRates.Api/Mqtt/MqttBackgroundService.cs +++ b/YD_AllHeartRates.Api/Mqtt/MqttBackgroundService.cs @@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore; using MQTTnet; using MQTTnet.Client; using MQTTnet.Protocol; +using System.Reflection.PortableExecutable; using System.Runtime.CompilerServices; using System.Text; using System.Text.Json; @@ -11,8 +12,10 @@ using YD_AllHeartRates.Api.Context; using YD_AllHeartRates.Api.Entitys; using YD_AllHeartRates.Api.SmartSportsEntitys; using YD_AllHeartRates.Api.Utilities; +using YD_AllHeartRates.Api.WebSocket; using YD_AllHeartRates.Commons.Dto.Mqtt; using YD_AllHeartRates.Commons.Dto.Student; +using YD_AllHeartRates.Commons.MemoryCaches; namespace YD_AllHeartRates.Api.Mqtt { @@ -23,34 +26,38 @@ namespace YD_AllHeartRates.Api.Mqtt { private readonly IMqttClient _client; private readonly ILogger _log; - private UserContext _userContext; - private SmartSportsContext _smartSportsContext; + private readonly UserContext _userContext; + private readonly SmartSportsContext _smartSportsContext; + private readonly ICaching _caching; - private List _studentList = new List(); + private readonly List _studentList = new(); + private readonly List _pendingHeartRates = new(); + private readonly Dictionary _jumpDailyMap = new(); - static readonly JsonSerializerOptions _jsonOpt = new() - { - PropertyNameCaseInsensitive = true - }; + private DateTime _lastHeartRateSaveTime = DateTime.UtcNow; + private DateTime _lastJumpRopeSaveTime = DateTime.UtcNow; - /// - /// 构造函数 - /// - /// - /// - public MqttBackgroundService(IMqttClient client, ILogger log, UserContext userContext, SmartSportsContext smartSportsContext) + private readonly Channel _queue = Channel.CreateUnbounded(); + + static readonly JsonSerializerOptions _jsonOpt = new() { PropertyNameCaseInsensitive = true }; + + public MqttBackgroundService( + IMqttClient client, + ILogger log, + UserContext userContext, + SmartSportsContext smartSportsContext, + ICaching caching + ) { _client = client; _log = log; _userContext = userContext; _smartSportsContext = smartSportsContext; + _caching = caching; _studentList = (from d in _smartSportsContext.Device - join s in _smartSportsContext.Student - on d.StudentNo equals s.StudentNo - join c in _smartSportsContext.Class - on s.ClassId equals c.Id - //where d.SchoolCode == "202501060001" && s.StudentStatus == 1 + join s in _smartSportsContext.Student on d.StudentNo equals s.StudentNo + join c in _smartSportsContext.Class on s.ClassId equals c.Id where s.StudentStatus == 1 select new StudentDto { @@ -69,16 +76,8 @@ namespace YD_AllHeartRates.Api.Mqtt }).ToList(); } - private readonly Channel _queue = Channel.CreateUnbounded(); - - /// - /// 消费队列 - /// - /// - /// protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - // 连接 MQTT await _client.ConnectAsync(new MqttClientOptionsBuilder() .WithTcpServer(AppSettings.Mqtt.Host, AppSettings.Mqtt.Port) .Build(), stoppingToken); @@ -88,33 +87,25 @@ namespace YD_AllHeartRates.Api.Mqtt .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce) .Build(), stoppingToken); - _log.LogInformation("MQTT subscribed: {Topic}", AppSettings.Mqtt.Topic); - - // 消息回调 → 入队 _client.ApplicationMessageReceivedAsync += e => { var msg = new MqttMessage { Topic = e.ApplicationMessage.Topic, - Payload = e.ApplicationMessage.Payload == null - ? string.Empty - : Encoding.UTF8.GetString(e.ApplicationMessage.Payload), + Payload = e.ApplicationMessage.Payload == null ? string.Empty : Encoding.UTF8.GetString(e.ApplicationMessage.Payload), ReceivedAt = DateTime.UtcNow }; return _queue.Writer.WriteAsync(msg, stoppingToken).AsTask(); }; - // ✅ 启动消费循环(写入数据库) - //await foreach (var msg in _queue.Reader.ReadAllAsync(stoppingToken)) await foreach (var batch in ReadBatchesAsync(stoppingToken)) { var heartRateEntities = new List(); var jumpRopeEntities = new List(); - foreach (var msg in batch) // batch 内部还是你业务消息 + foreach (var msg in batch) { - if (string.IsNullOrWhiteSpace(msg.Payload)) - continue; + if (string.IsNullOrWhiteSpace(msg.Payload)) continue; List? list; try @@ -127,59 +118,37 @@ namespace YD_AllHeartRates.Api.Mqtt continue; } - if (list is null || list.Count == 0) - continue; + if (list is null || list.Count == 0) continue; var heartRateList = list.Where(x => x.BleName.Contains("GTY0")).ToList(); var jumpRopeList = list.Where(x => x.BleName.Contains("RS207")).ToList(); foreach (var ble in heartRateList) { - if (string.IsNullOrWhiteSpace(ble.RawData)) - continue; + if (string.IsNullOrWhiteSpace(ble.RawData)) continue; var student = _studentList.FirstOrDefault(x => x.HeartRateId == ble.BleName); - if (student == null) - continue; + if (student == null || student.GradeId == 0 || student.ClassId == 0) continue; - if (student.GradeId == 0 || student.ClassId == 0) - continue; + var data = ParseHexData(ble.RawData); + if (data == null) continue; - int len = ble.RawData.Length / 2; - var data = new byte[len]; - try - { - for (int i = 0; i < len; i++) - data[i] = Convert.ToByte(ble.RawData.Substring(i * 2, 2), 16); - } - catch (FormatException ex) - { - _log.LogWarning(ex, "rawData 非十六进制: {Raw}", ble.RawData); - continue; - } - - // 2) 定位 0xCD 起始字段 int cd = Array.IndexOf(data, (byte)0xCD); - if (cd < 0 || data.Length < cd + 9) - { - _log.LogWarning("找不到 0xCD 或字段不足: {Raw}", ble.RawData); - continue; - } - - // 3) 逐字段解析 - int battery = data[cd + 1]; // 电量 % - int heartRate = data[cd + 2]; // 心率 bpm + if (cd < 0 || data.Length < cd + 9) continue; + int battery = data[cd + 1]; + int heartRate = data[cd + 2]; if (heartRate == 0) continue; - // 4) 写实体 - heartRateEntities.Add(new HeartRateData + + + var entity = new HeartRateData { ScoreTime = ble.Timestamp, Code = ble.BleName, GradeId = student.GradeId, GradeName = student.GradeName, - ClassId = student?.ClassId ?? 0, + ClassId = student.ClassId, ClassName = student.ClassName ?? "", SchoolCode = student.SchoolCode ?? "", Sex = student.Sex, @@ -188,92 +157,133 @@ namespace YD_AllHeartRates.Api.Mqtt StudentNo = student.StudentNo, StudentName = student.StudentName, Strength = (int)Math.Round(((double)heartRate / (220 - student.Age)) * 100) - }); - } + }; + heartRateEntities.Add(entity); + var heartRateKey = $"heartRate:{student.StudentNo}"; + _caching.AddObject(heartRateKey, entity, 600); // 10分钟缓存 + + // 更新学校学生编号集合(Set) + var studentSetKey = $"school:{student.SchoolCode}:students"; + RedisHelper.SAdd(studentSetKey, student.StudentNo); // 自动去重 + } foreach (var ble in jumpRopeList) { - if (string.IsNullOrWhiteSpace(ble.RawData)) - continue; + if (string.IsNullOrWhiteSpace(ble.RawData)) continue; var student = _studentList.FirstOrDefault(x => x.JumpRopeId == ble.BleName); - if (student == null) - continue; + if (student == null || student.GradeId == 0 || student.ClassId == 0) continue; - if (student.GradeId == 0 || student.ClassId == 0) - continue; + var data = ParseHexData(ble.RawData); + if (data == null) continue; - int len = ble.RawData.Length / 2; - var data = new byte[len]; - try + int mfIndex = Array.IndexOf(data, new byte[] { 0xFF, 0x04, 0xFF, 0xCF }); + if (mfIndex < 0 || data.Length < mfIndex + 10) continue; + + int jumpCount = data[mfIndex + 5] + (data[mfIndex + 6] << 8); + int errorCount = data[mfIndex + 7]; + int battery = data[mfIndex + 9]; + + var jumpData = new JumpRopeData { - for (int i = 0; i < len; i++) - data[i] = Convert.ToByte(ble.RawData.Substring(i * 2, 2), 16); - } - catch (FormatException ex) - { - _log.LogWarning(ex, "rawData 非十六进制: {Raw}", ble.RawData); - continue; - } + ScoreTime = ble.Timestamp, + Code = ble.BleName, + GradeId = student.GradeId, + GradeName = student.GradeName, + ClassId = student.ClassId, + ClassName = student.ClassName ?? "", + SchoolCode = student.SchoolCode ?? "", + Sex = student.Sex, + JumpValue = jumpCount, + QuantityOfElectricity = battery, + ErrorNumber = errorCount, + StudentNo = student.StudentNo, + StudentName = student.StudentName + }; - int mfIndex = -1; - for (int i = 0; i < data.Length - 4; i++) - { - if (data[i] == 0xFF && data[i + 1] == 0x04 && data[i + 2] == 0xFF && data[i + 3] == 0xCF) - { - mfIndex = i + 4; // CF 后第一个字节是数据起始位置 - break; - } - } + //if (!string.IsNullOrWhiteSpace(student.SchoolCode)) + //{ + // // WebSocket 推送 + // await _wsSender.SendToSchoolAsync(student.SchoolCode, "heartRate", heartRateEntities); + // await _wsSender.SendToSchoolAsync(student.SchoolCode, "jumpRope", _jumpDailyMap.Values.ToList()); + //} - // 确保数据长度足够读取所有字段 - if (mfIndex >= 0 && data.Length > mfIndex + 5) + string key = $"{jumpData.StudentNo}_{jumpData.ScoreTime:yyyyMMdd}"; + if (_jumpDailyMap.ContainsKey(key)) { - // 解析跳绳个数,低位 + 高位 << 8 - int jumpCount = data[mfIndex + 1] + (data[mfIndex + 2] << 8); - int errorCount = data[mfIndex + 3]; - int battery = data[mfIndex + 5]; - - // 添加跳绳数据实体 - jumpRopeEntities.Add(new JumpRopeData - { - ScoreTime = ble.Timestamp, - Code = ble.BleName, - GradeId = student.GradeId, - GradeName = student.GradeName, - ClassId = student?.ClassId ?? 0, - ClassName = student.ClassName ?? "", - SchoolCode = student.SchoolCode ?? "", - Sex = student.Sex, - JumpValue = jumpCount, - QuantityOfElectricity = battery, - ErrorNumber = errorCount, - StudentNo = student.StudentNo, - StudentName = student.StudentName - }); + _jumpDailyMap[key].JumpValue += jumpData.JumpValue; + _jumpDailyMap[key].ErrorNumber += jumpData.ErrorNumber; + _jumpDailyMap[key].QuantityOfElectricity = jumpData.QuantityOfElectricity; + _jumpDailyMap[key].ScoreTime = jumpData.ScoreTime; } + else + { + _jumpDailyMap[key] = jumpData; + } + var studentSetKey = $"school:{student.SchoolCode}:students"; + var jumpKey = $"jumpRope:{student.StudentNo}"; + _caching.AddObject(jumpKey, jumpData, 600); + + RedisHelper.SAdd(studentSetKey, student.StudentNo); } } - // 心率 - if (heartRateEntities.Count > 0) - _userContext.HeartRateData.AddRange(heartRateEntities); + // 心率每分钟保存一次 + if ((DateTime.UtcNow - _lastHeartRateSaveTime).TotalSeconds >= 60 && _pendingHeartRates.Any()) + { + _userContext.HeartRateData.AddRange(_pendingHeartRates); + await _userContext.SaveChangesAsync(); + _pendingHeartRates.Clear(); + _lastHeartRateSaveTime = DateTime.UtcNow; + } + _pendingHeartRates.AddRange(heartRateEntities); - // 跳绳 - if (jumpRopeEntities.Count > 0) - _userContext.JumpRopeData.AddRange(jumpRopeEntities); - - await _userContext.SaveChangesAsync(); + // 跳绳每日更新保存 + if ((DateTime.UtcNow - _lastJumpRopeSaveTime).TotalSeconds >= 60 && _jumpDailyMap.Any()) + { + foreach (var data in _jumpDailyMap.Values) + { + var exist = await _userContext.JumpRopeData + .FirstOrDefaultAsync(x => x.StudentNo == data.StudentNo && x.ScoreTime.Date == data.ScoreTime.Date); + if (exist != null) + { + exist.JumpValue = data.JumpValue; + exist.ErrorNumber = data.ErrorNumber; + exist.QuantityOfElectricity = data.QuantityOfElectricity; + exist.ScoreTime = data.ScoreTime; + } + else + { + _userContext.JumpRopeData.Add(data); + } + } + await _userContext.SaveChangesAsync(); + _lastJumpRopeSaveTime = DateTime.UtcNow; + } } } + private byte[]? ParseHexData(string hex) + { + try + { + int len = hex.Length / 2; + var bytes = new byte[len]; + for (int i = 0; i < len; i++) + bytes[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); + return bytes; + } + catch + { + return null; + } + } private async IAsyncEnumerable> ReadBatchesAsync( - [EnumeratorCancellation] CancellationToken ct) + [EnumeratorCancellation] CancellationToken ct) { var buffer = new List(AppSettings.Mqtt.BatchSize); - while (await _queue.Reader.WaitToReadAsync(ct)) { while (_queue.Reader.TryRead(out var m)) @@ -286,7 +296,6 @@ namespace YD_AllHeartRates.Api.Mqtt } } } - if (buffer.Count > 0) yield return buffer; } } diff --git a/YD_AllHeartRates.Api/Services/Impl/LargeScreenService.cs b/YD_AllHeartRates.Api/Services/Impl/LargeScreenService.cs index dd46aa1..d7b1737 100644 --- a/YD_AllHeartRates.Api/Services/Impl/LargeScreenService.cs +++ b/YD_AllHeartRates.Api/Services/Impl/LargeScreenService.cs @@ -2,11 +2,14 @@ using AutoMapper; using Microsoft.EntityFrameworkCore; using System.Drawing; +using System.Reflection.PortableExecutable; using YD_AllHeartRates.Api.Context; +using YD_AllHeartRates.Api.Entitys; using YD_AllHeartRates.Api.Services.Interface; using YD_AllHeartRates.Api.SmartSportsEntitys; using YD_AllHeartRates.Api.Utilities; using YD_AllHeartRates.Commons.Dto.LargeScreen; +using YD_AllHeartRates.Commons.MemoryCaches; namespace YD_AllHeartRates.Api.Services.Impl { @@ -19,15 +22,18 @@ namespace YD_AllHeartRates.Api.Services.Impl public UserContext _userContext; private readonly LoginContext _loginContext; private string schoolCode; + private readonly ICaching _caching; /// /// 构造 /// - public LargeScreenService(SmartSportsContext sportsContext, UserContext userContext, LoginContext loginContext) + public LargeScreenService(SmartSportsContext sportsContext, UserContext userContext, LoginContext loginContext, ICaching caching) { _sportsContext = sportsContext; _userContext = userContext; _loginContext = loginContext; + _caching = caching; + schoolCode = _loginContext.SchoolCode; } @@ -138,46 +144,78 @@ namespace YD_AllHeartRates.Api.Services.Impl { var res = new HeartRateDataDto(); - var tenMinutesAgo = DateTime.Now.AddMinutes(-1); + var now = DateTime.Now; + var tenMinutesAgo = now.AddMinutes(-10); - var studentList = await _sportsContext.Student.Where(x => x.ClassId == classId && x.SchoolCode == schoolCode).ToListAsync(); + // 班级学生列表 + var studentList = await _sportsContext.Student + .Where(x => x.ClassId == classId && x.SchoolCode == schoolCode && x.StudentStatus == 1) + .ToListAsync(); - var heartRateData = await _userContext.HeartRateData - .Where(x => x.SchoolCode == schoolCode && x.ScoreTime >= tenMinutesAgo) - .GroupBy(x => x.StudentNo) - .Select(g => g.OrderByDescending(x => x.ScoreTime).FirstOrDefault()) - .ToListAsync(); - - var jumpingRopeData = await _userContext.JumpRopeData - .Where(x => x.SchoolCode == schoolCode && x.ScoreTime >= tenMinutesAgo) - .GroupBy(x => x.StudentNo) - .Select(g => g.OrderByDescending(x => x.ScoreTime).FirstOrDefault()) - .ToListAsync(); - - res.WarmUp = studentList.Count == 0 ? 0 : (int)Math.Round(heartRateData.Count(x => x.Strength < 50) * 100.0 / studentList.Count); - res.Low = studentList.Count == 0 ? 0 : (int)Math.Round(heartRateData.Count(x => x.Strength >= 50 && x.Strength < 60) * 100.0 / studentList.Count); - res.Medium = studentList.Count == 0 ? 0 : (int)Math.Round(heartRateData.Count(x => x.Strength >= 60 && x.Strength < 70) * 100.0 / studentList.Count); - res.High = studentList.Count == 0 ? 0 : (int)Math.Round(heartRateData.Count(x => x.Strength >= 70 && x.Strength < 85) * 100.0 / studentList.Count); - res.Warning = studentList.Count == 0 ? 0 : (int)Math.Round(heartRateData.Count(x => x.Strength >= 85) * 100.0 / studentList.Count); + int warmUp = 0, low = 0, medium = 0, high = 0, warning = 0; foreach (var student in studentList) { - var heartRate = heartRateData.FirstOrDefault(x => x.StudentNo == student.StudentNo); - var jumpingRope = jumpingRopeData.FirstOrDefault(x => x.StudentNo == student.StudentNo); + var heartRateKey = $"heartRate:{student.StudentNo}"; + var jumpRopeKey = $"jumpRope:{student.StudentNo}"; - res.StudentList.Add(new StudentDto() + // 先从缓存拿 + var heartRate = _caching.Get(heartRateKey); + var jumpRope = _caching.Get(jumpRopeKey); + + // ❗心率缓存未命中 → 单独查数据库 + if (heartRate == null) + { + heartRate = await _userContext.HeartRateData + .Where(x => x.StudentNo == student.StudentNo && x.ScoreTime >= tenMinutesAgo) + .OrderByDescending(x => x.ScoreTime) + .FirstOrDefaultAsync(); + + if (heartRate != null) + _caching.AddObject(heartRateKey, heartRate, 600); + } + + // ❗跳绳缓存未命中 → 单独查数据库 + if (jumpRope == null) + { + jumpRope = await _userContext.JumpRopeData + .Where(x => x.StudentNo == student.StudentNo && x.ScoreTime.Date == now.Date) + .OrderByDescending(x => x.ScoreTime) + .FirstOrDefaultAsync(); + + if (jumpRope != null) + _caching.AddObject(jumpRopeKey, jumpRope, 600); + } + + // 心率强度判断 + int strength = heartRate?.Strength ?? 0; + if (strength < 50) warmUp++; + else if (strength < 60) low++; + else if (strength < 70) medium++; + else if (strength < 85) high++; + else warning++; + + res.StudentList.Add(new StudentDto { StudentName = student.StudentName, - StudentNo = student?.StudentNo ?? "", - Photo = student?.Photo ?? "", + StudentNo = student.StudentNo, + Photo = student.Photo ?? "", Sex = student.Sex, HeartRate = heartRate?.Value ?? 0, - JumpingRope = jumpingRope?.JumpValue ?? 0, - Strength = heartRate?.Strength ?? 0 + JumpingRope = jumpRope?.JumpValue ?? 0, + Strength = strength }); } + int total = studentList.Count == 0 ? 1 : studentList.Count; + res.WarmUp = (int)Math.Round(warmUp * 100.0 / total); + res.Low = (int)Math.Round(low * 100.0 / total); + res.Medium = (int)Math.Round(medium * 100.0 / total); + res.High = (int)Math.Round(high * 100.0 / total); + res.Warning = (int)Math.Round(warning * 100.0 / total); + return res; } + } } diff --git a/YD_AllHeartRates.Api/Services/Impl/UserService.cs b/YD_AllHeartRates.Api/Services/Impl/UserService.cs index af0c60f..baf259e 100644 --- a/YD_AllHeartRates.Api/Services/Impl/UserService.cs +++ b/YD_AllHeartRates.Api/Services/Impl/UserService.cs @@ -66,7 +66,7 @@ namespace YD_AllHeartRates.Api.Services.Impl }; } - + //GTY0|RS207 /// /// 获取个人信息 /// diff --git a/YD_AllHeartRates.Api/Startup.cs b/YD_AllHeartRates.Api/Startup.cs index 42ef5cd..6f4be32 100644 --- a/YD_AllHeartRates.Api/Startup.cs +++ b/YD_AllHeartRates.Api/Startup.cs @@ -27,6 +27,7 @@ using MQTTnet; using YD_AllHeartRates.Api.Mqtt; using YD_AllHeartRates.Commons.MemoryCaches; using YD_AllHeartRates.Api.Middlewares; +using YD_AllHeartRates.Api.WebSocket; namespace YD_AllHeartRates.Api { @@ -235,13 +236,14 @@ namespace YD_AllHeartRates.Api "https://*/404"; }); services.AddSignalR(); + services.AddSingleton(); + services.AddHttpClient(); services.AddAutoMapper(typeof(MappingProfile)); services.AddLogging(); - //设置文件上传大小限制 //设置文件上传大小限制 services.Configure(x => { diff --git a/YD_AllHeartRates.Api/WebSocket/IWebSocketSender.cs b/YD_AllHeartRates.Api/WebSocket/IWebSocketSender.cs new file mode 100644 index 0000000..6282781 --- /dev/null +++ b/YD_AllHeartRates.Api/WebSocket/IWebSocketSender.cs @@ -0,0 +1,25 @@ +namespace YD_AllHeartRates.Api.WebSocket +{ + /// + /// WebSocket + /// + public interface IWebSocketSender + { + /// + /// 消息发送 + /// + /// + /// + /// + Task SendAsync(string channel, object data); + + /// + /// 根据学校发送消息 + /// + /// + /// + /// + /// + Task SendToSchoolAsync(string schoolCode, string channel, object data); + } +} diff --git a/YD_AllHeartRates.Api/WebSocket/RealtimeHub.cs b/YD_AllHeartRates.Api/WebSocket/RealtimeHub.cs new file mode 100644 index 0000000..948963c --- /dev/null +++ b/YD_AllHeartRates.Api/WebSocket/RealtimeHub.cs @@ -0,0 +1,50 @@ +using Microsoft.AspNetCore.SignalR; + +namespace YD_AllHeartRates.Api.WebSocket +{ + /// + /// RealtimeHub + /// + public class RealtimeHub : Hub + { + /// + /// 客户端连接时调用(可选) + /// + public override async Task OnConnectedAsync() + { + var connectionId = Context.ConnectionId; + Console.WriteLine($"客户端连接:{connectionId}"); + + await base.OnConnectedAsync(); + } + + /// + /// 客户端断开连接时调用(可选) + /// + public override async Task OnDisconnectedAsync(Exception? exception) + { + var connectionId = Context.ConnectionId; + Console.WriteLine($"客户端断开连接:{connectionId}"); + + await base.OnDisconnectedAsync(exception); + } + + /// + /// 客户端可调用的方法:加入指定分组(如按学校、班级) + /// + public async Task JoinGroup(string groupName) + { + await Groups.AddToGroupAsync(Context.ConnectionId, groupName); + Console.WriteLine($"加入分组:{groupName}"); + } + + /// + /// 客户端可调用的方法:离开指定分组 + /// + public async Task LeaveGroup(string groupName) + { + await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName); + Console.WriteLine($"离开分组:{groupName}"); + } + } +} diff --git a/YD_AllHeartRates.Api/WebSocket/SignalRWebSocketSender.cs b/YD_AllHeartRates.Api/WebSocket/SignalRWebSocketSender.cs new file mode 100644 index 0000000..71e3ec3 --- /dev/null +++ b/YD_AllHeartRates.Api/WebSocket/SignalRWebSocketSender.cs @@ -0,0 +1,45 @@ +using Microsoft.AspNetCore.SignalR; + +namespace YD_AllHeartRates.Api.WebSocket +{ + /// + /// 发送SignalRWebSocket + /// + public class SignalRWebSocketSender : IWebSocketSender + { + private readonly IHubContext _hubContext; + + /// + /// hubContext + /// + /// + public SignalRWebSocketSender(IHubContext hubContext) + { + _hubContext = hubContext; + } + + /// + /// 消息发送 + /// + /// + /// + /// + public async Task SendAsync(string channel, object data) + { + await _hubContext.Clients.All.SendAsync(channel, data); + } + + /// + /// 根据学校发送消息 + /// + /// + /// + /// + /// + public async Task SendToSchoolAsync(string schoolCode, string channel, object data) + { + string groupName = $"school_{schoolCode}"; + await _hubContext.Clients.Group(groupName).SendAsync(channel, data); + } + } +} diff --git a/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/b/YD_AllHeartRates.Commons.1.0.0.0.Fakes.dll b/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/b/YD_AllHeartRates.Commons.1.0.0.0.Fakes.dll index 77edef3..3610168 100644 Binary files a/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/b/YD_AllHeartRates.Commons.1.0.0.0.Fakes.dll and b/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/b/YD_AllHeartRates.Commons.1.0.0.0.Fakes.dll differ diff --git a/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/b/YD_AllHeartRates.Commons.1.0.0.0.Fakes.pdb b/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/b/YD_AllHeartRates.Commons.1.0.0.0.Fakes.pdb index e1fcce6..d292b1d 100644 Binary files a/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/b/YD_AllHeartRates.Commons.1.0.0.0.Fakes.pdb and b/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/b/YD_AllHeartRates.Commons.1.0.0.0.Fakes.pdb differ diff --git a/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/f.ch b/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/f.ch index 557ca14..67870b0 100644 --- a/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/f.ch +++ b/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/f.ch @@ -1 +1 @@ -kpAOGOwQr+PqcqSkET2ftxen78nx6+ufMJyhzNQMesU=WWmIfOUbyYPYdKFHlVpzo+vbEnfIsJNTTcG8+oWdtSc= \ No newline at end of file +OZ2FMHhzcKLaTsTH79CqMYu8/tuIh7oBcr70vB907z4=WWmIfOUbyYPYdKFHlVpzo+vbEnfIsJNTTcG8+oWdtSc= \ No newline at end of file diff --git a/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/f.cs b/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/f.cs index 4956876..8398504 100644 Binary files a/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/f.cs and b/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/f.cs differ diff --git a/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/o/net6.0/YD_AllHeartRates.Commons.1.0.0.0.Fakes.dll b/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/o/net6.0/YD_AllHeartRates.Commons.1.0.0.0.Fakes.dll index 77edef3..3610168 100644 Binary files a/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/o/net6.0/YD_AllHeartRates.Commons.1.0.0.0.Fakes.dll and b/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/o/net6.0/YD_AllHeartRates.Commons.1.0.0.0.Fakes.dll differ diff --git a/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/o/net6.0/YD_AllHeartRates.Commons.1.0.0.0.Fakes.pdb b/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/o/net6.0/YD_AllHeartRates.Commons.1.0.0.0.Fakes.pdb index e1fcce6..d292b1d 100644 Binary files a/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/o/net6.0/YD_AllHeartRates.Commons.1.0.0.0.Fakes.pdb and b/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/o/net6.0/YD_AllHeartRates.Commons.1.0.0.0.Fakes.pdb differ diff --git a/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/o/net6.0/ref/YD_AllHeartRates.Commons.1.0.0.0.Fakes.dll b/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/o/net6.0/ref/YD_AllHeartRates.Commons.1.0.0.0.Fakes.dll index 1557022..7875e50 100644 Binary files a/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/o/net6.0/ref/YD_AllHeartRates.Commons.1.0.0.0.Fakes.dll and b/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/o/net6.0/ref/YD_AllHeartRates.Commons.1.0.0.0.Fakes.dll differ diff --git a/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/o/net6.0/refint/YD_AllHeartRates.Commons.1.0.0.0.Fakes.dll b/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/o/net6.0/refint/YD_AllHeartRates.Commons.1.0.0.0.Fakes.dll index 1557022..7875e50 100644 Binary files a/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/o/net6.0/refint/YD_AllHeartRates.Commons.1.0.0.0.Fakes.dll and b/YD_AllHeartRates.Api/obj/Debug/net6.0/Fakes/ydahrc/o/net6.0/refint/YD_AllHeartRates.Commons.1.0.0.0.Fakes.dll differ diff --git a/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.AssemblyInfo.cs b/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.AssemblyInfo.cs index e7d07f4..9c08893 100644 --- a/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.AssemblyInfo.cs +++ b/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.AssemblyInfo.cs @@ -14,7 +14,7 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("YD_AllHeartRates.Api")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+c65f0d665eff3d83c1cdb0eaf581369baecbe26a")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+ee9116aea22701bf6d184657d96c4e24b2bc0095")] [assembly: System.Reflection.AssemblyProductAttribute("YD_AllHeartRates.Api")] [assembly: System.Reflection.AssemblyTitleAttribute("YD_AllHeartRates.Api")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.AssemblyInfoInputs.cache b/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.AssemblyInfoInputs.cache index 4c6a012..37d8aa0 100644 --- a/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.AssemblyInfoInputs.cache +++ b/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.AssemblyInfoInputs.cache @@ -1 +1 @@ -0edc1169727cbac8f4192a409031e62e7df5ab080dd285125c8d176be132b106 +1c24cdf25780b49d3cd275d101f9545d4bc591310051a502fe2c6f782554626d diff --git a/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.csproj.AssemblyReference.cache b/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.csproj.AssemblyReference.cache index a5624f6..7f5394c 100644 Binary files a/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.csproj.AssemblyReference.cache and b/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.csproj.AssemblyReference.cache differ diff --git a/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.csproj.CoreCompileInputs.cache b/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.csproj.CoreCompileInputs.cache index d57d245..892fc15 100644 --- a/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.csproj.CoreCompileInputs.cache +++ b/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -e1c1286248133e4f873378c81bc1cba408058a1c11789d81f9a4f37908ac6243 +be24640c820a322f6ed07b42336b9100bbed9b708418756cbe716ef9eb7afbd3 diff --git a/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.csprojFakesResolveAssemblyReference.cache b/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.csprojFakesResolveAssemblyReference.cache index 2615dac..da83ab3 100644 Binary files a/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.csprojFakesResolveAssemblyReference.cache and b/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.csprojFakesResolveAssemblyReference.cache differ diff --git a/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.dll b/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.dll index e7e1adc..b4e00ea 100644 Binary files a/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.dll and b/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.dll differ diff --git a/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.pdb b/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.pdb index 597e59a..f473d3f 100644 Binary files a/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.pdb and b/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.pdb differ diff --git a/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.xml b/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.xml index e54f33a..513ef98 100644 --- a/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.xml +++ b/YD_AllHeartRates.Api/obj/Debug/net6.0/YD_AllHeartRates.Api.xml @@ -426,20 +426,6 @@ 接受写入数据 - - - 构造函数 - - - - - - - 消费队列 - - - - 服务实现 @@ -533,7 +519,7 @@ 服务实现 - + 构造 @@ -1385,5 +1371,80 @@ 每个UserContext的属性至多读取一次redis或Memory缓存从而提高查询效率 + + + WebSocket + + + + + 消息发送 + + + + + + + + 根据学校发送消息 + + + + + + + + + RealtimeHub + + + + + 客户端连接时调用(可选) + + + + + 客户端断开连接时调用(可选) + + + + + 客户端可调用的方法:加入指定分组(如按学校、班级) + + + + + 客户端可调用的方法:离开指定分组 + + + + + 发送SignalRWebSocket + + + + + hubContext + + + + + + 消息发送 + + + + + + + + 根据学校发送消息 + + + + + + diff --git a/YD_AllHeartRates.Api/obj/Debug/net6.0/apphost.exe b/YD_AllHeartRates.Api/obj/Debug/net6.0/apphost.exe index c088e0a..bbd5ab0 100644 Binary files a/YD_AllHeartRates.Api/obj/Debug/net6.0/apphost.exe and b/YD_AllHeartRates.Api/obj/Debug/net6.0/apphost.exe differ diff --git a/YD_AllHeartRates.Api/obj/Debug/net6.0/ref/YD_AllHeartRates.Api.dll b/YD_AllHeartRates.Api/obj/Debug/net6.0/ref/YD_AllHeartRates.Api.dll index 9a93fbc..042180f 100644 Binary files a/YD_AllHeartRates.Api/obj/Debug/net6.0/ref/YD_AllHeartRates.Api.dll and b/YD_AllHeartRates.Api/obj/Debug/net6.0/ref/YD_AllHeartRates.Api.dll differ diff --git a/YD_AllHeartRates.Api/obj/Debug/net6.0/refint/YD_AllHeartRates.Api.dll b/YD_AllHeartRates.Api/obj/Debug/net6.0/refint/YD_AllHeartRates.Api.dll index 9a93fbc..042180f 100644 Binary files a/YD_AllHeartRates.Api/obj/Debug/net6.0/refint/YD_AllHeartRates.Api.dll and b/YD_AllHeartRates.Api/obj/Debug/net6.0/refint/YD_AllHeartRates.Api.dll differ diff --git a/YD_AllHeartRates.Commons/bin/Debug/net6.0/YD_AllHeartRates.Commons.dll b/YD_AllHeartRates.Commons/bin/Debug/net6.0/YD_AllHeartRates.Commons.dll index 96942bb..413c7ee 100644 Binary files a/YD_AllHeartRates.Commons/bin/Debug/net6.0/YD_AllHeartRates.Commons.dll and b/YD_AllHeartRates.Commons/bin/Debug/net6.0/YD_AllHeartRates.Commons.dll differ diff --git a/YD_AllHeartRates.Commons/bin/Debug/net6.0/YD_AllHeartRates.Commons.pdb b/YD_AllHeartRates.Commons/bin/Debug/net6.0/YD_AllHeartRates.Commons.pdb index e658351..f8952eb 100644 Binary files a/YD_AllHeartRates.Commons/bin/Debug/net6.0/YD_AllHeartRates.Commons.pdb and b/YD_AllHeartRates.Commons/bin/Debug/net6.0/YD_AllHeartRates.Commons.pdb differ diff --git a/YD_AllHeartRates.Commons/obj/Debug/net6.0/YD_AllHeartRates.Commons.AssemblyInfo.cs b/YD_AllHeartRates.Commons/obj/Debug/net6.0/YD_AllHeartRates.Commons.AssemblyInfo.cs index 6a9ecc1..a5ceace 100644 --- a/YD_AllHeartRates.Commons/obj/Debug/net6.0/YD_AllHeartRates.Commons.AssemblyInfo.cs +++ b/YD_AllHeartRates.Commons/obj/Debug/net6.0/YD_AllHeartRates.Commons.AssemblyInfo.cs @@ -14,7 +14,7 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("YD_AllHeartRates.Commons")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+580d586b2d20254267b1182a8ceff393c252da81")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+ee9116aea22701bf6d184657d96c4e24b2bc0095")] [assembly: System.Reflection.AssemblyProductAttribute("YD_AllHeartRates.Commons")] [assembly: System.Reflection.AssemblyTitleAttribute("YD_AllHeartRates.Commons")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/YD_AllHeartRates.Commons/obj/Debug/net6.0/YD_AllHeartRates.Commons.AssemblyInfoInputs.cache b/YD_AllHeartRates.Commons/obj/Debug/net6.0/YD_AllHeartRates.Commons.AssemblyInfoInputs.cache index 1382558..e1fae59 100644 --- a/YD_AllHeartRates.Commons/obj/Debug/net6.0/YD_AllHeartRates.Commons.AssemblyInfoInputs.cache +++ b/YD_AllHeartRates.Commons/obj/Debug/net6.0/YD_AllHeartRates.Commons.AssemblyInfoInputs.cache @@ -1 +1 @@ -35d58a7d90dcca1c52be85a8d3e3e2c8fc425c6c7ec2830346ca890579682685 +ad77ca7765704675fc50e971094eeabaa926244d017d6f5a36917f704f444668 diff --git a/YD_AllHeartRates.Commons/obj/Debug/net6.0/YD_AllHeartRates.Commons.dll b/YD_AllHeartRates.Commons/obj/Debug/net6.0/YD_AllHeartRates.Commons.dll index 96942bb..413c7ee 100644 Binary files a/YD_AllHeartRates.Commons/obj/Debug/net6.0/YD_AllHeartRates.Commons.dll and b/YD_AllHeartRates.Commons/obj/Debug/net6.0/YD_AllHeartRates.Commons.dll differ diff --git a/YD_AllHeartRates.Commons/obj/Debug/net6.0/YD_AllHeartRates.Commons.pdb b/YD_AllHeartRates.Commons/obj/Debug/net6.0/YD_AllHeartRates.Commons.pdb index e658351..f8952eb 100644 Binary files a/YD_AllHeartRates.Commons/obj/Debug/net6.0/YD_AllHeartRates.Commons.pdb and b/YD_AllHeartRates.Commons/obj/Debug/net6.0/YD_AllHeartRates.Commons.pdb differ diff --git a/YD_AllHeartRates.Commons/obj/Debug/net6.0/ref/YD_AllHeartRates.Commons.dll b/YD_AllHeartRates.Commons/obj/Debug/net6.0/ref/YD_AllHeartRates.Commons.dll index f6301f9..fb2be90 100644 Binary files a/YD_AllHeartRates.Commons/obj/Debug/net6.0/ref/YD_AllHeartRates.Commons.dll and b/YD_AllHeartRates.Commons/obj/Debug/net6.0/ref/YD_AllHeartRates.Commons.dll differ diff --git a/YD_AllHeartRates.Commons/obj/Debug/net6.0/refint/YD_AllHeartRates.Commons.dll b/YD_AllHeartRates.Commons/obj/Debug/net6.0/refint/YD_AllHeartRates.Commons.dll index f6301f9..fb2be90 100644 Binary files a/YD_AllHeartRates.Commons/obj/Debug/net6.0/refint/YD_AllHeartRates.Commons.dll and b/YD_AllHeartRates.Commons/obj/Debug/net6.0/refint/YD_AllHeartRates.Commons.dll differ