Files
NextEdu/scripts/diagnose-error-book.ts
SpecialX 9783be58c0 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
2026-06-24 12:01:54 +08:00

182 lines
6.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 诊断脚本:检查错题本数据状态
* 用法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)
})