From b1f7281a7412f6aadb00cd67a43d7094f8bbc158 Mon Sep 17 00:00:00 2001 From: mago Date: Tue, 13 May 2025 17:18:50 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4=E8=80=83=E7=94=9F=E7=BB=84?= =?UTF-8?q?=E6=97=B6=E6=B7=BB=E5=8A=A0=E6=98=AF=E5=90=A6=E6=9C=89=E8=80=83?= =?UTF-8?q?=E7=94=9F=E9=AA=8C=E8=AF=81=EF=BC=88=E9=9C=80=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=90=8E=E5=86=8D=E5=85=81=E8=AE=B8=E5=88=A0=E9=99=A4=EF=BC=89?= =?UTF-8?q?=20=E5=88=A0=E9=99=A4=E8=80=83=E7=94=9F=E6=97=B6=E4=B8=80?= =?UTF-8?q?=E5=B9=B6=E5=88=A0=E9=99=A4=E7=94=A8=E6=88=B7=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0Rabbit=20MQ?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Src/CodeSpirit.ConfigCenter/Program.cs | 3 +- Src/CodeSpirit.Core/CodeSpirit.Core.csproj | 4 + Src/CodeSpirit.Core/RabbitEvent.cs | 17 ++++ .../CodeSpirit.ExamApi.csproj | 1 + .../Controllers/StudentGroupsController.cs | 20 ++++- .../Controllers/StudentsController.cs | 83 +++++++++++++------ Src/CodeSpirit.ExamApi/Program.cs | 2 + .../Implementations/StudentService.cs | 6 ++ .../Services/Interfaces/IStudentService.cs | 2 + Src/CodeSpirit.IdentityApi/Program.cs | 1 + .../RabbitMQProcessingJob.cs | 65 +++++++++++++++ 11 files changed, 174 insertions(+), 30 deletions(-) create mode 100644 Src/CodeSpirit.Core/RabbitEvent.cs create mode 100644 Src/CodeSpirit.IdentityApi/RabbitMQProcessingJob.cs diff --git a/Src/CodeSpirit.ConfigCenter/Program.cs b/Src/CodeSpirit.ConfigCenter/Program.cs index ab36694..dbc4a72 100644 --- a/Src/CodeSpirit.ConfigCenter/Program.cs +++ b/Src/CodeSpirit.ConfigCenter/Program.cs @@ -1,7 +1,8 @@ Console.OutputEncoding = System.Text.Encoding.UTF8; WebApplicationBuilder builder = WebApplication.CreateBuilder(args); - +//获取对 RabbitMQ 消息代理的引用 +builder.AddRabbitMQClient("rabbitmq"); // 使用 ConfigCenter 扩展方法注册所有服务 builder.AddConfigCenter(); diff --git a/Src/CodeSpirit.Core/CodeSpirit.Core.csproj b/Src/CodeSpirit.Core/CodeSpirit.Core.csproj index eabb21d..c6ab74c 100644 --- a/Src/CodeSpirit.Core/CodeSpirit.Core.csproj +++ b/Src/CodeSpirit.Core/CodeSpirit.Core.csproj @@ -4,4 +4,8 @@ net9.0 disable + + + + diff --git a/Src/CodeSpirit.Core/RabbitEvent.cs b/Src/CodeSpirit.Core/RabbitEvent.cs new file mode 100644 index 0000000..85ef888 --- /dev/null +++ b/Src/CodeSpirit.Core/RabbitEvent.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json.Linq; + +namespace CodeSpirit.Core +{ + public class RabbitEvent + { + /// + /// 名字 + /// + public string EventName { get; set; } = string.Empty; + + /// + /// 数据 + /// + public JToken EventData { get; set; } + } +} diff --git a/Src/CodeSpirit.ExamApi/CodeSpirit.ExamApi.csproj b/Src/CodeSpirit.ExamApi/CodeSpirit.ExamApi.csproj index 256105d..039440d 100644 --- a/Src/CodeSpirit.ExamApi/CodeSpirit.ExamApi.csproj +++ b/Src/CodeSpirit.ExamApi/CodeSpirit.ExamApi.csproj @@ -11,6 +11,7 @@ + diff --git a/Src/CodeSpirit.ExamApi/Controllers/StudentGroupsController.cs b/Src/CodeSpirit.ExamApi/Controllers/StudentGroupsController.cs index 86574e2..924f077 100644 --- a/Src/CodeSpirit.ExamApi/Controllers/StudentGroupsController.cs +++ b/Src/CodeSpirit.ExamApi/Controllers/StudentGroupsController.cs @@ -2,8 +2,10 @@ using CodeSpirit.Core.Attributes; using CodeSpirit.Core.Dtos; using CodeSpirit.ExamApi.Data.Models; using CodeSpirit.ExamApi.Dtos.StudentGroup; +using CodeSpirit.ExamApi.Services.Implementations; using CodeSpirit.ExamApi.Services.Interfaces; using CodeSpirit.Shared.Dtos.Common; +using LinqKit; using Microsoft.AspNetCore.Mvc; using System.Linq.Expressions; @@ -17,6 +19,7 @@ namespace CodeSpirit.ExamApi.Controllers; public class StudentGroupsController : ApiControllerBase { private readonly IStudentGroupService _studentGroupService; + private readonly IStudentService _studentService; private readonly ILogger _logger; /// @@ -26,13 +29,15 @@ public class StudentGroupsController : ApiControllerBase /// 日志记录器 public StudentGroupsController( IStudentGroupService studentGroupService, - ILogger logger) + ILogger logger, + IStudentService studentService) { ArgumentNullException.ThrowIfNull(studentGroupService); ArgumentNullException.ThrowIfNull(logger); _studentGroupService = studentGroupService; _logger = logger; + _studentService = studentService; } /// @@ -100,6 +105,11 @@ public class StudentGroupsController : ApiControllerBase [DisplayName("删除考生组")] public async Task> DeleteStudentGroup(long id) { + var falg = await _studentService.JudgeWhetherStudentByGroupIdAsync(new List { id }); + if (falg) + { + return BadResponse("该考生组下有考生,无法删除!"); + } await _studentGroupService.DeleteAsync(id); return SuccessResponse(); } @@ -116,6 +126,12 @@ public class StudentGroupsController : ApiControllerBase { ArgumentNullException.ThrowIfNull(request); + var falg = await _studentService.JudgeWhetherStudentByGroupIdAsync(request.Ids); + if (falg) + { + return BadResponse("该考生组下有考生,无法删除!"); + } + (int successCount, List failedIds) = await _studentGroupService.BatchDeleteAsync(request.Ids); return failedIds.Any() @@ -177,7 +193,7 @@ public class StudentGroupsController : ApiControllerBase /// 学生组列表 [HttpGet("select")] [DisplayName("获取考生组选择列表")] - public async Task>>>> GetStudentGroupsForSelect([FromQuery]bool hasNoGroup = false) + public async Task>>>> GetStudentGroupsForSelect([FromQuery] bool hasNoGroup = false) { var groups = await _studentGroupService.GetAllActiveGroupsAsync(); var options = groups.Select(p => new OptionDto() { Id = p.Id, Name = p.Name }).ToList(); diff --git a/Src/CodeSpirit.ExamApi/Controllers/StudentsController.cs b/Src/CodeSpirit.ExamApi/Controllers/StudentsController.cs index 0aaf6e4..adc7bc7 100644 --- a/Src/CodeSpirit.ExamApi/Controllers/StudentsController.cs +++ b/Src/CodeSpirit.ExamApi/Controllers/StudentsController.cs @@ -1,9 +1,13 @@ using CodeSpirit.Core.Attributes; using CodeSpirit.Core.Dtos; +using CodeSpirit.ExamApi.Data.Models; using CodeSpirit.ExamApi.Dtos.Student; using CodeSpirit.ExamApi.Services.Interfaces; using CodeSpirit.Shared.Dtos.Common; using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Text; namespace CodeSpirit.ExamApi.Controllers; @@ -16,7 +20,8 @@ public class StudentsController : ApiControllerBase { private readonly IStudentService _studentService; private readonly ILogger _logger; - + private readonly RabbitMQ.Client.IConnection _connection; + /// /// 初始化考生管理控制器 /// @@ -24,15 +29,17 @@ public class StudentsController : ApiControllerBase /// 日志记录器 public StudentsController( IStudentService studentService, - ILogger logger) + ILogger logger, + RabbitMQ.Client.IConnection connection) { ArgumentNullException.ThrowIfNull(studentService); ArgumentNullException.ThrowIfNull(logger); - + _studentService = studentService; _logger = logger; + _connection = connection; } - + /// /// 获取考生列表 /// @@ -45,7 +52,7 @@ public class StudentsController : ApiControllerBase var result = await _studentService.GetStudentsAsync(queryDto); return SuccessResponse(result); } - + /// /// 导出考生列表 /// @@ -59,16 +66,16 @@ public class StudentsController : ApiControllerBase const int MaxExportLimit = 10000; // 最大导出数量限制 queryDto.PerPage = MaxExportLimit; queryDto.Page = 1; - + // 获取考生数据 var result = await _studentService.GetStudentsAsync(queryDto); - + // 如果数据为空则返回错误信息 - return result.Items.Count == 0 - ? BadResponse>("没有数据可供导出") + return result.Items.Count == 0 + ? BadResponse>("没有数据可供导出") : SuccessResponse(result); } - + /// /// 获取考生详情 /// @@ -79,11 +86,11 @@ public class StudentsController : ApiControllerBase public async Task>> GetStudent(long id) { if (id <= 0) return BadRequest("无效的ID"); - + var result = await _studentService.GetAsync(id); return SuccessResponse(result); } - + /// /// 创建考生 /// @@ -96,7 +103,7 @@ public class StudentsController : ApiControllerBase var result = await _studentService.CreateAsync(createDto); return SuccessResponse(result); } - + /// /// 更新考生信息 /// @@ -110,7 +117,7 @@ public class StudentsController : ApiControllerBase await _studentService.UpdateAsync(id, updateDto); return SuccessResponse(); } - + /// /// 删除考生 /// @@ -121,10 +128,20 @@ public class StudentsController : ApiControllerBase [DisplayName("删除考生")] public async Task> DeleteStudent(long id) { + var student = await _studentService.GetAsync(id); await _studentService.DeleteAsync(id); + var channel = _connection.CreateModel(); + var enentObj = new RabbitEvent() + { + EventName = "StudentDeleted", + EventData = JToken.FromObject(new List { student.UserId }) + }; + var body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(enentObj)); + channel.QueueDeclare(queue: "ExamEvents", durable: false, exclusive: false, autoDelete: false, arguments: null); + channel.BasicPublish(exchange: string.Empty, routingKey: "ExamEvents", mandatory: false, basicProperties: null, body: body); return SuccessResponse(); } - + /// /// 批量导入考生 /// @@ -135,13 +152,13 @@ public class StudentsController : ApiControllerBase public async Task> BatchImport([FromBody] BatchImportDtoBase importDto) { ArgumentNullException.ThrowIfNull(importDto); - + var result = await _studentService.BatchImportAsync(importDto.ImportData); return result.failedIds.Any() ? SuccessResponse($"成功导入 {result.successCount} 个考生,但以下考生导入失败: {string.Join(", ", result.failedIds)}") : SuccessResponse($"成功导入 {result.successCount} 个考生!"); } - + /// /// 批量删除考生 /// @@ -153,15 +170,27 @@ public class StudentsController : ApiControllerBase public async Task> BatchDelete([FromBody] BatchOperationDto request) { ArgumentNullException.ThrowIfNull(request); - + (int successCount, List failedIds) = await _studentService.BatchDeleteAsync(request.Ids); - + + List remainingIds = request.Ids.Except(failedIds).ToList(); + + var channel = _connection.CreateModel(); + var enentObj = new RabbitEvent() + { + EventName = "StudentDeleted", + EventData = JToken.FromObject(remainingIds) + }; + var body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(enentObj)); + channel.QueueDeclare(queue: "ExamEvents", durable: false, exclusive: false, autoDelete: false, arguments: null); + channel.BasicPublish(exchange: string.Empty, routingKey: "ExamEvents", mandatory: false, basicProperties: null, body: body); + return failedIds.Any() ? SuccessResponse($"成功删除 {successCount} 个考生,但以下考生删除失败: {string.Join(", ", failedIds)}") : SuccessResponse($"成功删除 {successCount} 个考生!"); } - - + + /// /// 通过学号查询考生 /// @@ -172,11 +201,11 @@ public class StudentsController : ApiControllerBase public async Task>> GetByStudentNumber(string studentNumber) { if (string.IsNullOrEmpty(studentNumber)) return BadRequest("学号不能为空"); - + var result = await _studentService.GetByStudentNumberAsync(studentNumber); return SuccessResponse(result); } - + /// /// 通过用户ID查询考生 /// @@ -189,7 +218,7 @@ public class StudentsController : ApiControllerBase var result = await _studentService.GetByUserIdAsync(userId); return SuccessResponse(result); } - + /// /// 批量分配考生到考生组 /// @@ -201,11 +230,11 @@ public class StudentsController : ApiControllerBase public async Task> BatchAssignGroups([FromBody] BatchAssignGroupsDto request) { ArgumentNullException.ThrowIfNull(request); - + (int successCount, List failedIds) = await _studentService.BatchAssignGroupsAsync(request.Ids, request.GroupIds); - + return failedIds.Any() ? SuccessResponse($"成功分配 {successCount} 个考生到考生组,但以下考生分配失败: {string.Join(", ", failedIds)}") : SuccessResponse($"成功分配 {successCount} 个考生到考生组!"); } -} \ No newline at end of file +} \ No newline at end of file diff --git a/Src/CodeSpirit.ExamApi/Program.cs b/Src/CodeSpirit.ExamApi/Program.cs index 66c6c1b..63bba1c 100644 --- a/Src/CodeSpirit.ExamApi/Program.cs +++ b/Src/CodeSpirit.ExamApi/Program.cs @@ -7,6 +7,8 @@ using CodeSpirit.Shared.DistributedLock; Console.OutputEncoding = Encoding.UTF8; var builder = WebApplication.CreateBuilder(args); +//获取对 RabbitMQ 消息代理的引用 +builder.AddRabbitMQClient("rabbitmq"); builder.AddExam(); // 添加AI题目生成服务 - 使用Extensions命名空间下的方法 diff --git a/Src/CodeSpirit.ExamApi/Services/Implementations/StudentService.cs b/Src/CodeSpirit.ExamApi/Services/Implementations/StudentService.cs index d37f5e1..514b120 100644 --- a/Src/CodeSpirit.ExamApi/Services/Implementations/StudentService.cs +++ b/Src/CodeSpirit.ExamApi/Services/Implementations/StudentService.cs @@ -514,4 +514,10 @@ public class StudentService : BaseCRUDIService JudgeWhetherStudentByGroupIdAsync(List groupIds) + { + return await Repository.CreateQuery() + .AnyAsync(s => s.StudentGroups.Any(g => groupIds.Contains(g.StudentGroupId))); + } } \ No newline at end of file diff --git a/Src/CodeSpirit.ExamApi/Services/Interfaces/IStudentService.cs b/Src/CodeSpirit.ExamApi/Services/Interfaces/IStudentService.cs index 18fa625..bce0389 100644 --- a/Src/CodeSpirit.ExamApi/Services/Interfaces/IStudentService.cs +++ b/Src/CodeSpirit.ExamApi/Services/Interfaces/IStudentService.cs @@ -33,4 +33,6 @@ public interface IStudentService : IBaseCRUDIService考生组ID列表 /// 成功数量和失败的ID列表 Task<(int successCount, List failedIds)> BatchAssignGroupsAsync(List studentIds, List groupIds); + + Task JudgeWhetherStudentByGroupIdAsync(List groupIds); } \ No newline at end of file diff --git a/Src/CodeSpirit.IdentityApi/Program.cs b/Src/CodeSpirit.IdentityApi/Program.cs index 5bf7ce9..2b9c423 100644 --- a/Src/CodeSpirit.IdentityApi/Program.cs +++ b/Src/CodeSpirit.IdentityApi/Program.cs @@ -11,6 +11,7 @@ builder.AddIdentityApiServices(); // 注册JWT服务 builder.Services.AddScoped(); +builder.Services.AddHostedService(); // 注册登录日志仓储 builder.Services.AddScoped(); diff --git a/Src/CodeSpirit.IdentityApi/RabbitMQProcessingJob.cs b/Src/CodeSpirit.IdentityApi/RabbitMQProcessingJob.cs new file mode 100644 index 0000000..ea03f8b --- /dev/null +++ b/Src/CodeSpirit.IdentityApi/RabbitMQProcessingJob.cs @@ -0,0 +1,65 @@ + +using CodeSpirit.Core; +using CodeSpirit.IdentityApi.Services; +using Newtonsoft.Json; +using RabbitMQ.Client; +using RabbitMQ.Client.Events; +using System.Text; +using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database; + +namespace CodeSpirit.IdentityApi +{ + public class RabbitMQProcessingJob : BackgroundService + { + private readonly ILogger _logger; + private readonly RabbitMQ.Client.IConnection _connection; + private readonly IServiceProvider _serviceProvider; + private EventingBasicConsumer consumer; + + public RabbitMQProcessingJob(ILogger logger, RabbitMQ.Client.IConnection connection, IServiceProvider serviceProvider) + { + _logger = logger; + _connection = connection; + _serviceProvider = serviceProvider; + } + + + protected override Task ExecuteAsync(CancellationToken stoppingToken) + { + var channel = _connection.CreateModel(); + channel.QueueDeclare(queue: "ExamEvents", durable: false, exclusive: false, autoDelete: false, arguments: null); + consumer = new EventingBasicConsumer(channel); + consumer.Received += ProcessMessageAsync; + channel.BasicConsume(queue: "ExamEvents", autoAck: true, consumer: consumer); + return Task.CompletedTask; + + } + + private async void ProcessMessageAsync(object? sender, BasicDeliverEventArgs args) + { + var body = args.Body.ToArray(); + var message = System.Text.Encoding.UTF8.GetString(body); + + var eventObj = JsonConvert.DeserializeObject(message); + switch (eventObj.EventName) + { + case "StudentDeleted": + // 处理学生删除事件 + var ids = eventObj.EventData.ToObject>(); + _logger.LogInformation("Received StudentDeleted event for Users ID: {userId}", ids); + using (var scope = _serviceProvider.CreateScope()) + { + var userService = scope.ServiceProvider.GetRequiredService(); + await userService.BatchDeleteAsync(ids); + + } + break; + default: + _logger.LogWarning("Unknown event type: {eventName}", eventObj.EventName); + break; + } + + } + + } +} -- Gitee