refactor(grades,diagnostic): 完成成绩和学情诊断模块审计 P1+P2 改进项

P1-1: 抽取 stats-service.ts,将 8 个统计计算纯函数从 data-access 层分离
P1-5: 创建 WidgetBoundary 组件 + 补齐 teacher 路由 loading.tsx/error.tsx (14 文件)
P1-6: 同步架构图文档 004/005,新增 stats-service 与 widget-boundary 节点
P2-1: 补充 a11y ARIA 属性(5 图表 role=img + aria-label,2 表格 caption,3 列表 role=list,3 按钮 aria-label)
P2-3: 修复班级报告 studentId 字段语义错误(schema 改为可空 + 迁移 + 代码适配)
P2-4: 修复 grade_managed scope 返回空数据(改为子查询 classes 表按 gradeId 过滤)
P2-5: 新增 /parent/diagnostic/ 页面(多子女学情诊断聚合 + loading + error)
P2-6: 统一 SearchParams 工具(student/grades 和 management/grade/insights 改用 @/shared/lib/search-params)
This commit is contained in:
SpecialX
2026-06-22 17:07:32 +08:00
parent e997abaf5e
commit 5f3a1a4662
41 changed files with 9043 additions and 381 deletions

View File

@@ -1247,7 +1247,7 @@ export const diagnosticReportTypeEnum = mysqlEnum("report_type", ["individual",
export const learningDiagnosticReports = mysqlTable("learning_diagnostic_reports", {
id: id("id").primaryKey(),
studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }),
studentId: varchar("student_id", { length: 128 }).references(() => users.id, { onDelete: "cascade" }),
generatedBy: varchar("generated_by", { length: 128 }).references(() => users.id, { onDelete: "set null" }),
reportType: diagnosticReportTypeEnum.default("individual").notNull(),
period: varchar("period", { length: 50 }),
@@ -1317,3 +1317,23 @@ export const lessonPlanTemplates = mysqlTable("lesson_plan_templates", {
}, (table) => ({
typeCreatorIdx: index("lpt_type_creator_idx").on(table.type, table.creatorId),
}));
// --- 25. System Settings (系统设置 - 键值对存储) ---
export const systemSettings = mysqlTable("system_settings", {
id: id("id").primaryKey(),
/** 设置分组school_info / security_policy / file_upload / notification_config */
category: varchar("category", { length: 50 }).notNull(),
/** 设置键名,如 schoolName / passwordMinLength / maxFileSize */
key: varchar("key", { length: 100 }).notNull(),
/** 设置值JSON 字符串,支持字符串/数字/布尔/对象) */
value: text("value").notNull(),
/** 值类型string / number / boolean / json */
valueType: varchar("value_type", { length: 20 }).default("string").notNull(),
updatedBy: varchar("updated_by", { length: 128 }),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
categoryKeyIdx: uniqueIndex("ss_category_key_idx").on(table.category, table.key),
categoryIdx: index("ss_category_idx").on(table.category),
}));