refactor: fix all P0/P1/P2 bugs and architecture issues
Bug fixes (from bugs/ directory): - Fix cross-module DB queries in 9 modules (homework, grades, parent, diagnostic, elective, proctoring, notifications, scheduling, classes) by routing through data-access functions - Fix shared/lib <-> auth circular dependency via new session.ts module - Fix divide-by-zero guard in grades data-access - Fix audit export data truncation (paginated fetch for full datasets) - Fix missing transactions in homework grading and elective lottery - Fix missing revalidatePath in course-plans actions - Fix frontend permission checks using requirePermission instead of requireAuth - Fix dashboard role routing using session.user.roles - Fix student auth pattern (migrate getDemoStudentUser to users module) - Fix ActionState return type handling in components Code quality fixes: - Remove 60+ as type assertions (replace with type guards) - Remove non-null assertions (use optional chaining or explicit checks) - Convert dynamic imports to static imports (grades, diagnostic) - Add React.cache() wrapping for read functions - Parallelize independent queries with Promise.all - Add explicit return types to 30+ arrow functions - Replace any with unknown + type guards - Fix import type for type-only imports - Add Zod validation schemas for classes and diagnostic modules - Extract duplicate code (normalizeRoleName, normalizeBcryptHash, logger IP extraction) - Add console.error to silent catch blocks - Fix permission naming consistency (exam:proctor_read -> exam:proctor:read) Architecture doc sync: - Update 004_architecture_impact_map.md and 005_architecture_data.json - Update management-modules-audit.md for P0-7 cross-module fix Moved deleted proctoring event route to deletes/ folder.
This commit is contained in:
@@ -13,6 +13,14 @@ import {
|
||||
publishDiagnosticReport,
|
||||
deleteDiagnosticReport,
|
||||
} from "./data-access-reports"
|
||||
import {
|
||||
GenerateStudentReportSchema,
|
||||
GenerateClassReportSchema,
|
||||
PublishReportSchema,
|
||||
DeleteReportSchema,
|
||||
GetDiagnosticReportsSchema,
|
||||
GetDiagnosticReportByIdSchema,
|
||||
} from "./schema"
|
||||
import type { DiagnosticReportQueryParams } from "./types"
|
||||
|
||||
/** 生成学生个人诊断报告 */
|
||||
@@ -23,15 +31,15 @@ export async function generateStudentReportAction(
|
||||
try {
|
||||
const ctx = await requirePermission(Permissions.DIAGNOSTIC_MANAGE)
|
||||
|
||||
const studentId = formData.get("studentId")
|
||||
const period = formData.get("period")
|
||||
if (typeof studentId !== "string" || studentId.length === 0) {
|
||||
return { success: false, message: "Missing studentId" }
|
||||
}
|
||||
if (typeof period !== "string" || period.length === 0) {
|
||||
return { success: false, message: "Missing period" }
|
||||
const parsed = GenerateStudentReportSchema.safeParse({
|
||||
studentId: formData.get("studentId"),
|
||||
period: formData.get("period"),
|
||||
})
|
||||
if (!parsed.success) {
|
||||
return { success: false, message: "Missing studentId or period" }
|
||||
}
|
||||
|
||||
const { studentId, period } = parsed.data
|
||||
const id = await generateDiagnosticReport(studentId, period, ctx.userId)
|
||||
revalidatePath("/teacher/diagnostic")
|
||||
revalidatePath(`/teacher/diagnostic/student/${studentId}`)
|
||||
@@ -51,15 +59,15 @@ export async function generateClassReportAction(
|
||||
try {
|
||||
const ctx = await requirePermission(Permissions.DIAGNOSTIC_MANAGE)
|
||||
|
||||
const classId = formData.get("classId")
|
||||
const period = formData.get("period")
|
||||
if (typeof classId !== "string" || classId.length === 0) {
|
||||
return { success: false, message: "Missing classId" }
|
||||
}
|
||||
if (typeof period !== "string" || period.length === 0) {
|
||||
return { success: false, message: "Missing period" }
|
||||
const parsed = GenerateClassReportSchema.safeParse({
|
||||
classId: formData.get("classId"),
|
||||
period: formData.get("period"),
|
||||
})
|
||||
if (!parsed.success) {
|
||||
return { success: false, message: "Missing classId or period" }
|
||||
}
|
||||
|
||||
const { classId, period } = parsed.data
|
||||
const id = await generateClassDiagnosticReport(classId, period, ctx.userId)
|
||||
revalidatePath("/teacher/diagnostic")
|
||||
revalidatePath(`/teacher/diagnostic/class/${classId}`)
|
||||
@@ -79,12 +87,14 @@ export async function publishReportAction(
|
||||
try {
|
||||
await requirePermission(Permissions.DIAGNOSTIC_MANAGE)
|
||||
|
||||
const id = formData.get("id")
|
||||
if (typeof id !== "string" || id.length === 0) {
|
||||
const parsed = PublishReportSchema.safeParse({
|
||||
id: formData.get("id"),
|
||||
})
|
||||
if (!parsed.success) {
|
||||
return { success: false, message: "Missing report id" }
|
||||
}
|
||||
|
||||
await publishDiagnosticReport(id)
|
||||
await publishDiagnosticReport(parsed.data.id)
|
||||
revalidatePath("/teacher/diagnostic")
|
||||
return { success: true, message: "Report published" }
|
||||
} catch (e) {
|
||||
@@ -102,12 +112,14 @@ export async function deleteReportAction(
|
||||
try {
|
||||
await requirePermission(Permissions.DIAGNOSTIC_MANAGE)
|
||||
|
||||
const id = formData.get("id")
|
||||
if (typeof id !== "string" || id.length === 0) {
|
||||
const parsed = DeleteReportSchema.safeParse({
|
||||
id: formData.get("id"),
|
||||
})
|
||||
if (!parsed.success) {
|
||||
return { success: false, message: "Missing report id" }
|
||||
}
|
||||
|
||||
await deleteDiagnosticReport(id)
|
||||
await deleteDiagnosticReport(parsed.data.id)
|
||||
revalidatePath("/teacher/diagnostic")
|
||||
return { success: true, message: "Report deleted" }
|
||||
} catch (e) {
|
||||
@@ -123,7 +135,13 @@ export async function getDiagnosticReportsAction(
|
||||
): Promise<ActionState<Awaited<ReturnType<typeof getDiagnosticReports>>>> {
|
||||
try {
|
||||
await requirePermission(Permissions.DIAGNOSTIC_READ)
|
||||
const reports = await getDiagnosticReports(params)
|
||||
|
||||
const parsed = GetDiagnosticReportsSchema.safeParse(params)
|
||||
if (!parsed.success) {
|
||||
return { success: false, message: "Invalid query params" }
|
||||
}
|
||||
|
||||
const reports = await getDiagnosticReports(parsed.data)
|
||||
return { success: true, data: reports }
|
||||
} catch (e) {
|
||||
if (e instanceof PermissionDeniedError) return { success: false, message: e.message }
|
||||
@@ -138,7 +156,13 @@ export async function getDiagnosticReportByIdAction(
|
||||
): Promise<ActionState<Awaited<ReturnType<typeof getDiagnosticReportById>>>> {
|
||||
try {
|
||||
await requirePermission(Permissions.DIAGNOSTIC_READ)
|
||||
const report = await getDiagnosticReportById(id)
|
||||
|
||||
const parsed = GetDiagnosticReportByIdSchema.safeParse({ id })
|
||||
if (!parsed.success) {
|
||||
return { success: false, message: "Missing report id" }
|
||||
}
|
||||
|
||||
const report = await getDiagnosticReportById(parsed.data.id)
|
||||
return { success: true, data: report }
|
||||
} catch (e) {
|
||||
if (e instanceof PermissionDeniedError) return { success: false, message: e.message }
|
||||
|
||||
Reference in New Issue
Block a user