3
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
namespace Jiaowu.Api.Contracts;
|
||||
|
||||
public sealed record PagedResult<T>(
|
||||
IReadOnlyCollection<T> Items,
|
||||
int Total,
|
||||
int Page,
|
||||
int PageSize);
|
||||
@@ -0,0 +1,215 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Security.Claims;
|
||||
using Jiaowu.Api.Contracts;
|
||||
using Jiaowu.Api.Domain.Academic;
|
||||
using Jiaowu.Api.Domain.Identity;
|
||||
using Jiaowu.Api.Infrastructure.Persistence;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Jiaowu.Api.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
[Route("api/courses")]
|
||||
public sealed class CoursesController(AppDbContext db) : ControllerBase
|
||||
{
|
||||
private const string WriteRoles =
|
||||
SystemRoles.SuperAdmin + "," +
|
||||
SystemRoles.AcademicAdmin + "," +
|
||||
SystemRoles.CollegeAdmin;
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<PagedResult<object>>> Get(
|
||||
int page = 1,
|
||||
int pageSize = 20,
|
||||
string? keyword = null,
|
||||
Guid? collegeId = null,
|
||||
CourseNature? nature = null,
|
||||
bool? isEnabled = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
page = Math.Max(1, page);
|
||||
pageSize = Math.Clamp(pageSize, 10, 100);
|
||||
var source = db.Courses.AsNoTracking().AsQueryable();
|
||||
var scopedCollegeId = GetScopedCollegeId();
|
||||
if (scopedCollegeId.HasValue)
|
||||
source = source.Where(x => x.CollegeId == scopedCollegeId.Value);
|
||||
if (collegeId.HasValue)
|
||||
source = source.Where(x => x.CollegeId == collegeId.Value);
|
||||
if (nature.HasValue)
|
||||
source = source.Where(x => x.Nature == nature.Value);
|
||||
if (isEnabled.HasValue)
|
||||
source = source.Where(x => x.IsEnabled == isEnabled.Value);
|
||||
if (!string.IsNullOrWhiteSpace(keyword))
|
||||
{
|
||||
keyword = keyword.Trim();
|
||||
source = source.Where(x =>
|
||||
x.Code.Contains(keyword) ||
|
||||
x.Name.Contains(keyword) ||
|
||||
(x.EnglishName != null && x.EnglishName.Contains(keyword)));
|
||||
}
|
||||
|
||||
var total = await source.CountAsync(cancellationToken);
|
||||
var items = await source
|
||||
.OrderBy(x => x.SortOrder)
|
||||
.ThenBy(x => x.Code)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.Select(x => new
|
||||
{
|
||||
x.Id,
|
||||
x.Code,
|
||||
x.Name,
|
||||
x.EnglishName,
|
||||
x.CollegeId,
|
||||
CollegeName = x.College!.Name,
|
||||
x.Credits,
|
||||
x.TotalHours,
|
||||
x.LectureHours,
|
||||
x.PracticeHours,
|
||||
x.Nature,
|
||||
x.AssessmentMethod,
|
||||
x.Description,
|
||||
x.IsEnabled,
|
||||
x.SortOrder,
|
||||
x.CreatedAt
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return Ok(new PagedResult<object>(items, total, page, pageSize));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Authorize(Roles = WriteRoles)]
|
||||
public async Task<ActionResult> Create(
|
||||
CourseRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var validation = await ValidateAsync(request, cancellationToken);
|
||||
if (validation is not null) return validation;
|
||||
|
||||
var entity = new Course
|
||||
{
|
||||
Code = request.Code.Trim(),
|
||||
Name = request.Name.Trim(),
|
||||
EnglishName = Normalize(request.EnglishName),
|
||||
CollegeId = request.CollegeId,
|
||||
Credits = request.Credits,
|
||||
TotalHours = request.TotalHours,
|
||||
LectureHours = request.LectureHours,
|
||||
PracticeHours = request.PracticeHours,
|
||||
Nature = request.Nature,
|
||||
AssessmentMethod = request.AssessmentMethod,
|
||||
Description = Normalize(request.Description),
|
||||
IsEnabled = request.IsEnabled,
|
||||
SortOrder = request.SortOrder
|
||||
};
|
||||
db.Courses.Add(entity);
|
||||
return await SaveAsync(entity.Id, true, cancellationToken);
|
||||
}
|
||||
|
||||
[HttpPut("{id:guid}")]
|
||||
[Authorize(Roles = WriteRoles)]
|
||||
public async Task<ActionResult> Update(
|
||||
Guid id,
|
||||
CourseRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var entity = await db.Courses.FindAsync([id], cancellationToken);
|
||||
if (entity is null) return NotFound();
|
||||
if (!CanAccessCollege(entity.CollegeId)) return Forbid();
|
||||
var validation = await ValidateAsync(request, cancellationToken);
|
||||
if (validation is not null) return validation;
|
||||
|
||||
entity.Code = request.Code.Trim();
|
||||
entity.Name = request.Name.Trim();
|
||||
entity.EnglishName = Normalize(request.EnglishName);
|
||||
entity.CollegeId = request.CollegeId;
|
||||
entity.Credits = request.Credits;
|
||||
entity.TotalHours = request.TotalHours;
|
||||
entity.LectureHours = request.LectureHours;
|
||||
entity.PracticeHours = request.PracticeHours;
|
||||
entity.Nature = request.Nature;
|
||||
entity.AssessmentMethod = request.AssessmentMethod;
|
||||
entity.Description = Normalize(request.Description);
|
||||
entity.IsEnabled = request.IsEnabled;
|
||||
entity.SortOrder = request.SortOrder;
|
||||
return await SaveAsync(entity.Id, false, cancellationToken);
|
||||
}
|
||||
|
||||
[HttpDelete("{id:guid}")]
|
||||
[Authorize(Roles = WriteRoles)]
|
||||
public async Task<ActionResult> Delete(Guid id, CancellationToken cancellationToken)
|
||||
{
|
||||
var entity = await db.Courses.FindAsync([id], cancellationToken);
|
||||
if (entity is null) return NotFound();
|
||||
if (!CanAccessCollege(entity.CollegeId)) return Forbid();
|
||||
db.Courses.Remove(entity);
|
||||
return await SaveAsync(id, false, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task<ActionResult?> ValidateAsync(
|
||||
CourseRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (!CanAccessCollege(request.CollegeId)) return Forbid();
|
||||
if (!await db.Colleges.AnyAsync(x => x.Id == request.CollegeId, cancellationToken))
|
||||
return ValidationProblem("所选学院不存在。");
|
||||
if (request.LectureHours + request.PracticeHours > request.TotalHours)
|
||||
return ValidationProblem("讲授学时与实践学时之和不能超过总学时。");
|
||||
return null;
|
||||
}
|
||||
|
||||
private Guid? GetScopedCollegeId()
|
||||
{
|
||||
if (!User.IsInRole(SystemRoles.CollegeAdmin))
|
||||
return null;
|
||||
return Guid.TryParse(User.FindFirstValue("college_id"), out var collegeId)
|
||||
? collegeId
|
||||
: Guid.Empty;
|
||||
}
|
||||
|
||||
private bool CanAccessCollege(Guid collegeId) =>
|
||||
!GetScopedCollegeId().HasValue || GetScopedCollegeId() == collegeId;
|
||||
|
||||
private async Task<ActionResult> SaveAsync(
|
||||
Guid id,
|
||||
bool created,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
return created ? Created(string.Empty, new { id }) : NoContent();
|
||||
}
|
||||
catch (DbUpdateException)
|
||||
{
|
||||
return Conflict(new ProblemDetails
|
||||
{
|
||||
Title = "无法完成操作",
|
||||
Detail = "课程编码已存在,或课程已被其他教学业务引用。",
|
||||
Status = StatusCodes.Status409Conflict
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static string? Normalize(string? value) =>
|
||||
string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
||||
}
|
||||
|
||||
public sealed record CourseRequest(
|
||||
[Required, MaxLength(40)] string Code,
|
||||
[Required, MaxLength(100)] string Name,
|
||||
[MaxLength(150)] string? EnglishName,
|
||||
Guid CollegeId,
|
||||
[Range(typeof(decimal), "0.1", "99")] decimal Credits,
|
||||
[Range(1, 1000)] int TotalHours,
|
||||
[Range(0, 1000)] int LectureHours,
|
||||
[Range(0, 1000)] int PracticeHours,
|
||||
CourseNature Nature,
|
||||
AssessmentMethod AssessmentMethod,
|
||||
[MaxLength(1000)] string? Description,
|
||||
bool IsEnabled,
|
||||
int SortOrder);
|
||||
@@ -29,6 +29,9 @@ public sealed class DashboardController(AppDbContext db) : ControllerBase
|
||||
Majors = await db.Majors.CountAsync(cancellationToken),
|
||||
Classes = await db.AdministrativeClasses.CountAsync(cancellationToken),
|
||||
Classrooms = await db.Classrooms.CountAsync(cancellationToken),
|
||||
Teachers = await db.Teachers.CountAsync(cancellationToken),
|
||||
Students = await db.Students.CountAsync(cancellationToken),
|
||||
Courses = await db.Courses.CountAsync(cancellationToken),
|
||||
Users = await db.Users.CountAsync(cancellationToken)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,410 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Security.Claims;
|
||||
using Jiaowu.Api.Contracts;
|
||||
using Jiaowu.Api.Domain.Academic;
|
||||
using Jiaowu.Api.Domain.Identity;
|
||||
using Jiaowu.Api.Infrastructure.Persistence;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Jiaowu.Api.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Authorize(Roles = ReadRoles)]
|
||||
[Route("api/personnel")]
|
||||
public sealed class PersonnelController(AppDbContext db) : ControllerBase
|
||||
{
|
||||
private const string ReadRoles =
|
||||
SystemRoles.SuperAdmin + "," +
|
||||
SystemRoles.AcademicAdmin + "," +
|
||||
SystemRoles.CollegeAdmin + "," +
|
||||
SystemRoles.Counselor;
|
||||
|
||||
private const string WriteRoles =
|
||||
SystemRoles.SuperAdmin + "," +
|
||||
SystemRoles.AcademicAdmin + "," +
|
||||
SystemRoles.CollegeAdmin;
|
||||
|
||||
[HttpGet("teachers")]
|
||||
public async Task<ActionResult<PagedResult<object>>> GetTeachers(
|
||||
[FromQuery] PersonnelQuery query,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var page = NormalizePage(query.Page);
|
||||
var pageSize = NormalizePageSize(query.PageSize);
|
||||
var source = db.Teachers.AsNoTracking().AsQueryable();
|
||||
var scopedCollegeId = GetScopedCollegeId();
|
||||
if (scopedCollegeId.HasValue)
|
||||
source = source.Where(x => x.CollegeId == scopedCollegeId.Value);
|
||||
if (query.CollegeId.HasValue)
|
||||
source = source.Where(x => x.CollegeId == query.CollegeId);
|
||||
if (!string.IsNullOrWhiteSpace(query.Keyword))
|
||||
{
|
||||
var keyword = query.Keyword.Trim();
|
||||
source = source.Where(x =>
|
||||
x.TeacherNumber.Contains(keyword) ||
|
||||
x.Name.Contains(keyword) ||
|
||||
(x.Title != null && x.Title.Contains(keyword)));
|
||||
}
|
||||
if (query.TeacherStatus.HasValue)
|
||||
source = source.Where(x => x.Status == query.TeacherStatus);
|
||||
|
||||
var total = await source.CountAsync(cancellationToken);
|
||||
var items = await source
|
||||
.OrderBy(x => x.TeacherNumber)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.Select(x => new
|
||||
{
|
||||
x.Id,
|
||||
x.TeacherNumber,
|
||||
x.Name,
|
||||
x.Gender,
|
||||
x.CollegeId,
|
||||
CollegeName = x.College!.Name,
|
||||
x.Title,
|
||||
x.Status,
|
||||
x.HireDate,
|
||||
x.IsExternal,
|
||||
x.Phone,
|
||||
x.Email,
|
||||
x.Notes,
|
||||
x.UserId,
|
||||
x.CreatedAt
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return Ok(new PagedResult<object>(items, total, page, pageSize));
|
||||
}
|
||||
|
||||
[HttpPost("teachers")]
|
||||
[Authorize(Roles = WriteRoles)]
|
||||
public async Task<ActionResult> CreateTeacher(
|
||||
TeacherRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var validation = await ValidateCollegeAsync(request.CollegeId, cancellationToken);
|
||||
if (validation is not null) return validation;
|
||||
|
||||
var entity = new Teacher
|
||||
{
|
||||
TeacherNumber = request.TeacherNumber.Trim(),
|
||||
Name = request.Name.Trim(),
|
||||
Gender = request.Gender,
|
||||
CollegeId = request.CollegeId,
|
||||
Title = Normalize(request.Title),
|
||||
Status = request.Status,
|
||||
HireDate = request.HireDate,
|
||||
IsExternal = request.IsExternal,
|
||||
Phone = Normalize(request.Phone),
|
||||
Email = Normalize(request.Email),
|
||||
Notes = Normalize(request.Notes)
|
||||
};
|
||||
db.Teachers.Add(entity);
|
||||
return await SaveCreatedAsync(entity.Id, cancellationToken);
|
||||
}
|
||||
|
||||
[HttpPut("teachers/{id:guid}")]
|
||||
[Authorize(Roles = WriteRoles)]
|
||||
public async Task<ActionResult> UpdateTeacher(
|
||||
Guid id,
|
||||
TeacherRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var entity = await db.Teachers.FindAsync([id], cancellationToken);
|
||||
if (entity is null) return NotFound();
|
||||
if (!CanAccessCollege(entity.CollegeId)) return Forbid();
|
||||
var validation = await ValidateCollegeAsync(request.CollegeId, cancellationToken);
|
||||
if (validation is not null) return validation;
|
||||
|
||||
entity.TeacherNumber = request.TeacherNumber.Trim();
|
||||
entity.Name = request.Name.Trim();
|
||||
entity.Gender = request.Gender;
|
||||
entity.CollegeId = request.CollegeId;
|
||||
entity.Title = Normalize(request.Title);
|
||||
entity.Status = request.Status;
|
||||
entity.HireDate = request.HireDate;
|
||||
entity.IsExternal = request.IsExternal;
|
||||
entity.Phone = Normalize(request.Phone);
|
||||
entity.Email = Normalize(request.Email);
|
||||
entity.Notes = Normalize(request.Notes);
|
||||
return await SaveNoContentAsync(cancellationToken);
|
||||
}
|
||||
|
||||
[HttpDelete("teachers/{id:guid}")]
|
||||
[Authorize(Roles = WriteRoles)]
|
||||
public async Task<IActionResult> DeleteTeacher(Guid id, CancellationToken cancellationToken)
|
||||
{
|
||||
var entity = await db.Teachers.FindAsync([id], cancellationToken);
|
||||
if (entity is null) return NotFound();
|
||||
if (!CanAccessCollege(entity.CollegeId)) return Forbid();
|
||||
if (entity.UserId.HasValue)
|
||||
{
|
||||
return ConflictProblem("教师已关联登录账号,请先解除账号关联后再删除。");
|
||||
}
|
||||
|
||||
db.Teachers.Remove(entity);
|
||||
return await SaveNoContentAsync(cancellationToken);
|
||||
}
|
||||
|
||||
[HttpGet("students")]
|
||||
public async Task<ActionResult<PagedResult<object>>> GetStudents(
|
||||
[FromQuery] PersonnelQuery query,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var page = NormalizePage(query.Page);
|
||||
var pageSize = NormalizePageSize(query.PageSize);
|
||||
var source = db.Students.AsNoTracking().AsQueryable();
|
||||
var scopedCollegeId = GetScopedCollegeId();
|
||||
if (scopedCollegeId.HasValue)
|
||||
{
|
||||
source = source.Where(x =>
|
||||
x.AdministrativeClass!.Major!.CollegeId == scopedCollegeId.Value);
|
||||
}
|
||||
if (query.CollegeId.HasValue)
|
||||
{
|
||||
source = source.Where(x =>
|
||||
x.AdministrativeClass!.Major!.CollegeId == query.CollegeId.Value);
|
||||
}
|
||||
if (query.MajorId.HasValue)
|
||||
source = source.Where(x => x.AdministrativeClass!.MajorId == query.MajorId);
|
||||
if (query.ClassId.HasValue)
|
||||
source = source.Where(x => x.AdministrativeClassId == query.ClassId);
|
||||
if (query.EnrollmentYear.HasValue)
|
||||
source = source.Where(x => x.EnrollmentYear == query.EnrollmentYear);
|
||||
if (query.StudentStatus.HasValue)
|
||||
source = source.Where(x => x.Status == query.StudentStatus);
|
||||
if (!string.IsNullOrWhiteSpace(query.Keyword))
|
||||
{
|
||||
var keyword = query.Keyword.Trim();
|
||||
source = source.Where(x =>
|
||||
x.StudentNumber.Contains(keyword) || x.Name.Contains(keyword));
|
||||
}
|
||||
|
||||
var total = await source.CountAsync(cancellationToken);
|
||||
var items = await source
|
||||
.OrderBy(x => x.StudentNumber)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.Select(x => new
|
||||
{
|
||||
x.Id,
|
||||
x.StudentNumber,
|
||||
x.Name,
|
||||
x.Gender,
|
||||
x.AdministrativeClassId,
|
||||
ClassName = x.AdministrativeClass!.Name,
|
||||
MajorId = x.AdministrativeClass.MajorId,
|
||||
MajorName = x.AdministrativeClass.Major!.Name,
|
||||
CollegeId = x.AdministrativeClass.Major.CollegeId,
|
||||
CollegeName = x.AdministrativeClass.Major.College!.Name,
|
||||
x.EnrollmentYear,
|
||||
x.EnrollmentDate,
|
||||
x.Status,
|
||||
x.DateOfBirth,
|
||||
x.Phone,
|
||||
x.Email,
|
||||
x.Notes,
|
||||
x.UserId,
|
||||
x.CreatedAt
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return Ok(new PagedResult<object>(items, total, page, pageSize));
|
||||
}
|
||||
|
||||
[HttpPost("students")]
|
||||
[Authorize(Roles = WriteRoles)]
|
||||
public async Task<ActionResult> CreateStudent(
|
||||
StudentRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var targetClass = await db.AdministrativeClasses.AsNoTracking()
|
||||
.Where(x => x.Id == request.AdministrativeClassId)
|
||||
.Select(x => new { x.Id, CollegeId = x.Major!.CollegeId })
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (targetClass is null)
|
||||
return ValidationProblem("所选行政班不存在。");
|
||||
if (!CanAccessCollege(targetClass.CollegeId))
|
||||
return Forbid();
|
||||
|
||||
var entity = new Student
|
||||
{
|
||||
StudentNumber = request.StudentNumber.Trim(),
|
||||
Name = request.Name.Trim(),
|
||||
Gender = request.Gender,
|
||||
AdministrativeClassId = request.AdministrativeClassId,
|
||||
EnrollmentYear = request.EnrollmentYear,
|
||||
EnrollmentDate = request.EnrollmentDate,
|
||||
Status = request.Status,
|
||||
DateOfBirth = request.DateOfBirth,
|
||||
Phone = Normalize(request.Phone),
|
||||
Email = Normalize(request.Email),
|
||||
Notes = Normalize(request.Notes)
|
||||
};
|
||||
db.Students.Add(entity);
|
||||
return await SaveCreatedAsync(entity.Id, cancellationToken);
|
||||
}
|
||||
|
||||
[HttpPut("students/{id:guid}")]
|
||||
[Authorize(Roles = WriteRoles)]
|
||||
public async Task<ActionResult> UpdateStudent(
|
||||
Guid id,
|
||||
StudentRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var entity = await db.Students
|
||||
.Include(x => x.AdministrativeClass)
|
||||
.ThenInclude(x => x!.Major)
|
||||
.FirstOrDefaultAsync(x => x.Id == id, cancellationToken);
|
||||
if (entity is null) return NotFound();
|
||||
if (!CanAccessCollege(entity.AdministrativeClass!.Major!.CollegeId)) return Forbid();
|
||||
|
||||
var targetClass = await db.AdministrativeClasses.AsNoTracking()
|
||||
.Where(x => x.Id == request.AdministrativeClassId)
|
||||
.Select(x => new { x.Id, CollegeId = x.Major!.CollegeId })
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (targetClass is null)
|
||||
return ValidationProblem("所选行政班不存在。");
|
||||
if (!CanAccessCollege(targetClass.CollegeId))
|
||||
return Forbid();
|
||||
|
||||
entity.StudentNumber = request.StudentNumber.Trim();
|
||||
entity.Name = request.Name.Trim();
|
||||
entity.Gender = request.Gender;
|
||||
entity.AdministrativeClassId = request.AdministrativeClassId;
|
||||
entity.EnrollmentYear = request.EnrollmentYear;
|
||||
entity.EnrollmentDate = request.EnrollmentDate;
|
||||
entity.Status = request.Status;
|
||||
entity.DateOfBirth = request.DateOfBirth;
|
||||
entity.Phone = Normalize(request.Phone);
|
||||
entity.Email = Normalize(request.Email);
|
||||
entity.Notes = Normalize(request.Notes);
|
||||
return await SaveNoContentAsync(cancellationToken);
|
||||
}
|
||||
|
||||
[HttpDelete("students/{id:guid}")]
|
||||
[Authorize(Roles = WriteRoles)]
|
||||
public async Task<IActionResult> DeleteStudent(Guid id, CancellationToken cancellationToken)
|
||||
{
|
||||
var entity = await db.Students
|
||||
.Include(x => x.AdministrativeClass)
|
||||
.ThenInclude(x => x!.Major)
|
||||
.FirstOrDefaultAsync(x => x.Id == id, cancellationToken);
|
||||
if (entity is null) return NotFound();
|
||||
if (!CanAccessCollege(entity.AdministrativeClass!.Major!.CollegeId)) return Forbid();
|
||||
if (entity.UserId.HasValue)
|
||||
{
|
||||
return ConflictProblem("学生已关联登录账号,请先解除账号关联后再删除。");
|
||||
}
|
||||
|
||||
db.Students.Remove(entity);
|
||||
return await SaveNoContentAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private Guid? GetScopedCollegeId()
|
||||
{
|
||||
if (!User.IsInRole(SystemRoles.CollegeAdmin) &&
|
||||
!User.IsInRole(SystemRoles.Counselor))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Guid.TryParse(User.FindFirstValue("college_id"), out var collegeId)
|
||||
? collegeId
|
||||
: Guid.Empty;
|
||||
}
|
||||
|
||||
private bool CanAccessCollege(Guid collegeId) =>
|
||||
!GetScopedCollegeId().HasValue || GetScopedCollegeId() == collegeId;
|
||||
|
||||
private async Task<ActionResult?> ValidateCollegeAsync(
|
||||
Guid collegeId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (!CanAccessCollege(collegeId)) return Forbid();
|
||||
if (!await db.Colleges.AnyAsync(x => x.Id == collegeId, cancellationToken))
|
||||
return ValidationProblem("所选学院不存在。");
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<ActionResult> SaveCreatedAsync(
|
||||
Guid id,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
return Created(string.Empty, new { id });
|
||||
}
|
||||
catch (DbUpdateException)
|
||||
{
|
||||
return ConflictProblem("编号已存在,或关联数据无效。");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<ActionResult> SaveNoContentAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
return NoContent();
|
||||
}
|
||||
catch (DbUpdateException)
|
||||
{
|
||||
return ConflictProblem("编号已存在,或该档案已被其他业务引用。");
|
||||
}
|
||||
}
|
||||
|
||||
private ActionResult ConflictProblem(string detail) =>
|
||||
Conflict(new ProblemDetails
|
||||
{
|
||||
Title = "无法完成操作",
|
||||
Detail = detail,
|
||||
Status = StatusCodes.Status409Conflict
|
||||
});
|
||||
|
||||
private static string? Normalize(string? value) =>
|
||||
string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
||||
|
||||
private static int NormalizePage(int page) => Math.Max(1, page);
|
||||
private static int NormalizePageSize(int pageSize) => Math.Clamp(pageSize, 10, 100);
|
||||
}
|
||||
|
||||
public sealed record PersonnelQuery(
|
||||
int Page = 1,
|
||||
int PageSize = 20,
|
||||
string? Keyword = null,
|
||||
Guid? CollegeId = null,
|
||||
Guid? MajorId = null,
|
||||
Guid? ClassId = null,
|
||||
int? EnrollmentYear = null,
|
||||
TeacherStatus? TeacherStatus = null,
|
||||
StudentStatus? StudentStatus = null);
|
||||
|
||||
public sealed record TeacherRequest(
|
||||
[Required, MaxLength(30)] string TeacherNumber,
|
||||
[Required, MaxLength(50)] string Name,
|
||||
Gender Gender,
|
||||
Guid CollegeId,
|
||||
[MaxLength(30)] string? Title,
|
||||
TeacherStatus Status,
|
||||
DateOnly? HireDate,
|
||||
bool IsExternal,
|
||||
[MaxLength(30)] string? Phone,
|
||||
[EmailAddress, MaxLength(100)] string? Email,
|
||||
[MaxLength(500)] string? Notes);
|
||||
|
||||
public sealed record StudentRequest(
|
||||
[Required, MaxLength(30)] string StudentNumber,
|
||||
[Required, MaxLength(50)] string Name,
|
||||
Gender Gender,
|
||||
Guid AdministrativeClassId,
|
||||
[Range(2000, 2200)] int EnrollmentYear,
|
||||
DateOnly EnrollmentDate,
|
||||
StudentStatus Status,
|
||||
DateOnly? DateOfBirth,
|
||||
[MaxLength(30)] string? Phone,
|
||||
[EmailAddress, MaxLength(100)] string? Email,
|
||||
[MaxLength(500)] string? Notes);
|
||||
@@ -0,0 +1,89 @@
|
||||
using Jiaowu.Api.Domain.Common;
|
||||
|
||||
namespace Jiaowu.Api.Domain.Academic;
|
||||
|
||||
public sealed class Teacher : EntityBase
|
||||
{
|
||||
public required string TeacherNumber { get; set; }
|
||||
public required string Name { get; set; }
|
||||
public Gender Gender { get; set; }
|
||||
public Guid CollegeId { get; set; }
|
||||
public College? College { get; set; }
|
||||
public string? Title { get; set; }
|
||||
public TeacherStatus Status { get; set; } = TeacherStatus.Active;
|
||||
public DateOnly? HireDate { get; set; }
|
||||
public bool IsExternal { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
}
|
||||
|
||||
public sealed class Student : EntityBase
|
||||
{
|
||||
public required string StudentNumber { get; set; }
|
||||
public required string Name { get; set; }
|
||||
public Gender Gender { get; set; }
|
||||
public Guid AdministrativeClassId { get; set; }
|
||||
public AdministrativeClass? AdministrativeClass { get; set; }
|
||||
public int EnrollmentYear { get; set; }
|
||||
public DateOnly EnrollmentDate { get; set; }
|
||||
public StudentStatus Status { get; set; } = StudentStatus.Active;
|
||||
public DateOnly? DateOfBirth { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
}
|
||||
|
||||
public sealed class Course : CatalogEntity
|
||||
{
|
||||
public Guid CollegeId { get; set; }
|
||||
public College? College { get; set; }
|
||||
public string? EnglishName { get; set; }
|
||||
public decimal Credits { get; set; }
|
||||
public int TotalHours { get; set; }
|
||||
public int LectureHours { get; set; }
|
||||
public int PracticeHours { get; set; }
|
||||
public CourseNature Nature { get; set; }
|
||||
public AssessmentMethod AssessmentMethod { get; set; }
|
||||
public string? Description { get; set; }
|
||||
}
|
||||
|
||||
public enum Gender
|
||||
{
|
||||
Unknown = 0,
|
||||
Male = 1,
|
||||
Female = 2
|
||||
}
|
||||
|
||||
public enum TeacherStatus
|
||||
{
|
||||
Active = 1,
|
||||
OnLeave = 2,
|
||||
Retired = 3,
|
||||
Departed = 4
|
||||
}
|
||||
|
||||
public enum StudentStatus
|
||||
{
|
||||
Active = 1,
|
||||
Suspended = 2,
|
||||
Graduated = 3,
|
||||
Withdrawn = 4
|
||||
}
|
||||
|
||||
public enum CourseNature
|
||||
{
|
||||
GeneralRequired = 1,
|
||||
MajorRequired = 2,
|
||||
MajorElective = 3,
|
||||
GeneralElective = 4,
|
||||
Practice = 5
|
||||
}
|
||||
|
||||
public enum AssessmentMethod
|
||||
{
|
||||
Examination = 1,
|
||||
Assessment = 2
|
||||
}
|
||||
@@ -17,6 +17,9 @@ public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
|
||||
public DbSet<Building> Buildings => Set<Building>();
|
||||
public DbSet<Classroom> Classrooms => Set<Classroom>();
|
||||
public DbSet<AcademicTerm> AcademicTerms => Set<AcademicTerm>();
|
||||
public DbSet<Teacher> Teachers => Set<Teacher>();
|
||||
public DbSet<Student> Students => Set<Student>();
|
||||
public DbSet<Course> Courses => Set<Course>();
|
||||
public DbSet<AuditLog> AuditLogs => Set<AuditLog>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
@@ -42,6 +45,7 @@ public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
|
||||
ConfigureCatalog<Building>(builder);
|
||||
ConfigureCatalog<Classroom>(builder);
|
||||
ConfigureCatalog<AcademicTerm>(builder);
|
||||
ConfigureCatalog<Course>(builder);
|
||||
|
||||
builder.Entity<College>()
|
||||
.HasOne(x => x.Campus)
|
||||
@@ -76,6 +80,58 @@ public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
|
||||
builder.Entity<AcademicTerm>()
|
||||
.HasIndex(x => x.IsCurrent);
|
||||
|
||||
builder.Entity<Teacher>(entity =>
|
||||
{
|
||||
entity.Property(x => x.TeacherNumber).HasMaxLength(30);
|
||||
entity.Property(x => x.Name).HasMaxLength(50);
|
||||
entity.Property(x => x.Title).HasMaxLength(30);
|
||||
entity.Property(x => x.Phone).HasMaxLength(30);
|
||||
entity.Property(x => x.Email).HasMaxLength(100);
|
||||
entity.Property(x => x.Notes).HasMaxLength(500);
|
||||
entity.HasIndex(x => x.TeacherNumber).IsUnique();
|
||||
entity.HasIndex(x => new { x.CollegeId, x.Status });
|
||||
entity.HasOne(x => x.College)
|
||||
.WithMany()
|
||||
.HasForeignKey(x => x.CollegeId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
entity.HasOne<ApplicationUser>()
|
||||
.WithMany()
|
||||
.HasForeignKey(x => x.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
});
|
||||
|
||||
builder.Entity<Student>(entity =>
|
||||
{
|
||||
entity.Property(x => x.StudentNumber).HasMaxLength(30);
|
||||
entity.Property(x => x.Name).HasMaxLength(50);
|
||||
entity.Property(x => x.Phone).HasMaxLength(30);
|
||||
entity.Property(x => x.Email).HasMaxLength(100);
|
||||
entity.Property(x => x.Notes).HasMaxLength(500);
|
||||
entity.HasIndex(x => x.StudentNumber).IsUnique();
|
||||
entity.HasIndex(x => new { x.AdministrativeClassId, x.Status });
|
||||
entity.HasIndex(x => x.EnrollmentYear);
|
||||
entity.HasOne(x => x.AdministrativeClass)
|
||||
.WithMany()
|
||||
.HasForeignKey(x => x.AdministrativeClassId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
entity.HasOne<ApplicationUser>()
|
||||
.WithMany()
|
||||
.HasForeignKey(x => x.UserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
});
|
||||
|
||||
builder.Entity<Course>(entity =>
|
||||
{
|
||||
entity.Property(x => x.EnglishName).HasMaxLength(150);
|
||||
entity.Property(x => x.Credits).HasPrecision(5, 2);
|
||||
entity.Property(x => x.Description).HasMaxLength(1000);
|
||||
entity.HasIndex(x => new { x.CollegeId, x.Nature });
|
||||
entity.HasOne(x => x.College)
|
||||
.WithMany()
|
||||
.HasForeignKey(x => x.CollegeId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
});
|
||||
|
||||
builder.Entity<AuditLog>(entity =>
|
||||
{
|
||||
entity.Property(x => x.Method).HasMaxLength(10);
|
||||
|
||||
@@ -9,6 +9,7 @@ public sealed class DatabaseInitializer(
|
||||
AppDbContext db,
|
||||
RoleManager<ApplicationRole> roleManager,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
DevelopmentSqliteMigrator sqliteMigrator,
|
||||
IConfiguration configuration,
|
||||
IHostEnvironment environment,
|
||||
ILogger<DatabaseInitializer> logger)
|
||||
@@ -18,6 +19,7 @@ public sealed class DatabaseInitializer(
|
||||
if (environment.IsDevelopment())
|
||||
{
|
||||
await db.Database.EnsureCreatedAsync();
|
||||
await sqliteMigrator.MigrateAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -108,71 +110,182 @@ public sealed class DatabaseInitializer(
|
||||
|
||||
private async Task SeedDevelopmentDataAsync()
|
||||
{
|
||||
if (await db.Campuses.AnyAsync())
|
||||
if (!await db.Campuses.AnyAsync())
|
||||
{
|
||||
return;
|
||||
var campus = new Campus
|
||||
{
|
||||
Code = "MAIN",
|
||||
Name = "主校区",
|
||||
Address = "大学路 1 号"
|
||||
};
|
||||
var college = new College
|
||||
{
|
||||
Code = "CS",
|
||||
Name = "计算机学院",
|
||||
ShortName = "计算机学院",
|
||||
CampusId = campus.Id
|
||||
};
|
||||
var major = new Major
|
||||
{
|
||||
Code = "080901",
|
||||
Name = "计算机科学与技术",
|
||||
CollegeId = college.Id,
|
||||
DegreeType = "工学学士",
|
||||
SchoolingYears = 4
|
||||
};
|
||||
var building = new Building
|
||||
{
|
||||
Code = "J1",
|
||||
Name = "第一教学楼",
|
||||
CampusId = campus.Id
|
||||
};
|
||||
|
||||
db.AddRange(
|
||||
campus,
|
||||
college,
|
||||
major,
|
||||
new AdministrativeClass
|
||||
{
|
||||
Code = "CS2026-01",
|
||||
Name = "计科 2026-1 班",
|
||||
MajorId = major.Id,
|
||||
Grade = 2026,
|
||||
CounselorName = "陈老师"
|
||||
},
|
||||
building,
|
||||
new Classroom
|
||||
{
|
||||
Code = "J1-201",
|
||||
Name = "J1-201",
|
||||
BuildingId = building.Id,
|
||||
Capacity = 60,
|
||||
RoomType = "多媒体教室",
|
||||
Equipment = "投影、扩声、录播"
|
||||
},
|
||||
new AcademicTerm
|
||||
{
|
||||
Code = "2026-2027-1",
|
||||
Name = "2026—2027 学年第一学期",
|
||||
AcademicYear = "2026-2027",
|
||||
Season = TermSeason.Autumn,
|
||||
StartDate = new DateOnly(2026, 9, 7),
|
||||
EndDate = new DateOnly(2027, 1, 17),
|
||||
IsCurrent = true
|
||||
});
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
var campus = new Campus
|
||||
{
|
||||
Code = "MAIN",
|
||||
Name = "主校区",
|
||||
Address = "大学路 1 号"
|
||||
};
|
||||
var college = new College
|
||||
{
|
||||
Code = "CS",
|
||||
Name = "计算机学院",
|
||||
ShortName = "计算机学院",
|
||||
CampusId = campus.Id
|
||||
};
|
||||
var major = new Major
|
||||
{
|
||||
Code = "080901",
|
||||
Name = "计算机科学与技术",
|
||||
CollegeId = college.Id,
|
||||
DegreeType = "工学学士",
|
||||
SchoolingYears = 4
|
||||
};
|
||||
var building = new Building
|
||||
{
|
||||
Code = "J1",
|
||||
Name = "第一教学楼",
|
||||
CampusId = campus.Id
|
||||
};
|
||||
var computerCollege = await db.Colleges.SingleAsync(x => x.Code == "CS");
|
||||
var computerClass = await db.AdministrativeClasses
|
||||
.SingleAsync(x => x.Code == "CS2026-01");
|
||||
|
||||
db.AddRange(
|
||||
campus,
|
||||
college,
|
||||
major,
|
||||
new AdministrativeClass
|
||||
{
|
||||
Code = "CS2026-01",
|
||||
Name = "计科 2026-1 班",
|
||||
MajorId = major.Id,
|
||||
Grade = 2026,
|
||||
CounselorName = "陈老师"
|
||||
},
|
||||
building,
|
||||
new Classroom
|
||||
{
|
||||
Code = "J1-201",
|
||||
Name = "J1-201",
|
||||
BuildingId = building.Id,
|
||||
Capacity = 60,
|
||||
RoomType = "多媒体教室",
|
||||
Equipment = "投影、扩声、录播"
|
||||
},
|
||||
new AcademicTerm
|
||||
{
|
||||
Code = "2026-2027-1",
|
||||
Name = "2026—2027 学年第一学期",
|
||||
AcademicYear = "2026-2027",
|
||||
Season = TermSeason.Autumn,
|
||||
StartDate = new DateOnly(2026, 9, 7),
|
||||
EndDate = new DateOnly(2027, 1, 17),
|
||||
IsCurrent = true
|
||||
});
|
||||
if (!await db.Teachers.AnyAsync())
|
||||
{
|
||||
db.Teachers.AddRange(
|
||||
new Teacher
|
||||
{
|
||||
TeacherNumber = "T2026001",
|
||||
Name = "陈明远",
|
||||
Gender = Gender.Male,
|
||||
CollegeId = computerCollege.Id,
|
||||
Title = "副教授",
|
||||
Status = TeacherStatus.Active,
|
||||
HireDate = new DateOnly(2018, 7, 1),
|
||||
Email = "chenmy@example.edu.cn"
|
||||
},
|
||||
new Teacher
|
||||
{
|
||||
TeacherNumber = "T2026002",
|
||||
Name = "林书雅",
|
||||
Gender = Gender.Female,
|
||||
CollegeId = computerCollege.Id,
|
||||
Title = "讲师",
|
||||
Status = TeacherStatus.Active,
|
||||
HireDate = new DateOnly(2022, 9, 1),
|
||||
Email = "linsy@example.edu.cn"
|
||||
});
|
||||
}
|
||||
|
||||
if (!await db.Students.AnyAsync())
|
||||
{
|
||||
db.Students.AddRange(
|
||||
new Student
|
||||
{
|
||||
StudentNumber = "202601001",
|
||||
Name = "周启航",
|
||||
Gender = Gender.Male,
|
||||
AdministrativeClassId = computerClass.Id,
|
||||
EnrollmentYear = 2026,
|
||||
EnrollmentDate = new DateOnly(2026, 9, 7),
|
||||
Status = StudentStatus.Active
|
||||
},
|
||||
new Student
|
||||
{
|
||||
StudentNumber = "202601002",
|
||||
Name = "许知夏",
|
||||
Gender = Gender.Female,
|
||||
AdministrativeClassId = computerClass.Id,
|
||||
EnrollmentYear = 2026,
|
||||
EnrollmentDate = new DateOnly(2026, 9, 7),
|
||||
Status = StudentStatus.Active
|
||||
},
|
||||
new Student
|
||||
{
|
||||
StudentNumber = "202601003",
|
||||
Name = "方嘉树",
|
||||
Gender = Gender.Male,
|
||||
AdministrativeClassId = computerClass.Id,
|
||||
EnrollmentYear = 2026,
|
||||
EnrollmentDate = new DateOnly(2026, 9, 7),
|
||||
Status = StudentStatus.Active
|
||||
});
|
||||
}
|
||||
|
||||
if (!await db.Courses.AnyAsync())
|
||||
{
|
||||
db.Courses.AddRange(
|
||||
new Course
|
||||
{
|
||||
Code = "CS101",
|
||||
Name = "程序设计基础",
|
||||
EnglishName = "Fundamentals of Programming",
|
||||
CollegeId = computerCollege.Id,
|
||||
Credits = 4,
|
||||
TotalHours = 64,
|
||||
LectureHours = 40,
|
||||
PracticeHours = 24,
|
||||
Nature = CourseNature.MajorRequired,
|
||||
AssessmentMethod = AssessmentMethod.Examination,
|
||||
Description = "面向一年级学生的程序设计入门课程。"
|
||||
},
|
||||
new Course
|
||||
{
|
||||
Code = "CS201",
|
||||
Name = "数据结构",
|
||||
EnglishName = "Data Structures",
|
||||
CollegeId = computerCollege.Id,
|
||||
Credits = 3.5m,
|
||||
TotalHours = 56,
|
||||
LectureHours = 40,
|
||||
PracticeHours = 16,
|
||||
Nature = CourseNature.MajorRequired,
|
||||
AssessmentMethod = AssessmentMethod.Examination
|
||||
},
|
||||
new Course
|
||||
{
|
||||
Code = "CS305",
|
||||
Name = "软件工程实践",
|
||||
EnglishName = "Software Engineering Practice",
|
||||
CollegeId = computerCollege.Id,
|
||||
Credits = 2,
|
||||
TotalHours = 48,
|
||||
LectureHours = 8,
|
||||
PracticeHours = 40,
|
||||
Nature = CourseNature.Practice,
|
||||
AssessmentMethod = AssessmentMethod.Assessment
|
||||
});
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Jiaowu.Api.Infrastructure.Persistence;
|
||||
|
||||
public sealed class DevelopmentSqliteMigrator(
|
||||
AppDbContext db,
|
||||
ILogger<DevelopmentSqliteMigrator> logger)
|
||||
{
|
||||
private const string PeopleAndCoursesMigration = "20260724_01_people_courses";
|
||||
|
||||
public async Task MigrateAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!db.Database.IsSqlite())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await db.Database.ExecuteSqlRawAsync(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS "__DevelopmentSchemaHistory" (
|
||||
"MigrationId" TEXT NOT NULL CONSTRAINT "PK___DevelopmentSchemaHistory" PRIMARY KEY,
|
||||
"AppliedAt" TEXT NOT NULL
|
||||
);
|
||||
""",
|
||||
cancellationToken);
|
||||
|
||||
var applied = await db.Database
|
||||
.SqlQuery<string>(
|
||||
$"""
|
||||
SELECT "MigrationId" AS "Value"
|
||||
FROM "__DevelopmentSchemaHistory"
|
||||
WHERE "MigrationId" = {PeopleAndCoursesMigration}
|
||||
""")
|
||||
.AnyAsync(cancellationToken);
|
||||
if (applied)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await using var transaction = await db.Database.BeginTransactionAsync(cancellationToken);
|
||||
foreach (var statement in PeopleAndCoursesStatements)
|
||||
{
|
||||
await db.Database.ExecuteSqlRawAsync(statement, cancellationToken);
|
||||
}
|
||||
|
||||
await db.Database.ExecuteSqlInterpolatedAsync(
|
||||
$"""
|
||||
INSERT INTO "__DevelopmentSchemaHistory" ("MigrationId", "AppliedAt")
|
||||
VALUES ({PeopleAndCoursesMigration}, {DateTime.UtcNow});
|
||||
""",
|
||||
cancellationToken);
|
||||
await transaction.CommitAsync(cancellationToken);
|
||||
logger.LogInformation(
|
||||
"Applied SQLite development schema migration {MigrationId}.",
|
||||
PeopleAndCoursesMigration);
|
||||
}
|
||||
|
||||
private static readonly string[] PeopleAndCoursesStatements =
|
||||
[
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS "Teachers" (
|
||||
"Id" TEXT NOT NULL CONSTRAINT "PK_Teachers" PRIMARY KEY,
|
||||
"TeacherNumber" TEXT NOT NULL,
|
||||
"Name" TEXT NOT NULL,
|
||||
"Gender" INTEGER NOT NULL,
|
||||
"CollegeId" TEXT NOT NULL,
|
||||
"Title" TEXT NULL,
|
||||
"Status" INTEGER NOT NULL,
|
||||
"HireDate" TEXT NULL,
|
||||
"IsExternal" INTEGER NOT NULL,
|
||||
"Phone" TEXT NULL,
|
||||
"Email" TEXT NULL,
|
||||
"Notes" TEXT NULL,
|
||||
"UserId" TEXT NULL,
|
||||
"CreatedAt" TEXT NOT NULL,
|
||||
"UpdatedAt" TEXT NOT NULL,
|
||||
CONSTRAINT "FK_Teachers_Colleges_CollegeId"
|
||||
FOREIGN KEY ("CollegeId") REFERENCES "Colleges" ("Id") ON DELETE RESTRICT,
|
||||
CONSTRAINT "FK_Teachers_AspNetUsers_UserId"
|
||||
FOREIGN KEY ("UserId") REFERENCES "AspNetUsers" ("Id") ON DELETE SET NULL
|
||||
);
|
||||
""",
|
||||
"""
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "IX_Teachers_TeacherNumber"
|
||||
ON "Teachers" ("TeacherNumber");
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS "IX_Teachers_CollegeId_Status"
|
||||
ON "Teachers" ("CollegeId", "Status");
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS "IX_Teachers_UserId"
|
||||
ON "Teachers" ("UserId");
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS "Students" (
|
||||
"Id" TEXT NOT NULL CONSTRAINT "PK_Students" PRIMARY KEY,
|
||||
"StudentNumber" TEXT NOT NULL,
|
||||
"Name" TEXT NOT NULL,
|
||||
"Gender" INTEGER NOT NULL,
|
||||
"AdministrativeClassId" TEXT NOT NULL,
|
||||
"EnrollmentYear" INTEGER NOT NULL,
|
||||
"EnrollmentDate" TEXT NOT NULL,
|
||||
"Status" INTEGER NOT NULL,
|
||||
"DateOfBirth" TEXT NULL,
|
||||
"Phone" TEXT NULL,
|
||||
"Email" TEXT NULL,
|
||||
"Notes" TEXT NULL,
|
||||
"UserId" TEXT NULL,
|
||||
"CreatedAt" TEXT NOT NULL,
|
||||
"UpdatedAt" TEXT NOT NULL,
|
||||
CONSTRAINT "FK_Students_AdministrativeClasses_AdministrativeClassId"
|
||||
FOREIGN KEY ("AdministrativeClassId") REFERENCES "AdministrativeClasses" ("Id")
|
||||
ON DELETE RESTRICT,
|
||||
CONSTRAINT "FK_Students_AspNetUsers_UserId"
|
||||
FOREIGN KEY ("UserId") REFERENCES "AspNetUsers" ("Id") ON DELETE SET NULL
|
||||
);
|
||||
""",
|
||||
"""
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "IX_Students_StudentNumber"
|
||||
ON "Students" ("StudentNumber");
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS "IX_Students_AdministrativeClassId_Status"
|
||||
ON "Students" ("AdministrativeClassId", "Status");
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS "IX_Students_EnrollmentYear"
|
||||
ON "Students" ("EnrollmentYear");
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS "IX_Students_UserId"
|
||||
ON "Students" ("UserId");
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS "Courses" (
|
||||
"Id" TEXT NOT NULL CONSTRAINT "PK_Courses" PRIMARY KEY,
|
||||
"CollegeId" TEXT NOT NULL,
|
||||
"EnglishName" TEXT NULL,
|
||||
"Credits" TEXT NOT NULL,
|
||||
"TotalHours" INTEGER NOT NULL,
|
||||
"LectureHours" INTEGER NOT NULL,
|
||||
"PracticeHours" INTEGER NOT NULL,
|
||||
"Nature" INTEGER NOT NULL,
|
||||
"AssessmentMethod" INTEGER NOT NULL,
|
||||
"Description" TEXT NULL,
|
||||
"CreatedAt" TEXT NOT NULL,
|
||||
"UpdatedAt" TEXT NOT NULL,
|
||||
"Code" TEXT NOT NULL,
|
||||
"Name" TEXT NOT NULL,
|
||||
"SortOrder" INTEGER NOT NULL,
|
||||
"IsEnabled" INTEGER NOT NULL,
|
||||
CONSTRAINT "FK_Courses_Colleges_CollegeId"
|
||||
FOREIGN KEY ("CollegeId") REFERENCES "Colleges" ("Id") ON DELETE RESTRICT
|
||||
);
|
||||
""",
|
||||
"""
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "IX_Courses_Code" ON "Courses" ("Code");
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS "IX_Courses_IsEnabled_SortOrder"
|
||||
ON "Courses" ("IsEnabled", "SortOrder");
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS "IX_Courses_CollegeId_Nature"
|
||||
ON "Courses" ("CollegeId", "Nature");
|
||||
"""
|
||||
];
|
||||
}
|
||||
+984
@@ -0,0 +1,984 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Jiaowu.Api.Infrastructure.Persistence;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20260724045307_PeopleAndCourses")]
|
||||
partial class PeopleAndCourses
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.10")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AcademicTerm", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("AcademicYear")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("varchar(40)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateOnly>("EndDate")
|
||||
.HasColumnType("date");
|
||||
|
||||
b.Property<bool>("IsCurrent")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("IsEnabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.Property<int>("Season")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateOnly>("StartDate")
|
||||
.HasColumnType("date");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Code")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("IsCurrent");
|
||||
|
||||
b.HasIndex("IsEnabled", "SortOrder");
|
||||
|
||||
b.ToTable("AcademicTerms");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AdministrativeClass", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("varchar(40)");
|
||||
|
||||
b.Property<string>("CounselorName")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("Grade")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<bool>("IsEnabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<Guid>("MajorId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Code")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("MajorId");
|
||||
|
||||
b.HasIndex("IsEnabled", "SortOrder");
|
||||
|
||||
b.ToTable("AdministrativeClasses");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Building", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("CampusId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("varchar(40)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<bool>("IsEnabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CampusId");
|
||||
|
||||
b.HasIndex("Code")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("IsEnabled", "SortOrder");
|
||||
|
||||
b.ToTable("Buildings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Campus", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("Address")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("varchar(40)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<bool>("IsEnabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Code")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("IsEnabled", "SortOrder");
|
||||
|
||||
b.ToTable("Campuses");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Classroom", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("BuildingId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<int>("Capacity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("varchar(40)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Equipment")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("IsEnabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.Property<string>("RoomType")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("BuildingId");
|
||||
|
||||
b.HasIndex("Code")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("IsEnabled", "SortOrder");
|
||||
|
||||
b.ToTable("Classrooms");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.College", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid?>("CampusId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("varchar(40)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<bool>("IsEnabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.Property<string>("ShortName")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CampusId");
|
||||
|
||||
b.HasIndex("Code")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("IsEnabled", "SortOrder");
|
||||
|
||||
b.ToTable("Colleges");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Course", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<int>("AssessmentMethod")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("varchar(40)");
|
||||
|
||||
b.Property<Guid>("CollegeId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<decimal>("Credits")
|
||||
.HasPrecision(5, 2)
|
||||
.HasColumnType("decimal(5,2)");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("varchar(1000)");
|
||||
|
||||
b.Property<string>("EnglishName")
|
||||
.HasMaxLength(150)
|
||||
.HasColumnType("varchar(150)");
|
||||
|
||||
b.Property<bool>("IsEnabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<int>("LectureHours")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.Property<int>("Nature")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("PracticeHours")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("TotalHours")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Code")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("CollegeId", "Nature");
|
||||
|
||||
b.HasIndex("IsEnabled", "SortOrder");
|
||||
|
||||
b.ToTable("Courses");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Major", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("varchar(40)");
|
||||
|
||||
b.Property<Guid>("CollegeId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("DegreeType")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("IsEnabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.Property<int>("SchoolingYears")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Code")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("CollegeId");
|
||||
|
||||
b.HasIndex("IsEnabled", "SortOrder");
|
||||
|
||||
b.ToTable("Majors");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Student", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("AdministrativeClassId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateOnly?>("DateOfBirth")
|
||||
.HasColumnType("date");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.Property<DateOnly>("EnrollmentDate")
|
||||
.HasColumnType("date");
|
||||
|
||||
b.Property<int>("EnrollmentYear")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Gender")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("varchar(500)");
|
||||
|
||||
b.Property<string>("Phone")
|
||||
.HasMaxLength(30)
|
||||
.HasColumnType("varchar(30)");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("StudentNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(30)
|
||||
.HasColumnType("varchar(30)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<Guid?>("UserId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("EnrollmentYear");
|
||||
|
||||
b.HasIndex("StudentNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("AdministrativeClassId", "Status");
|
||||
|
||||
b.ToTable("Students");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Teacher", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("CollegeId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.Property<int>("Gender")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateOnly?>("HireDate")
|
||||
.HasColumnType("date");
|
||||
|
||||
b.Property<bool>("IsExternal")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("varchar(500)");
|
||||
|
||||
b.Property<string>("Phone")
|
||||
.HasMaxLength(30)
|
||||
.HasColumnType("varchar(30)");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("TeacherNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(30)
|
||||
.HasColumnType("varchar(30)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasMaxLength(30)
|
||||
.HasColumnType("varchar(30)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<Guid?>("UserId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TeacherNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("CollegeId", "Status");
|
||||
|
||||
b.ToTable("Teachers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Identity.ApplicationRole", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("DataScope")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Identity.ApplicationUser", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<Guid?>("CollegeId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("IsEnabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<DateTime?>("LastLoginAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("datetime");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("StaffNumber")
|
||||
.HasMaxLength(30)
|
||||
.HasColumnType("varchar(30)");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("varchar(256)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex");
|
||||
|
||||
b.HasIndex("StaffNumber");
|
||||
|
||||
b.ToTable("AspNetUsers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.System.AuditLog", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("IpAddress")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("varchar(64)");
|
||||
|
||||
b.Property<string>("Method")
|
||||
.IsRequired()
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("varchar(10)");
|
||||
|
||||
b.Property<string>("Path")
|
||||
.IsRequired()
|
||||
.HasMaxLength(300)
|
||||
.HasColumnType("varchar(300)");
|
||||
|
||||
b.Property<int>("StatusCode")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<Guid?>("UserId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CreatedAt");
|
||||
|
||||
b.ToTable("AuditLogs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<Guid>("RoleId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
||||
{
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("RoleId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||
{
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AdministrativeClass", b =>
|
||||
{
|
||||
b.HasOne("Jiaowu.Api.Domain.Academic.Major", "Major")
|
||||
.WithMany()
|
||||
.HasForeignKey("MajorId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Major");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Building", b =>
|
||||
{
|
||||
b.HasOne("Jiaowu.Api.Domain.Academic.Campus", "Campus")
|
||||
.WithMany()
|
||||
.HasForeignKey("CampusId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Campus");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Classroom", b =>
|
||||
{
|
||||
b.HasOne("Jiaowu.Api.Domain.Academic.Building", "Building")
|
||||
.WithMany()
|
||||
.HasForeignKey("BuildingId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Building");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.College", b =>
|
||||
{
|
||||
b.HasOne("Jiaowu.Api.Domain.Academic.Campus", "Campus")
|
||||
.WithMany()
|
||||
.HasForeignKey("CampusId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.Navigation("Campus");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Course", b =>
|
||||
{
|
||||
b.HasOne("Jiaowu.Api.Domain.Academic.College", "College")
|
||||
.WithMany()
|
||||
.HasForeignKey("CollegeId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("College");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Major", b =>
|
||||
{
|
||||
b.HasOne("Jiaowu.Api.Domain.Academic.College", "College")
|
||||
.WithMany()
|
||||
.HasForeignKey("CollegeId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("College");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Student", b =>
|
||||
{
|
||||
b.HasOne("Jiaowu.Api.Domain.Academic.AdministrativeClass", "AdministrativeClass")
|
||||
.WithMany()
|
||||
.HasForeignKey("AdministrativeClassId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("AdministrativeClass");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Teacher", b =>
|
||||
{
|
||||
b.HasOne("Jiaowu.Api.Domain.Academic.College", "College")
|
||||
.WithMany()
|
||||
.HasForeignKey("CollegeId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("College");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class PeopleAndCourses : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Courses",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
CollegeId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
EnglishName = table.Column<string>(type: "varchar(150)", maxLength: 150, nullable: true),
|
||||
Credits = table.Column<decimal>(type: "decimal(5,2)", precision: 5, scale: 2, nullable: false),
|
||||
TotalHours = table.Column<int>(type: "int", nullable: false),
|
||||
LectureHours = table.Column<int>(type: "int", nullable: false),
|
||||
PracticeHours = table.Column<int>(type: "int", nullable: false),
|
||||
Nature = table.Column<int>(type: "int", nullable: false),
|
||||
AssessmentMethod = table.Column<int>(type: "int", nullable: false),
|
||||
Description = table.Column<string>(type: "varchar(1000)", maxLength: 1000, nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
Code = table.Column<string>(type: "varchar(40)", maxLength: 40, nullable: false),
|
||||
Name = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: false),
|
||||
SortOrder = table.Column<int>(type: "int", nullable: false),
|
||||
IsEnabled = table.Column<bool>(type: "tinyint(1)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Courses", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Courses_Colleges_CollegeId",
|
||||
column: x => x.CollegeId,
|
||||
principalTable: "Colleges",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
})
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Students",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
StudentNumber = table.Column<string>(type: "varchar(30)", maxLength: 30, nullable: false),
|
||||
Name = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: false),
|
||||
Gender = table.Column<int>(type: "int", nullable: false),
|
||||
AdministrativeClassId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
EnrollmentYear = table.Column<int>(type: "int", nullable: false),
|
||||
EnrollmentDate = table.Column<DateOnly>(type: "date", nullable: false),
|
||||
Status = table.Column<int>(type: "int", nullable: false),
|
||||
DateOfBirth = table.Column<DateOnly>(type: "date", nullable: true),
|
||||
Phone = table.Column<string>(type: "varchar(30)", maxLength: 30, nullable: true),
|
||||
Email = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: true),
|
||||
Notes = table.Column<string>(type: "varchar(500)", maxLength: 500, nullable: true),
|
||||
UserId = table.Column<Guid>(type: "char(36)", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Students", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Students_AdministrativeClasses_AdministrativeClassId",
|
||||
column: x => x.AdministrativeClassId,
|
||||
principalTable: "AdministrativeClasses",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_Students_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
})
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Teachers",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
TeacherNumber = table.Column<string>(type: "varchar(30)", maxLength: 30, nullable: false),
|
||||
Name = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: false),
|
||||
Gender = table.Column<int>(type: "int", nullable: false),
|
||||
CollegeId = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
Title = table.Column<string>(type: "varchar(30)", maxLength: 30, nullable: true),
|
||||
Status = table.Column<int>(type: "int", nullable: false),
|
||||
HireDate = table.Column<DateOnly>(type: "date", nullable: true),
|
||||
IsExternal = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
Phone = table.Column<string>(type: "varchar(30)", maxLength: 30, nullable: true),
|
||||
Email = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: true),
|
||||
Notes = table.Column<string>(type: "varchar(500)", maxLength: 500, nullable: true),
|
||||
UserId = table.Column<Guid>(type: "char(36)", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Teachers", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Teachers_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_Teachers_Colleges_CollegeId",
|
||||
column: x => x.CollegeId,
|
||||
principalTable: "Colleges",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
})
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Courses_Code",
|
||||
table: "Courses",
|
||||
column: "Code",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Courses_CollegeId_Nature",
|
||||
table: "Courses",
|
||||
columns: new[] { "CollegeId", "Nature" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Courses_IsEnabled_SortOrder",
|
||||
table: "Courses",
|
||||
columns: new[] { "IsEnabled", "SortOrder" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Students_AdministrativeClassId_Status",
|
||||
table: "Students",
|
||||
columns: new[] { "AdministrativeClassId", "Status" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Students_EnrollmentYear",
|
||||
table: "Students",
|
||||
column: "EnrollmentYear");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Students_StudentNumber",
|
||||
table: "Students",
|
||||
column: "StudentNumber",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Students_UserId",
|
||||
table: "Students",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Teachers_CollegeId_Status",
|
||||
table: "Teachers",
|
||||
columns: new[] { "CollegeId", "Status" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Teachers_TeacherNumber",
|
||||
table: "Teachers",
|
||||
column: "TeacherNumber",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Teachers_UserId",
|
||||
table: "Teachers",
|
||||
column: "UserId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Courses");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Students");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Teachers");
|
||||
}
|
||||
}
|
||||
}
|
||||
+258
-8
@@ -72,7 +72,7 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
|
||||
b.HasIndex("IsEnabled", "SortOrder");
|
||||
|
||||
b.ToTable("AcademicTerms");
|
||||
b.ToTable("AcademicTerms", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AdministrativeClass", b =>
|
||||
@@ -121,7 +121,7 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
|
||||
b.HasIndex("IsEnabled", "SortOrder");
|
||||
|
||||
b.ToTable("AdministrativeClasses");
|
||||
b.ToTable("AdministrativeClasses", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Building", b =>
|
||||
@@ -164,7 +164,7 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
|
||||
b.HasIndex("IsEnabled", "SortOrder");
|
||||
|
||||
b.ToTable("Buildings");
|
||||
b.ToTable("Buildings", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Campus", b =>
|
||||
@@ -205,7 +205,7 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
|
||||
b.HasIndex("IsEnabled", "SortOrder");
|
||||
|
||||
b.ToTable("Campuses");
|
||||
b.ToTable("Campuses", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Classroom", b =>
|
||||
@@ -258,7 +258,7 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
|
||||
b.HasIndex("IsEnabled", "SortOrder");
|
||||
|
||||
b.ToTable("Classrooms");
|
||||
b.ToTable("Classrooms", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.College", b =>
|
||||
@@ -304,7 +304,77 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
|
||||
b.HasIndex("IsEnabled", "SortOrder");
|
||||
|
||||
b.ToTable("Colleges");
|
||||
b.ToTable("Colleges", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Course", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<int>("AssessmentMethod")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasMaxLength(40)
|
||||
.HasColumnType("varchar(40)");
|
||||
|
||||
b.Property<Guid>("CollegeId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<decimal>("Credits")
|
||||
.HasPrecision(5, 2)
|
||||
.HasColumnType("decimal(5,2)");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("varchar(1000)");
|
||||
|
||||
b.Property<string>("EnglishName")
|
||||
.HasMaxLength(150)
|
||||
.HasColumnType("varchar(150)");
|
||||
|
||||
b.Property<bool>("IsEnabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<int>("LectureHours")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.Property<int>("Nature")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("PracticeHours")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("TotalHours")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Code")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("CollegeId", "Nature");
|
||||
|
||||
b.HasIndex("IsEnabled", "SortOrder");
|
||||
|
||||
b.ToTable("Courses", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Major", b =>
|
||||
@@ -354,7 +424,144 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
|
||||
b.HasIndex("IsEnabled", "SortOrder");
|
||||
|
||||
b.ToTable("Majors");
|
||||
b.ToTable("Majors", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Student", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("AdministrativeClassId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateOnly?>("DateOfBirth")
|
||||
.HasColumnType("date");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.Property<DateOnly>("EnrollmentDate")
|
||||
.HasColumnType("date");
|
||||
|
||||
b.Property<int>("EnrollmentYear")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Gender")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("varchar(500)");
|
||||
|
||||
b.Property<string>("Phone")
|
||||
.HasMaxLength(30)
|
||||
.HasColumnType("varchar(30)");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("StudentNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(30)
|
||||
.HasColumnType("varchar(30)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<Guid?>("UserId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("EnrollmentYear");
|
||||
|
||||
b.HasIndex("StudentNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("AdministrativeClassId", "Status");
|
||||
|
||||
b.ToTable("Students", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Teacher", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<Guid>("CollegeId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.Property<int>("Gender")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateOnly?>("HireDate")
|
||||
.HasColumnType("date");
|
||||
|
||||
b.Property<bool>("IsExternal")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("varchar(50)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("varchar(500)");
|
||||
|
||||
b.Property<string>("Phone")
|
||||
.HasMaxLength(30)
|
||||
.HasColumnType("varchar(30)");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("TeacherNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(30)
|
||||
.HasColumnType("varchar(30)");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.HasMaxLength(30)
|
||||
.HasColumnType("varchar(30)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<Guid?>("UserId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TeacherNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("CollegeId", "Status");
|
||||
|
||||
b.ToTable("Teachers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Identity.ApplicationRole", b =>
|
||||
@@ -519,7 +726,7 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
|
||||
b.HasIndex("CreatedAt");
|
||||
|
||||
b.ToTable("AuditLogs");
|
||||
b.ToTable("AuditLogs", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||
@@ -664,6 +871,17 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
b.Navigation("Campus");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Course", b =>
|
||||
{
|
||||
b.HasOne("Jiaowu.Api.Domain.Academic.College", "College")
|
||||
.WithMany()
|
||||
.HasForeignKey("CollegeId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("College");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Major", b =>
|
||||
{
|
||||
b.HasOne("Jiaowu.Api.Domain.Academic.College", "College")
|
||||
@@ -675,6 +893,38 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
b.Navigation("College");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Student", b =>
|
||||
{
|
||||
b.HasOne("Jiaowu.Api.Domain.Academic.AdministrativeClass", "AdministrativeClass")
|
||||
.WithMany()
|
||||
.HasForeignKey("AdministrativeClassId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("AdministrativeClass");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Teacher", b =>
|
||||
{
|
||||
b.HasOne("Jiaowu.Api.Domain.Academic.College", "College")
|
||||
.WithMany()
|
||||
.HasForeignKey("CollegeId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("College");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||
{
|
||||
b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationRole", null)
|
||||
|
||||
@@ -83,6 +83,7 @@ builder.Services.Configure<JwtOptions>(
|
||||
builder.Configuration.GetSection(JwtOptions.SectionName));
|
||||
builder.Services.AddScoped<ITokenService, TokenService>();
|
||||
builder.Services.AddScoped<DatabaseInitializer>();
|
||||
builder.Services.AddScoped<DevelopmentSqliteMigrator>();
|
||||
|
||||
builder.Services
|
||||
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
|
||||
Reference in New Issue
Block a user