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

@@ -0,0 +1,6 @@
-- 已存在的管理员默认组补升级键;默认组尚未初始化时由运行时模板创建。
INSERT INTO "PermissionGroupItem" ("id", "groupId", "key")
SELECT 'promotion-key-' || "id", "id", 'promotion:manage'
FROM "PermissionGroup"
WHERE "defaultRole" = 'ADMIN'
AND NOT EXISTS (SELECT 1 FROM "PermissionGroupItem" i WHERE i."groupId" = "PermissionGroup"."id" AND i."key" = 'promotion:manage');
@@ -0,0 +1,23 @@
ALTER TABLE "StudentClassTransfer" ADD COLUMN "kind" TEXT NOT NULL DEFAULT 'class-change';
ALTER TABLE "StudentClassTransfer" ADD COLUMN "batchId" TEXT NOT NULL DEFAULT '';
ALTER TABLE "StudentClassTransfer" ADD COLUMN "academicYear" INTEGER;
ALTER TABLE "StudentClassTransfer" ADD COLUMN "fromGrade" TEXT NOT NULL DEFAULT '';
ALTER TABLE "StudentClassTransfer" ADD COLUMN "toGrade" TEXT NOT NULL DEFAULT '';
ALTER TABLE "StudentClassTransfer" ADD COLUMN "fromSchool" TEXT NOT NULL DEFAULT '';
ALTER TABLE "StudentClassTransfer" ADD COLUMN "toSchool" TEXT NOT NULL DEFAULT '';
UPDATE "StudentClassTransfer" SET "kind" = CASE "mode" WHEN 'transfer-out' THEN 'transfer-out' WHEN 'restore-transfer' THEN 'restore' ELSE 'class-change' END;
ALTER TABLE "StudentClassTransfer" DROP COLUMN "mode";
CREATE INDEX "StudentClassTransfer_batchId_idx" ON "StudentClassTransfer"("batchId");
ALTER TABLE "ArchivedYear" ADD COLUMN "inactiveCount" INTEGER NOT NULL DEFAULT 0;
ALTER TABLE "PromotionRun" ADD COLUMN "operatorId" TEXT;
ALTER TABLE "PromotionRun" ADD COLUMN "operatorName" TEXT NOT NULL DEFAULT '';
ALTER TABLE "PromotionRun" ADD COLUMN "detailsJson" TEXT NOT NULL DEFAULT '{}';
ALTER TABLE "PromotionRun" ADD COLUMN "snapshotSha256" TEXT NOT NULL DEFAULT '';
ALTER TABLE "PromotionRun" ADD COLUMN "error" TEXT NOT NULL DEFAULT '';
CREATE TABLE "PermissionAudit" (
"id" TEXT NOT NULL PRIMARY KEY, "actorId" TEXT NOT NULL, "actorName" TEXT NOT NULL,
"action" TEXT NOT NULL, "targetType" TEXT NOT NULL, "targetId" TEXT NOT NULL,
"beforeJson" TEXT NOT NULL DEFAULT '{}', "afterJson" TEXT NOT NULL DEFAULT '{}',
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX "PermissionAudit_createdAt_idx" ON "PermissionAudit"("createdAt");
+30 -2
View File
@@ -121,14 +121,21 @@ model StudentClassTransfer {
/// 操作人(User.id);账号被删后留空,姓名快照保留
operatorId String?
operatorName String @default("")
/// single = 单个调班 / batch = 批量调班 / transfer-out = 转出(离开本校)
mode String @default("single")
/// 生命周期类型;旧 mode 仅在 DTO 中派生兼容
kind String @default("class-change")
batchId String @default("")
academicYear Int?
fromGrade String @default("")
toGrade String @default("")
fromSchool String @default("")
toSchool String @default("")
note String @default("")
createdAt DateTime @default(now())
student Student @relation(fields: [studentId], references: [id], onDelete: Cascade)
@@index([studentId, createdAt])
@@index([batchId])
@@index([createdAt])
}
@@ -157,6 +164,7 @@ model ArchivedYear {
classCount Int @default(0)
studentCount Int @default(0)
transferredCount Int @default(0)
inactiveCount Int @default(0)
homeworkCount Int @default(0)
notificationCount Int @default(0)
archivedAt DateTime @default(now())
@@ -717,12 +725,32 @@ model PromotionRun {
year Int @id
status String
snapshotPath String @default("")
operatorId String?
operatorName String @default("")
detailsJson String @default("{}")
snapshotSha256 String @default("")
error String @default("")
message String @default("")
classCount Int @default(0)
studentCount Int @default(0)
createdAt DateTime @default(now())
finishedAt DateTime?
}
/// 权限变更与人工升级审计;不建账号外键,删除账号后仍保留操作人快照。
model PermissionAudit {
id String @id @default(cuid())
actorId String
actorName String
action String
targetType String
targetId String
beforeJson String @default("{}")
afterJson String @default("{}")
createdAt DateTime @default(now())
@@index([createdAt])
}
/// 课件只在完整接收并计算摘要后入库;存储路径不对客户端暴露。
model Courseware {
id String @id @default(cuid())
+2 -2
View File
@@ -465,7 +465,7 @@ async function main(): Promise<void> {
toClassName: classes[1]!.name,
operatorId: admin.id,
operatorName: admin.name,
mode: 'batch',
kind: 'class-change',
note: '军训后按分班考试成绩重新均衡',
createdAt: new Date(now - 30 * day),
},
@@ -492,7 +492,7 @@ async function main(): Promise<void> {
toClassName: '转出本校',
operatorId: admin.id,
operatorName: admin.name,
mode: 'transfer-out',
kind: 'transfer-out',
note: '随家长工作调动转学至外地',
createdAt: new Date(now - 12 * day),
},