feat(scripts): add diagnostic, seed, and test scripts

- Add add-ai-provider-visibility and add-missing-columns migration scripts

- Add clear-error-book, seed-error-book, diagnose-error-book scripts

- Add diagnose-tables and create-missing-tables scripts

- Add test-failing-modules and test-teacher-pages test scripts
This commit is contained in:
SpecialX
2026-06-24 12:01:54 +08:00
parent e4254f0f8e
commit 9783be58c0
9 changed files with 1713 additions and 0 deletions

View File

@@ -0,0 +1,181 @@
/**
* 诊断脚本:检查错题本数据状态
* 用法npx tsx scripts/diagnose-error-book.ts
*/
import "dotenv/config"
import { db } from "../src/shared/db"
import {
errorBookItems,
errorBookReviews,
examSubmissions,
homeworkSubmissions,
users,
usersToRoles,
roles,
classes,
classEnrollments,
classSubjectTeachers,
grades,
exams,
homeworkAssignments,
} from "../src/shared/db/schema"
import { eq, inArray, count } from "drizzle-orm"
async function diagnose() {
console.log("🔍 错题本数据诊断开始...\n")
// 1. 检查表是否存在且有数据
const [itemsCount] = await db.select({ value: count() }).from(errorBookItems)
const [reviewsCount] = await db.select({ value: count() }).from(errorBookReviews)
console.log(`📊 错题本表数据:`)
console.log(` error_book_items: ${itemsCount.value}`)
console.log(` error_book_reviews: ${reviewsCount.value}`)
if (Number(itemsCount.value) === 0) {
console.log("\n❌ error_book_items 表为空!需要运行 seed-error-book.ts")
return
}
// 2. 检查错题的 subjectId 分布
const subjectDist = await db
.select({
subjectId: errorBookItems.subjectId,
count: count(),
})
.from(errorBookItems)
.groupBy(errorBookItems.subjectId)
console.log(`\n📊 错题 subjectId 分布:`)
for (const row of subjectDist) {
console.log(` subjectId=${row.subjectId ?? "NULL"}: ${row.count}`)
}
// 3. 检查错题的 sourceType 分布
const sourceDist = await db
.select({
sourceType: errorBookItems.sourceType,
count: count(),
})
.from(errorBookItems)
.groupBy(errorBookItems.sourceType)
console.log(`\n📊 错题 sourceType 分布:`)
for (const row of sourceDist) {
console.log(` ${row.sourceType}: ${row.count}`)
}
// 4. 检查有错题的学生
const studentsWithErrors = await db
.select({
studentId: errorBookItems.studentId,
itemCount: count(),
})
.from(errorBookItems)
.groupBy(errorBookItems.studentId)
console.log(`\n📊 有错题的学生 (${studentsWithErrors.length} 名):`)
for (const row of studentsWithErrors) {
console.log(` ${row.studentId}: ${row.itemCount}`)
}
// 5. 检查教师角色用户
const teacherRole = await db.select({ id: roles.id }).from(roles).where(eq(roles.name, "teacher")).limit(1)
if (teacherRole.length > 0) {
const teachers = await db
.select({ userId: usersToRoles.userId })
.from(usersToRoles)
.where(eq(usersToRoles.roleId, teacherRole[0].id))
console.log(`\n📊 教师用户 (${teachers.length} 名):`)
for (const t of teachers) {
const user = await db.select({ name: users.name }).from(users).where(eq(users.id, t.userId)).limit(1)
// 查询该教师能访问的班级
const homeroomClasses = await db.select({ id: classes.id, name: classes.name }).from(classes).where(eq(classes.teacherId, t.userId))
const subjectClasses = await db
.select({ classId: classSubjectTeachers.classId })
.from(classSubjectTeachers)
.where(eq(classSubjectTeachers.teacherId, t.userId))
const allClassIds = [...new Set([...homeroomClasses.map((c) => c.id), ...subjectClasses.map((c) => c.classId)])]
// 查询这些班级的学生
let studentCount = 0
let errorCount = 0
if (allClassIds.length > 0) {
const students = await db
.select({ studentId: classEnrollments.studentId })
.from(classEnrollments)
.where(inArray(classEnrollments.classId, allClassIds))
const studentIds = [...new Set(students.map((s) => s.studentId))]
studentCount = studentIds.length
if (studentIds.length > 0) {
const errorItems = await db
.select({ value: count() })
.from(errorBookItems)
.where(inArray(errorBookItems.studentId, studentIds))
errorCount = Number(errorItems[0]?.value ?? 0)
}
}
console.log(` ${user[0]?.name ?? t.userId} (${t.userId}): 班级=${allClassIds.length}, 学生=${studentCount}, 错题=${errorCount}`)
}
}
// 6. 检查年级组长
const gradeHeadRole = await db.select({ id: roles.id }).from(roles).where(eq(roles.name, "grade_head")).limit(1)
if (gradeHeadRole.length > 0) {
const gradeHeads = await db
.select({ userId: usersToRoles.userId })
.from(usersToRoles)
.where(eq(usersToRoles.roleId, gradeHeadRole[0].id))
console.log(`\n📊 年级组长 (${gradeHeads.length} 名):`)
for (const gh of gradeHeads) {
const user = await db.select({ name: users.name }).from(users).where(eq(users.id, gh.userId)).limit(1)
const managedGrades = await db.select({ id: grades.id, name: grades.name }).from(grades).where(eq(grades.gradeHeadId, gh.userId))
console.log(` ${user[0]?.name ?? gh.userId} (${gh.userId}): 管理年级=${managedGrades.length}`)
for (const g of managedGrades) {
const gradeClasses = await db.select({ id: classes.id }).from(classes).where(eq(classes.gradeId, g.id))
console.log(` 年级 ${g.name}: ${gradeClasses.length} 个班级`)
}
}
}
// 7. 检查已批改的提交数量
const gradedExams = await db.select({ value: count() }).from(examSubmissions).where(eq(examSubmissions.status, "graded"))
const gradedHw = await db.select({ value: count() }).from(homeworkSubmissions).where(eq(homeworkSubmissions.status, "graded"))
console.log(`\n📊 已批改提交:`)
console.log(` 考试提交: ${gradedExams[0]?.value ?? 0}`)
console.log(` 作业提交: ${gradedHw[0]?.value ?? 0}`)
// 8. 检查 exams 表的 subjectId 是否有值
const examSubjectStats = await db
.select({
subjectId: exams.subjectId,
count: count(),
})
.from(exams)
.groupBy(exams.subjectId)
console.log(`\n📊 exams 表 subjectId 分布:`)
for (const row of examSubjectStats) {
console.log(` subjectId=${row.subjectId ?? "NULL"}: ${row.count} 个考试`)
}
// 9. 检查 homeworkAssignments 是否有 subjectId 字段(应该没有)
console.log(`\n📊 homeworkAssignments 表结构检查:`)
try {
const hwRows = await db.select().from(homeworkAssignments).limit(1)
if (hwRows.length > 0) {
const keys = Object.keys(hwRows[0])
console.log(` 字段列表: ${keys.join(", ")}`)
console.log(` 是否有 subjectId 字段: ${keys.includes("subjectId") ? "是" : "否"}`)
} else {
console.log(` 表为空`)
}
} catch (e) {
console.log(` 查询失败: ${(e as Error).message}`)
}
console.log("\n✅ 诊断完成")
process.exit(0)
}
diagnose().catch((e) => {
console.error("❌ 诊断失败:", e)
process.exit(1)
})