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