diff --git a/src/Jiaowu.Api/Controllers/CourseEquivalencyRulesController.cs b/src/Jiaowu.Api/Controllers/CourseEquivalencyRulesController.cs index ee36ba4..2dd1ea9 100644 --- a/src/Jiaowu.Api/Controllers/CourseEquivalencyRulesController.cs +++ b/src/Jiaowu.Api/Controllers/CourseEquivalencyRulesController.cs @@ -40,6 +40,8 @@ public sealed class CourseEquivalencyRulesController( return Forbid(); if (request.MajorId.HasValue && !await db.Majors.AnyAsync(x => x.Id == request.MajorId && (!collegeId.HasValue || x.CollegeId == collegeId), token)) return ValidationProblem("适用专业不在当前维护范围内。"); + if (await HasSameScopeRuleAsync(request, null, token)) + return ConflictProblem(); var rule = new CourseEquivalencyRule { OriginalCourseId = request.OriginalCourseId, SubstituteCourseId = request.SubstituteCourseId, MajorId = request.MajorId, EffectiveGrade = request.EffectiveGrade, IsEnabled = request.IsEnabled, Notes = request.Notes?.Trim() }; db.CourseEquivalencyRules.Add(rule); @@ -58,11 +60,48 @@ public sealed class CourseEquivalencyRulesController( var collegeId = scope.Current.RestrictedCollegeId; if (request.MajorId.HasValue && !await db.Majors.AnyAsync(x => x.Id == request.MajorId && (!collegeId.HasValue || x.CollegeId == collegeId), token)) return ValidationProblem("适用专业不在当前维护范围内。"); + if (await HasSameScopeRuleAsync(request, id, token)) + return ConflictProblem(); rule.MajorId = request.MajorId; rule.EffectiveGrade = request.EffectiveGrade; rule.IsEnabled = request.IsEnabled; rule.Notes = request.Notes?.Trim(); await db.SaveChangesAsync(token); return NoContent(); } + [HttpDelete("{id:guid}")] + public async Task Delete(Guid id, CancellationToken token) + { + var rule = await Scoped(scope.Current.RestrictedCollegeId) + .FirstOrDefaultAsync(x => x.Id == id, token); + if (rule is null) return NotFound(); + if (await db.CourseSubstitutions.AnyAsync( + x => x.CourseEquivalencyRuleId == id, token)) + return Conflict(new ProblemDetails + { + Title = "该规则已有课程替代申请引用,不能删除;请改为停用以保留审批和毕业审核留痕。", + Status = StatusCodes.Status409Conflict + }); + db.CourseEquivalencyRules.Remove(rule); + await db.SaveChangesAsync(token); + return NoContent(); + } + + private Task HasSameScopeRuleAsync( + CourseEquivalencyRuleRequest request, + Guid? exceptId, + CancellationToken token) => db.CourseEquivalencyRules.AnyAsync(x => + x.OriginalCourseId == request.OriginalCourseId && + x.SubstituteCourseId == request.SubstituteCourseId && + x.MajorId == request.MajorId && + x.EffectiveGrade == request.EffectiveGrade && + (!exceptId.HasValue || x.Id != exceptId.Value), token); + + private static ConflictObjectResult ConflictProblem() => + new(new ProblemDetails + { + Title = "相同适用范围的等价课程规则已存在。", + Status = StatusCodes.Status409Conflict + }); + private IQueryable Scoped(Guid? collegeId) => collegeId.HasValue ? db.CourseEquivalencyRules.Where(x => x.OriginalCourse!.CollegeId == collegeId.Value) : db.CourseEquivalencyRules; diff --git a/web/src/views/CourseEquivalencyRulesView.vue b/web/src/views/CourseEquivalencyRulesView.vue index 38eee1e..ff20790 100644 --- a/web/src/views/CourseEquivalencyRulesView.vue +++ b/web/src/views/CourseEquivalencyRulesView.vue @@ -1,6 +1,6 @@ @@ -44,6 +59,6 @@ onMounted(load)
EQUIVALENCY RULES

等价课程规则

先维护可追溯的等价关系;学生只能按适用规则申请课程替代。

建立规则 - +