/** * 诊断脚本:检查错题本数据状态 * 用法: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) })