新建时可选“自动生成顺序号”:例如 数据库实验 01、02…
或选“逐行粘贴名称”:每行一个名称,按实际生成顺序对应项目。 草稿项目支持勾选后“批量改名”,可直接粘贴多行名称。 服务端会校验名称数量、长度和草稿状态,避免错位或修改已发布项目。
This commit is contained in:
@@ -574,6 +574,27 @@ public sealed class ExperimentsController(
|
||||
}
|
||||
}
|
||||
|
||||
var names = NormalizeBatchProjectNames(request.Names);
|
||||
if (request.GenerateSequentialNames)
|
||||
{
|
||||
if (names is not null)
|
||||
return ValidationProblem("自动生成顺序号时无需填写逐行项目名称。");
|
||||
var width = Math.Max(2, projects.Count.ToString().Length);
|
||||
if (request.Name.Trim().Length + width + 1 > 120)
|
||||
return ValidationProblem("项目名称前缀过长,无法追加顺序号。");
|
||||
for (var index = 0; index < projects.Count; index++)
|
||||
projects[index].Name = $"{request.Name.Trim()} {(index + 1).ToString($"D{width}")}";
|
||||
}
|
||||
else if (names is not null)
|
||||
{
|
||||
if (names.Any(name => string.IsNullOrWhiteSpace(name) || name.Length > 120))
|
||||
return ValidationProblem("每个实验项目名称均不能为空,且不能超过 120 个字符。");
|
||||
if (names.Count != projects.Count)
|
||||
return ValidationProblem($"已填写 {names.Count} 个项目名称,但本次将生成 {projects.Count} 个实验项目;请确保每行对应一个项目。");
|
||||
for (var index = 0; index < projects.Count; index++)
|
||||
projects[index].Name = names[index];
|
||||
}
|
||||
|
||||
db.ExperimentProjects.AddRange(projects);
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
return Created(string.Empty, new
|
||||
@@ -645,6 +666,34 @@ public sealed class ExperimentsController(
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPut("batch/names")]
|
||||
[Authorize(Roles = Managers)]
|
||||
public async Task<ActionResult> UpdateProjectNames(
|
||||
ExperimentProjectBatchNamesRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var ids = ValidateBulkProjectIds(request.ProjectIds);
|
||||
if (ids is null) return ValidationProblem("请选择 1 至 100 个实验项目。");
|
||||
|
||||
var names = NormalizeBatchProjectNames(request.Names);
|
||||
if (names is null || names.Count != ids.Count || names.Any(name => string.IsNullOrWhiteSpace(name) || name.Length > 120))
|
||||
return ValidationProblem("请为每个已选实验项目填写一个名称,每行一个且数量必须一致。");
|
||||
|
||||
var projects = await ScopedProjects()
|
||||
.Where(x => ids.Contains(x.Id))
|
||||
.ToListAsync(cancellationToken);
|
||||
if (projects.Count != ids.Count) return NotFound();
|
||||
if (projects.Any(x => x.Status != ExperimentProjectStatus.Draft))
|
||||
return ConflictProblem("批量修改名称只能包含草稿实验项目。");
|
||||
|
||||
var projectsById = projects.ToDictionary(x => x.Id);
|
||||
for (var index = 0; index < ids.Count; index++)
|
||||
projectsById[ids[index]].Name = names[index];
|
||||
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpDelete("batch")]
|
||||
[Authorize(Roles = Managers)]
|
||||
public async Task<ActionResult> DeleteProjects(
|
||||
@@ -1494,6 +1543,12 @@ public sealed class ExperimentsController(
|
||||
private static string? Normalize(string? value) =>
|
||||
string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
||||
|
||||
private static List<string>? NormalizeBatchProjectNames(IReadOnlyList<string>? names)
|
||||
{
|
||||
if (names is null || names.Count == 0) return null;
|
||||
return names.Select(name => name.Trim()).ToList();
|
||||
}
|
||||
|
||||
private ActionResult ConflictProblem(string detail) =>
|
||||
Conflict(new ProblemDetails
|
||||
{
|
||||
@@ -1522,7 +1577,9 @@ public sealed record ExperimentProjectBatchRequest(
|
||||
[MaxLength(1000)] string? Description,
|
||||
[MaxLength(1000)] string? Requirements,
|
||||
DateOnly StartDate,
|
||||
DateOnly EndDate)
|
||||
DateOnly EndDate,
|
||||
IReadOnlyList<string>? Names = null,
|
||||
bool GenerateSequentialNames = false)
|
||||
{
|
||||
public ExperimentProjectRequest ForTeachingTask(Guid teachingTaskId) =>
|
||||
new(
|
||||
@@ -1539,6 +1596,10 @@ public sealed record ExperimentProjectBatchRequest(
|
||||
public sealed record ExperimentProjectBulkRequest(
|
||||
[Required] IReadOnlyList<Guid> ProjectIds);
|
||||
|
||||
public sealed record ExperimentProjectBatchNamesRequest(
|
||||
[Required] IReadOnlyList<Guid> ProjectIds,
|
||||
[Required] IReadOnlyList<string> Names);
|
||||
|
||||
public sealed record ExperimentSessionRequest(
|
||||
Guid ClassroomId,
|
||||
DateOnly SessionDate,
|
||||
|
||||
Reference in New Issue
Block a user