refactor(classes): data-access 迁移至 cacheFn + 双导出 raw 版本
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import "server-only";
|
||||
|
||||
import { cache } from "react"
|
||||
import { cacheFn } from "@/shared/lib/cache"
|
||||
import { and, asc, eq, inArray, or, sql } from "drizzle-orm"
|
||||
import { createId } from "@paralleldrive/cuid2"
|
||||
|
||||
@@ -39,7 +39,7 @@ const isClassSubject = (v: unknown): v is ClassSubject =>
|
||||
const toClassSubject = (v: string): ClassSubject | null =>
|
||||
isClassSubject(v) ? v : null
|
||||
|
||||
export const getAdminClasses = cache(async (): Promise<AdminClassListItem[]> => {
|
||||
export const getAdminClassesRaw = async (): Promise<AdminClassListItem[]> => {
|
||||
const [rows, subjectRows] = await Promise.all([
|
||||
(async () => {
|
||||
try {
|
||||
@@ -183,9 +183,14 @@ export const getAdminClasses = cache(async (): Promise<AdminClassListItem[]> =>
|
||||
|
||||
list.sort(compareClassLike)
|
||||
return list
|
||||
}
|
||||
export const getAdminClasses = cacheFn(getAdminClassesRaw, {
|
||||
tags: ["classes", "classes:list"],
|
||||
ttl: 300,
|
||||
keyParts: ["classes", "admin", "list"],
|
||||
})
|
||||
|
||||
export const getGradeManagedClasses = cache(async (userId: string): Promise<AdminClassListItem[]> => {
|
||||
export const getGradeManagedClassesRaw = async (userId: string): Promise<AdminClassListItem[]> => {
|
||||
const managedGradeIds = await db
|
||||
.select({ id: grades.id })
|
||||
.from(grades)
|
||||
@@ -308,9 +313,14 @@ export const getGradeManagedClasses = cache(async (userId: string): Promise<Admi
|
||||
|
||||
list.sort(compareClassLike)
|
||||
return list
|
||||
}
|
||||
export const getGradeManagedClasses = cacheFn(getGradeManagedClassesRaw, {
|
||||
tags: ["classes"],
|
||||
ttl: 300,
|
||||
keyParts: ["classes", "admin", "getGradeManagedClasses"],
|
||||
})
|
||||
|
||||
export const getManagedGrades = cache(async (userId: string): Promise<{ id: string; name: string; schoolId: string; schoolName: string }[]> => {
|
||||
export const getManagedGradesRaw = async (userId: string): Promise<{ id: string; name: string; schoolId: string; schoolName: string }[]> => {
|
||||
return await db
|
||||
.select({
|
||||
id: grades.id,
|
||||
@@ -322,6 +332,11 @@ export const getManagedGrades = cache(async (userId: string): Promise<{ id: stri
|
||||
.innerJoin(schools, eq(schools.id, grades.schoolId))
|
||||
.where(or(eq(grades.gradeHeadId, userId), eq(grades.teachingHeadId, userId)))
|
||||
.orderBy(asc(schools.name), asc(grades.name))
|
||||
}
|
||||
export const getManagedGrades = cacheFn(getManagedGradesRaw, {
|
||||
tags: ["classes"],
|
||||
ttl: 300,
|
||||
keyParts: ["classes", "admin", "getManagedGrades"],
|
||||
})
|
||||
|
||||
export async function createAdminClass(data: CreateTeacherClassInput & { teacherId: string }): Promise<string> {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import "server-only";
|
||||
|
||||
import { cache } from "react"
|
||||
import { cacheFn } from "@/shared/lib/cache"
|
||||
import { and, asc, eq, inArray, type SQL } from "drizzle-orm"
|
||||
|
||||
import { db } from "@/shared/db"
|
||||
@@ -37,7 +37,7 @@ const isWeekday = (n: unknown): n is 1 | 2 | 3 | 4 | 5 | 6 | 7 =>
|
||||
const toWeekday = (n: number): 1 | 2 | 3 | 4 | 5 | 6 | 7 =>
|
||||
isWeekday(n) ? n : 1
|
||||
|
||||
export const getStudentSchedule = cache(async (studentId: string): Promise<StudentScheduleItem[]> => {
|
||||
export const getStudentScheduleRaw = async (studentId: string): Promise<StudentScheduleItem[]> => {
|
||||
const id = studentId.trim()
|
||||
if (!id) return []
|
||||
|
||||
@@ -68,10 +68,16 @@ export const getStudentSchedule = cache(async (studentId: string): Promise<Stude
|
||||
course: r.course,
|
||||
location: r.location,
|
||||
}))
|
||||
}
|
||||
export const getStudentSchedule = cacheFn(getStudentScheduleRaw, {
|
||||
tags: ["classes:schedule"],
|
||||
ttl: 600,
|
||||
keyParts: ["classes", "schedule", "getStudentSchedule"],
|
||||
})
|
||||
|
||||
export const getClassSchedule = cache(
|
||||
async (params?: { classId?: string; teacherId?: string }): Promise<ClassScheduleItem[]> => {
|
||||
export const getClassScheduleRaw = async (
|
||||
params?: { classId?: string; teacherId?: string }
|
||||
): Promise<ClassScheduleItem[]> => {
|
||||
const teacherId = params?.teacherId ?? (await getSessionTeacherId())
|
||||
if (!teacherId) return []
|
||||
|
||||
@@ -108,4 +114,8 @@ export const getClassSchedule = cache(
|
||||
location: r.location,
|
||||
}))
|
||||
}
|
||||
)
|
||||
export const getClassSchedule = cacheFn(getClassScheduleRaw, {
|
||||
tags: ["classes:schedule:{classId}"],
|
||||
ttl: 600,
|
||||
keyParts: ["classes", "schedule", "by-class"],
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import "server-only";
|
||||
|
||||
import { cache } from "react"
|
||||
import { cacheFn } from "@/shared/lib/cache"
|
||||
import { and, asc, count, eq, inArray } from "drizzle-orm"
|
||||
|
||||
import { db } from "@/shared/db"
|
||||
@@ -123,8 +123,9 @@ const computeAssignmentStats = (params: {
|
||||
return { stats, allScored }
|
||||
}
|
||||
|
||||
export const getClassHomeworkInsights = cache(
|
||||
async (params: { classId: string; teacherId?: string; limit?: number }): Promise<ClassHomeworkInsights | null> => {
|
||||
export const getClassHomeworkInsightsRaw = async (
|
||||
params: { classId: string; teacherId?: string; limit?: number }
|
||||
): Promise<ClassHomeworkInsights | null> => {
|
||||
const teacherId = params.teacherId ?? (await getSessionTeacherId())
|
||||
if (!teacherId) return null
|
||||
|
||||
@@ -274,7 +275,11 @@ export const getClassHomeworkInsights = cache(
|
||||
overallScores,
|
||||
}
|
||||
}
|
||||
)
|
||||
export const getClassHomeworkInsights = cacheFn(getClassHomeworkInsightsRaw, {
|
||||
tags: ["classes:stats:{classId}"],
|
||||
ttl: 60,
|
||||
keyParts: ["classes", "stats", "by-class"],
|
||||
})
|
||||
|
||||
const avg = (values: number[]): number | null => {
|
||||
if (values.length === 0) return null
|
||||
@@ -282,8 +287,9 @@ const avg = (values: number[]): number | null => {
|
||||
return sum / values.length
|
||||
}
|
||||
|
||||
export const getGradeHomeworkInsights = cache(
|
||||
async (params: { gradeId: string; limit?: number }): Promise<GradeHomeworkInsights | null> => {
|
||||
export const getGradeHomeworkInsightsRaw = async (
|
||||
params: { gradeId: string; limit?: number }
|
||||
): Promise<GradeHomeworkInsights | null> => {
|
||||
const gradeId = params.gradeId.trim()
|
||||
if (!gradeId) return null
|
||||
|
||||
@@ -501,13 +507,22 @@ export const getGradeHomeworkInsights = cache(
|
||||
classes: classSummaries,
|
||||
}
|
||||
}
|
||||
)
|
||||
export const getGradeHomeworkInsights = cacheFn(getGradeHomeworkInsightsRaw, {
|
||||
tags: ["classes:stats"],
|
||||
ttl: 60,
|
||||
keyParts: ["classes", "stats", "getGradeHomeworkInsights"],
|
||||
})
|
||||
|
||||
export type ClassesDashboardStats = {
|
||||
classCount: number
|
||||
}
|
||||
|
||||
export const getClassesDashboardStats = cache(async (): Promise<ClassesDashboardStats> => {
|
||||
export const getClassesDashboardStatsRaw = async (): Promise<ClassesDashboardStats> => {
|
||||
const [row] = await db.select({ value: count() }).from(classes)
|
||||
return { classCount: Number(row?.value ?? 0) }
|
||||
}
|
||||
export const getClassesDashboardStats = cacheFn(getClassesDashboardStatsRaw, {
|
||||
tags: ["classes:stats"],
|
||||
ttl: 60,
|
||||
keyParts: ["classes", "stats", "getClassesDashboardStats"],
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import "server-only";
|
||||
|
||||
import { cache } from "react"
|
||||
import { cacheFn } from "@/shared/lib/cache"
|
||||
import { and, asc, eq, inArray, sql, type SQL } from "drizzle-orm"
|
||||
|
||||
import { db } from "@/shared/db"
|
||||
@@ -26,8 +26,9 @@ import {
|
||||
getTeacherSubjectIdsForClass,
|
||||
} from "./data-access"
|
||||
|
||||
export const getStudentsSubjectScores = cache(
|
||||
async (studentIds: string[]): Promise<Map<string, Record<string, number | null>>> => {
|
||||
export const getStudentsSubjectScoresRaw = async (
|
||||
studentIds: string[]
|
||||
): Promise<Map<string, Record<string, number | null>>> => {
|
||||
if (studentIds.length === 0) return new Map()
|
||||
|
||||
// 1. Find assignments targeted at these students (via homework module data-access)
|
||||
@@ -81,10 +82,15 @@ export const getStudentsSubjectScores = cache(
|
||||
|
||||
return studentScores
|
||||
}
|
||||
)
|
||||
export const getStudentsSubjectScores = cacheFn(getStudentsSubjectScoresRaw, {
|
||||
tags: ["classes:students"],
|
||||
ttl: 60,
|
||||
keyParts: ["classes", "students", "getStudentsSubjectScores"],
|
||||
})
|
||||
|
||||
export const getClassStudentSubjectScoresV2 = cache(
|
||||
async (params: { classId: string; teacherId?: string }): Promise<Map<string, Record<string, number | null>>> => {
|
||||
export const getClassStudentSubjectScoresV2Raw = async (
|
||||
params: { classId: string; teacherId?: string }
|
||||
): Promise<Map<string, Record<string, number | null>>> => {
|
||||
const teacherId = params.teacherId ?? (await getSessionTeacherId())
|
||||
if (!teacherId) return new Map()
|
||||
const classId = params.classId.trim()
|
||||
@@ -132,9 +138,13 @@ export const getClassStudentSubjectScoresV2 = cache(
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
)
|
||||
export const getClassStudentSubjectScoresV2 = cacheFn(getClassStudentSubjectScoresV2Raw, {
|
||||
tags: ["classes:students:{classId}"],
|
||||
ttl: 60,
|
||||
keyParts: ["classes", "students", "by-class"],
|
||||
})
|
||||
|
||||
export const getStudentClasses = cache(async (studentId: string): Promise<StudentEnrolledClass[]> => {
|
||||
export const getStudentClassesRaw = async (studentId: string): Promise<StudentEnrolledClass[]> => {
|
||||
const id = studentId.trim()
|
||||
if (!id) return []
|
||||
|
||||
@@ -190,14 +200,21 @@ export const getStudentClasses = cache(async (studentId: string): Promise<Studen
|
||||
|
||||
list.sort(compareClassLike)
|
||||
return list
|
||||
}
|
||||
export const getStudentClasses = cacheFn(getStudentClassesRaw, {
|
||||
tags: ["classes:students"],
|
||||
ttl: 60,
|
||||
keyParts: ["classes", "students", "getStudentClasses"],
|
||||
})
|
||||
|
||||
/**
|
||||
* Get a single class details for a student (verifies enrollment).
|
||||
* Returns null if the student is not enrolled in the class.
|
||||
*/
|
||||
export const getStudentClassById = cache(
|
||||
async (studentId: string, classId: string): Promise<StudentEnrolledClass | null> => {
|
||||
export const getStudentClassByIdRaw = async (
|
||||
studentId: string,
|
||||
classId: string
|
||||
): Promise<StudentEnrolledClass | null> => {
|
||||
const sid = studentId.trim()
|
||||
const cid = classId.trim()
|
||||
if (!sid || !cid) return null
|
||||
@@ -232,10 +249,15 @@ export const getStudentClassById = cache(
|
||||
teacherEmail: r.teacherEmail,
|
||||
}
|
||||
}
|
||||
)
|
||||
export const getStudentClassById = cacheFn(getStudentClassByIdRaw, {
|
||||
tags: ["classes:students:{classId}"],
|
||||
ttl: 60,
|
||||
keyParts: ["classes", "students", "by-class"],
|
||||
})
|
||||
|
||||
export const getClassStudents = cache(
|
||||
async (params?: { classId?: string; q?: string; status?: string; teacherId?: string }): Promise<ClassStudent[]> => {
|
||||
export const getClassStudentsRaw = async (
|
||||
params?: { classId?: string; q?: string; status?: string; teacherId?: string }
|
||||
): Promise<ClassStudent[]> => {
|
||||
const teacherId = params?.teacherId ?? (await getSessionTeacherId())
|
||||
if (!teacherId) return []
|
||||
|
||||
@@ -293,7 +315,11 @@ export const getClassStudents = cache(
|
||||
joinedAt: r.joinedAt,
|
||||
}))
|
||||
}
|
||||
)
|
||||
export const getClassStudents = cacheFn(getClassStudentsRaw, {
|
||||
tags: ["classes:students:{classId}"],
|
||||
ttl: 60,
|
||||
keyParts: ["classes", "students", "by-class"],
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// DataScope resolver helpers (P1-5/P1-6 audit fix)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import "server-only";
|
||||
|
||||
import { cache } from "react"
|
||||
import { cacheFn } from "@/shared/lib/cache"
|
||||
import { and, asc, eq, inArray, isNull, sql } from "drizzle-orm"
|
||||
import { createId } from "@paralleldrive/cuid2"
|
||||
|
||||
@@ -43,7 +43,7 @@ const isClassSubject = (v: unknown): v is ClassSubject =>
|
||||
const toClassSubject = (v: string): ClassSubject | null =>
|
||||
isClassSubject(v) ? v : null
|
||||
|
||||
export const getTeacherClasses = cache(async (params?: { teacherId?: string }): Promise<TeacherClass[]> => {
|
||||
export const getTeacherClassesRaw = async (params?: { teacherId?: string }): Promise<TeacherClass[]> => {
|
||||
const teacherId = params?.teacherId ?? (await getSessionTeacherId())
|
||||
if (!teacherId) return []
|
||||
|
||||
@@ -116,9 +116,14 @@ export const getTeacherClasses = cache(async (params?: { teacherId?: string }):
|
||||
)
|
||||
|
||||
return listWithTrends
|
||||
}
|
||||
export const getTeacherClasses = cacheFn(getTeacherClassesRaw, {
|
||||
tags: ["classes", "classes:list"],
|
||||
ttl: 300,
|
||||
keyParts: ["classes", "teacher", "list"],
|
||||
})
|
||||
|
||||
export const getTeacherOptions = cache(async (): Promise<TeacherOption[]> => {
|
||||
export const getTeacherOptionsRaw = async (): Promise<TeacherOption[]> => {
|
||||
const rows = await db
|
||||
.select({ id: users.id, name: users.name, email: users.email })
|
||||
.from(users)
|
||||
@@ -132,9 +137,14 @@ export const getTeacherOptions = cache(async (): Promise<TeacherOption[]> => {
|
||||
name: r.name ?? "Unnamed",
|
||||
email: r.email,
|
||||
}))
|
||||
}
|
||||
export const getTeacherOptions = cacheFn(getTeacherOptionsRaw, {
|
||||
tags: ["classes"],
|
||||
ttl: 300,
|
||||
keyParts: ["classes", "teacher", "getTeacherOptions"],
|
||||
})
|
||||
|
||||
export const getTeacherTeachingSubjects = cache(async (): Promise<ClassSubject[]> => {
|
||||
export const getTeacherTeachingSubjectsRaw = async (): Promise<ClassSubject[]> => {
|
||||
const teacherId = await getSessionTeacherId()
|
||||
if (!teacherId) return []
|
||||
|
||||
@@ -149,6 +159,11 @@ export const getTeacherTeachingSubjects = cache(async (): Promise<ClassSubject[]
|
||||
return rows
|
||||
.map((r) => toClassSubject(r.subject))
|
||||
.filter((s): s is ClassSubject => s !== null)
|
||||
}
|
||||
export const getTeacherTeachingSubjects = cacheFn(getTeacherTeachingSubjectsRaw, {
|
||||
tags: ["classes"],
|
||||
ttl: 300,
|
||||
keyParts: ["classes", "teacher", "getTeacherTeachingSubjects"],
|
||||
})
|
||||
|
||||
export async function createTeacherClass(data: CreateTeacherClassInput): Promise<string> {
|
||||
|
||||
Reference in New Issue
Block a user