refactor(data-access): 组 A 模块迁移至 cacheFn(users/school/rbac/textbooks/questions/course-plans/standards/files/dashboard/parent/proctoring)

This commit is contained in:
SpecialX
2026-07-05 21:56:18 +08:00
parent 6dee6b6299
commit 27374d1b2c
13 changed files with 765 additions and 388 deletions

View File

@@ -1,6 +1,6 @@
import "server-only"
import { cache } from "react"
import { cacheFn } from "@/shared/lib/cache"
import { and, asc, eq, inArray } from "drizzle-orm"
import { db } from "@/shared/db"
@@ -39,7 +39,7 @@ const toWeekday = (d: Date): Weekday => {
return isWeekday(normalized) ? normalized : 1
}
export const getChildren = cache(async (parentId: string): Promise<ParentChildRelation[]> => {
export const getChildrenRaw = async (parentId: string): Promise<ParentChildRelation[]> => {
const id = parentId.trim()
if (!id) return []
@@ -62,14 +62,19 @@ export const getChildren = cache(async (parentId: string): Promise<ParentChildRe
relation: r.relation,
createdAt: r.createdAt.toISOString(),
}))
}
export const getChildren = cacheFn(getChildrenRaw, {
tags: ["parent"],
ttl: 300,
keyParts: ["parent", "getChildren"],
})
/**
* 校验家长与子女的关系是否存在,并返回关系类型。
* 同时按 parentId 与 studentId 过滤,防止跨家庭信息泄露。
*/
export const verifyParentChildRelation = cache(
async (studentId: string, parentId: string): Promise<string | null> => {
export const verifyParentChildRelationRaw = async (studentId: string, parentId: string): Promise<string | null> => {
const [row] = await db
.select({ relation: parentStudentRelations.relation })
.from(parentStudentRelations)
@@ -81,11 +86,15 @@ export const verifyParentChildRelation = cache(
)
.limit(1)
return row?.relation ?? null
},
)
}
export const getChildBasicInfo = cache(
async (
export const verifyParentChildRelation = cacheFn(verifyParentChildRelationRaw, {
tags: ["parent"],
ttl: 300,
keyParts: ["parent", "verifyParentChildRelation"],
})
export const getChildBasicInfoRaw = async (
studentId: string,
relation: string | null = null,
): Promise<ChildBasicInfo | null> => {
@@ -109,8 +118,13 @@ export const getChildBasicInfo = cache(
classId: activeClass?.classId ?? null,
relation,
}
},
)
}
export const getChildBasicInfo = cacheFn(getChildBasicInfoRaw, {
tags: ["parent"],
ttl: 300,
keyParts: ["parent", "getChildBasicInfo"],
})
const buildHomeworkSummary = (
assignments: Awaited<ReturnType<typeof getStudentHomeworkAssignments>>,
@@ -197,8 +211,7 @@ const buildWeeklySchedule = (
)
}
export const getChildDashboardData = cache(
async (studentId: string, relation: string | null = null): Promise<ChildDashboardData | null> => {
export const getChildDashboardDataRaw = async (studentId: string, relation: string | null = null): Promise<ChildDashboardData | null> => {
const basicInfo = await getChildBasicInfo(studentId, relation)
if (!basicInfo) return null
@@ -221,11 +234,15 @@ export const getChildDashboardData = cache(
gradeSummary,
examResults,
}
},
)
}
export const getParentDashboardData = cache(
async (parentId: string): Promise<ParentDashboardData> => {
export const getChildDashboardData = cacheFn(getChildDashboardDataRaw, {
tags: ["parent"],
ttl: 300,
keyParts: ["parent", "getChildDashboardData"],
})
export const getParentDashboardDataRaw = async (parentId: string): Promise<ParentDashboardData> => {
const id = parentId.trim()
if (!id) return { parentName: null, children: [] }
@@ -248,15 +265,19 @@ export const getParentDashboardData = cache(
parentName,
children: children.filter((c): c is ChildDashboardData => c !== null),
}
},
)
}
export const getParentDashboardData = cacheFn(getParentDashboardDataRaw, {
tags: ["parent"],
ttl: 300,
keyParts: ["parent", "getParentDashboardData"],
})
/**
* 获取家长所有子女的轻量列表id + name用于详情页头部多子女切换器。
* 一次批量查询,避免 N+1。
*/
export const getChildNameList = cache(
async (parentId: string): Promise<Array<{ id: string; name: string | null }>> => {
export const getChildNameListRaw = async (parentId: string): Promise<Array<{ id: string; name: string | null }>> => {
const relations = await getChildren(parentId)
if (relations.length === 0) return []
@@ -265,32 +286,40 @@ export const getChildNameList = cache(
id: r.studentId,
name: nameMap.get(r.studentId)?.name ?? null,
}))
},
)
}
export const getChildNameList = cacheFn(getChildNameListRaw, {
tags: ["parent"],
ttl: 300,
keyParts: ["parent", "getChildNameList"],
})
/**
* v4-P1-5: 批量查询多个学生的家长 userId 列表。
* 用于通知场景:报告/成绩发布时同步通知所有相关家长。
* 返回去重后的 parentId 数组。
*/
export const getParentIdsByStudentIds = cache(
async (studentIds: string[]): Promise<string[]> => {
export const getParentIdsByStudentIdsRaw = async (studentIds: string[]): Promise<string[]> => {
if (studentIds.length === 0) return []
const rows = await db
.select({ parentId: parentStudentRelations.parentId })
.from(parentStudentRelations)
.where(inArray(parentStudentRelations.studentId, studentIds))
return Array.from(new Set(rows.map((r) => r.parentId)))
},
)
}
export const getParentIdsByStudentIds = cacheFn(getParentIdsByStudentIdsRaw, {
tags: ["parent"],
ttl: 300,
keyParts: ["parent", "getParentIdsByStudentIds"],
})
/**
* L-2: 批量查询学生→家长映射(保留关联关系)。
* 用于考勤通知等需要按家长-学生分组发送的场景,避免 N+1 查询。
* 返回数组形如 [{ parentId, studentId }],同一家长多个学生会出现多条。
*/
export const getParentStudentMapByStudentIds = cache(
async (studentIds: string[]): Promise<Array<{ parentId: string; studentId: string }>> => {
export const getParentStudentMapByStudentIdsRaw = async (studentIds: string[]): Promise<Array<{ parentId: string; studentId: string }>> => {
if (studentIds.length === 0) return []
const rows = await db
.select({
@@ -300,5 +329,10 @@ export const getParentStudentMapByStudentIds = cache(
.from(parentStudentRelations)
.where(inArray(parentStudentRelations.studentId, studentIds))
return rows.map((r) => ({ parentId: r.parentId, studentId: r.studentId }))
},
)
}
export const getParentStudentMapByStudentIds = cacheFn(getParentStudentMapByStudentIdsRaw, {
tags: ["parent"],
ttl: 300,
keyParts: ["parent", "getParentStudentMapByStudentIds"],
})