feat: 完善学生生命周期管理与权限范围并发布 1.4.2

This commit is contained in:
LoveChengke committed 2026-10-23 22:55:21 +08:00
1 parent 6347d1e5aa
commit 24e8dd902b
92 files changed
+6801 -2255

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@classhelper/shared",
"version": "1.4.1",
"version": "1.4.2",
"private": true,
"description": "ClassHelper - 三端共享的类型、常量与工具函数",
"type": "module",
+1
View File
@@ -10,3 +10,4 @@ export * from './class-plan.js';
export * from './island-layout.js';
export type * from './types.js';
export * from './courseware.js';
export * from './lifecycle.js';
+52
View File
@@ -0,0 +1,52 @@
import type { PermissionScope } from './class-plan.js';
/** 范围解析供保存、查询与展示共用;旧库空岗位范围会由调用方拒绝访问。 */
export function parsePermissionScope(scope: PermissionScope, value = '', knownGrades?: readonly string[]) {
if (scope === 'ALL') return { scope, scopeValue: '', grades: [], scopeLabel: '全校' };
if (scope === 'SELF_CLASS') {
if (value !== '' && value !== 'HEAD') throw new Error('关联班级范围只允许全部关联班级或仅班主任班');
return {
scope,
scopeValue: value,
grades: [],
scopeLabel: value === 'HEAD' ? '仅班主任班' : '班主任与任课班级',
};
}
if (scope !== 'GRADE' && scope !== 'DIVISION') throw new Error('未知的数据范围');
const grades = [...new Set(value.split(/[,,、\s]+/u).filter(Boolean))].sort();
if (!grades.length) throw new Error('请选择范围中的年级');
if (scope === 'GRADE' && grades.length !== 1) throw new Error('年级范围只能选择一个年级');
if (knownGrades && grades.some((grade) => !knownGrades.includes(grade)))
throw new Error('范围包含未知年级,请刷新选项');
return {
scope,
scopeValue: grades.join(','),
grades,
scopeLabel: `${scope === 'GRADE' ? '年级' : '部'}:${grades.join('、')}`,
};
}
export interface PromotionPlan {
tracks: {
id: string;
name: string;
entryClassCount: number;
stages: { grade: string; terminal?: boolean }[];
}[];
termStartDate: string | null;
resetTermWeeks: boolean;
}
export function defaultPromotionPlan(classCount = 1): PromotionPlan {
return {
tracks: [
{
id: 'high',
name: '高中',
entryClassCount: classCount,
stages: [{ grade: '高一' }, { grade: '高二' }, { grade: '高三', terminal: true }],
},
],
termStartDate: null,
resetTermWeeks: true,
};
}
+9 -2
View File
@@ -200,6 +200,7 @@ export const PERMISSION_KEYS = [
// 学期与归档
'term:read',
'term:write',
'promotion:manage',
'archive:manage',
'dashboard:read',
// 系统与安全
@@ -258,7 +259,7 @@ export const PERMISSION_LABELS: Record<PermissionKey, string> = {
'class:read': '查看班级与班级列表',
'class:manage': '创建 / 修改 / 删除班级(含班级账号密码)',
'roster:read': '查看学生名单',
'roster:manage': '学生名单管理(增删 / 调班 / 导入)',
'roster:manage': '学生名单管理(资料 / 调班 / 转入转出 / 停用)',
'teacher:read': '查看教师名单',
'teacher:manage': '教师账号管理(新建 / 导入 / 重置密码 / 删除)',
'schedule:read': '查看课表',
@@ -285,6 +286,7 @@ export const PERMISSION_LABELS: Record<PermissionKey, string> = {
'remote:control': '远控客户端电脑(关机 / 重启 / 注销 / 锁屏)',
'term:read': '查看学期周次',
'term:write': '修改学期周次与开学日期',
'promotion:manage': '配置、预览与执行学年升级',
'archive:manage': '毕业归档',
'dashboard:read': '查看统计看板',
'database:manage': '数据库快照 / 导出 / 切库',
@@ -342,7 +344,10 @@ export const PERMISSION_CATEGORIES: readonly { title: string; keys: readonly Per
title: '客户端设备',
keys: ['client:read', 'client:manage', 'client:update', 'client:update-push', 'remote:control'],
},
{ title: '学期与统计', keys: ['term:read', 'term:write', 'archive:manage', 'dashboard:read'] },
{
title: '学期与统计',
keys: ['term:read', 'term:write', 'promotion:manage', 'archive:manage', 'dashboard:read'],
},
{
title: '系统与安全',
keys: [
@@ -399,6 +404,7 @@ export const PERMISSION_DEFAULT_ROLES: Record<PermissionKey, readonly UserRole[]
'remote:control': ['ADMIN'],
'term:read': ['ADMIN', 'TEACHER'],
'term:write': ['ADMIN'],
'promotion:manage': ['ADMIN'],
'archive:manage': ['ADMIN'],
'dashboard:read': ['ADMIN', 'TEACHER'],
'database:manage': ['ADMIN'],
@@ -454,6 +460,7 @@ export const PERMISSION_MATRIX: readonly {
subject: boolean;
classDevice?: boolean;
}[] = [
{ action: '学年升级(默认管理员,可按权限组授权)', admin: true, head: false, subject: false },
{ action: '班级创建 / 修改 / 删除', admin: true, head: false, subject: false },
{ action: '分配班主任与各科科任老师', admin: true, head: false, subject: false },
{ action: '学生名单管理(增删 / 导入 / 调班)', admin: true, head: true, subject: false },
+21 -9
View File
@@ -186,16 +186,14 @@ export interface CreateStudentRequest {
classId?: string | null;
gender?: StudentGender;
guardianPhone?: string;
status?: StudentStatus;
status?: 'active' | 'inactive';
}
export interface UpdateStudentRequest {
studentNo?: string;
name?: string;
classId?: string | null;
gender?: StudentGender;
guardianPhone?: string;
status?: StudentStatus;
}
/** 学生是否属于某个班的判定范围(列表查询用) */
@@ -207,10 +205,19 @@ export interface StudentQueryParams {
/* ------------------------------------------------------------------ 调班 */
/** 调班 / 转出的方式 */
export type StudentTransferMode = 'single' | 'batch' | 'transfer-out';
export type StudentTransferMode = 'single' | 'batch' | 'transfer-out' | 'restore-transfer';
export type StudentLifecycleKind =
'class-change' | 'transfer-in' | 'transfer-out' | 'restore' | 'unassign' | 'deactivate';
/** 一次调班 / 转出的历史记录(原班级 / 新班级 / 操作人 / 时间) */
export interface StudentTransferDto {
kind: StudentLifecycleKind;
batchId: string;
academicYear: number | null;
fromGrade: string;
toGrade: string;
fromSchool: string;
toSchool: string;
id: string;
studentId: string;
/** 学号快照:学生被删除后历史仍可读 */
@@ -234,6 +241,7 @@ export interface StudentTransferDto {
* **学号不变**,只改学生当前的班级归属;历史作业与成绩保留原归属。
*/
export interface TransferStudentsRequest {
allowGradeChange?: boolean;
studentIds: string[];
toClassId: string | null;
note?: string;
@@ -244,6 +252,7 @@ export interface TransferStudentsRequest {
* 单个与批量共用 —— 传一个 id 就是单个。
*/
export interface TransferOutStudentsRequest {
toSchool?: string;
studentIds: string[];
/** 转出去向 / 原因备注 */
note?: string;
@@ -272,6 +281,7 @@ export interface ArchivedYearDto {
classCount: number;
studentCount: number;
transferredCount: number;
inactiveCount: number;
homeworkCount: number;
notificationCount: number;
archivedAt: string;
@@ -292,11 +302,14 @@ export interface ArchivedClassDto {
}
export interface ArchivedYearDetailDto extends ArchivedYearDto {
/** 本届保留名单中有转入记录的学生数,按学生去重 */
transferInCount: number;
classes: ArchivedClassDto[];
/** 本届毕业生 */
graduates: StudentDto[];
/** 本届在读期间转出的学生 */
transferred: StudentDto[];
inactive: StudentDto[];
}
/** 归档班级的作业与通知(只读) */
@@ -488,8 +501,6 @@ export interface UpdateClassRequest {
enrollmentYear?: number;
/** 班号(同上) */
classIndex?: number;
/** 当前年级(高一 / 高二 / 高三;学年升级时改它,**不归档**) */
grade?: string;
/** 本学期教学周数(1~30,仅管理员可调) */
termWeeks?: number;
}
@@ -1456,6 +1467,7 @@ export interface UpdateInfo {
export interface PermissionGroupDto {
scope?: ContractPermissionScope;
scopeValue?: string;
scopeLabel: string;
id: string;
name: string;
description: string;
@@ -1476,10 +1488,10 @@ export interface PermissionMemberDto {
role: UserRole;
/** 所属权限组;空数组 = 走角色默认模板 */
groupIds: string[];
/** 生效权限数量(ADMIN 恒为全集;班级端账号恒为 0,见权限页的说明) */
/** 生效权限数量;管理员在分组并集之外始终保留两个管理入口 */
permissionCount: number;
/** 该账号固定拥有全部权限,不接受分配(管理员) */
fixed: boolean;
permissions: PermissionKey[];
scopeLabels: string[];
}
/** `GET /api/permissions/catalog`:权限键目录(与 shared 的常量一一对应,供权限页渲染) */