2.3.0-rc2
This commit is contained in:
@@ -1639,13 +1639,25 @@ public sealed class CourseSelectionsController(
|
||||
var students = await LoadTeachingTaskRosterAsync(id, cancellationToken);
|
||||
var bytes = ExcelWorkbookHelper.Create(
|
||||
"教学班名单",
|
||||
["学号", "姓名", "班级", "专业", "进入方式", "选课时间"],
|
||||
[
|
||||
"学号", "姓名", "班级", "专业", "联系电话", "电子邮箱",
|
||||
"微信", "紧急联系人", "与本人关系", "紧急联系电话",
|
||||
"特殊标记", "特殊情况说明", "进入方式", "选课时间"
|
||||
],
|
||||
students.Select(student => new List<object?>
|
||||
{
|
||||
student.StudentNumber,
|
||||
student.Name,
|
||||
student.ClassName,
|
||||
student.MajorName,
|
||||
student.Phone,
|
||||
student.Email,
|
||||
student.WeChat,
|
||||
student.EmergencyContactName,
|
||||
student.EmergencyContactRelationship,
|
||||
student.EmergencyContactPhone,
|
||||
student.SpecialTags,
|
||||
student.SpecialNeeds,
|
||||
student.EnrolledAt.HasValue ? "选课" : "行政班关联",
|
||||
student.EnrolledAt?.ToString("yyyy-MM-dd HH:mm") ?? "-"
|
||||
}).ToList<IReadOnlyList<object?>>());
|
||||
@@ -1754,6 +1766,14 @@ public sealed class CourseSelectionsController(
|
||||
x.Name,
|
||||
x.AdministrativeClass!.Name,
|
||||
x.AdministrativeClass.Major!.Name,
|
||||
x.Phone,
|
||||
x.Email,
|
||||
x.WeChat,
|
||||
x.EmergencyContactName,
|
||||
x.EmergencyContactRelationship,
|
||||
x.EmergencyContactPhone,
|
||||
x.SpecialTags,
|
||||
x.SpecialNeeds,
|
||||
db.CourseEnrollments
|
||||
.Where(enrollment =>
|
||||
enrollment.StudentId == x.Id &&
|
||||
@@ -2154,6 +2174,14 @@ public sealed class CourseSelectionsController(
|
||||
string Name,
|
||||
string ClassName,
|
||||
string MajorName,
|
||||
string? Phone,
|
||||
string? Email,
|
||||
string? WeChat,
|
||||
string? EmergencyContactName,
|
||||
string? EmergencyContactRelationship,
|
||||
string? EmergencyContactPhone,
|
||||
string? SpecialTags,
|
||||
string? SpecialNeeds,
|
||||
DateTime? EnrolledAt);
|
||||
}
|
||||
|
||||
|
||||
@@ -224,6 +224,12 @@ public sealed class PersonnelController(
|
||||
[FromQuery] PersonnelQuery query,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var roles = currentUserDataScope.Current.Roles;
|
||||
var canReadFullProfile = roles.Any(role =>
|
||||
role is SystemRoles.SuperAdmin or
|
||||
SystemRoles.AcademicAdmin or
|
||||
SystemRoles.CollegeAdmin or
|
||||
SystemRoles.Counselor);
|
||||
var page = NormalizePage(query.Page);
|
||||
var pageSize = NormalizePageSize(query.PageSize);
|
||||
var source = ApplyStudentScope(db.Students.AsNoTracking());
|
||||
@@ -268,9 +274,26 @@ public sealed class PersonnelController(
|
||||
x.EnrollmentDate,
|
||||
x.Status,
|
||||
x.DateOfBirth,
|
||||
EnglishName = canReadFullProfile ? x.EnglishName : null,
|
||||
IdCardNumber = canReadFullProfile ? x.IdCardNumber : null,
|
||||
Nationality = canReadFullProfile ? x.Nationality : null,
|
||||
Ethnicity = canReadFullProfile ? x.Ethnicity : null,
|
||||
PoliticalStatus = canReadFullProfile ? x.PoliticalStatus : null,
|
||||
NativePlace = canReadFullProfile ? x.NativePlace : null,
|
||||
HouseholdAddress = canReadFullProfile ? x.HouseholdAddress : null,
|
||||
CurrentAddress = canReadFullProfile ? x.CurrentAddress : null,
|
||||
PostalCode = canReadFullProfile ? x.PostalCode : null,
|
||||
x.Phone,
|
||||
x.Email,
|
||||
x.Notes,
|
||||
Qq = canReadFullProfile ? x.Qq : null,
|
||||
x.WeChat,
|
||||
x.EmergencyContactName,
|
||||
x.EmergencyContactRelationship,
|
||||
x.EmergencyContactPhone,
|
||||
x.SpecialTags,
|
||||
x.SpecialNeeds,
|
||||
Biography = canReadFullProfile ? x.Biography : null,
|
||||
Notes = canReadFullProfile ? x.Notes : null,
|
||||
x.UserId,
|
||||
x.CreatedAt
|
||||
})
|
||||
@@ -304,8 +327,26 @@ public sealed class PersonnelController(
|
||||
EnrollmentDate = request.EnrollmentDate,
|
||||
Status = request.Status,
|
||||
DateOfBirth = request.DateOfBirth,
|
||||
EnglishName = Normalize(request.EnglishName),
|
||||
IdCardNumber = Normalize(request.IdCardNumber),
|
||||
Nationality = Normalize(request.Nationality),
|
||||
Ethnicity = Normalize(request.Ethnicity),
|
||||
PoliticalStatus = Normalize(request.PoliticalStatus),
|
||||
NativePlace = Normalize(request.NativePlace),
|
||||
HouseholdAddress = Normalize(request.HouseholdAddress),
|
||||
CurrentAddress = Normalize(request.CurrentAddress),
|
||||
PostalCode = Normalize(request.PostalCode),
|
||||
Phone = Normalize(request.Phone),
|
||||
Email = Normalize(request.Email),
|
||||
Qq = Normalize(request.Qq),
|
||||
WeChat = Normalize(request.WeChat),
|
||||
EmergencyContactName = Normalize(request.EmergencyContactName),
|
||||
EmergencyContactRelationship = Normalize(
|
||||
request.EmergencyContactRelationship),
|
||||
EmergencyContactPhone = Normalize(request.EmergencyContactPhone),
|
||||
SpecialTags = Normalize(request.SpecialTags),
|
||||
SpecialNeeds = Normalize(request.SpecialNeeds),
|
||||
Biography = Normalize(request.Biography),
|
||||
Notes = Normalize(request.Notes)
|
||||
};
|
||||
db.Students.Add(entity);
|
||||
@@ -343,8 +384,26 @@ public sealed class PersonnelController(
|
||||
entity.EnrollmentDate = request.EnrollmentDate;
|
||||
entity.Status = request.Status;
|
||||
entity.DateOfBirth = request.DateOfBirth;
|
||||
entity.EnglishName = Normalize(request.EnglishName);
|
||||
entity.IdCardNumber = Normalize(request.IdCardNumber);
|
||||
entity.Nationality = Normalize(request.Nationality);
|
||||
entity.Ethnicity = Normalize(request.Ethnicity);
|
||||
entity.PoliticalStatus = Normalize(request.PoliticalStatus);
|
||||
entity.NativePlace = Normalize(request.NativePlace);
|
||||
entity.HouseholdAddress = Normalize(request.HouseholdAddress);
|
||||
entity.CurrentAddress = Normalize(request.CurrentAddress);
|
||||
entity.PostalCode = Normalize(request.PostalCode);
|
||||
entity.Phone = Normalize(request.Phone);
|
||||
entity.Email = Normalize(request.Email);
|
||||
entity.Qq = Normalize(request.Qq);
|
||||
entity.WeChat = Normalize(request.WeChat);
|
||||
entity.EmergencyContactName = Normalize(request.EmergencyContactName);
|
||||
entity.EmergencyContactRelationship = Normalize(
|
||||
request.EmergencyContactRelationship);
|
||||
entity.EmergencyContactPhone = Normalize(request.EmergencyContactPhone);
|
||||
entity.SpecialTags = Normalize(request.SpecialTags);
|
||||
entity.SpecialNeeds = Normalize(request.SpecialNeeds);
|
||||
entity.Biography = Normalize(request.Biography);
|
||||
entity.Notes = Normalize(request.Notes);
|
||||
return await SaveNoContentAsync(cancellationToken);
|
||||
}
|
||||
@@ -522,8 +581,25 @@ public sealed record StudentRequest(
|
||||
DateOnly EnrollmentDate,
|
||||
StudentStatus Status,
|
||||
DateOnly? DateOfBirth,
|
||||
[MaxLength(100)] string? EnglishName,
|
||||
[MaxLength(30)] string? IdCardNumber,
|
||||
[MaxLength(50)] string? Nationality,
|
||||
[MaxLength(50)] string? Ethnicity,
|
||||
[MaxLength(50)] string? PoliticalStatus,
|
||||
[MaxLength(100)] string? NativePlace,
|
||||
[MaxLength(300)] string? HouseholdAddress,
|
||||
[MaxLength(300)] string? CurrentAddress,
|
||||
[MaxLength(20)] string? PostalCode,
|
||||
[MaxLength(30)] string? Phone,
|
||||
[EmailAddress, MaxLength(100)] string? Email,
|
||||
[MaxLength(30)] string? Qq,
|
||||
[MaxLength(60)] string? WeChat,
|
||||
[MaxLength(50)] string? EmergencyContactName,
|
||||
[MaxLength(30)] string? EmergencyContactRelationship,
|
||||
[MaxLength(30)] string? EmergencyContactPhone,
|
||||
[MaxLength(300)] string? SpecialTags,
|
||||
[MaxLength(1000)] string? SpecialNeeds,
|
||||
[MaxLength(1000)] string? Biography,
|
||||
[MaxLength(500)] string? Notes);
|
||||
|
||||
public sealed record TeacherAccountActivationRequest(
|
||||
|
||||
@@ -31,6 +31,9 @@ public sealed class PersonnelExcelController(
|
||||
SystemRoles.AcademicAdmin + "," +
|
||||
SystemRoles.CollegeAdmin;
|
||||
|
||||
private const string ExportRoles =
|
||||
WriteRoles + "," + SystemRoles.Counselor;
|
||||
|
||||
private static readonly string[] TeacherHeaders =
|
||||
[
|
||||
"工号", "姓名", "性别", "学院编码", "职称", "任职状态",
|
||||
@@ -40,7 +43,10 @@ public sealed class PersonnelExcelController(
|
||||
private static readonly string[] StudentHeaders =
|
||||
[
|
||||
"学号", "姓名", "性别", "行政班编码", "入学年级", "入学日期",
|
||||
"学籍状态", "出生日期", "联系电话", "电子邮箱", "备注"
|
||||
"学籍状态", "出生日期", "英文姓名", "证件号码", "国籍", "民族",
|
||||
"政治面貌", "籍贯", "户籍地址", "现居住地址", "邮政编码",
|
||||
"联系电话", "电子邮箱", "QQ", "微信", "紧急联系人", "与本人关系",
|
||||
"紧急联系电话", "特殊标记", "特殊情况说明", "个人简介", "备注"
|
||||
];
|
||||
|
||||
[HttpGet("{kind}/template")]
|
||||
@@ -66,6 +72,7 @@ public sealed class PersonnelExcelController(
|
||||
}
|
||||
|
||||
[HttpGet("{kind}/export")]
|
||||
[Authorize(Roles = ExportRoles)]
|
||||
public async Task<IActionResult> Export(
|
||||
string kind,
|
||||
[FromQuery] PersonnelQuery query,
|
||||
@@ -96,7 +103,12 @@ public sealed class PersonnelExcelController(
|
||||
.Select(x => Row(
|
||||
x.StudentNumber, x.Name, GenderName(x.Gender),
|
||||
x.AdministrativeClass!.Code, x.EnrollmentYear, x.EnrollmentDate,
|
||||
StudentStatusName(x.Status), x.DateOfBirth, x.Phone, x.Email, x.Notes))
|
||||
StudentStatusName(x.Status), x.DateOfBirth, x.EnglishName,
|
||||
x.IdCardNumber, x.Nationality, x.Ethnicity, x.PoliticalStatus,
|
||||
x.NativePlace, x.HouseholdAddress, x.CurrentAddress, x.PostalCode,
|
||||
x.Phone, x.Email, x.Qq, x.WeChat, x.EmergencyContactName,
|
||||
x.EmergencyContactRelationship, x.EmergencyContactPhone,
|
||||
x.SpecialTags, x.SpecialNeeds, x.Biography, x.Notes))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
@@ -296,8 +308,25 @@ public sealed class PersonnelExcelController(
|
||||
entity.EnrollmentDate = enrollmentDate.Value;
|
||||
entity.Status = status.Value;
|
||||
entity.DateOfBirth = dateOfBirth;
|
||||
entity.EnglishName = Optional(row, "英文姓名");
|
||||
entity.IdCardNumber = Optional(row, "证件号码");
|
||||
entity.Nationality = Optional(row, "国籍");
|
||||
entity.Ethnicity = Optional(row, "民族");
|
||||
entity.PoliticalStatus = Optional(row, "政治面貌");
|
||||
entity.NativePlace = Optional(row, "籍贯");
|
||||
entity.HouseholdAddress = Optional(row, "户籍地址");
|
||||
entity.CurrentAddress = Optional(row, "现居住地址");
|
||||
entity.PostalCode = Optional(row, "邮政编码");
|
||||
entity.Phone = Optional(row, "联系电话");
|
||||
entity.Email = Optional(row, "电子邮箱");
|
||||
entity.Qq = Optional(row, "QQ");
|
||||
entity.WeChat = Optional(row, "微信");
|
||||
entity.EmergencyContactName = Optional(row, "紧急联系人");
|
||||
entity.EmergencyContactRelationship = Optional(row, "与本人关系");
|
||||
entity.EmergencyContactPhone = Optional(row, "紧急联系电话");
|
||||
entity.SpecialTags = Optional(row, "特殊标记");
|
||||
entity.SpecialNeeds = Optional(row, "特殊情况说明");
|
||||
entity.Biography = Optional(row, "个人简介");
|
||||
entity.Notes = Optional(row, "备注");
|
||||
}
|
||||
return new(created, updated, rows.Count);
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Jiaowu.Api.Domain.Academic;
|
||||
using Jiaowu.Api.Domain.Identity;
|
||||
using Jiaowu.Api.Infrastructure.Auth;
|
||||
using Jiaowu.Api.Infrastructure.Caching;
|
||||
using Jiaowu.Api.Infrastructure.Persistence;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Jiaowu.Api.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Authorize(Roles = SystemRoles.Student)]
|
||||
[Route("api/student/profile")]
|
||||
public sealed class StudentProfileController(
|
||||
AppDbContext db,
|
||||
ICurrentUserDataScope currentUserDataScope,
|
||||
IAppCache cache) : ControllerBase
|
||||
{
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<StudentProfileDto>> Get(
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var userId = currentUserDataScope.Current.UserId;
|
||||
var profile = await db.Students.AsNoTracking()
|
||||
.Where(x => x.UserId == userId)
|
||||
.Select(x => new StudentProfileDto(
|
||||
x.StudentNumber,
|
||||
x.Name,
|
||||
x.AdministrativeClass!.Name,
|
||||
x.AdministrativeClass.Major!.Name,
|
||||
x.AdministrativeClass.Major.College!.Name,
|
||||
x.EnrollmentYear,
|
||||
x.EnrollmentDate,
|
||||
x.Status,
|
||||
x.Gender,
|
||||
x.DateOfBirth,
|
||||
x.EnglishName,
|
||||
x.IdCardNumber,
|
||||
x.Nationality,
|
||||
x.Ethnicity,
|
||||
x.PoliticalStatus,
|
||||
x.NativePlace,
|
||||
x.HouseholdAddress,
|
||||
x.CurrentAddress,
|
||||
x.PostalCode,
|
||||
x.Phone,
|
||||
x.Email,
|
||||
x.Qq,
|
||||
x.WeChat,
|
||||
x.EmergencyContactName,
|
||||
x.EmergencyContactRelationship,
|
||||
x.EmergencyContactPhone,
|
||||
x.SpecialTags,
|
||||
x.SpecialNeeds,
|
||||
x.Biography))
|
||||
.SingleOrDefaultAsync(cancellationToken);
|
||||
|
||||
return profile is null ? NotFound() : Ok(profile);
|
||||
}
|
||||
|
||||
[HttpPut]
|
||||
public async Task<IActionResult> Update(
|
||||
StudentProfileUpdateRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var userId = currentUserDataScope.Current.UserId;
|
||||
var student = await db.Students.SingleOrDefaultAsync(
|
||||
x => x.UserId == userId,
|
||||
cancellationToken);
|
||||
if (student is null) return NotFound();
|
||||
|
||||
student.Gender = request.Gender;
|
||||
student.DateOfBirth = request.DateOfBirth;
|
||||
student.EnglishName = Normalize(request.EnglishName);
|
||||
student.IdCardNumber = Normalize(request.IdCardNumber);
|
||||
student.Nationality = Normalize(request.Nationality);
|
||||
student.Ethnicity = Normalize(request.Ethnicity);
|
||||
student.PoliticalStatus = Normalize(request.PoliticalStatus);
|
||||
student.NativePlace = Normalize(request.NativePlace);
|
||||
student.HouseholdAddress = Normalize(request.HouseholdAddress);
|
||||
student.CurrentAddress = Normalize(request.CurrentAddress);
|
||||
student.PostalCode = Normalize(request.PostalCode);
|
||||
student.Phone = Normalize(request.Phone);
|
||||
student.Email = Normalize(request.Email);
|
||||
student.Qq = Normalize(request.Qq);
|
||||
student.WeChat = Normalize(request.WeChat);
|
||||
student.EmergencyContactName = Normalize(request.EmergencyContactName);
|
||||
student.EmergencyContactRelationship = Normalize(
|
||||
request.EmergencyContactRelationship);
|
||||
student.EmergencyContactPhone = Normalize(request.EmergencyContactPhone);
|
||||
student.SpecialTags = Normalize(request.SpecialTags);
|
||||
student.SpecialNeeds = Normalize(request.SpecialNeeds);
|
||||
student.Biography = Normalize(request.Biography);
|
||||
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
await cache.RemoveByTagAsync(AppCacheTags.Analytics, cancellationToken);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
private static string? Normalize(string? value) =>
|
||||
string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
||||
}
|
||||
|
||||
public sealed record StudentProfileDto(
|
||||
string StudentNumber,
|
||||
string Name,
|
||||
string ClassName,
|
||||
string MajorName,
|
||||
string CollegeName,
|
||||
int EnrollmentYear,
|
||||
DateOnly EnrollmentDate,
|
||||
StudentStatus Status,
|
||||
Gender Gender,
|
||||
DateOnly? DateOfBirth,
|
||||
string? EnglishName,
|
||||
string? IdCardNumber,
|
||||
string? Nationality,
|
||||
string? Ethnicity,
|
||||
string? PoliticalStatus,
|
||||
string? NativePlace,
|
||||
string? HouseholdAddress,
|
||||
string? CurrentAddress,
|
||||
string? PostalCode,
|
||||
string? Phone,
|
||||
string? Email,
|
||||
string? Qq,
|
||||
string? WeChat,
|
||||
string? EmergencyContactName,
|
||||
string? EmergencyContactRelationship,
|
||||
string? EmergencyContactPhone,
|
||||
string? SpecialTags,
|
||||
string? SpecialNeeds,
|
||||
string? Biography);
|
||||
|
||||
public sealed record StudentProfileUpdateRequest(
|
||||
Gender Gender,
|
||||
DateOnly? DateOfBirth,
|
||||
[MaxLength(100)] string? EnglishName,
|
||||
[MaxLength(30)] string? IdCardNumber,
|
||||
[MaxLength(50)] string? Nationality,
|
||||
[MaxLength(50)] string? Ethnicity,
|
||||
[MaxLength(50)] string? PoliticalStatus,
|
||||
[MaxLength(100)] string? NativePlace,
|
||||
[MaxLength(300)] string? HouseholdAddress,
|
||||
[MaxLength(300)] string? CurrentAddress,
|
||||
[MaxLength(20)] string? PostalCode,
|
||||
[MaxLength(30)] string? Phone,
|
||||
[EmailAddress, MaxLength(100)] string? Email,
|
||||
[MaxLength(30)] string? Qq,
|
||||
[MaxLength(60)] string? WeChat,
|
||||
[MaxLength(50)] string? EmergencyContactName,
|
||||
[MaxLength(30)] string? EmergencyContactRelationship,
|
||||
[MaxLength(30)] string? EmergencyContactPhone,
|
||||
[MaxLength(300)] string? SpecialTags,
|
||||
[MaxLength(1000)] string? SpecialNeeds,
|
||||
[MaxLength(1000)] string? Biography);
|
||||
Reference in New Issue
Block a user