实验批量发布现在会创建持久化后台任务,不再在 HTTP 请求里逐项校验和通知。

This commit is contained in:
2026-08-09 20:08:10 +08:00 Unverified
parent d05b6df9f5
commit e8261714da
5 changed files with 140 additions and 34 deletions
@@ -1,7 +1,9 @@
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Text.Json;
using Jiaowu.Api.Domain.Academic;
using Jiaowu.Api.Domain.Identity;
using Jiaowu.Api.Domain.System;
using Jiaowu.Api.Infrastructure.Auth;
using Jiaowu.Api.Infrastructure.Experiments;
using Jiaowu.Api.Infrastructure.Persistence;
@@ -706,38 +708,33 @@ public sealed class ExperimentsController(
{
var ids = ValidateBulkProjectIds(request.ProjectIds);
if (ids is null) return ValidationProblem("请选择 1 至 100 个实验项目。");
var projects = await ScopedProjects()
.Include(x => x.Sessions)
.Include(x => x.TeachingTask)
.ThenInclude(x => x!.Course)
.Where(x => ids.Contains(x.Id))
.ToListAsync(cancellationToken);
if (projects.Count != ids.Count) return NotFound();
foreach (var project in projects)
if (await ScopedProjects().CountAsync(x => ids.Contains(x.Id), cancellationToken) != ids.Count)
return NotFound();
var userId = currentUserDataScope.Current.UserId;
var job = new ExamPublishJob
{
if (project.Status != ExperimentProjectStatus.Draft)
return ConflictProblem("批量发布只能包含草稿实验项目。");
var hasSchedule = project.ArrangementMode == ExperimentArrangementMode.Centralized
? project.ScheduleEntryId.HasValue || project.Sessions.Any(x => x.Status == ExperimentSessionStatus.Scheduled)
: project.Sessions.Any(x => x.Status == ExperimentSessionStatus.Scheduled);
if (!hasSchedule)
return ConflictProblem($"“{project.Name}”尚未具备发布条件。");
if (project.Sessions.Any(x => x.Status == ExperimentSessionStatus.Scheduled &&
(x.SessionDate < project.StartDate || x.SessionDate > project.EndDate)))
return ConflictProblem($"“{project.Name}”存在不在开放日期范围内的实验场次。");
}
var publishedAt = DateTime.UtcNow;
foreach (var project in projects)
{
project.Status = ExperimentProjectStatus.Published;
project.PublishedAt = publishedAt;
}
Kind = ExamPublishJobKind.ExperimentProjects,
PlanId = ids[0],
RequestedByUserId = userId == Guid.Empty ? null : userId,
ProjectIdsJson = JsonSerializer.Serialize(ids),
CurrentStep = "等待后台校验"
};
db.ExamPublishJobs.Add(job);
db.BackgroundJobOutboxMessages.Add(BackgroundJobOutboxMessage.Create(
BackgroundJobKind.ExamPublish, job.Id));
await db.SaveChangesAsync(cancellationToken);
foreach (var project in projects)
await NotifyProjectPublishedAsync(project, cancellationToken);
return NoContent();
return Accepted(new { JobId = job.Id, Status = job.Status, Message = "实验项目发布任务已提交。" });
}
[HttpGet("batch/publish-jobs/{jobId:guid}")]
[Authorize(Roles = Managers)]
public async Task<ActionResult> GetPublishJob(Guid jobId, CancellationToken cancellationToken)
{
var job = await db.ExamPublishJobs.AsNoTracking().FirstOrDefaultAsync(x =>
x.Id == jobId && x.Kind == ExamPublishJobKind.ExperimentProjects,
cancellationToken);
if (job is null) return NotFound();
return Ok(new { job.Id, job.Status, job.CurrentStep, job.ErrorMessage, job.StartedAt, job.CompletedAt });
}
[HttpPost("{id:guid}/close")]