diff --git a/Src/CodeSpirit.ConfigCenter/Program.cs b/Src/CodeSpirit.ConfigCenter/Program.cs
index ab36694ea5c2344d21547f8df6d18585ec9e2ed0..dbc4a72d633e81b4e15befd14c6380d2616e59bf 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 eabb21d871609e256fbe519dae14b72fa2bcad9a..c6ab74c712709b81afa5ae0a172f367615fb218a 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 0000000000000000000000000000000000000000..85ef888a38cd5cdf716d974ab36eeebbb7240fec
--- /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 256105dc121b940398fbb3e255a5fad087b25308..039440df81e0ee7eb9c87856362f592082d028e0 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 86574e24d9b710b431e6d95921f60228493a4330..924f0771376c2d941b9b7da292e4ebc7630bea97 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 0aaf6e4b0b89e2efc47cb4bbaa2b286b476912d3..adc7bc7dbfb526ffbc32c31dd8ef4685b64528b1 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 66c6c1b4ecf0881c992478852055b1b9076be69d..63bba1ca270ef02776a89e53f4083bc5a177ee9c 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 d37f5e1879557681291513ecc64334d8f8ac1f1e..514b1206994d36b15a1389c89e180c3edaf706a4 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 18fa62575d886dfeee2b9849c8ec3b870990e74e..bce03892d5fe069ca9421b15cfa79b831c03e7ae 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 5bf7ce9aa88e48f8cd63da622f66c8334ae2dd63..2b9c423aefd165828b96a691a2be236b48173d07 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 0000000000000000000000000000000000000000..ea03f8b55b5407fcee545912e5621800cbad1e42
--- /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;
+ }
+
+ }
+
+ }
+}